Files
cloud-ip-validator/README.md
T
ayurishchevandClaude Sonnet 5.5 debf2afed2 Add authentication: admin/agent bearer tokens for the API, login for the dashboard
control-api: every route now carries a mandatory access level (admin / agent /
open) in a route table. All /api/v1/admin/* require the admin token; the
write calls of validator-agent and prober (self-check, events, results,
complete) require a separate static agent token; register, heartbeat and
fetching the assignment stay open. Tokens come from env vars, are compared in
constant time and never logged. An empty token leaves that level open with a
startup warning (backward compatible).

validator-agent / prober: apiclient sends the agent token only to control-api.

admin-dashboard: login/password (from env) with a stateless HMAC session
cookie, Origin-based CSRF check, per-IP brute-force throttle, HX-Redirect for
htmx polls, logout in the sidebar; the dashboard calls control-api with the
admin token. Login page layout fixed after review.

Also: env plumbing in docker-compose/rxprod-compose/systemd/config examples,
e2e script with token assertions, tests, docs (API, SETUP, USAGE, DASHBOARD,
README), plan and review under docs/changes/, bin/ rebuilt with new
SHA256SUMS.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 11:35:24 +03:00

28 KiB
Raw Blame History

Cloud IP Validator

Система проверки освобождённых публичных IPv4-адресов перед их повторной выдачей. Каждый адрес временно привязывается как Floating IP к ВМ-валидатору в облаке (OpenStack) и проверяется в двух направлениях одновременно: исходящий трафик валидатора (egress: HTTPS/ICMP/опционально SSH до внешних целей) и входящая доступность самого адреса с нескольких независимых внешних площадок (inbound: TCP 22/80/443/8080 + ICMP). Итог по каждому адресу — pass/partial/fail; полная история проверок сохраняется в реестре даже после удаления адреса из очереди.

Быстрый старт

go build ./... && go test ./...            # сборка и юнит-тесты
scripts/run-local-e2e.sh                   # офлайн-прогон всей системы: mock OpenStack, без облака и интернета
cd deploy/docker
cp .env.example .env
cp control-api/control-api.docker.example.yaml control-api/control-api.docker.yaml
docker compose up -d --build               # весь стенд на одной машине в mock-режиме (профили control-plane, dashboard, prober, validator)
  • UI: http://<хост>:8090/, API: http://<хост>:8080/api/v1, проверка живости: GET /healthz.
  • Docker-стенд работает с openstack.mode: mock и тестовыми адресами 203.0.113.10–12; реальный OpenStack и учётные данные не нужны.
  • Реальный стенд (systemd или Docker на нескольких хостах) — по шагам в docs/SETUP.md.

Конфигурация

Каждый компонент читает свой YAML (примеры — configs/*.example.yaml). Учётные данные OpenStack в YAML не хранятся — только имена переменных окружения.

control-api (control-api.yaml)

Ключ Назначение
server.listen_addr Адрес API (:8080)
database.path Файл SQLite (/var/lib/cloud-ip-validator/control-api.db)
openstack.mode real или mock (встроенная заглушка OpenStack для разработки и тестов)
openstack.auth_method token (готовый токен проекта из OS_TOKEN, сам не обновляется) или password (логин/пароль Keystone, токен перевыпускается автоматически)
openstack.*_env Имена переменных окружения: OS_AUTH_URL, OS_PROJECT_ID, OS_REGION_NAME, OS_INTERFACE, OS_TOKEN либо OS_USERNAME/OS_USER_DOMAIN_NAME/OS_PASSWORD
orchestrator.poll_interval_seconds Период такта оркестратора (5)
orchestrator.self_check_timeout_seconds, max_self_check_retries Ожидание self-check валидатора (60) и число его повторов (3)
orchestrator.checking_window_seconds Сколько ждать результаты проверок, прежде чем подвести итог (120)
orchestrator.lease_ttl_seconds, max_retries Лизинг адреса за валидатором (180) и число возвратов в очередь при его истечении (3)
orchestrator.heartbeat_timeout_seconds После скольких секунд тишины валидатор или площадка считаются потерянными (30)
orchestrator.fip_settle_seconds Только начальное значение: пауза между привязкой FIP и self-check; далее управляется на лету. Должно выполняться fip_settle_seconds + self_check_timeout_seconds < lease_ttl_seconds
orchestrator.fip_scan_interval_seconds Периодический скан Floating IP (0 — выключен). Не выполняется, пока включён автоматический цикл
auth.admin_token_env, auth.agent_token_env Имена переменных окружения с токеном администратора (CONTROL_API_ADMIN_TOKEN) и токеном агентов (CONTROL_API_AGENT_TOKEN). Значения в YAML не хранятся; пустой токен — соответствующий уровень API открыт (с предупреждением в логе)
aggregation.missing_counts_as_fail Отсутствие ответа источника засчитывается как провал (true)
validators, sites, check_types, targets, inbound_checks Начальная загрузка пустой БД: валидаторы (validator_id + os_port_id), внешние площадки, типы и цели egress-проверок, порты inbound-проверок. Дальше источник истины — БД, правки через API/UI
ip_addresses Адреса, которые доливаются в очередь при каждом старте (только новые)

Остальные компоненты

Компонент Ключи
validator-agent validator_id, control_api_url, control_api_token_env (CONTROL_API_AGENT_TOKEN), poll_interval_seconds, self_check.* (таймаут, ip_echo_urls), checks.* (таймауты HTTPS/ICMP, число ICMP-пакетов, ssh.*)
prober site_id, control_api_url, control_api_token_env (CONTROL_API_AGENT_TOKEN), poll_interval_seconds, checks.* (таймауты TCP/ICMP, число ICMP-пакетов)
admin-dashboard server.listen_addr (:8090), control_api.base_url, control_api.timeout_seconds, control_api.token_env (ADMIN_DASHBOARD_CONTROL_API_TOKEN), auth.username_env / password_env / session_secret_env (ADMIN_DASHBOARD_USERNAME / _PASSWORD / _SESSION_SECRET), auth.session_ttl_minutes (480), overview.last_completed_count (20), overview.poll_interval_seconds (5)

Секреты (токены, пароль дашборда, ключ сессии) задаются только переменными окружения; генерация — openssl rand -hex 32. Подробности и порядок включения — docs/SETUP.md.

Настройки, меняющиеся на лету (пауза перед self-check, глубина истории, типы inbound-проверок, автоматический цикл, валидаторы, площадки, цели), хранятся в БД и правятся через API или страницу /settings.

Архитектура

Слой Технологии
Backend Go 1.26, net/http (маршруты Go 1.22), SQLite (чистый Go-драйвер modernc.org/sqlite, WAL, одно соединение), встроенные миграции 0001–0008
Облако Интерфейс FloatingIPClient: реальный клиент OpenStack Neutron и MockClient
UI Серверный рендеринг (Go-шаблоны) + htmx и Alpine.js, шрифты и скрипты лежат в репозитории, внешних зависимостей нет
Сборка Статические бинарники (CGO_ENABLED=0) в bin/, Docker-образы, systemd-юниты
Компонент Роль Состояние Где работает
control-api Ведёт очередь адресов и реестр их истории, назначает валидаторов, агрегирует результаты. Единственная точка, с которой общаются все остальные компоненты Хранит (SQLite) Одна управляющая машина
validator-agent Привязывает к себе выданный Floating IP и прогоняет egress-проверки до внешних целей Без состояния Каждая ВМ-валидатор в облаке
prober Проверяет входящую доступность адреса снаружи (TCP/ICMP) — с независимой от облака сети Без состояния Каждая внешняя тестовая площадка
admin-dashboard Браузерная админ-панель — то же, что API control-api, но графически. Опционален Без состояния Любая машина с доступом к control-api

Все четыре общаются только через HTTP API control-api. Pull-модель: validator-agent и prober сами опрашивают control-api, он к ним не обращается. Схемы control/data plane — docs/DIAGRAMS.md.

cmd/            control-api  validator-agent  prober  admin-dashboard
internal/       config  db  orchestrator  httpapi  openstack  dashboard  agentcore  probercore  checkrunner  apiclient
  db/migrations/  схема SQLite (0001–0008)
configs/        примеры конфигураций компонентов
deploy/         docker/ (compose, Dockerfile, RUN.txt)   systemd/ (юниты)
rxprod-compose/ compose реального стенда (control-api на :8081, дашборд на :8091)
scripts/        run-local-e2e.sh   httpstub/ (заглушки целей для e2e)
bin/            готовые бинарники linux/amd64 и SHA256SUMS
docs/           документация и планы доработок

Модель данных и правила

ip_queue (текущая очередь) · ip_registry (все адреса, когда-либо бывшие в очереди) · checks (результаты по циклам) · events (журнал) · validators · sites · target_groups · check_types · settings · inbound_checks_settings · auto_cycle.

Очередь и состояния

  • Путь адреса: queued → assigning_fip → awaiting_self_check → checking → aggregating → done или failed. Все переходы, кроме команд оператора, выполняет оркестратор сам по таймеру.
  • Терминальные состояния: done, failed, occupied. occupied — Floating IP к моменту привязки уже занят чужим портом: цикл проверки не стартует, overall_result пуст, это не fail.
  • Оператор может отменить проверку (failed + cancelled), перепроверить завершённый адрес (возврат в queued) или удалить адрес из очереди.
  • Адреса обрабатываются в порядке sequence; каждый свободный валидатор получает следующий queued-адрес под лизинг. Истёкший лизинг возвращает адрес в очередь, после max_retries — в failed.

Итоговый результат

  • pass — прошли все проверки: исходящие и все настроенные площадки по всем портам и ICMP. partial — часть прошла, часть нет. fail — не прошла ни одна проверка или адрес не дошёл до проверок (self-check не подтвердился). cancelled — остановлено оператором.
  • Итог подводится, когда отчитались валидатор и все настроенные площадки либо истекло checking_window_seconds. Молчание источника — провал (aggregation.missing_counts_as_fail).
  • Площадки опциональны: с пустым списком sites итог строится только по egress-проверкам.

Реестр

  • Запись реестра создаётся при первой постановке адреса и не удаляется: она переживает удаление из очереди и повторное добавление.
  • История проверок хранится по циклам; history_retention_cycles ограничивает глубину (0 — без ограничения), сама запись реестра остаётся.

Автоматический цикл (опционально, по умолчанию выключен)

  • Один цикл: очистить очередь → просканировать Floating IP → дождаться, пока все адреса станут терминальными → пауза interval_seconds → заново. Пауза считается от завершения цикла.
  • interval_seconds — по умолчанию 3600, минимум 60; max_run_seconds — максимальное ожидание проверок (0 — без лимита), по истечении исход timeout.
  • Исходы цикла: completed, no_free_ips, timeout, error, stopped. Опустевшая очередь посреди цикла считается завершением.
  • Состояние хранится в БД и переживает перезапуск; выключение не прерывает идущие проверки. Подробности — docs/USAGE.md.

Удаление и сканирование

  • Удаление (точечное, списком, «очистить всё») убирает строку очереди, но не историю в реестре.
  • Скан Floating IP ставит в очередь только свободные адреса (не привязанные ни к одному порту); уже идущие проверки не трогаются.

API (/api/v1)

Область Эндпоинты
Валидатор POST /agents/register, POST /agents/{id}/heartbeat, GET /agents/{id}/assignment, POST /agents/{id}/self-check|events|results|complete
Пробер POST /probers/register, POST /probers/{site_id}/heartbeat, GET /probers/{site_id}/assignments, POST /probers/{site_id}/results
Очередь GET /admin/status, GET|POST /admin/ips, GET /admin/ips/{ip}, POST /admin/ips/{ip}/cancel, DELETE /admin/ips/{ip}, POST /admin/ips/delete|clear|scan
Реестр GET /admin/registry, GET /admin/registry/{ip}
Автоцикл GET|PUT /admin/auto-cycle, POST /admin/auto-cycle/start|stop
Конфигурация /admin/config/validators, /sites, /targets, /check-types, GET|PUT /admin/config/orchestrator, GET|PUT /admin/config/inbound-checks
Служебное GET /admin/validators, GET /healthz

Соглашения

  • Тело запросов и ответов — JSON. Успех — 200, 204 — когда данных нет (например, у валидатора нет назначения).
  • Ошибки — 4xx/5xx с телом {"error": "..."}; нет или неверен токен — 401 (с WWW-Authenticate: Bearer), неизвестная сущность — 404, конфликт состояния — 409, неверные данные — 400, ошибка OpenStack при скане — 502.
  • Токен передаётся заголовком Authorization: Bearer <токен> (docs/API.md).
  • Времена — RFC 3339. Полная спецификация и примеры curl — docs/API.md.

Безопасность

  • Доступ к API — два статических Bearer-токена (без срока жизни, из переменных окружения, сравнение в константное время):
    Уровень Токен Методы
    admin CONTROL_API_ADMIN_TOKEN все /api/v1/admin/*
    agent CONTROL_API_AGENT_TOKEN запись результатов валидатора и пробера: self-check, events, results, complete
    открыто — GET /healthz, register, heartbeat и получение задания (GET assignment / assignments)
    Токены разные: административный не открывает методы агентов, и наоборот. Валидатор и пробер получают настройку и задание без токена, но не могут отправить результат без токена агентов.
  • Пустой токен — уровень открыт (обратная совместимость): control-api стартует с предупреждением в логе. На реальном стенде задайте оба токена и ограничьте доступ на уровне сети (docs/SETUP.md); токены идут открытым текстом без TLS — публикуйте через reverse-proxy с TLS. Включать токен агентов нужно после его раздачи валидаторам и проберам (порядок).
  • Дашборд закрыт логином и паролем (один администратор; пароль и ключ сессии — из env). Сессия — подписанная cookie (HttpOnly, SameSite=Strict, без состояния на сервере), CSRF-защита по Origin, 5 неудачных входов за 10 минут с одного IP → 429. Без заданных логина/пароля дашборд открыт (с предупреждением в логе). Дашборд ходит в API с токеном администратора. Подробности — docs/DASHBOARD.md.
  • Учётные данные OpenStack передаются только через переменные окружения процесса (EnvironmentFile= в systemd, OS_* в Docker) и не попадают в YAML; режим password перевыпускает токен сам, режим token — нет.
  • Компоненты работают по pull-модели: на валидаторах и площадках не нужно открывать входящие порты для control-api.
  • Бинарники статические, без cgo; целостность проверяется sha256sum -c bin/SHA256SUMS.

Публикация и эксплуатация

  • Бинарники. Готовые linux/amd64 лежат в bin/ и не обновляются автоматически: после правок кода пересоберите их и обновите SHA256SUMS (команды — docs/SETUP.md); Dockerfile копируют именно bin/*.
  • systemd. Юниты в deploy/systemd/; у control-api — EnvironmentFile с учётными данными OpenStack.
  • Docker. deploy/docker/docker-compose.yml + docker-compose.override.yml (dev, mock) или docker-compose.prod.yml (без публикации портов, restart: unless-stopped). Какие сервисы поднимаются на хосте, задаёт COMPOSE_PROFILES: control-plane, dashboard, prober, validator. БД — volume cloud-ip-validator-db.
  • Реальный стенд. rxprod-compose/ — compose с готовыми образами, собственным control-api.yaml и каталогом БД capi-db/; .env с учётными данными в репозиторий не входит.
  • Миграции применяются при старте control-api; версия схемы — PRAGMA user_version. Начальная загрузка (validators, sites, targets, check_types, inbound_checks) выполняется только в пустые таблицы.
  • Остановка (SIGTERM) корректно завершает HTTP-сервер и фоновые циклы. Состояние автоцикла и очереди сохраняется в БД.

Интерфейс

Страницы admin-dashboard (подробно — docs/DASHBOARD.md):

Страница Назначение
/overview Счётчики по состояниям, «текущая» и «последние завершённые» проверки, поиск по IP и фильтр по статусу, индикатор автоцикла; обновляется без перезагрузки
/ips, /ips/{ip} Очередь: добавление адресов, «Сканировать Floating IP», перепроверка, отмена, удаление (в том числе списком и «Очистить всё»); детали и события адреса
/registry, /registry/{ip} Реестр всех адресов и полная история проверок адреса; поиск и фильтр сохраняются в адресной строке
/validators, /sites, /targets, /check-types Управление валидаторами, внешними площадками, группами целей и типами проверок
/settings Панель «Автоматический цикл», пауза перед self-check, глубина истории, TCP-порты и ICMP для inbound-проверок
  • Порядок блоков на /overview фиксирован: статистика → фильтр → таблицы; поллится только блок таблиц, поэтому набранный в фильтре текст не сбрасывается.
  • Ошибки control-api показываются баннером; при недоступном API страница остаётся рабочей.
  • Тёмная и светлая темы, переключатель в шапке.

Тесты

go build ./... && go vet ./... && go test ./...      # юнит-тесты: db, orchestrator, httpapi, dashboard, agentcore, probercore, checkrunner, openstack
go test -race ./internal/orchestrator ./internal/httpapi ./internal/dashboard ./internal/db
scripts/run-local-e2e.sh                             # сквозной прогон: lease-reclaim, перепроверка, автоматический цикл

Юнит-тесты используют временную SQLite и MockClient, внешних ресурсов не требуют. E2E поднимает все компоненты локальными процессами с включёнными токенами (проверяет 401/открытые маршруты и работу агента и пробера с токеном) и завершается ненулевым кодом при провале проверок автоцикла — docs/LOCAL_E2E.md.

Документация

Документ Для чего
docs/SETUP.md Развёртывание с нуля: бинарники или сборка из исходников, конфигурация, systemd и Docker/docker-compose — пошагово
docs/USAGE.md Повседневная работа: очередь, сканирование Floating IP, автоматический цикл, статус, реестр и история, разбор результатов
docs/API.md Спецификация HTTP API control-api и примеры запросов (curl)
docs/DASHBOARD.md Устройство admin-dashboard: страницы, поиск и фильтр, обработка ошибок
docs/DIAGRAMS.md Диаграммы потоков данных: control plane, egress-проверка, телеметрия
docs/LOCAL_E2E.md Полностью офлайн-прогон всей системы одним скриптом
docs/changes/ Планы доработок и отчёты ревью с отметкой времени в имени файла (последняя: аутентификация)
docs/CONTROL_DATA_PLANE.html Презентационные схемы control/data plane для docker-compose-деплоя — открыть в браузере

История изменений

Дизайн крупных доработок зафиксирован в планах docs/PLAN_*.md; остальное — в истории git (git log).

Изменение Документ
Веб-панель администратора admin-dashboard план · описание
Динамическое управление конфигурацией и очередью через API план · API
Удаление адресов: точечное, массовое, «очистить всё» план
Пауза перед self-check после привязки Floating IP (fip_settle_seconds) план · USAGE
Механизм аутентификации OpenStack-клиента (token / password) план
Дата Веха Документ
2026-10-01 Аутентификация: токены администратора и агентов для API, логин и пароль для дашборда план · ревью и тесты · API
2026-10-01 Автоматический цикл проверок по сценарию: очистка → скан FIP → проверка → пауза USAGE · API
2026-09-23 Сканирование Floating IP и устойчивый реестр адресов с настраиваемой глубиной истории USAGE
2026-09-23 Поиск по IP и фильтр по статусу на «Обзоре» и «Реестре» DASHBOARD
2026-09-23 Пошаговое руководство по развёртыванию в Docker SETUP
2026-09-18 Массовая перепроверка в дашборде; compose реального стенда rxprod-compose DASHBOARD
2026-09-13 Состояние occupied: пропуск цикла для уже занятого Floating IP; схемы control/data plane DIAGRAMS
2026-08-26 Управление внешними площадками и heartbeat пробера, новый UI дашборда, переключатель темы DASHBOARD