Prod-инцидент: FRR реэкспортировал входящий 10.0.0.0/8 обратно
eBGP-соседям. Причина — ни у одного соседа не было outbound-фильтра
(no bgp ebgp-requires-policy отключал защиту FRR, единственный
route-map RM-PREPEND-OUT permit-all фильтрации не делал).
frr.conf.j2: добавлен ip prefix-list PL-ANYCAST-OUT (permit только
anycast_prefix) и route-map RM-OUT / RM-OUT-PREPEND (фильтр, либо
фильтр+prepend), оба с явным deny в конце. Назначены outbound на
ВСЕХ соседей без исключения — наружу теперь уходит исключительно
анонсируемый anycast-префикс, что бы ни попало в локальную
BGP-таблицу от других соседей.
Уязвимость структурная (в общей роли), поэтому фикс задел и
площадку A — golden-файлы frr/a{1,2,3}/frr.conf синхронизированы
с новым рендером (намеренно, не регресс). no bgp ebgp-requires-policy
сознательно не тронут — его включение требует ещё inbound-policy на
каждого соседа, отдельная задача.
render-check.sh 6/6, render-check-cloud.sh 84/84 (включая nft -c).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bc3HkMiDwooSyLBLoMKNyF
4.3 KiB
Summary: outbound-фильтр BGP (устранение утечки 10.0.0.0/8)
Дата: 2026-09-10 План: 2026-09-10-bgp-outbound-filter-plan.md
Инцидент и причина
В PROD (site-cloud) FRR реэкспортировал входящий маршрут 10.0.0.0/8 обратно eBGP-соседям. Причина — структурная: ни у одного соседа (ни в site-a, ни в site-cloud) не было outbound-фильтра. no bgp ebgp-requires-policy отключал защитный механизм FRR (RFC 8212), который иначе не дал бы поднять eBGP-сессию без явной policy; единственный существующий route-map (RM-PREPEND-OUT, только на резервном канале site-cloud) не фильтровал — permit 10 без match пропускал всё. Роутеры многодомны в разные внешние AS без изоляции по префиксам → любой маршрут в локальной BGP-таблице реэкспортировался всем eBGP-соседям.
Что сделано
ansible/roles/frr_anycast/templates/frr.conf.j2:
- Добавлен
ip prefix-list PL-ANYCAST-OUT seq 5 permit <anycast_prefix>. - Добавлены
route-map RM-OUT(только фильтр) иroute-map RM-OUT-PREPEND(фильтр + prepend, рендерится только при наличии соседей сprepend: true) — оба матчатPL-ANYCAST-OUT, оба с явнымdeny 20в конце. - Каждому соседу в
bgp_neighborsтеперь назначен outbound route-map:RM-OUT-PREPEND, еслиprepend: true, иначеRM-OUT— раньше route-map вообще не навешивался на не-prepend соседей (0% фильтрации на основном канале site-cloud и на обоих соседях site-a). no bgp ebgp-requires-policyсознательно оставлен — включение потребовало бы также inbound-policy на каждого соседа (иначе FRR перестанет принимать вообще все входящие маршруты), это отдельная, не запрошенная сейчас задача.
Golden-файлы площадки A обновлены (frr/a{1,2,3}/frr.conf) — добавлен тот же prefix-list/route-map-блок. Это намеренное изменение: та же уязвимость структурно присутствовала и в Docker-лабе (просто менее заметна, т.к. там оба BGP-соседа логически «свои»), фикс применён единообразно везде, где используется роль.
Результаты проверки
render-check.sh(площадка A) — 6/6 OK против обновлённых golden-файлов.render-check-cloud.sh— 80/80 проверок: у всех 4 узлов ровно один outbound route-map на каждого из 4 соседей (по правильному типу),nft -c -fподтверждает синтаксическую валидность nftables (без изменений в этой части).--syntax-check— OK для обоих playbook'ов.
Не входит в этот фикс (сознательно)
- Inbound-фильтрация (RFC1918/bogon-фильтр на приём) — рекомендована как defense-in-depth, но не реализована.
- Включение
bgp ebgp-requires-policyобратно — требует отдельной проработки inbound-policy на каждого соседа.
Изменённые файлы
ansible/roles/frr_anycast/templates/frr.conf.j2.frr/a1/frr.conf,frr/a2/frr.conf,frr/a3/frr.conf(golden, Docker-лаба).ansible/tests/render-check-cloud.sh— обновлены проверки под новые имена route-map/prefix-list.README.md— пояснение про обязательный outbound-фильтр.docs/2026-09-10-bgp-outbound-filter-plan.md,docs/2026-09-10-bgp-outbound-filter-summary.md.