FastNetMon — мониторинг DDoS атак. Docker-контейнер на базе Alpine

FastNetMon — один из сервисов который позволяет найти и предотвратить атаку на сеть. На сайте проекта описаны различные варианты установки и настройки мониторинга, также есть хорошая документация на русском. Мне нужен вариант с анализатором sflow-пакетов, и так как источников с которого летят сии пакеты более одного, решил остановиться на docker-контейнере.

Единственное что не устраивало в официальной реализации контейнера, то что собран он на базе ubuntu. Потому поискал любительские сборки на базе Alpine  и наткнулся на докерфайл Сборка делалась достаточно давно и код FastNetMon’a уже изрядно поменялся, плюс компиляция крашилась из-за неявно указаных модулей в коде. Поэтому пришлось подправить напильником. В результате контейнер собрался и работает. Правда в последствии я в него добавил еще curl для отправки нотификаций в telegram. Файлик доступен по ссылке

FROM alpine:3.12 as builder

RUN apk update && apk add 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
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 "echo Starting fastnetmon\n/fastnetmon --log_file /dev/stdout" > /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 update && apk add boost-thread boost-system boost-regex boost-program_options libpcap libstdc++ ncurses
COPY --from=builder /build.tgz /start.sh /
RUN tar zxf /build.tgz && \
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
VOLUME ["/configs"]
CMD ["/bin/sh", "/start.sh"]

Сборка контейнера укладывается в две команды:

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

Так как я запускаю не один контейнер, а несколько: сделал два скрипта которые подготавливают и запускают контейнеры.

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

Скрипт генерации конфига:

#!/usr/bin/env bash

# WARNING
# This script will remove all unused docker volume

echo y | docker volume prune

GRAPHITE_HOST=10.10.24.25

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
sed -i 's/\$AS/'$AS'/g' $VOLUME_PATH/fastnetmon.conf
sed -i 's/\$GRAPHITE_HOST/'$GRAPHITE_HOST'/g' $VOLUME_PATH/fastnetmon.conf
cp notify_about_attack.sh $VOLUME_PATH/notify_about_attack.sh
done

Так как в Alpine-контейнере используется sh интерпретатор, необходимо изменить интерпритатор в скрипте нотификаций notify_about_attack.sh

Скрипт запуска контейнеров:

#!/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 -p $AS:$AS/udp --mount source=$AS,target=/configs fastnetmon

sleep 5
done

Немного о конфиге InfluxDB базы: сам конфиг описан на сайте программы

[[graphite]]
  enabled = true
  bind-address = ":2003"
  protocol = "tcp"
  consistency-level = "one"
  separator = "."
  batch-size = 5000 # will flush if this many points get buffered
  batch-timeout = "1s" # will flush at least this often even if we haven't hit buffer limit

Но у нас источников данных несколько, поправим темплейт:

  templates = [
     "*.hosts.* app.measurement.cidr.direction.function.resource",
     "*.networks.* app.measurement.cidr.direction.resource",
     "*.total.* app.measurement.direction.resource"
  ]

Подключимся к инфлакс базе и создадим graphite базу и правило для ротации записей:

influx -host localhost -port 8086 -precision rfc3339
# Создадим нашу базу
> create database graphite
# и правило для ротации базы в 1час
> CREATE RETENTION POLICY "1_hours_only" ON "graphite" DURATION 1h REPLICATION 1 DEFAULT

Теперь можно визуализировать вывод данных в grafana подключив как источник данных нашу базу и импортировав этот темплейт