FastNetMon — добавляем ExaBGP в контейнер и настраиваем автобан.

В прошлой статье мы подготовили docker-контейнер для FastNetMon’a и настроили мониторинг в Grafana. Тестирование показало что трафик мониторится коректно, нагруженные и служебные подсети были перенесы в отдельные группы. Пришло время расширить функционал контейнера и добавить ExaBGP.

ExaBGP это инструмент для внедрения произвольных маршрутов в сеть, включая IPv6 и FlowSpec. Вместе с FastNetMon позволяет автоматически детектить и блокировать DDoS-атаки. Варианты использования инструмента описаны на страничке проекта. В моем случае оборудованиие не поддерживает FlowSpec, поэтому все настраивается под анонсы в blackhole-community.

Первым делом добавил exabgp в контейнер:

FROM alpine:3.12 as builder
RUN apk add --no-cache cmake g++ make git autoconf automake libtool boost-dev boost-thread boost-system boost-regex boost-program_options ncurses-dev libpcap-dev
RUN git clone https://github.com/pavel-odintsov/fastnetmon && cd fastnetmon && git reset --hard 5a1db9e4db37bd00df3d57751f83aac4fc08d25e
RUN cd / && wget --no-check-certificate https://sourceforge.net/projects/log4cpp/files/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-1.1.1.tar.gz/download -O /log4cpp-1.1.1.tar.gz && tar zxvf /log4cpp-1.1.1.tar.gz && rm /log4cpp-1.1.1.tar.gz && cd /log4cpp && ./configure --prefix=/opt/log4cpp1.1.1 && make && make install
RUN cd / && git clone -b json-c-0.13 https://github.com/json-c/json-c.git && cd /json-c && ./autogen.sh && ./configure --prefix=/opt/json-c-0.13 && touch aclocal.m4 Makefile.in && make && make install
RUN cd / && git clone -b 2.2.2-stable https://github.com/ntop/nDPI.git && cd /nDPI && ./autogen.sh && ./configure --prefix=/opt/ndpi && make && make install
RUN mv /fastnetmon /fastnetmon_src && \
    mkdir -p /fastnetmon_src/src/build && \
    cd /fastnetmon_src/src/build && \
# Build with fresh release of libndpi
    sed -i 's/set(NDPI_INCLUDE_DIRS "\${FASTNETMON_LIBRARIES_GLOBAL_PATH}\/ndpi\/include\/libndpi-1.7.1")/set(NDPI_INCLUDE_DIRS "\${FASTNETMON_LIBRARIES_GLOBAL_PATH}\/ndpi\/include\/libndpi-2.2.2")/' ../CMakeLists.txt && \
# Some fixes, because builds crashes with errors 
    sed -i '5a\#include \<sys\/time.h\>\' ../fastnetmon_simple_packet.h && \
    sed -i '4a\#define _BSD_SOURCE\' ../fast_endianless.hpp && \
    sed -i '5a\#include \<endian.h\>\' ../fast_endianless.hpp && \
# Hack to disable AF_PACKET support, it builds with error and we don't need it for NetFlow anyway
    sed -i 's/if\s*(ENABLE_AFPACKET_SUPPORT)/set (ENABLE_AFPACKET_SUPPORT OFF)\nif (ENABLE_AFPACKET_SUPPORT)/' ../CMakeLists.txt && \
    cmake .. -DDISABLE_PF_RING_SUPPORT=ON -DDISABLE_NETMAP_SUPPORT=ON -DENABLE_LUA_SUPPORT=NO && \
    make && \
    cd /fastnetmon_src/src
