Files
ayurishchevandClaude Sonnet 5 bcf8156085 Initial commit: RIPE CIDR/FQDN collector
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>
2026-09-21 07:29:38 +03:00

66 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: форматы вывода, агрегация 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`.
- Все скрипты начинаются с комментарием `# 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`
- Примечания: имя `name` определяет имена наборов; у MikroTik замена списка даёт короткое окно без записей; агрегация удаляет `/32` внутри более широкого префикса.
### 4. Тесты (минимум 3, `tests/test_formats.py`, запуск в контейнере)
1. `select`: по умолчанию вывод равен прежнему (сортировка, без /32), `ip_version=4|6` фильтрует, `aggregate=true` схлопывает `/23`+`/24` и вложенный host-IP.
2. `render`: параметризованный тест по 4 форматам - ключевые строки (для v4 и v6, пустая версия пропущена).
3. 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 . .` подхватит модуль).
## Проверка
1. `docker build -f Dockerfile.test -t ripe-collector-test . && docker run --rm ripe-collector-test` - все тесты (включая 3 прежних) проходят.
2. На копии реальных данных: `curl /addresses` до и после изменений даёт идентичный ответ (сравнить diff).
3. Вручную: `format=...` для каждого формата; `aggregate=true` уменьшает число строк; `ip_version=6` не содержит IPv4.
4. Проверка синтаксиса генерируемых конфигов в контейнерах, где возможно: BIRD (`bird -p -c`), nftables (`nft -c -f`, если хватит прав). MikroTik и FRR сверить по документации (эмулятора нет); риск: поведение `no ip prefix-list` для несуществующего списка в FRR - проверить по документации.