domovoy/PLAN.md

20 KiB
Raw Blame History

План реализации проекта "Домовой"

Документ представляет собой пошаговый план разработки. Каждая итерация — самостоятельный кусок функционала, который можно реализовать, протестировать и запустить независимо.

После каждой итерации запускается scripts/check.sh. Без успешного smoke test итерация не считается завершённой.


Итерация 1. Каркас приложения

Цель: приложение открывается, есть логин, база, миграции, layout.

Что делаем:

  • Slim 4 приложение с маршрутами
  • Docker Compose: app (PHP 8.3) + MariaDB
  • .env.example со всеми переменными
  • Phinx: конфигурация, миграция users
  • Регистрация первого пользователя (CLI setup)
  • Логин/Logout с сессиями
  • Bootstrap layout с левым меню и верхней панелью
  • Dashboard с заглушками счётчиков
  • scripts/check.sh с базовыми проверками

Файлы: composer.json phinx.php docker-compose.yml docker/Dockerfile .env.example public/index.php public/assets/css/app.css app/Controllers/AuthController.php app/Controllers/DashboardController.php app/Controllers/SetupController.php app/Middleware/AuthMiddleware.php app/Repositories/UserRepository.php app/Services/AuthService.php templates/layout.php templates/auth/login.php templates/dashboard/index.php migrations/20250526000001CreateUsers.php bin/console scripts/check.sh README.md

Проверка: docker compose up -d --build docker compose exec app php vendor/bin/phinx migrate docker compose exec app php bin/console setup:user curl -I http://localhost:8080/login

Открыть в браузере, залогиниться, увидеть dashboard


Итерация 2. Сканирование сети

Цель: добавить диапазон, запустить scan, увидеть найденные хосты.

Что делаем:

  • Миграции: network_ranges, scan_jobs, discovered_hosts
  • NetworkRanges CRUD (вкладка Discovery)
  • NetworkScanner: ping sweep + TCP connect + ARP + reverse DNS
  • CLI worker bin/run-scan-worker.php
  • Страница Discovery: таблица discovered_hosts
  • htmx для запуска scan и обновления статуса scan_jobs
  • Audit log для действий сканрования

Файлы (новые/изменённые): migrations/20250526000002CreateNetworkRanges.php migrations/20250526000003CreateScanJobs.php migrations/20250526000004CreateDiscoveredHosts.php migrations/20250526000005CreateAuditLog.php app/Controllers/DiscoveryController.php app/Controllers/NetworkRangeController.php app/Services/Discovery/NetworkScanner.php app/Services/Discovery/PingScanner.php app/Services/Discovery/TcpPortScanner.php app/Services/Discovery/ArpTableReader.php app/Services/Discovery/HostFingerprintService.php app/Services/Jobs/ScanJobRunner.php app/Services/Jobs/JobQueue.php app/Repositories/ScanJobRepository.php app/Repositories/DiscoveredHostRepository.php app/Repositories/NetworkRangeRepository.php app/Repositories/AuditLogRepository.php templates/discovery/index.php templates/discovery/ranges.php templates/discovery/hosts.php bin/run-scan-worker.php

Проверка:

Добавить диапазон 192.168.1.0/24

Запустить scan через UI

В другом терминале:

docker compose exec app php bin/run-scan-worker.php

Увидеть найденные хосты в таблице Discovery


Итерация 3. Инвентарь устройств

Цель: создавать карточки устройств руками и из найденных хостов.

Что делаем:

  • Миграция devices
  • Devices CRUD (список, создание, редактирование, карточка)
  • Создание device из discovered_host (кнопка "Создать устройство")
  • Игнорирование discovered_host (кнопка "Игнорировать")
  • Карточка устройства со всеми полями
  • Merge suggestions (по MAC → высокая, hostname → средняя, IP → низкая)
  • Обновление Dashboard: реальные счётчики устройств

Файлы (новые/изменённые): migrations/...CreateDevices.php app/Controllers/DeviceController.php app/Services/Inventory/DeviceService.php app/Services/Inventory/MergeSuggestionService.php app/Repositories/DeviceRepository.php templates/devices/index.php templates/devices/create.php templates/devices/edit.php templates/devices/show.php templates/dashboard/index.php (обновить счётчики)

