
Инциденты с prefix hijacking случаются регулярно, когда трафик массово отправляется не тем путем, которым должен. Некоторые из них, может, и связаны со злым умыслом, но большинство случайны и происходят по вине невнимательных админов. Сегодня я расскажу, как провайдеры могут предотвратить их или свести к минимуму, а заодно и как самому не стать их виновником.
Протокол маршрутизации BGP сам по себе не содержит никакой информации о том, кому принадлежит та или иная сеть. Анонсировать чужие префиксы при этом вполне обычная практика — небольшие сети анонсируют свои сети транзитным провайдерам с развитой сетью, а те уже анонсируют их всем остальным. Кроме того, на точках обмена трафиком активно используются так называемые сервера маршрутов (route servers), которые позволяют получать маршруты ко всем сетям участников точки сразу, без настройки отдельной сессии с каждым из них.
BGP намеренно спроектирован так, чтобы можно было фильтровать и модифицировать маршруты произвольным образом без нарушения работы самого протокола. , к примеру, принципиально не позволяет даже фильтровать входящие маршруты: поскольку каждый маршрутизатор OSPF строит и поддерживает полную карту соединений в сети, отказ одного маршрутизатора запоминать часть сети может нарушить ее связь со всеми остальными. Протоколы с отслеживанием состояния каналов (link-state) вроде OSPF прекрасно подходят для внутренней маршрутизации, где на выбор пути влияют только технические соображения: длина пути и его пропускная способность.
Поскольку интернет — объединение независимых сетей, в силу вступают экономические и политические факторы. Самый короткий путь не всегда самый выгодный. Из-за этого реализации BGP вынужденно позволяют такие виды фильтрации и модификации анонсов, которые неприемлемы в других протоколах.
Отсутствие аутентификации открывает большой простор для случайных ошибок и намеренных злоупотреблений. Давай разбираться, как их избежать.
Фильтрация исходящих анонсов
Прежде чем рассматривать механизмы защиты от чужих ошибок, давай рассмотрим, как не стать виновником инцидента самому.
Случайная утечка одного префикса встречается крайне редко, в большинстве случаев утекает вся таблица маршрутов. Эта ситуация весьма неприятна для всех участников. Инициатор ненамеренно становится транзитным провайдером для жертв и берет на себя расходы по передаче их трафика. Если сеть не готова к возросшей нагрузке, такой инцидент способен положить и сеть инициатора, и сети жертв.
Неопытные админы, которые до этого работали с единственным подключением к провайдеру, часто забывают про фильтры.
Предположим, есть вот такой конфиг:
Код:
router bgp 64496
network 192.0.2.0/24
neighbor 203.0.113.10 remote-as 64500
neighbor 203.0.113.10 description AwfulTransit
Сети и номера AS из частных диапазонов выделены для того, чтобы внутренние ресурсы организаций не конфликтовали в публичном интернете, а зарезервированные для документации — чтобы бездумное применение примеров из статей не конфликтовало с реальными сетями.
Мы анонсируем провайдеру сеть 192.0.2.0/24. Провайдер анонсирует нам все маршруты интернета. В этой ситуации никакие фильтры не нужны, потому что ни один протокол маршрутизации не анонсирует полученные от соседа маршруты обратно ему же. Это базовый механизм предотвращения петель маршрутизации.
В то же время все протоколы, включая BGP, анонсируют все маршруты всем остальным соседям, если не указано обратное. Чем больше связность сети и число альтернативных маршрутов к каждой сети, тем она надежнее. Если, конечно, все участники могут и согласны пропускать через себя этот трафик — а клиенты провайдеров не готовы и не согласны.
Предположим, что ты добавил подключение ко второму провайдеру:
Код:
router bgp 64496
neighbor 203.0.113.20 remote-as 64510
С такими настройками твой маршрутизатор начнет анонсировать маршруты одного провайдера другому.
Чтобы этого не произошло, нужно явно указать, что анонсировать.
Если у тебя всего один маршрутизатор BGP и все маршруты приходят из какого-то внутреннего протокола или опций network, проще всего отфильтровать по пустому AS path. Номер автономной системы добавляется в путь маршрута не в момент его зарождения, а в момент анонса, поэтому у локально порожденных маршрутов он пустой. Пустой строке соответствует регулярное выражение ^$.
Код:
bgp as-path access-list LocalOnly permit ^$
!
route-map LocalOnly permit 10
match as-path LocalOnly
!
На практике в случае с клиентом и провайдером при утечке таблицы провайдер автоматически отключит сессию клиента или проигнорирует лишние маршруты. Успешный захват префиксов обычно происходит между равноправными сетями: транзитными провайдерами или участниками точки обмена трафиков.
Как провайдеры это предотвращают? Самая простая и неспецифичная мера защиты — опция maximum-prefix.
Опция maximum-prefix
Эта опция делает ровно то, о чем говорит ее название. Если количество сетей в анонсе соседа превышает указанное значение, сессия автоматически обрывается и соседу отправляется BGP notification.
В FRR это делается вот так:
Код:
router bgp 64500
address-family ipv4 unicast
neighbor 192.0.2.10 maximum-prefix 1
Альтернативы
Увы, все альтернативы в рамках самого BGP — либо немасштабируемые, либо чисто эвристические.
Очевидный вариант, подходящий для провайдеров и клиентов — явный список разрешенных префиксов. В этом случае клиенту также нужно уведомлять провайдера каждый раз при появлении новой сети, но новые префиксы не обрушат внезапно всю сессию — и можно разрешить не точное совпадение, а сеть со всеми ее подсетями.
Например, разрешим принимать от клиента сети 192.0.2.0/24 и 203.0.113.0/24 вместе со всеми их подсетями вплоть до /32:
Код:
!
ip prefix-list MyPrefixes seq 10 permit 192.0.2.0/24 le 32
ip prefix-list MyPrefixes seq 20 permit 203.0.113.0/24 le 32
!
route-map Transit-Out permit 10
match ip address prefix-list MyPrefixes
!
router bgp 64500
neighbor 198.51.100.1 remote-as 64501
!
address-family ipv4 unicast
neighbor 198.51.100.1 route-map Transit-Out out
exit-address-family
!
Почему не [0-9]+? Многие люди для контроля за тем, через какого провайдера к ним пойдет входящий трафик, используют опцию AS path prepend, которая искусственно делает путь длиннее, чем он есть, поскольку добавляет в него локальную автономную систему несколько раз. Выражение, которое соответствует пути из ровно одного номера AS, запретило бы такие анонсы.
Ни один из этих вариантов не подходит для пиринга между провайдерами или участниками точек обмена трафиком. Знать содержимое анонсов наперед в этих случаях невозможно, нужно проверять именно аутентичность анонсов.
Сам BGP используется еще с девяностых, но механизм для проверки аутентичности анонсов появился недавно и называется (RPKI).
RPKI
RPKI — сравнительно новый механизм, который позволяет автоматически проверять принадлежность префиксов к автономным системам.
RPKI не является частью BGP и не ограничивается им. По сути, это эквивалент whois с цифровыми подписями и набором протоколов для их автоматической проверки. Подписывать сами анонсы было бы бессмысленно, поскольку их модификация при передаче неизбежна — каждый маршрутизатор должен добавить в AS path свой номер автономной системы. Вместо этого BGP origin validation проверяет соответствие адреса сети и автономной системы источника — крайнего правого номера в AS path.
К сожалению, RPKI — не настоящее, а будущее. Еще далеко не все операторы автономных систем участвуют в ней, несмотря на очевидные преимущества, поэтому просто отфильтровать все анонсы, для которых нет данных валидации, не выйдет. Можно только отфильтровать явно не соответствующие автономной системе источника. Тем не менее ознакомиться с ней и увидеть ее в работе сейчас может каждый.
Хранение базы данных соответствия сетей и автономных систем и проверка подписей производятся не на самом маршрутизаторе, а на отдельном сервере — для экономии ресурсов.
Запускаем сервер RPKI
Для демонстрации потребуется хост для сервера RPKI с любой UNIX-подобной системой, программы rsync и JDK8+ и два хоста с установленным FRR.
Прежде чем настраивать что-то в FRR, нужно запустить сам сервер RPKI. Мы будем использовать реализацию RIPE, которая состоит из двух компонентов: rpki-validator-3 и rpki-rtr-server.
Запускаем rpki-validator
Сначала нужно запустить . Он доступен в виде образа для Docker и пакетов RPM для CentOS 7, а также в виде обычного tgz-архива с исполняемым файлом, который запускается на любой системе.
Код:
$ wget https://ftp.ripe.net/tools/rpki/validator3/prod/generic/rpki-validator-3-latest-dist.tar.gz
$ tar xfz ./rpki-validator-3-latest-dist.tar.gz
$ cd rpki-validator-<номер версии>
$ ./rpki-validator-3.sh
Если ты планируешь запускать оба сервиса на одной машине, дополнительная настройка не требуется. Если нет, нужно поправить опцию server.address в conf/application.properties: по умолчанию он слушает только на localhost.
Запускаем rpki-rtr-server
Сам протокол RTR реализует другой проект: . Для своей работы он требует запущенного rpki-validator, поэтому предыдущий шаг пропустить нельзя. Если он не сможет подключиться к валидатору, он сообщит об этом исключением вида I/O error on GET request for "": Connection refused (Connection refused).
Процедура его запуска столь же проста:
Код:
$ wget https://ftp.ripe.net/tools/rpki/validator3/prod/generic/rpki-rtr-server-latest-dist.tar.gz
$ tar xfz ./rpki-rtr-server-latest-dist.tar.gz
$ cd rpki-rtr-server-<номер версии>
$ ./rpki-rtr-server.sh
После того как оба сервиса успешно запустятся, можно настроить сам FRR.
Настраиваем FRR
Клиент RPKI реализован в библиотеке . RTR здесь — RPKI to Router protocol, протокол обмена данными между сервером RPKI и маршрутизатором. Большинство свободных реализаций BGP, включая FRR, используют именно ее.
По умолчанию RPKI в BGPd выключен, и чтобы его включить, нужно поправить /etc/frr/daemons:
Код:
bgpd_options=" --daemon -A 127.0.0.1 -M rpki"
Код:
rpki
rpki polling_period 1000
rpki timeout 10
rpki initial-synchronisation-timeout 30
rpki cache 192.168.56.1 8323 preference 1
Код:
Connected to group 1
rpki tcp cache 192.168.56.1 8323 pref 1
Код:
router bgp 64496
neighbor 192.0.2.10 remote-as 64501
!
address-family ipv4 unicast
neighbor 10.46.1.100 route-map RPKI-Test in
exit-address-family
!
route-map RPKI-Test permit 10
match rpki invalid
set local-preference 5
Код:
ip route 1.1.1.0/24 blackhole
router bgp 64501
neighbor 192.0.2.1 remote-as 64496
!
address-family ipv4 unicast
network 1.1.1.0/24
exit-address-family
Код:
Network Next Hop Metric LocPrf Weight Path
*> 1.1.1.0/24 192.0.2.10 0 5 0 64501 i