1221 lines
25 KiB
Markdown
1221 lines
25 KiB
Markdown
|
||
**Self-hosted система инвентаризации домашней/малой инфраструктуры с автосканированием сети, глубоким сканированием хостов и созданием карточек устройств/сервисов из найденного.**
|
||
|
||
Ключевая цепочка:
|
||
|
||
```
|
||
скан сети→ найденные хосты→ подтверждение человеком→ карточки устройств→ добавление доступов→ глубокий скан хоста→ найденные сервисы/контейнеры/домены/бэкапы→ подтверждение человеком→ живая карта инфраструктуры
|
||
```
|
||
|
||
И важная поправка под Hermes/owl-alpha: **не давать кодеру “построй весь Домовой” одним заходом**. Лучше давать стадии по 1–2 экрана/модуля за раз, иначе он может нагенерировать архитектурной каши.
|
||
|
||
---
|
||
|
||
# 1. Базовый стек
|
||
|
||
Я бы оставил стек максимально простой и твой:
|
||
|
||
```
|
||
Backend:
|
||
PHP 8.3+
|
||
Framework: Slim Framework 4
|
||
DB: MariaDB / MySQL
|
||
Frontend: Bootstrap 5.3 + htmx + Alpine.js при необходимости
|
||
Templates: PHP templates или Twig
|
||
Auth: локальная авторизация
|
||
Deploy: Docker Compose
|
||
Scanning: PHP services + системные read-only команды
|
||
SSH: phpseclib
|
||
Encryption: defuse/php-encryption или libsodium
|
||
```
|
||
|
||
Slim 4 ставится через Composer как `slim/slim:"4.*"`, а Bootstrap сейчас живёт в ветке 5.3, у которой официальная документация ведёт current major release v5.x и последнюю ветку 5.3.x. Для SSH из PHP нормально подходит phpseclib 3.x, он ставится через Composer как `phpseclib/phpseclib:~3.0`. Для шифрования секретов можно взять `defuse/php-encryption`, это PHP-библиотека для шифрования данных ключом/паролем.
|
||
|
||
---
|
||
|
||
# 2. Composer-зависимости
|
||
|
||
Минимальный набор:
|
||
|
||
```
|
||
composer require slim/slim:"4.*"
|
||
composer require slim/psr7
|
||
composer require php-di/php-di
|
||
composer require monolog/monolog
|
||
composer require vlucas/phpdotenv
|
||
composer require ramsey/uuid
|
||
composer require symfony/process
|
||
composer require phpseclib/phpseclib:"~3.0"
|
||
composer require defuse/php-encryption
|
||
```
|
||
|
||
Миграции и тесты:
|
||
|
||
```
|
||
composer require robmorgan/phinx
|
||
composer require --dev phpunit/phpunit
|
||
```
|
||
|
||
Опционально:
|
||
|
||
```
|
||
composer require twig/twig
|
||
composer require nesbot/carbon
|
||
```
|
||
|
||
Я бы **не тащил ORM** на первом этапе. Обычный PDO + Repository-классы. Так проще, прозрачнее и меньше магии.
|
||
|
||
---
|
||
|
||
# 3. Системные зависимости на хосте
|
||
|
||
Для контейнера/хоста, где крутится Домовой:
|
||
|
||
```
|
||
php-cli
|
||
php-fpm
|
||
php-mysql
|
||
php-curl
|
||
php-mbstring
|
||
php-xml
|
||
php-zip
|
||
mariadb-client
|
||
iproute2
|
||
iputils-ping
|
||
arp-sca
|
||
nnmap
|
||
net-tools
|
||
dnsutils
|
||
avahi-utils
|
||
smbclient / nbtscan опционально
|
||
snmp опционально
|
||
openssh-client
|
||
openssl
|
||
docker-cli опционально
|
||
```
|
||
|
||
Но важно: **nmap и arp-scan — необязательные backends**, а не обязательная основа. Первый MVP может жить на:
|
||
|
||
```
|
||
ping sweep
|
||
ARP table
|
||
TCP connect scan
|
||
reverse DNS
|
||
```
|
||
|
||
А потом уже добавить nmap/arp-scan как улучшение.
|
||
|
||
---
|
||
|
||
# 4. Границы безопасности
|
||
|
||
Это надо заложить сразу, иначе проект может превратиться в мутный сетевой сканер.
|
||
|
||
## Жёсткие правила MVP
|
||
|
||
```
|
||
1. Сканируются только явно добавленные пользователем диапазоны.
|
||
2. По умолчанию разрешены только private/local ranges:
|
||
- 10.0.0.0/8
|
||
- 172.16.0.0/12
|
||
- 192.168.0.0/16
|
||
- fd00::/8
|
||
- link-local
|
||
3. Никаких brute force.
|
||
4. Никаких exploit/probe.
|
||
5. Никаких попыток подобрать пароли.
|
||
6. Deep scan только после явного добавления доступа.
|
||
7. Все команды deep scan — read-only.
|
||
8. Секреты в UI никогда не показываются открытым текстом.
|
||
9. Любой найденный объект сначала попадает в "Найденное", а не сразу в основной инвентарь.
|
||
10. Всё, что делает сканер, логируется.
|
||
```
|
||
|
||
Идеология:
|
||
|
||
```
|
||
Домовой не чинит и не ломает.
|
||
Домовой смотрит, запоминает и предлагает.
|
||
```
|
||
|
||
## Архитектура выполнения сканирования
|
||
|
||
HTTP-запрос НЕ должен выполнять сканирование напрямую.
|
||
Сканирование сети и deep scan выполняются отдельным CLI worker-ом:
|
||
|
||
1. Web UI создаёт scan_job со статусом "pending".
|
||
2. CLI worker (bin/run-scan-worker.php) забирает pending-задачи
|
||
из базы и выполняет их.
|
||
3. Web UI показывает статус задачи через htmx polling.
|
||
|
||
Worker должен:
|
||
- обрабатывать SIGTERM для graceful shutdown;
|
||
- запускать только одну задачу за раз (для MVP);
|
||
- обновлять статус scan_jobs в процессе;
|
||
- логировать прогресс.
|
||
|
||
Аргументы запуска worker-а:
|
||
```
|
||
php bin/run-scan-worker.php # забрать одну pending-задачу и выполнить
|
||
php bin/run-scan-worker.php --loop # бесконечный цикл обработки
|
||
```
|
||
|
||
Стратегии сканирования сети (по приоритету):
|
||
|
||
Приоритет 1 — если доступны arp-scan или nmap:
|
||
- arp-scan для быстрого обнаружения MAC + IP
|
||
- nmap для диапазонного скана с портами
|
||
|
||
Приоритит 2 — системные команды, работающие без дополнительных PAK-тов:
|
||
- ip neigh / arp table для ARP-записей
|
||
- ping sweep с жёстким timeout (200ms)
|
||
- TCP connect scan с timeout=200ms, concurrency=50
|
||
|
||
Приоритет 3 — fallback:
|
||
- только ping sweep, если ничего другого не доступно
|
||
|
||
Порядок имеет значение — быстрые методы сначала, медленные как fallback.
|
||
Для /24 сети с 18 портами TCP-скан может занять минуты.
|
||
Поэтому CLI worker обязателен, а PHP-скан внутри HTTP-запроса запрещён.
|
||
|
||
---
|
||
|
||
# 5. Архитектура проекта
|
||
|
||
```
|
||
domovoy/
|
||
app/
|
||
Controllers/
|
||
AuthController.php
|
||
DashboardController.php
|
||
DiscoveryController.php
|
||
DeviceController.php
|
||
CredentialController.php
|
||
HostScanController.php
|
||
ServiceController.php
|
||
DocumentController.php
|
||
Services/
|
||
Discovery/
|
||
NetworkScanner.php
|
||
PingScanner.php
|
||
TcpPortScanner.php
|
||
ArpTableReader.php
|
||
MdnsScanner.php
|
||
HostFingerprintService.php
|
||
HostScan/
|
||
SshClientFactory.php
|
||
LinuxHostScanner.php
|
||
DockerScanner.php
|
||
NginxScanner.php
|
||
CronScanner.php
|
||
SystemdScanner.php
|
||
BackupHintScanner.php
|
||
Inventory/
|
||
DeviceService.php
|
||
ServiceInventoryService.php
|
||
MergeSuggestionService.php
|
||
RelationService.php
|
||
Security/
|
||
CredentialVault.php
|
||
CommandWhitelist.php
|
||
SecretMasker.php
|
||
Jobs/
|
||
ScanJobRunner.php
|
||
JobQueue.php
|
||
Analysis/
|
||
RiskAnalyzer.php
|
||
DiffAnalyzer.php
|
||
Repositories/
|
||
UserRepository.php
|
||
ScanJobRepository.php
|
||
DiscoveredHostRepository.php
|
||
DeviceRepository.php
|
||
CredentialRepository.php
|
||
HostScanRepository.php
|
||
ServiceRepository.php
|
||
RelationRepository.php
|
||
AuditLogRepository.php
|
||
Middleware/
|
||
AuthMiddleware.php
|
||
CsrfMiddleware.php
|
||
public/
|
||
index.php
|
||
assets/
|
||
css/
|
||
js/
|
||
templates/
|
||
layout.php
|
||
auth/
|
||
dashboard/
|
||
discovery/
|
||
devices/
|
||
credentials/
|
||
services/
|
||
documents/
|
||
migrations/
|
||
storage/
|
||
logs/
|
||
scans/
|
||
reports/
|
||
bin/
|
||
console
|
||
run-scan-worker.php
|
||
docker/
|
||
docker-compose.yml
|
||
composer.json
|
||
.env.example
|
||
README.md
|
||
```
|
||
|
||
---
|
||
|
||
# 6. Главные сущности БД
|
||
|
||
## `users`
|
||
|
||
```
|
||
id
|
||
username
|
||
password_hash
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
## `network_ranges`
|
||
|
||
```
|
||
id
|
||
name
|
||
cidr
|
||
enabled
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
Пример:
|
||
|
||
```
|
||
Home LAN — 192.168.1.0/24
|
||
WireGuard LAN — 10.12.1.0/24
|
||
```
|
||
|
||
## `scan_jobs`
|
||
|
||
```
|
||
id
|
||
type
|
||
status
|
||
started_at
|
||
finished_at
|
||
error_message
|
||
created_by
|
||
created_at
|
||
```
|
||
|
||
Типы:
|
||
|
||
```
|
||
network_discovery
|
||
host_deep_scan
|
||
docker_scan
|
||
nginx_scan
|
||
cron_scan
|
||
```
|
||
|
||
Статусы:
|
||
|
||
```
|
||
pending
|
||
running
|
||
done
|
||
failed
|
||
cancelled
|
||
```
|
||
|
||
## `discovered_hosts`
|
||
|
||
Это всё, что нашёл сканер, но пользователь ещё не подтвердил.
|
||
|
||
```
|
||
id
|
||
scan_job_id
|
||
ip_address
|
||
mac_address
|
||
hostname
|
||
vendor
|
||
detected_os
|
||
open_ports_json
|
||
protocols_json
|
||
fingerprint_json
|
||
confidence
|
||
status
|
||
matched_device_id
|
||
first_seen
|
||
last_seen
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
Статусы:
|
||
|
||
```
|
||
new
|
||
accepted
|
||
merged
|
||
ignored
|
||
ignored_always
|
||
```
|
||
|
||
## `devices`
|
||
|
||
Это уже подтверждённые железки/виртуалки.
|
||
|
||
```
|
||
id
|
||
name
|
||
type
|
||
description
|
||
primary_ip
|
||
mac_address
|
||
hostname
|
||
vendor
|
||
os_name
|
||
os_version
|
||
location
|
||
importance
|
||
status
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
Типы:
|
||
|
||
```
|
||
server
|
||
router
|
||
nas
|
||
desktop
|
||
laptop
|
||
phone
|
||
printer
|
||
iot
|
||
vm
|
||
container_host
|
||
unknown
|
||
```
|
||
|
||
## `credentials`
|
||
|
||
Доступы к устройствам.
|
||
|
||
```
|
||
id
|
||
device_id
|
||
type
|
||
name
|
||
username
|
||
port
|
||
auth_method
|
||
encrypted_secret
|
||
encrypted_private_key
|
||
public_key_fingerprint
|
||
last_test_status
|
||
last_test_at
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
Типы:
|
||
|
||
```
|
||
ssh
|
||
snmp
|
||
http_api
|
||
routeros
|
||
manual
|
||
```
|
||
|
||
Для MVP реально нужен только `ssh`.
|
||
|
||
## `host_scans`
|
||
|
||
Результаты глубокого скана устройства.
|
||
|
||
```
|
||
id
|
||
device_id
|
||
credential_id
|
||
scan_job_id
|
||
status
|
||
raw_json_path
|
||
summary_json
|
||
started_at
|
||
finished_at
|
||
error_message
|
||
created_at
|
||
```
|
||
|
||
## `detected_services`
|
||
|
||
То, что найдено на хосте, но ещё не подтверждено как сервис.
|
||
|
||
```
|
||
id
|
||
host_scan_id
|
||
device_id
|
||
kind
|
||
name
|
||
source
|
||
port
|
||
protocol
|
||
raw_json
|
||
suggested_service_id
|
||
status
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
`kind`:
|
||
|
||
```
|
||
open_port
|
||
systemd_unit
|
||
docker_container
|
||
nginx_vhost
|
||
cron_job
|
||
backup_hint
|
||
database_hint
|
||
```
|
||
|
||
## `services`
|
||
|
||
Подтверждённые сервисы.
|
||
|
||
```
|
||
id
|
||
name
|
||
type
|
||
description
|
||
device_id
|
||
status
|
||
importance
|
||
url
|
||
main_port
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
Типы:
|
||
|
||
```
|
||
web_app
|
||
database
|
||
reverse_proxy
|
||
backup
|
||
monitoring
|
||
storage
|
||
git
|
||
media
|
||
system
|
||
unknown
|
||
```
|
||
|
||
## `service_endpoints`
|
||
|
||
```
|
||
id
|
||
service_id
|
||
protocol
|
||
host
|
||
port
|
||
url
|
||
is_public
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
## `domains`
|
||
|
||
```
|
||
id
|
||
domain
|
||
target_service_id
|
||
target_device_id
|
||
source
|
||
notes
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
## `relations`
|
||
|
||
Универсальный граф связей.
|
||
|
||
```
|
||
id
|
||
from_type
|
||
from_id
|
||
to_type
|
||
to_id
|
||
relation_type
|
||
created_at
|
||
```
|
||
|
||
Типы связей:
|
||
|
||
```
|
||
runs_on
|
||
depends_on
|
||
proxied_by
|
||
uses_database
|
||
uses_volume
|
||
backed_up_by
|
||
exposes
|
||
resolves_to
|
||
```
|
||
|
||
## `documents`
|
||
|
||
```
|
||
id
|
||
entity_type
|
||
entity_id
|
||
title
|
||
body_markdown
|
||
created_at
|
||
updated_at
|
||
```
|
||
|
||
Для заметок/runbook-ов.
|
||
|
||
## `audit_log`
|
||
|
||
```
|
||
id
|
||
user_id
|
||
action
|
||
entity_type
|
||
entity_id
|
||
details_json
|
||
created_at
|
||
```
|
||
|
||
---
|
||
|
||
# 7. Логика сканирования сети
|
||
|
||
## Что делает network discovery
|
||
|
||
На входе:
|
||
|
||
```
|
||
CIDR: 192.168.1.0/24
|
||
```
|
||
|
||
На выходе:
|
||
|
||
```
|
||
список DiscoveredHost
|
||
```
|
||
|
||
Методы первого MVP:
|
||
|
||
```
|
||
1. Ping sweep
|
||
2. TCP connect scan по базовым портам
|
||
3. чтение локальной ARP-таблицы
|
||
4. reverse DNS lookup
|
||
5. определение vendor по MAC OUI, если MAC известен
|
||
```
|
||
|
||
Базовые порты:
|
||
|
||
```
|
||
22 SSH
|
||
23 Telnet
|
||
53 DNS
|
||
80 HTTP
|
||
443 HTTPS
|
||
445 SMB
|
||
548 AFP
|
||
631 CUPS
|
||
3306 MySQL
|
||
5432 PostgreSQL
|
||
6379 Redis
|
||
8000 HTTP-alt
|
||
8080 HTTP-alt
|
||
8443 HTTPS-alt
|
||
9000 Portainer/various
|
||
9090 Prometheus/various
|
||
9100 Printer/Node exporter
|
||
```
|
||
|
||
Позже добавить:
|
||
|
||
```
|
||
mDNS
|
||
NetBIOS
|
||
SNMP
|
||
nmap backend
|
||
router DHCP leases importer
|
||
```
|
||
|
||
---
|
||
|
||
# 8. Логика deep scan по SSH
|
||
|
||
После того как пользователь создал устройство и добавил SSH-доступ, можно нажать:
|
||
|
||
```
|
||
[Тест подключения]
|
||
[Глубокий скан]
|
||
```
|
||
|
||
## Команды Linux scan
|
||
|
||
Только read-only:
|
||
|
||
```
|
||
hostname
|
||
hostnamectl
|
||
uname -a
|
||
cat /etc/os-release
|
||
ip -j addr
|
||
ip route
|
||
ss -tulpen
|
||
df -h
|
||
lsblk -J
|
||
mount
|
||
systemctl list-units --type=service --all --no-pager
|
||
systemctl list-timers --all --no-pager
|
||
crontab -l
|
||
cat /etc/crontab
|
||
ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly
|
||
docker ps --format '{{json .}}'
|
||
docker network ls --format '{{json .}}'
|
||
docker volume ls --format '{{json .}}'
|
||
docker compose ls --format json
|
||
```
|
||
|
||
Команды должны идти через **whitelist**, а не через произвольный shell.
|
||
|
||
То есть не так:
|
||
|
||
```
|
||
$ssh->exec($userCommand);
|
||
```
|
||
|
||
А так:
|
||
|
||
```
|
||
$scanner->runAllowedCommand('linux.hostnamectl');
|
||
```
|
||
|
||
И уже внутри:
|
||
|
||
```
|
||
'linux.hostnamectl' => ['hostnamectl']
|
||
```
|
||
|
||
Формат команд в CommandWhitelist — массивы аргументов, НЕ строки.
|
||
Это важно: docker-команды содержат {{json .}}, что ломает и Symfony Process,
|
||
и Twig-шаблонизатор. Массив аргументов не требует экранирования.
|
||
|
||
ЗАПРЕЩЕНО:
|
||
```
|
||
'linux.docker_ps' => "docker ps --format '{{json .}}'"
|
||
```
|
||
|
||
ПРАВИЛЬНО:
|
||
```
|
||
'linux.docker_ps' => ['docker', 'ps', '--format', '{{json .}}']
|
||
```
|
||
|
||
## SSH таймауты
|
||
|
||
Все SSH-операции должны иметь жёсткие таймауты.
|
||
Конфигурируются через .env:
|
||
|
||
```
|
||
SSH_CONNECT_TIMEOUT_SECONDS=5
|
||
SSH_AUTH_TIMEOUT_SECONDS=10
|
||
SSH_COMMAND_TIMEOUT_SECONDS=8
|
||
SSH_TOTAL_SCAN_TIMEOUT_SECONDS=60
|
||
SSH_RETRY_COUNT=0
|
||
```
|
||
|
||
Для MVP retry отключён — лучше быстро и честно упасть, чем зависнуть.
|
||
Каждый collector внутри deep scan обрабатывает timeout отдельно —
|
||
ошибка одного collector-а не роняет весь scan.
|
||
|
||
---
|
||
|
||
# 9. Сервисные сканеры
|
||
|
||
## DockerScanner
|
||
|
||
Собирает:
|
||
|
||
```
|
||
containers
|
||
images
|
||
ports
|
||
mounts
|
||
volumes
|
||
networks
|
||
labels
|
||
restart policy
|
||
health status
|
||
compose project
|
||
```
|
||
|
||
Из контейнеров создаёт `detected_services`.
|
||
|
||
Пример:
|
||
|
||
```
|
||
container: nextcloud-app
|
||
image: nextcloud:apache
|
||
ports: 8080:80
|
||
mounts: /srv/nextcloud/data:/var/www/html/data
|
||
suggested service type: web_app
|
||
```
|
||
|
||
## NginxScanner
|
||
|
||
Читает:
|
||
|
||
```
|
||
/etc/nginx/nginx.conf
|
||
/etc/nginx/sites-enabled/*
|
||
/etc/nginx/conf.d/*
|
||
```
|
||
|
||
Вынимает:
|
||
|
||
```
|
||
server_name
|
||
listen
|
||
proxy_pass
|
||
root
|
||
ssl_certificate
|
||
ssl_certificate_key path, но ключ не читать
|
||
access_log
|
||
error_log
|
||
```
|
||
|
||
Важно: приватные ключи сертификатов не читать.
|
||
|
||
## CronScanner
|
||
|
||
Читает:
|
||
|
||
```
|
||
user crontab
|
||
/etc/crontab
|
||
/etc/cron.d/*
|
||
```
|
||
|
||
И создаёт кандидатов:
|
||
|
||
```
|
||
cron_job
|
||
backup_hint
|
||
maintenance_task
|
||
unknown_task
|
||
```
|
||
|
||
## BackupHintScanner
|
||
|
||
Ищет в командах/скриптах признаки:
|
||
|
||
```
|
||
rsync
|
||
borg
|
||
restic
|
||
rclone
|
||
tar
|
||
zip
|
||
mysqldump
|
||
mariadb-dump
|
||
pg_dump
|
||
sqlite dump
|
||
docker exec ... dump
|
||
scp
|
||
sftp
|
||
```
|
||
|
||
Но вывод должен быть осторожным:
|
||
|
||
```
|
||
Похоже на backup job
|
||
```
|
||
|
||
а не:
|
||
|
||
```
|
||
Это точно полноценный бэкап
|
||
```
|
||
|
||
---
|
||
|
||
# 10. Экранная структура
|
||
|
||
## Dashboard
|
||
|
||
```
|
||
Домовой
|
||
|
||
Устройства: 12
|
||
Сервисы: 18
|
||
Новые находки: 7
|
||
Требуют внимания: 4
|
||
Последний скан сети: сегодня 14:22
|
||
```
|
||
|
||
Блоки:
|
||
|
||
```
|
||
- Новые найденные устройства
|
||
- Сервисы без подтверждённого бэкапа
|
||
- Хосты без свежего скана
|
||
- Истекающие сертификаты
|
||
- Последние изменения
|
||
```
|
||
|
||
## Discovery
|
||
|
||
```
|
||
Сканирование сети
|
||
|
||
[Добавить диапазон]
|
||
[Запустить скан]
|
||
|
||
Таблица:
|
||
IP | Hostname | MAC | Vendor | Ports | Статус | Действия
|
||
```
|
||
|
||
Действия:
|
||
|
||
```
|
||
[Создать устройство]
|
||
[Объединить]
|
||
[Игнорировать]
|
||
[Детали]
|
||
```
|
||
|
||
## Devices
|
||
|
||
Карточка устройства:
|
||
|
||
```
|
||
Название
|
||
Тип
|
||
IP
|
||
MAC
|
||
Hostname
|
||
Vendor
|
||
OS
|
||
Роль
|
||
Важность
|
||
Заметки
|
||
Доступы
|
||
Найденные сервисы
|
||
Подтверждённые сервисы
|
||
Документы
|
||
История
|
||
```
|
||
|
||
## Credentials
|
||
|
||
Для SSH:
|
||
|
||
```
|
||
Название доступа
|
||
Host
|
||
Port
|
||
Username
|
||
Auth method:
|
||
- password
|
||
- private key
|
||
Password/private key
|
||
[Тест]
|
||
[Сохранить]
|
||
```
|
||
|
||
## Host Scan
|
||
|
||
На карточке устройства:
|
||
|
||
```
|
||
[Глубокий скан]
|
||
|
||
Результаты:
|
||
- Система
|
||
- Открытые порты
|
||
- Docker
|
||
- Systemd
|
||
- Cron
|
||
- Nginx
|
||
- Возможные сервисы
|
||
- Возможные бэкапы
|
||
```
|
||
|
||
## Найденные сервисы
|
||
|
||
```
|
||
Название | Тип | Источник | Устройство | Порт | Уверенность | Действия
|
||
```
|
||
|
||
Действия:
|
||
|
||
```
|
||
[Создать сервис]
|
||
[Объединить]
|
||
[Игнорировать]
|
||
[Детали]
|
||
```
|
||
|
||
---
|
||
|
||
# 11. CI/CD и smoke tests
|
||
|
||
Создать файл scripts/check.sh:
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
echo "=== composer validate ==="
|
||
composer validate --no-check-publish
|
||
|
||
echo "=== composer install ==="
|
||
composer install --no-interaction --prefer-dist
|
||
|
||
echo "=== PHP syntax check ==="
|
||
find app public bin -name "*.php" -print0 | xargs -0 -n1 php -l
|
||
|
||
echo "=== docker compose config ==="
|
||
docker compose config > /dev/null
|
||
|
||
echo "=== docker compose up ==="
|
||
docker compose up -d --build
|
||
|
||
echo "=== phinx migrate (test DB) ==="
|
||
docker compose exec app php vendor/bin/phinx migrate
|
||
|
||
echo "=== HTTP health check ==="
|
||
curl -sI http://localhost:8080/login | head -1
|
||
|
||
echo "=== ALL CHECKS PASSED ==="
|
||
```
|
||
|
||
Головное правило: итерация не считается завершённой,
|
||
пока check.sh не прошёл успешно без ошибок.
|
||
|
||
Для следующих итераций добавлять свои проверки:
|
||
- network scan: "docker compose exec app php bin/console scan:test"
|
||
- device CRUD: curl к /devices, /devices/create
|
||
- credentials: тест SSH mock-connection
|
||
|
||
Не переходить к следующей итерации без успешного запуска check.sh.
|
||
Обязательно показывать реальный вывод, а не утверждать "готово".
|
||
|
||
---
|
||
|
||
# 12. План реализации
|
||
|
||
## Этап 0. Каркас проекта
|
||
|
||
Цель: приложение открывается, есть логин, база, миграции, layout.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- docker-compose поднимает app + db
|
||
- есть /login
|
||
- есть /dashboard
|
||
- есть миграции
|
||
- есть базовый UI
|
||
```
|
||
|
||
## Этап 1. Инвентарь устройств вручную
|
||
|
||
Цель: можно создавать карточки устройств руками.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- CRUD устройств
|
||
- типы устройств
|
||
- заметки
|
||
- список устройств
|
||
- карточка устройства
|
||
```
|
||
|
||
## Этап 2. Сканирование сети
|
||
|
||
Цель: можно добавить диапазон и найти хосты.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- CRUD network_ranges
|
||
- запуск network scan
|
||
- таблица discovered_hosts
|
||
- ping/tcp scan
|
||
- ARP table enrichment
|
||
- reverse DNS
|
||
```
|
||
|
||
## Этап 3. Превращение найденного в устройство
|
||
|
||
Цель: найденный хост можно принять в инвентарь.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- кнопка "Создать устройство"
|
||
- кнопка "Игнорировать"
|
||
- простое объединение с существующим устройством
|
||
- discovered_host получает статус
|
||
```
|
||
|
||
## Этап 4. SSH-доступы
|
||
|
||
Цель: к устройству можно добавить SSH-доступ и проверить его.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- CRUD credentials
|
||
- шифрование секрета
|
||
- тест подключения
|
||
- сохранение результата теста
|
||
```
|
||
|
||
## Этап 5. Deep scan Linux-хоста
|
||
|
||
Цель: по SSH собрать базовую информацию.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- hostname/os/ip/ports/disk/systemd/timers/cron
|
||
- сохранение raw scan JSON
|
||
- отображение summary
|
||
```
|
||
|
||
## Этап 6. Docker scan
|
||
|
||
Цель: найти контейнеры и предложить сервисы.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- docker ps
|
||
- docker inspect для контейнеров
|
||
- volumes/networks/ports
|
||
- detected_services kind=docker_container
|
||
```
|
||
|
||
## Этап 7. Nginx scan
|
||
|
||
Цель: найти домены и proxy_pass.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- чтение nginx-конфигов
|
||
- парсинг server_name/listen/proxy_pass
|
||
- detected_services kind=nginx_vhost
|
||
- domains/proxy suggestions
|
||
```
|
||
|
||
## Этап 8. Создание сервисов из найденного
|
||
|
||
Цель: пользователь подтверждает найденный сервис.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- service proposals
|
||
- create service from detected_service
|
||
- relation Device → Service
|
||
- endpoint creation
|
||
```
|
||
|
||
## Этап 9. Cron/backup hints
|
||
|
||
Цель: находить задачи, похожие на бэкапы.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- cron parser
|
||
- systemd timer parser
|
||
- backup keyword detection
|
||
- detected_services kind=backup_hint
|
||
```
|
||
|
||
## Этап 10. Риски
|
||
|
||
Цель: простые предупреждения.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- сервис без backup_hint
|
||
- публичный web endpoint без документации
|
||
- SSH открыт
|
||
- нет свежего deep scan
|
||
- TLS скоро истекает, если cert найден
|
||
```
|
||
|
||
## Этап 11. История и diff
|
||
|
||
Цель: видеть, что изменилось между сканами.
|
||
|
||
Результат:
|
||
|
||
```
|
||
- история scan jobs
|
||
- сравнение open ports
|
||
- новые/пропавшие хосты
|
||
- новые/пропавшие контейнеры
|
||
```
|
||
|
||
---
|
||
|
||
# 13. Как давать задание Hermes/owl-alpha
|
||
|
||
Я бы не давал ему “весь проект” целиком. Лучше так:
|
||
|
||
```
|
||
1. Сначала дать общее ТЗ и попросить создать каркас.
|
||
2. Потом отдельной задачей миграции.
|
||
3. Потом отдельной задачей CRUD устройств.
|
||
4. Потом отдельной задачей network scan.
|
||
5. Потом отдельной задачей SSH credentials.
|
||
```
|
||
|
||
Для owl-alpha особенно важно:
|
||
|
||
```
|
||
- запрещать переписывать уже готовое без причины;
|
||
- требовать маленькие коммиты;
|
||
- требовать список изменённых файлов;
|
||
- требовать инструкции запуска;
|
||
- требовать self-check;
|
||
- не разрешать выдумывать несуществующие зависимости;
|
||
- не разрешать делать произвольный shell executor;
|
||
- не разрешать писать "чистый JS" без обоснования;
|
||
- запрещать строковые шаблоны для docker-команд;
|
||
- требовать запуск scripts/check.sh после каждой итерации;
|
||
- требовать показать реальный вывод check.sh, а не утверждать "готово".
|
||
```
|
||
|
||
Головное правило: итерация не считается завершённой, пока check.sh
|
||
не прошёл успешно. Без smoke test — нет завершения.
|
||
|
||
--- |