Backend (FastAPI, SQLAlchemy 2, Alembic, PostgreSQL 16): - организации, VRF, префиксы (дерево, использование, автоназначение), адреса, операторы связи, устройства и типы устройств; JWT, роли admin/viewer; - VRF принадлежит организации (составной FK), смена VRF у префикса переносит поддерево, имя VRF уникально в организации; - журнал аудита: поиск и фильтры, ротация (срок/количество), очистка по паролю с блокировкой, IP клиента и метаданные запроса (X-Forwarded-For только от TRUSTED_PROXIES). UI (web/, без сборки): экраны и диалоги по макетам «IPAM Manager», кликабельные строки реестров, локальные шрифты IBM Plex, собственные выпадающие списки. Окружение: docker-compose (postgres + app), миграции Alembic 0001-0004, scripts/gen_env.py, scripts/seed_demo.py, 11 автотестов (pytest). Документация: README.md и docs/changes/001-005 (планы и итоги). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.6 KiB
IPAM Manager
Реестр IP-адресов и адресных префиксов в разрезе организаций. API-first: backend на Python (FastAPI) с PostgreSQL,
UI-админка — отдельный лёгкий SPA (web/, без сборки), который только визуализирует ответы API.
Макеты: страница «IPAM Manager» дизайн-канваса ros_control.
Быстрый старт
python3 scripts/gen_env.py # .env со случайными паролями/секретами (в .gitignore)
docker compose up -d --build # postgres + app; миграции применяются автоматически
python3 -m venv venv && venv/bin/pip install -r requirements-dev.txt
venv/bin/python scripts/seed_demo.py # (по желанию) демо-данные из макетов
- UI: http://127.0.0.1:8088/ · Swagger: http://127.0.0.1:8088/docs (порт приложения —
APP_PORTв.env, слушает 0.0.0.0; порт БД — только 127.0.0.1) - Логин/пароль администратора —
ADMIN_USERNAME/ADMIN_PASSWORDиз.env(создаётся при первом старте).
Архитектура
| Слой | Технологии |
|---|---|
| API | FastAPI, pydantic v2, JWT (argon2), роли admin (запись) / viewer (чтение) |
| БД | PostgreSQL 16, SQLAlchemy 2, Alembic (alembic/versions), типы CIDR/INET |
| UI | статический SPA (ES-модуль, vanilla JS), раздаётся приложением; шрифты IBM Plex — локально в web/fonts/ (woff2 latin+cyrillic, лицензия OFL), внешних зависимостей нет |
app/ main.py config.py db.py security.py models.py schemas.py services.py api/v1/{auth,refs,prefixes,overview}.py
web/ index.html styles.css app.js
alembic/ scripts/{gen_env,seed_demo}.py tests/ docs/changes/
Модель данных
organizations → vrfs (по организации, «default» создаётся автоматически) → prefixes (дерево через parent_id,
вложенность определяется автоматически) → addresses; devices + device_types; isps + isp_networks; users; audit_log.
- Ёмкость листового префикса — размер подсети (IPv4 без сетевого/broadcast); родителя — сумма вложенных листьев.
- «Свободные» адреса не хранятся, а вычисляются; в списке адресов они показываются для подсетей до /20.
- Обзор считает использование только по IPv4.
- VRF — часть адресного плана организации: имя уникально в пределах организации (без учёта регистра), в разных организациях имена могут совпадать; в одном VRF может быть много префиксов. Принадлежность VRF организации префикса гарантирует составной FK в БД.
- Смена VRF у префикса (
PATCH /prefixes/{id}сvrf_id) — только среди VRF той же организации; переносится префикс вместе с вложенными, дубль CIDR в целевом VRF → 409 (без частичных изменений), VRF другой организации → 422. - VRF, тип устройства, организация с зависимыми объектами не удаляются (409).
API (/api/v1)
POST /auth/login · GET /auth/me · GET /overview
CRUD: /organizations, /vrfs, /isps, /device-types, /devices, /prefixes, /addresses/{id}
Адреса префикса: GET|POST /prefixes/{id}/addresses (status, q, limit, offset), POST …/addresses/next — автоназначение из пула.
Ошибки: {code, message, fields} (для блокировок/лимитов — дополнительные поля attempts_left, retry_after_seconds). Каждое изменение пишется в audit_log.
Журнал
GET /audit— поиск и фильтры:q(сообщение, метка объекта, начало ID записи),event_type(prefix.created),entity_type,actor(ui:admin,system,anonymous),date_from/date_to(UTC),limit/offset;GET /audit/summary,/audit/facets,/audit/{uid}.GET|PUT /journal/settings— ротация:retention_days(по умолчанию 90) иmax_entries(100 000),0— без ограничения. Ротация идёт раз в час и сразу при сохранении настроек (advisory-lock защищает от параллельного запуска); каждая ротация с удалениями фиксируется записьюjournal.rotated. Запись — только admin.POST /journal/clear {password}— очистка с подтверждением пароля текущего пользователя (admin); 5 неверных попыток за 10 минут → блокировка на 10 минут (429). В журнале остаётся записьjournal.cleared.- IP и метаданные запроса: каждая запись, созданная в рамках HTTP-запроса, хранит
client_ipиmeta(user_agent,method,path,request_id; ответ содержитX-Request-ID); системные события (ротация) — без IP. ФильтрGET /audit?client_ip=принимает IP или подсеть (192.168.5.0/24), текстовый поискqищет и по началу IP. IP берётся из адреса сокета.X-Forwarded-Forучитывается только от прокси изTRUSTED_PROXIES(CIDR через запятую в.env, по умолчанию пусто) — иначе IP можно подделать. Запросы с самой машины через127.0.0.1Docker показывает адресом шлюза сети (172.x.0.1); с LAN-адреса и удалённых хостов виден реальный источник. - В журнал пишутся также входы (
session.login,session.failed— акторanonymous) и служебные события (journal.*, акторsystem).
Поведение таблиц UI
Строка реестра кликабельна целиком (как в журнале): «Префиксы» — лист открывает адреса подсети, родитель сворачивает/разворачивает ветку; «Организации» — префиксы организации; «Операторы», «Устройства» и «Адреса» — окно редактирования (свободный адрес — «Назначить адрес» с этим IP). Ссылки, шеврон, меню «⋯» работают как раньше и не запускают действие строки; Ctrl/Shift+клик и выделение текста тоже игнорируются.
Тесты
Идут против приложения в контейнерах, учётные данные берутся из .env:
docker compose up -d --build && venv/bin/python -m pytest -q