20 KiB
План реализации проекта "Домовой"
Документ представляет собой пошаговый план разработки. Каждая итерация — самостоятельный кусок функционала, который можно реализовать, протестировать и запустить независимо.
После каждой итерации запускается 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-а хотя бы на/32loopback/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
Правила для каждой итерации
- Не переходить к следующей, пока check.sh не прошёл.
- Показывать реальный вывод smoke test, а не "готово".
- Маленькие атомарные коммиты.
- Контроллеры — только роутинг, бизнес-логика в Services.
- SQL только в Repository-классах.
- Shell-команды только через CommandWhitelist (массивы аргументов).
- Никакого произвольного shell executor из UI.
- Никакого "чистый JS потом допишем" — весь JS заявлен заранее.
Порядок выдачи задания owl-alpha
Рекомендуется выдавать по одной итерации:
Шаг 1: "Прочитай ТЗ и PLAN.md. Сделай Итерацию 1." Шаг 2: "Прочитай текущий код. Сделай Итерацию 2 по PLAN.md." ...
После каждой итерации — ревью, smoke test, фикс замечаний, и только потом следующая итерация.
Никогда не давай больше одной итерации за раз.