docs(readme): обновить документацию по последним доработкам

Добавил в README новые API и UI-возможности: IF-MIB интерфейсы, гибкий ввод CIDR,
обновления по LLDP/SNMP, форматирование MAC и админ-очистку данных сканов.

Made-with: Cursor
This commit is contained in:
Луценко Андрей Анатольевич
2026-04-10 16:18:11 +10:00
parent a782d36af9
commit 526c67b5b1
+53 -11
View File
@@ -13,10 +13,12 @@
- `GET /api/scans/{id}/ports` - результаты проверки TCP-портов по скану. - `GET /api/scans/{id}/ports` - результаты проверки TCP-портов по скану.
- `GET /api/scans/{id}/snmp` - результаты SNMP v2c (`sysName`, `sysDescr`, `sysObjectID`). - `GET /api/scans/{id}/snmp` - результаты SNMP v2c (`sysName`, `sysDescr`, `sysObjectID`).
- `GET /api/scans/{id}/lldp` - LLDP соседи (если устройство отдает LLDP-MIB по SNMP). - `GET /api/scans/{id}/lldp` - LLDP соседи (если устройство отдает LLDP-MIB по SNMP).
- `GET /api/scans/{id}/interfaces?ip=` - интерфейсы устройства из IF-MIB (`ifIndex/ifName/ifDescr/ifOperStatus`).
- `GET /api/scans/{id}/links` - базовые связи из LLDP с попыткой сопоставления по `remote_sys_name`. - `GET /api/scans/{id}/links` - базовые связи из LLDP с попыткой сопоставления по `remote_sys_name`.
- `GET /api/scans/{id}/topology` - граф `nodes/edges` для визуализации. - `GET /api/scans/{id}/topology` - граф `nodes/edges` для визуализации.
- `POST /api/admin/purge-scans` - очистка всех scan-данных (защищено `X-Nettopo-Reset-Key`).
- `GET /` — UI для просмотра топологии (страница встроена в бинарник через `embed`, каталог `internal/webui/`; **не зависит** от `WorkingDirectory` и папки `web/` рядом с процессом). - `GET /` — UI для просмотра топологии (страница встроена в бинарник через `embed`, каталог `internal/webui/`; **не зависит** от `WorkingDirectory` и папки `web/` рядом с процессом).
- Валидация CIDR и exclude IP. - Валидация целей скана: одиночный CIDR, перечисление через запятую и диапазон `CIDR-CIDR` (IPv4).
- Два backend-хранилища: in-memory и PostgreSQL. - Два backend-хранилища: in-memory и PostgreSQL.
- После создания scan запускается фоновый discovery с обновлением статуса и прогресса. - После создания scan запускается фоновый discovery с обновлением статуса и прогресса.
- Отдельный чек-лист ручной приемки: `MANUAL_TEST_CHECKLIST.md`. - Отдельный чек-лист ручной приемки: `MANUAL_TEST_CHECKLIST.md`.
@@ -75,7 +77,11 @@ http://localhost:8080/
- подсветить на графе новые (`[+]`, зеленый) и пропавшие (`[-]`, красный) хосты по diff. - подсветить на графе новые (`[+]`, зеленый) и пропавшие (`[-]`, красный) хосты по diff.
- использовать легенду цветов и фильтр узлов: все / только новые / только пропавшие. - использовать легенду цветов и фильтр узлов: все / только новые / только пропавшие.
- экспортировать topology и diff в JSON-файлы кнопками из UI. - экспортировать topology и diff в JSON-файлы кнопками из UI.
- создать scan по CIDR из UI и дождаться завершения кнопкой "Ждать завершения". - создать scan по целям из UI и дождаться завершения кнопкой "Ждать завершения";
- вводить цели как: один CIDR (`192.168.1.0/24`), список через запятую (`172.16.0.0/24,192.168.160.0/24`) и диапазон (`192.168.160.0/24-192.168.190.0/24`);
- в таблице "Связи выбранного устройства" видеть не только LLDP-порты, но и все интерфейсы из IF-MIB с пометкой, где LLDP отсутствует;
- сортировать "Таблица 1. Связи" по столбцу "Локальный порт";
- выполнять "Начать всё заново" (очистка scan-таблиц) из UI в админ-блоке при наличии секрета.
По умолчанию используется `STORE_BACKEND=memory`. По умолчанию используется `STORE_BACKEND=memory`.
SNMP-проверка по умолчанию включена (`SNMP_ENABLED=true`, community `public`). SNMP-проверка по умолчанию включена (`SNMP_ENABLED=true`, community `public`).
@@ -84,13 +90,16 @@ SNMP-проверка по умолчанию включена (`SNMP_ENABLED=tr
Один запуск scan — это **одна фоновая задача** (`runner`), без отдельных фаз «только ping» / «только LLDP» в API. Один запуск scan — это **одна фоновая задача** (`runner`), без отдельных фаз «только ping» / «только LLDP» в API.
1. По полю `cidrs` строится список IP (с учётом `exclude_ips`). 1. По полю `cidrs` строится список IP (с учётом `exclude_ips`). Поддерживается:
- один CIDR;
- список CIDR через запятую;
- диапазон `CIDR-CIDR` (только IPv4, одинаковая маска, границы — сетевые адреса).
2. Параллельно (ограничение `max_parallel_hosts`) для каждого IP: 2. Параллельно (ограничение `max_parallel_hosts`) для каждого IP:
- **ping** (ICMP через системную утилиту `ping`); - **ping** (ICMP через системную утилиту `ping`);
- если хост «вверх» и включён port-scan — проверка **TCP-портов** из `options.ports`; - если хост «вверх» и включён port-scan — проверка **TCP-портов** из `options.ports`;
- если хост «вверх» и `SNMP_ENABLED=true`**SNMP v2c** (sysName, sysDescr, sysObjectID) и сразу **обход LLDP-MIB** (соседи, локальный/удалённый порт и т.д.). Это не отдельная кнопка: LLDP выполняется в том же проходе, что и базовый SNMP. - если хост «вверх» и `SNMP_ENABLED=true`**SNMP v2c** (sysName, sysDescr, sysObjectID), затем **LLDP-MIB** и **IF-MIB** (`ifIndex/ifName/ifDescr/ifOperStatus`).
3. Статус задачи: `queued``running` (поле `progress` 0…100, `stats.hosts_total` / `stats.hosts_up`) → `done` или `failed`. 3. Статус задачи: `queued``running` (поле `progress` 0…100, `stats.hosts_total` / `stats.hosts_up`) → `done` или `failed`.
4. Результаты читаются через API: `/hosts`, `/ports`, `/snmp`, `/lldp`, агрегированный граф — `/topology`. 4. Результаты читаются через API: `/hosts`, `/ports`, `/snmp`, `/lldp`, `/interfaces`, агрегированный граф — `/topology`.
В веб-UI после **«Создать scan и ждать»** опрашивается статус, показывается расшифровка в строке состояния, по завершении обновляется таблица **найденных устройств** (ping + SNMP) и строится граф по кнопке **«Загрузить»** (или автоматически после ожидания). В веб-UI после **«Создать scan и ждать»** опрашивается статус, показывается расшифровка в строке состояния, по завершении обновляется таблица **найденных устройств** (ping + SNMP) и строится граф по кнопке **«Загрузить»** (или автоматически после ожидания).
@@ -98,6 +107,26 @@ SNMP-проверка по умолчанию включена (`SNMP_ENABLED=tr
Опрос LLDP идёт по стандартному **LLDP-MIB** (`1.0.8802.1.1.2.1.4.1.1.*`) после успешного SNMP **Get** системной группы. Раньше при **GET-BULK** часть прошивок отвечала слишком долго или с ошибкой — обход обрывался, в БД не попадало ни строки («No LLDP data» в UI). Сейчас для фазы LLDP увеличен таймаут (не меньше **3 c** на запрос), снижен размер «пачки» **MaxRepetitions**, добавлен **повтор при ошибке через SNMP Walk (GetNext)**. В логах сервера ищите строки `lldp snmp BulkWalkAll` и `fallback Walk`. Проверьте с хоста nettopo: community (**`SNMP_COMMUNITY` / `public`**), доступ UDP **161** с этого сервера и что на свиче включён **SNMP v2c** (не только LLDP в L2, но и ответ агента SNMP). Опрос LLDP идёт по стандартному **LLDP-MIB** (`1.0.8802.1.1.2.1.4.1.1.*`) после успешного SNMP **Get** системной группы. Раньше при **GET-BULK** часть прошивок отвечала слишком долго или с ошибкой — обход обрывался, в БД не попадало ни строки («No LLDP data» в UI). Сейчас для фазы LLDP увеличен таймаут (не меньше **3 c** на запрос), снижен размер «пачки» **MaxRepetitions**, добавлен **повтор при ошибке через SNMP Walk (GetNext)**. В логах сервера ищите строки `lldp snmp BulkWalkAll` и `fallback Walk`. Проверьте с хоста nettopo: community (**`SNMP_COMMUNITY` / `public`**), доступ UDP **161** с этого сервера и что на свиче включён **SNMP v2c** (не только LLDP в L2, но и ответ агента SNMP).
### Частые замечания и пояснения
- **CLI LLDP есть, а в UI "No LLDP data"**: это значит, что LLDP на устройстве работает локально, но в конкретном скане данные не были успешно прочитаны из `lldpRemTable` по SNMP. Быстрая проверка: `snmpwalk -v2c -c <community> <ip> 1.0.8802.1.1.2.1.4.1`.
- **Поле "Порт соседа" вида `0x...`**: если это OctetString длиной 6 байт, теперь значение форматируется как MAC `aa:bb:cc:dd:ee:ff`; для остальных бинарных значений остаётся `0x...`.
- **SNMP "не опрашивался" при больших сканах**: `/hosts` и `/snmp` возвращают по одной актуальной записи на IP (dedupe latest), чтобы не терять устройства из-за ограничений выборки.
- **Почему раньше в "Таблица 1. Связи" были не все порты**: теперь таблица строится как IF-MIB + LLDP. Показываются все интерфейсы, а LLDP соседы подмешиваются по `ifIndex`.
### Начать всё заново (очистка БД)
Для полного сброса scan-данных добавлен endpoint:
```bash
POST /api/admin/purge-scans
X-Nettopo-Reset-Key: <секрет>
```
- Очистка доступна только при заданном `NETTOPO_RESET_DB_SECRET`.
- В UI есть админ-блок "Начать всё заново" с ручным вводом секрета.
- Для PostgreSQL выполняется `TRUNCATE scan_jobs RESTART IDENTITY CASCADE` (очищаются все связанные таблицы: hosts/ports/snmp/lldp/interfaces).
## PostgreSQL без Docker ## PostgreSQL без Docker
### Вариант A: macOS (Homebrew) ### Вариант A: macOS (Homebrew)
@@ -137,7 +166,7 @@ export RUN_MIGRATIONS=true
go run ./cmd/server go run ./cmd/server
``` ```
При первом запуске автоматически применится миграция `scan_jobs`. При первом запуске автоматически применяются SQL-миграции из `internal/store/sql/` (включая таблицу `scan_interfaces`).
## Быстрая проверка API ## Быстрая проверка API
@@ -193,7 +222,7 @@ curl -s "http://localhost:8080/api/scans/diff?from=<scan_id_1>&to=<scan_id_2>"
Получить результаты по хостам: Получить результаты по хостам:
```bash ```bash
curl -s http://localhost:8080/api/scans/<scan_id>/hosts?limit=200 curl -s http://localhost:8080/api/scans/<scan_id>/hosts?limit=200000
``` ```
Получить результаты по портам: Получить результаты по портам:
@@ -205,25 +234,38 @@ curl -s http://localhost:8080/api/scans/<scan_id>/ports?limit=500
Получить SNMP результаты: Получить SNMP результаты:
```bash ```bash
curl -s http://localhost:8080/api/scans/<scan_id>/snmp?limit=500 curl -s http://localhost:8080/api/scans/<scan_id>/snmp?limit=200000
``` ```
Получить LLDP результаты: Получить LLDP результаты:
```bash ```bash
curl -s http://localhost:8080/api/scans/<scan_id>/lldp?limit=1000 curl -s http://localhost:8080/api/scans/<scan_id>/lldp?limit=20000
```
Получить интерфейсы устройства (IF-MIB):
```bash
curl -s "http://localhost:8080/api/scans/<scan_id>/interfaces?ip=192.168.1.10&limit=4096"
``` ```
Получить связи: Получить связи:
```bash ```bash
curl -s http://localhost:8080/api/scans/<scan_id>/links?limit=1000 curl -s http://localhost:8080/api/scans/<scan_id>/links?limit=20000
``` ```
Получить topology (nodes/edges): Получить topology (nodes/edges):
```bash ```bash
curl -s http://localhost:8080/api/scans/<scan_id>/topology?limit=2000 curl -s http://localhost:8080/api/scans/<scan_id>/topology?limit=5000
```
Очистить все scan-данные (защищено секретом):
```bash
curl -s -X POST http://localhost:8080/api/admin/purge-scans \
-H "X-Nettopo-Reset-Key: <NETTOPO_RESET_DB_SECRET>"
``` ```
## Запуск как сервис на Linux (systemd) ## Запуск как сервис на Linux (systemd)