RUN mkdir -p /configs && \
cp /fastnetmon_src/src/fastnetmon.conf /configs/fastnetmon.conf && \
echo -e "192.168.0.0/16\n172.16.0.0/12\n10.0.0.0/8" > /configs/networks_list && \
cp /fastnetmon_src/src/notify_about_attack.sh /configs && \
chmod 755 /configs/notify_about_attack.sh && \
echo -e "#! /bin/sh\n echo "Starting fastnetmon"\n/fastnetmon --log_file /dev/stdout & env exabgp.daemon.user=root exabgp.daemon.daemonize=true exabgp.daemon.pid=/var/run/exabgp.pid exabgp.log.destination=/var/log/exabgp.log exabgp /etc/exabgp_blackhole.conf" > /start.sh && \
chmod 755 /start.sh && \
    cp /opt/json-c-0.13/lib/libjson-c.so* /usr/lib/ && \
    cp /opt/ndpi/lib/libndpi.so* /usr/lib/ && \
    cp /opt/log4cpp1.1.1/lib/liblog4cpp.so* /usr/lib/ && \
    cp /fastnetmon_src/src/fastnetmon.conf /etc/fastnetmon.conf && \
    cp /fastnetmon_src/src/build/fastnetmon / && \
    cp /fastnetmon_src/src/build/fastnetmon_client / && \
    tar czf /build.tgz /usr/lib/libjson-c.so* /usr/lib/libndpi.so* /usr/lib/liblog4cpp.so* /fastnetmon_client /fastnetmon /configs/*

FROM alpine:3.12
RUN apk add --no-cache boost-thread \
	boost-system \
	boost-regex \
	boost-program_options \
	libpcap \
	libstdc++ \
	ncurses \
	curl \
	socat \
	python2 \
	py2-setuptools
COPY --from=builder /build.tgz /start.sh /
RUN tar zxf /build.tgz && \
curl -L https://github.com/Exa-Networks/exabgp/archive/3.4.20.tar.gz | tar zx && \
cd exabgp-3.4.20 && python2.7 setup.py install && \
cd / && \
rm -rf exabgp-3.4.20 && \
ln -s /configs/fastnetmon.conf /etc/fastnetmon.conf && \
ln -s /configs/networks_list /etc/networks_list && \
ln -s /configs/notify_about_attack.sh /usr/local/bin/notify_about_attack.sh && \
ln -s /configs/exabgp_blackhole.conf /etc/exabgp_blackhole.conf
VOLUME ["/configs"]
CMD ./start.sh

Файлик доступен по ссылке. Сборка контейнера все также состоит из трех команд:

git clone https://github.com/FlashBurn-inc/fastnetmon-netflow
cd fastnetmon-netflow
docker build -t fastnetmon-exabgp -f Dockerfile-exabgp

Теперь с контейнерами необходимо строить BGP-сессии и нужно чтобы у контейнеров всегда был постоянный айпи. Для того чтобы контейнеру можно было асайнить постоянный айпи необходимо создать пользовательскую сеть в docker:

docker network create --subnet=172.18.0.0/24 fnm

BGP-сессии необходимо строить c BGP-соседом, поэтому следуя примерам конфигов для FastNetMon я также поднял на локальном сервере Quagga.

apt install -y quagga
cd /etc/quagga/
touch daemons
echo -e "zebra=yes\n bgpd=yes" >> daemons
touch /etc/quagga/zebra.conf
touch /etc/quagga/bgpd.conf
touch /etc/quagga/quagga.conf
chown quagga:quagga /etc/quagga/bgpd.conf
chown quagga:quagga /etc/quagga/zebra.conf
chown quagga:quagga /etc/quagga/quagga.conf
service zebra restart

Теперь правим скрипт для генерации волумов и конфигов для контейнеров. Кроме конфигов для контейнеров также генерим конфиг для bgpd сервиса Quagga:

#!/usr/bin/env bash

# WARNING
# This script will remove all unused docker volume

echo y | docker volume prune

GRAPHITE_HOST=10.10.24.25

#quagga conf
cat << bgpd > /etc/quagga/bgpd.conf
hostname SoftBGP
password zebra987
enable password zebra987
log file /var/log/quagga/bgpd.log
debug bgp events
debug bgp filters
debug bgp fsm
debug bgp keepalives
debug bgp updates
router bgp 1234
bgp router-id 10.10.24.24
bgp log-neighbor-changes

neighbor 10.10.24.11 remote-as 12345

bgpd

for AS in $(cat AS | grep -v "#");do

echo "$AS"
docker volume create $AS
VOLUME_PATH=$(docker volume inspect $AS | grep "Mountpoint" | awk -F\" '{print $4}')
echo "$VOLUME_PATH"
cp networks_list $VOLUME_PATH/networks_list
cp fastnetmon.conf $VOLUME_PATH/fastnetmon.conf
IP=$(echo $AS | sed 's/^65//g' | sed 's/\(^0\)\1//g' | sed 's/\(^0\)//g')
cp exabgp_blackhole.conf $VOLUME_PATH/exabgp_blackhole.conf
sed -i 's/\$AS/'$AS'/g' $VOLUME_PATH/fastnetmon.conf
sed -i 's/\$GRAPHITE_HOST/'$GRAPHITE_HOST'/g' $VOLUME_PATH/fastnetmon.conf
sed -i 's/\$AS/'$AS'/g' $VOLUME_PATH/exabgp_blackhole.conf
sed -i 's/\$IP/'$IP'/g' $VOLUME_PATH/exabgp_blackhole.conf
cp notify_about_attack.sh $VOLUME_PATH/notify_about_attack.sh
#bgpd
echo "neighbor 172.18.0.$IP remote-as $AS" >> /etc/quagga/bgpd.conf
sed -i 's/\$IP/'$IP'/g' $VOLUME_PATH/fastnetmon.conf
done

Переменные для скрипта все теже: sflow-пакеты летят с номера порта равного номеру фейковой AS внутри сети, потому для генерации конфига по sflow нам нужно две переменных:
— порт на котором слушать трафик;
— IP ifluxdb-базы куда программа будет отправлять статистику.

Для файла exabgp_blackhole.conf нужен номер AS и IP для пиринга. Не долго думая я решил оставить точно такой же номер AS для контейнера как и у анализируемой AS. Т.е. если мы принимаем пакеты с AS65010, то и в контейнере будет поднят пир от точно такой же AS65010, разница только в том что внутри сети эти AS являются частью конфедерации, а из контейнера анонсятся по eBGP как независимая AS. Решение не красивое и не правильное (так делать не надо!) ибо в итоге апстримам в блекхолы летят IP как будто не принадлежащие нашей сети с 3мя AS-path, но так как блекхолы апстримов отрабатываю эти анонсы, то внутренний перфекционист закусил губу и промолчал.

Все примеры файлов конфигов упоминающиеся в скрипте можно взять на странице проекта FastNetMon, так как они для каждого индивидуальные я свои не привожу, но считаю по скрипту понятно какие параметры в них я заменил переменными.

После того как скрипт создал волумы и сгенерировал конфиги, перезапускаем сервис bgpd:

service bgpd restart

И с помощью также поправленного скрипта запускаем контейнеры:

#!/usr/bin/env bash

docker rm -f $(docker ps -a | awk '{print $1}')

for AS in $(cat AS | grep -v "#");do

docker run -it --name $AS -d --net fnm --ip 172.18.0.$(echo $AS | sed 's/^65//g' | sed 's/\(^0\)\1//g' | sed 's/\(^0\)//g') -p $AS:$AS/udp --mount source=$AS,target=/configs fastnetmon-exabgp

sleep 5
done

Спустя пару минут проверяем что там с нашими сессиями:

/usr/bin/vtysh -d bgpd -c "show ip bgp summary"
BGP router identifier 10.10.24.24, local AS number 1234
RIB entries 0, using 0 bytes of memory
Peers 33, using 293 KiB of memory

Neighbor        V         AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
10.10.24.11     4 12345    7136    7304        0    0    0 4d22h54m        0
172.18.0.11     4 65011    7139    7298        0    0    0 4d22h54m        0
172.18.0.12     4 65012    7141    7296        0    0    0 4d22h54m        0
172.18.0.13     4 65013    7139    7298        0    0    0 4d22h54m        0
172.18.0.15     4 65015    7137    7300        0    0    0 4d22h54m        0
172.18.0.16     4 65016    7137    7300        0    0    0 4d22h54m        0
172.18.0.17     4 65017    7135    7302        0    0    0 4d22h54m        0
172.18.0.19     4 65019    7143    7294        0    0    0 4d22h54m        0

Total number of neighbors 33

Total num. Established sessions 33
Total num. of routes received     1