В прошлой статье мы подготовили 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