Проверка:

Создать устройство вручную

Создать устройство из discovered_host

Открыть карточку, проверить все поля

Проверить merge suggestions


Итерация 4. SSH-доступы

Цель: добавить SSH-доступ к устройству, проверить подключение.


Корректирующая итерация 3.1. Стабилизация Discovery и Inventory

Цель: привести уже заявленные итерации 2-3 к рабочему состоянию перед переходом к SSH-доступам и deep scan. Эта итерация обязательна, потому что часть функциональности сейчас реализована как каркас, но не выполняет пользовательский сценарий полностью.

Что исправляем:

  • Worker должен реально выполнять network_discovery, а не только менять статус scan job на done.
  • scan_jobs.network_range_id должен использоваться worker-ом для выбора конкретного диапазона.
  • NetworkScanner должен корректно перечислять IPv4 CIDR:
    • /32 — один IP;
    • /31оба адреса;
    • /30 и меньше — только usable host addresses без network/broadcast;
    • диапазоны крупнее /24 для MVP запрещаются или явно ограничиваются, чтобы случайно не запустить огромный scan.
  • discovered_hosts.scan_job_id должен заполняться найденными хостами.
  • Повторное обнаружение того же IP/MAC не должно плодить неуправляемые дубликаты без обновления last_seen.
  • Добавление network range должно запрещать публичные IPv4-сети по умолчанию. Разрешены только private/local ranges из ТЗ: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local, loopback для локального smoke test.
  • Discovery UI должен показывать ошибку scan job, если worker не смог выполнить задачу.
  • Merge suggestions должны быть либо доведены до рабочего UI, либо явно исключены из статуса "готово" итерации 3.
  • Ссылки в меню на ещё не реализованные разделы (/services, /documents, /settings) не должны выглядеть как готовая функциональность.
  • scripts/check.sh должен проверять не только открытие /login, но и smoke path: миграции, создание пользователя, добавление range, создание scan job, запуск worker-а хотя бы на /32 loopback/private IP.

Файлы (ожидаемо новые/изменённые): app/Services/Jobs/ScanJobRunner.php app/Services/Discovery/NetworkScanner.php app/Controllers/NetworkRangeController.php app/Repositories/DiscoveredHostRepository.php app/Repositories/NetworkRangeRepository.php app/Repositories/AuditLogRepository.php templates/discovery/index.php templates/layout.php scripts/check.sh tests/

Проверка: vendor/bin/phpunit composer validate --no-check-publish find app public bin tests -name "*.php" -print0 | xargs -0 -n1 php -l ./scripts/check.sh

Критерий готовности:

Добавить private range, например 127.0.0.1/32 или 192.168.1.1/32

Запустить scan через UI

docker compose exec app php bin/run-scan-worker.php

scan_job получает done/failed с понятной причиной

при успешном обнаружении discovered_hosts содержит scan_job_id

публичный range вроде 8.8.8.8/32 не принимается без отдельного разрешения


Итерация 4. SSH-доступы к устройствам

Цель: хранить SSH-доступы к устройству, не показывать секреты в UI и уметь проверить подключение.

Что делаем:

  • Миграция credentials
  • Credentials CRUD (только SSH для MVP)
  • Шифрование секретов (defuse/php-encryption)
  • Тест подключения через phpseclib
  • Блок доступов на карточке устройства
  • CredentialVault для шифрования/дешифрования
  • Сохранение результата теста (last_test_status, last_test_at)

Файлы (новые/изменённые): migrations/...CreateCredentials.php app/Controllers/CredentialController.php app/Services/Security/CredentialVault.php app/Services/Ssh/SshCredentialTester.php app/Repositories/CredentialRepository.php templates/devices/show.php (добавить блок доступов) .env.example (добавить ENCRYPTION_KEY) tests/Security/CredentialVaultTest.php tests/Repositories/CredentialRepositoryTest.php tests/Services/SshCredentialTesterTest.php

