Collector daemon, FastAPI server (addresses, diff, collect, sources), SQLite storage with change journal, Docker Compose deployment, tests, documentation and project rules. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.6 KiB
План: форматы вывода, агрегация CIDR и ip_version
Context
GET /addresses отдаёт только плоский JSON-список. Потребителям (маршрутизаторы, файрволы) приходится самим конвертировать его в конфигурацию и убирать пересекающиеся префиксы (/23 + /24). Цель: отдавать готовые конфигурации для nftables, MikroTik, BIRD и FRR, добавить агрегацию CIDR и фильтр по версии IP. Текущий ответ по умолчанию остаётся байт-в-байт прежним (старые потребители не ломаются).
Решения пользователя: форматы nftables, mikrotik, bird, frr (плюс текущий JSON); агрегация выключена по умолчанию, включается aggregate=true.
Артефакты по правилам проекта (создаются при реализации)
docs/plan-output-formats.md- копия этого плана (первым шагом)docs/summary-output-formats.md- итоги (в конце)- обновить
README.md(параметры, примеры интеграции)
API
GET /addresses получает параметры (существующий type=cidr|fqdn|all не меняется):
| Параметр | Значения | По умолчанию |
|---|---|---|
format |
json, nftables, mikrotik, bird, frr |
json |
ip_version |
all, 4, 6 |
all |
aggregate |
true/false |
false |
name |
имя списка/набора, ^[A-Za-z][A-Za-z0-9_]{0,31}$ (защита от инъекций в конфиг) |
ripe |
Не-JSON форматы отдаются как text/plain. Невалидные значения -> 422 (валидация FastAPI).
Изменения
1. Новый модуль formatters.py (чистые функции, без обращений к диску и сети)
select(values, ip_version, aggregate):- Путь по умолчанию (
json,aggregate=false): только фильтр по версии (по наличию:в строке), сортировка как сейчас (sorted(set(...))) - вывод не меняется. - Иначе: разбор через
ipaddress.ip_network(v, strict=False)(одиночные IP из FQDN становятся/32и/128), невалидные значения пропускаются сlogging.warning; приaggregate=true-ipaddress.collapse_addressesотдельно для v4 и v6 (после объединения источниковcidrиfqdn, поэтому IP внутри префикса исчезает); сортировка по (версия, сеть). - Для
jsonс агрегацией/32и/128печатаются как «голый» IP, как в текущем README.
- Путь по умолчанию (
render(fmt, v4, v6, name): каждый формат возвращает идемпотентный скрипт; пустая версия пропускается.- nftables (
nft -f):add table inet <name>;add set inet <name> <name>_v4 { type ipv4_addr; flags interval; auto-merge; };flush set ...;add element ... { ... }(то же для_v6,ipv6_addr).auto-mergeнужен, иначе nft отвергает пересекающиеся интервалы. Другие объекты таблицы не затрагиваются. - MikroTik (RouterOS
/import):/ip firewall address-list remove [find list=<name>]затемadd list=<name> address=...; для v6 -/ipv6 firewall address-list. - BIRD 2 (
include):define <NAME>_V4 = [ a/len, ... ];и..._V6- префикс-сеты для фильтров (не статические маршруты: next-hop определяет потребитель). - FRR (
vtysh -f):no ip prefix-list <name>_v4затемip prefix-list <name>_v4 seq 5|10|... permit <prefix>; для v6 -ipv6 prefix-list.
- nftables (
- Все скрипты начинаются с комментарием
# generated <ts>, ip_version=..., aggregate=...(в синтаксисе формата), заканчиваются переводом строки.
2. api_server.py (get_addresses)
- Добавить enum-параметры
format,ip_version,aggregate: bool,name(Query с regex). - Собрать множество как сейчас (
get_cidrs,get_fqdn_ips), затемformatters.selectиrender. - Убрать
response_model=List[str](ответ бывает текстовым); для JSON возвращать список как раньше; для остальных -PlainTextResponse. Описать типы ответов черезresponses=для Swagger. - Ошибки хранилища по-прежнему -> 503 (существующий обработчик).
3. Документация (README.md)
- Таблица параметров и примеры:
- nftables:
curl -s "$URL/addresses?format=nftables&ip_version=4&aggregate=true" | nft -f - - MikroTik:
/tool fetch url="$URL/addresses?format=mikrotik" dst-path=ripe.rscи/import ripe.rsc - BIRD: сохранить в файл,
include,birdc configure - FRR:
curl ... > ripe.conf && vtysh -f ripe.conf
- nftables:
- Примечания: имя
nameопределяет имена наборов; у MikroTik замена списка даёт короткое окно без записей; агрегация удаляет/32внутри более широкого префикса.
4. Тесты (минимум 3, tests/test_formats.py, запуск в контейнере)
select: по умолчанию вывод равен прежнему (сортировка, без /32),ip_version=4|6фильтрует,aggregate=trueсхлопывает/23+/24и вложенный host-IP.render: параметризованный тест по 4 форматам - ключевые строки (для v4 и v6, пустая версия пропущена).- API: дефолтный
/addressesвозвращает JSON-список,format=mikrotik-text/plain, некорректноеname-> 422 (данные подменены черезcc.DATA_FILE).
Критичные файлы
Новый formatters.py; api_server.py (get_addresses); tests/test_formats.py; README.md; Dockerfile.test не меняется (COPY . . подхватит модуль).
Проверка
docker build -f Dockerfile.test -t ripe-collector-test . && docker run --rm ripe-collector-test- все тесты (включая 3 прежних) проходят.- На копии реальных данных:
curl /addressesдо и после изменений даёт идентичный ответ (сравнить diff). - Вручную:
format=...для каждого формата;aggregate=trueуменьшает число строк;ip_version=6не содержит IPv4. - Проверка синтаксиса генерируемых конфигов в контейнерах, где возможно: BIRD (
bird -p -c), nftables (nft -c -f, если хватит прав). MikroTik и FRR сверить по документации (эмулятора нет); риск: поведениеno ip prefix-listдля несуществующего списка в FRR - проверить по документации.