Skip to content

Latest commit

 

History

History
251 lines (186 loc) · 18 KB

README.RUS.md

File metadata and controls

251 lines (186 loc) · 18 KB

SDBGP - приложение для управления BGP маршрутами в RIB GoBGP

Задача

Задача - в целях защиты периметра от внешних атак необходимо на границе сети фильтровать входящий трафик с известных (враждебных) префиксов. Префиксов таких многие тысячи, поэтому фильтровать с помощью ACL не вариант. Емкость ACL на маршрутизаторах ограничена. Кроме того любая масштабная ручная настройка неудобна и чревата ошибками. Особенно когда маршрутизаторов больше одного.

Решение

Решение - использовать технику защиты от ДДоС под названием: Remote Triggered Black Hole Filtering with Unicast Reverse Path Forwarding (uRPF) или S/RTBH
Эта техника описанна в RFC 5635

Суть техники следующая:
Нам нужен отдельный BGP демон, в RIB которого мы будем помещать нужные префиксы, а префиксы эти он будет анонсировать граничным маршрутизаторам с определенным next-hop-ом. На маршрутизаторах необходимо сделать всего две настройки:

  1. сеть в этот next-hop смаршрутизировать в Null
  2. на внешних интерфейсах включить uRPF в режиме loose. Таким образом будут отброшены входящие пакеты с враждебных префиксов

В качестве отдельного BGP демона выступает open-source продукт goBGP
Это надежный быстрый легковесный демон с развитой функциональностью. А главное - есть пограммный интерфейс (через grpc, в том числе и для python) и он не сказать что просто, но при должном усилии интегрируется в любой пограммный продукт. Собственно такой продукт здесь и представлен - это веб-приложение с рабочим названием SDBGP.
Приложение состоит из бекенда на python с использованием библиотеки FastAPI и базы данных Mongo, а так же фронтенда написанного на React. Такой стек достаточно популярен и даже имеет собственный акроним FARM (FastApi + React + Mongo)

Схема решения:

Стоит сказать, что ровно в такую же схему решения вписывается более подвинутая техника защиты от ДДоС под названием BGP Flowspec rfc5575. И конечно же goBGP эту продвинутую технологию поддерживает и поэтому Flowspec тоже реализован в приложении

Внешний вид

При этом в goBGP видим такую картину

$ docker exec -it sdbgp_gobgp_1 gobgp global rib | grep 101.7
*> 101.71.121.196/32    0.0.0.0                                   05:31:34   [{Origin: ?}]
*> 101.74.239.6/32      0.0.0.0                                   05:31:34   [{Origin: ?}]
*> 101.75.120.22/32     0.0.0.0                                   05:31:34   [{Origin: ?}]

и в маршрутизаторе

c4331-test#sh ip bgp | i 101.7
 *>i  101.71.121.196/32
 *>i  101.74.239.6/32  172.20.0.2                    100      0 ?
 *>i  101.75.120.22/32 172.20.0.2                    100      0 ?

Заметили, что next-hop вместо 0.0.0.0 стал 172.20.0.2?
Об этом отдельная заметка в примечании

Сверху две вкладки:

  1. BGP Unicast - вариант защиты S/RTBH
  2. BGP Flowspec - продвинутый вариант защиты FlowSpec

Название вкладок происходит от используемых Address Family (AFI). При желании можно будет дописать сюда поддержку и других AFI например ipv6-uniсast и ipv6-flowspec-unicast

Дальше идет поле фильтрации префиксов.

фильтрация происходит в вашем браузере и когда префиксов становится несколько тысяч это становится заметно. поэтому поле имеет ограничение в 5000 префиксов после которого поле становится неактивным. ограничение можно задать переменонй окружения SEARCH_FIELD_LIMIT

Затем как видно идет таблица префиксов с кнопками редактирования и удаления для каждого префикса. Ну в общем тут все должно быть понятно.

Далее два ряда кнопок

  1. Кнопки управления состоянием в приложении
  2. Кнопки управления состоянием в RIB BGP демона

Проще развернуть и потренироваться чтоб понять что к чему

Разворачивание приложения

Prerequisits