Проверка: docker compose exec app php vendor/bin/phinx migrate

Добавить SSH-доступ к устройству

Нажать "Тест" — увидеть результат подключения

Секрет не отображается в UI открытым текстом

В базе хранится зашифрованное значение


Итерация 5. Deep scan Linux-хоста

Цель: по SSH собрать read-only информацию с Linux-хоста.

Что делаем:

  • Миграции: host_scans
  • LinuxHostScanner: hostname, os, ip, ports, disk, systemd, cron
  • CommandWhitelist с массивами аргументов
  • SshClientFactory с таймаутами (connect=5s, auth=10s, cmd=8s)
  • Сохранение raw JSON в storage/scans/
  • Summary в host_scans.summary_json
  • Кнопка "Глубокий скан" на карточке устройства
  • Страница результата скана
  • Worker поддерживает тип host_deep_scan

Файлы (новые/изменённые): migrations/...CreateHostScans.php app/Controllers/HostScanController.php app/Services/HostScan/LinuxHostScanner.php app/Services/HostScan/CommandWhitelist.php app/Repositories/HostScanRepository.php templates/host_scans/show.php templates/devices/show.php (кнопка deep scan) bin/run-scan-worker.php (поддержка host_deep_scan) config/scan_commands.php (CommandWhitelist mapping) .env.example (добавить SSH_* таймауты)

Проверка:

Добавить SSH-доступ к устройству

Нажать "Глубокий скан"

В другом терминале:

docker compose exec app php bin/run-scan-worker.php

Увидеть результат: hostname, os, порты, диски, systemd, cron

raw JSON сохранён в storage/scans/


Итерация 6. Docker scan + detected_services

Цель: найти контейнеры и создать предложения сервисов.

Что делаем:

  • Миграция detected_services
  • DockerScanner: docker ps, inspect, volumes, networks
  • SecretMasker для env-переменных (PASSWORD, TOKEN, KEY...)
  • Создание detected_services kind=docker_container
  • Страница "Найденные сервисы" (detected services)
  • Действия: Создать сервис, Объединить, Игнорировать, Детали
  • Host scan обогащается docker-сканом

Файлы (новые/изменённые): migrations/...CreateDetectedServices.php app/Controllers/DetectedServiceController.php app/Services/HostScan/DockerScanner.php app/Services/Security/SecretMasker.php app/Repositories/DetectedServiceRepository.php templates/services/detected.php templates/services/_detected_row.php app/Services/HostScan/LinuxHostScanner.php (добавить docker)

Проверка:

Запустить deep scan на хосте с Docker

Увидеть detected_services с kind=docker_container

Секретные env-переменные замаскированы

Страница найденных сервисов показывает таблицу


Итерация 7. Создание сервисов + связи

Цель: превратить найденный сервис в карточку с отношениями.

Что делаем:

  • Миграции: services, service_endpoints, relations
  • Service CRUD
  • Создание service из detected_service (с выбором типа)
  • Автоматическое создание service_endpoints (port, url)
  • Создание связей (Device runs_on Service, Service exposes Endpoint)
  • Карточка сервиса
  • Обновление detected_service.status = accepted

Файлы (новые/изменённые): migrations/...CreateServices.php migrations/...CreateServiceEndpoints.php migrations/...CreateRelations.php app/Controllers/ServiceController.php app/Services/Inventory/ServiceInventoryService.php app/Services/Inventory/RelationService.php app/Repositories/ServiceRepository.php app/Repositories/ServiceEndpointRepository.php app/Repositories/RelationRepository.php templates/services/index.php templates/services/create.php templates/services/show.php templates/services/detected.php (кнопка "Создать сервис")

Проверка:

Нажать "Создать сервис" на detected_service

Выбрать тип (web_app, database...)

Сервис создан, endpoint-ы созданы, связи созданы

detected_service.status = accepted

Карточка сервиса показывает связи


Итерация 8. Nginx/Cron/Backup сканеры

Цель: находить nginx vhost-ы, cron-задачи, backup-подсказки.

