466 lines
20 KiB
Markdown
466 lines
20 KiB
Markdown
# План реализации проекта "Домовой"
|
||
|
||
Документ представляет собой пошаговый план разработки.
|
||
Каждая итерация — самостоятельный кусок функционала, который
|
||
можно реализовать, протестировать и запустить независимо.
|
||
|
||
После каждой итерации запускается 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, фикс замечаний,
|
||
и только потом следующая итерация.
|
||
|
||
Никогда не давай больше одной итерации за раз.
|