Приложение использует следующие компоненты:

  1. GoBGP - собственно bgp демон
  2. MongoDB - промежуточное хранение префиксов
  3. Python3 - бекенд приложения
  4. React - фронтенд приложения

GoBGP

GoBGP должен быть установлен и доведен до рабочего сотстояния.
Есть в стандартных линукс дистрибутивах, на гитхабе, докерхабе итд.
Для ubuntu достаточно такого

$ sudo apt install gobgpd
$ cat /etc/gobgpd.conf 
[global.config]
  as = 65100
  router-id = "192.168.255.1"
$ sudo systemctl start gobgpd

Приложение использует protobuf файлы, которые могут отличаться в разных версиях goBgp

В репозитории уже есть эти файлы для версии goBgp 2.12.0 (версия по цмолчанию Ubuntu 20.4 LTS)

Но если ваша версия goBgp отличается, то возможно приложение не зарабоает как надо и будет необходимо получить протобуфы именно для вашей версии

Делается это следующим образом

  1. выяснить текущую установленную версию goBgp

    $ gobgp --version
    gobgp version 2.12.0

  2. Зайти в gobgp репозиторий найти там подходящий релиз и получить хеш релиза (a4b688a)

  3. склонировать репозитарий

    $ git clone https://github.com/osrg/gobgp.git gobgp_repo

  4. перейти в репозитарий и получить слепок релиза

    $ cd gobgp_repo
    $ git checkout a4b688a

  5. скопировать *proto файлы в рабочий каталог

    $ cd ..
    $ cp gobgp_repo/api/*proto .

  6. из полученных protobuf файлов сгенерировать python-библиотеки

    $ python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. *.proto

  7. убедиться что появились/обновились файлы *.py соответствующие по названиям файлам *.proto:

    gobgp_pb2.py, gobgp_grpc_pb2.py, attribute_pb2.py, attribute_pb2_grpc.py *.proto файлы нам больше не понадобятся

Mongo

> apt install mongo //или как то так

Я не сильно знаком с монго, возможно, нужно заранее зайти и обозначить используемую в проекте базу данных sdbgp

> $ mongo  
> use sdbgp  
> exit  

Python

Бекенд реализован на FastAPI, и использует ряд модулей для работы с mongo и grpc
Окружение создается как обычно:

> $ cd backend
> $ python3 -m venv venv  
> $ source venv/bin/activate  
> $ pip3 install -r requirements.txt  

Далее необходимо скопировать config.py.example в config.py
и наверное имеет смысл заглянуть внутрь config.py и возможно подправить некоторые параметры

> cp config.py.example config.py

теперь можно запускать

> $ uvicorn routes:app

React

> $ cd frontend
> $ npm install

тут так же как и в случае с backend есть свой файл конфигурации src/config.js
но на данном этапе скорей всего его править не придется

> $ npm start

На этом шаге через некоторое время должен стартовать браузер и в отдельной его вкладке загрузиться приложение. Теперь мы можем добавлять свои префиксы или политики, сохранять их в базу, а из базы отправлять из в RIB демона goBGP

Для того чтобы увидеть состояние RIB пригодится пара CLI команд:

> gobgp global rib
> gobgp global rib -a ipv4-flowspec

Так же из командной строки можно инжектить префикcы или политики flowspec сразу в RIB:

> gobgp global rib add 1.2.3.56/32 nexthop 0.0.0.0 > gobgp global rib -a ipv4-flowspec add match source 3.3.3.3/30 destination 5.5.5.5/32 protocol '==tcp&==udp' destination-port '80,443' then discard

подробней в мануале goBGP

Описанный здесь процесс развернет приложение на localhost. Это удобно для разработки, но скорей всего не подойдет для прода.

Разворачивание с использованием Docker

Не знаю как в других дистрибуливах, но в Убунту нет возможности получить актуальную версию goBGP. Для версии Ubuntu 20.04LTS в пакетах идет достаточно старая 2.12, а для 18.04LTS идет совсем древняя версия 1.хх.
Поэтому возникают некоторые сложности на не самых свежих системах.

Здесь на помощь приходит докер.
Для моих экспериментов я выбрал эту сборку https://hub.docker.com/r/jauderho/gobgp

Причем так как я изначально в этом проекте начинал работать с версией goBGP 2.12, то не стал переходить на 3-ю ветку и взял последнюю версию из 2-й ветки - 2.34.
И версия поновее и протобуфы оказались совместимы

Изначально я не планировал докеризировать все приложение, а начинал использовать докер для того чтобы собрать виртуальную лабу с виртуальным же роутером.
И об этой лабе есть отдельная статья: Пример использования приложения в среде docker

Затем, когда приложение более менее выросло, стало понятно, что разворачивать его на проде та еще задача.
Заводить приложение на выделенный для него сервер как-то расточительно, а на существующий продовый сервер придется затащить кучу каких то библиотек, модулей, зависимостей. Под публикацию приложения надо будет перенастраивать существующий веб-сервер. А при удалении приложения хорошо было бы все вернуть обратно. И стабильность продового сервера в этот момент под вопросом. Работа достаточно скрупулезная, и нужна она ровно один раз потому что каждый следующий деплой скорее всего будет не похож на предыдущий.
Вот для таких сценариев Docker, а точнее docker-compose подходят как нельзя лучше.
Пришлось немного (пару дней) повозиться, но докеризацию осилил и теперь считаю этот метод деплоя предпочтительным. О том как разворачивать приложение с помощью docker-compose в отдельной статье Deploy with Docker


Примечания

Конфигурация gobgp

конфигурация gobgp хранится в gobgp_config/gobgp.toml

[global.config]
  as = 65100
  router-id = "1.1.1.1"

[[neighbors]]
  [neighbors.config]
    neighbor-address = "10.2.0.123"
    peer-as = 65100
  [[neighbors.afi-safis]]
    [neighbors.afi-safis.config]
      afi-safi-name = "ipv4-flowspec"
  [[neighbors.afi-safis]]
    [neighbors.afi-safis.config]
      afi-safi-name = "ipv4-unicast"

[[defined-sets.prefix-sets]]
  prefix-set-name = "px-all"
  [[defined-sets.prefix-sets.prefix-list]]
    ip-prefix = "0.0.0.0/0"
    masklength-range = "1..32"

[[defined-sets.neighbor-sets]]
  neighbor-set-name = "ext-neigh"
  neighbor-info-list = ["10.0.0.0/8"]

[[policy-definitions]]
  name = "policy-reject-all-from-ext-neigh"
  [[policy-definitions.statements]]
    name = "statement1"
    [policy-definitions.statements.conditions.match-prefix-set]
      prefix-set = "px-all"
      match-set-options = "any"
    [policy-definitions.statements.conditions.match-neighbor-set]
      neighbor-set = "ext-neigh"
      match-set-options = "any"
    [policy-definitions.statements.actions]
      route-disposition = "reject-route"

[global.apply-policy.config]
import-policy-list = ["policy-reject-all-from-ext-neigh"]
default-import-policy = "accept-route"
default-export-policy = "accept-route"

основной конфиг содержится в первых 14 строках сюда входит параметры global-config и прописывание neighbors и две address-family (AFI)

А начиная с 15-й строчки идет простая по сути, но многословная по форме policy, которая запрещает получать префиксы от BGP соседей. Полиси необходима из-за тогоу что в проде соседями у нас выступают реальные интернет бордеры. А в RIB каждого такого бордера находится под миллион префиксов (FullView). И нам нет никакого смысла принимать все эти маршруты

next-hop при использовании gobgp в docker

В стандартном network типа bridge в docker при соединении с внешними сетями происходит трансляция в адрес интерфейса хоста. При этом конечно внутри BGP сессии параметры никак не видоизменяются и атрибут next-hop приходит с внутренним адресом контейнера.
Но для нашей задачи это даже несет некоторое преимущество. Мы можем оставить для наших анонсируемых префиксов значение next-hop по умолчанию (0.0.0.0) и нам не нужно заботиться, о том, что трафик на эти префиксы польется в сторону демона. Более того мы зная адресацию docker network (172.20.0.0/16) можем заранее заблекхолить эту сеть на маршрутизаторах и сразу получить желаемое поведение

ToDo

  1. сделать авторизацию (генерация и проверка jwt на беке готова)
  2. сделать логи аудита - кто (авторизация) когда чего нажал. поместить в отдельную вкладку
  3. сгенерировать help.md загрузить в вкладку help