Что делаем:

  • NginxScanner: парсинг server_name, listen, proxy_pass, root
  • CronScanner: crontab -l, /etc/crontab, /etc/cron.d/*
  • BackupHintScanner: поиск rsync, borg, restic, tar, mysqldump...
  • detected_services kind=nginx_vhost, cron_job, backup_hint
  • Миграция domains
  • Создание domain suggestions из nginx server_name
  • Host scan обогащается nginx/cron/backup сканами

Файлы (новые/изменённые): migrations/...CreateDomains.php app/Services/HostScan/NginxScanner.php app/Services/HostScan/CronScanner.php app/Services/HostScan/BackupHintScanner.php app/Repositories/DomainRepository.php templates/host_scans/show.php (добавить nginx/cron/backup) templates/devices/show.php (найденные сервисы)

Проверка:

Deep scan на хосте с nginx + cron + backup scripts

Увидеть detected_services: nginx_vhost, cron_job, backup_hint

Формулировка backup_hint: "Похоже на backup job"

Domains созданы из nginx server_name

Приватные ключи сертификатов НЕ прочитаны


Итерация 9. Риски + Dashboard + История

Цель: предупреждения на dashboard, история сканов, diff.

Что делаем:

  • RiskAnalyzer: сервис без backup, публичный endpoint, SSH открыт, нет свежего scan, TLS истекает
  • Dashboard: реальные предупреждения
  • История scan jobs
  • DiffAnalyzer: новые/пропавшие хосты, порты, контейнеры между сканами
  • DiffAnalyzer: новые/пропавшие хосты, порты, контейнеры между сканами
  • Documents CRUD (заметки/runbooks)

Файлы (новые/изменённые): migrations/...CreateDocuments.php app/Controllers/DocumentController.php app/Controllers/ScanHistoryController.php app/Controllers/DiffController.php app/Services/Analysis/RiskAnalyzer.php app/Services/Analysis/DiffAnalyzer.php app/Repositories/DocumentRepository.php templates/dashboard/index.php (real warnings) templates/scans/history.php templates/scans/diff.php templates/documents/index.php templates/documents/create.php

Проверка:

Dashboard показывает предупреждения ( service без backup и т.д. )

История scan jobs с статусами

Diff между двумя сканами: новые/пропавшие хосты

Документы: создать runbook, привязать к устройству


Итерация 10. Полировка + документация

Цель: финальная доводка проекта.

Что делаем:

  • Полный audit log для всех действий
  • Документация в README полностью
  • Скриншоты в README (опционально)
  • Проверка всех security-правил
  • scripts/check.sh обновлён под все итерации
  • Финальный smoke test: весь цикл scan → device → service

Сводная таблица

Итерация | Что делаем | Зависимости 1 | Каркас: Slim, Docker, auth, layout | — 2 | Network scan | 1 3 | Devices CRUD | 1 4 | SSH credentials | 1, 3 5 | Deep scan Linux | 1, 3, 4 6 | Docker scan + detected_services | 1, 5 7 | Services + relations | 1, 6 8 | Nginx/Cron/Backup scanners | 1, 5, 7 9 | Risks + Dashboard + History + Docs | 1-8 10 | Полировка + README | 1-9


Правила для каждой итерации

  1. Не переходить к следующей, пока check.sh не прошёл.
  2. Показывать реальный вывод smoke test, а не "готово".
  3. Маленькие атомарные коммиты.
  4. Контроллеры — только роутинг, бизнес-логика в Services.
  5. SQL только в Repository-классах.
  6. Shell-команды только через CommandWhitelist (массивы аргументов).
  7. Никакого произвольного shell executor из UI.
  8. Никакого "чистый JS потом допишем" — весь JS заявлен заранее.

Порядок выдачи задания owl-alpha

Рекомендуется выдавать по одной итерации:

Шаг 1: "Прочитай ТЗ и PLAN.md. Сделай Итерацию 1." Шаг 2: "Прочитай текущий код. Сделай Итерацию 2 по PLAN.md." ...

После каждой итерации — ревью, smoke test, фикс замечаний, и только потом следующая итерация.

Никогда не давай больше одной итерации за раз.