Skip to content

Latest commit

 

History

History
1059 lines (753 loc) · 56.1 KB

STEP-BY-STEP.ru.md

File metadata and controls

1059 lines (753 loc) · 56.1 KB

Пошаговая инструкция по установке и настойке коллектора

Сборка и установка

В минимальном Debian 12 для сборки нужно доустановить такие пакеты:

$ sudo apt -y install git autoconf gcc make libpcap-dev libtool

Для Debian 13:

$ sudo apt -y install git autoconf gcc make libpcap-dev

Склонируйте репозиторий и инициализируйте подмодули

$ git clone --recurse-submodules https://github.com/vmxdev/xenoeye

Или

$ git clone https://github.com/vmxdev/xenoeye
$ cd xenoeye
$ git submodule update --init --recursive

Собственно сборка

$ autoreconf -i
$ ./configure --sysconfdir=/etc/xenoeye --localstatedir=/var/lib
$ make

После этого должен получиться бинарный файл xenoeye

Установка

$ sudo make install
# меняем владельца каталога на пользователя, от которого будем запускать коллектор (user)
$ sudo chown -R user:user /var/lib/xenoeye/

make install копирует бинарный файл xenoeye в /usr/local/bin, конфигурационные файлы xenoeye.conf и devices.conf в /etc/xenoeye и создает каталоги /var/lib/xenoeye/mo, /var/lib/xenoeye/exp и /var/lib/xenoeye/expfailed.

Каталог /var/lib/xenoeye должен быть доступен для записи процессу xenoeye

make install - опциональный шаг, коллектор можно запускать из произвольного места

Все пути по умолчанию можно изменить, они задаются в конфигурационном файле xenoeye.conf. Конфигурационные файлы в xenoeye - это JSON, в которых разрешены комментарии.

Проверяем получение Netflow

Коллектор может принимать Netflow двумя способами - обычным интерфейсом сокетов или захватывать с помощью pcap.

Второй способ больше подходит для тестов. Если у вас, например, есть .pcap-файлы с Netflow-трафиком, вы можете проиграть их на loopback интерфейсе с помощью tcpreplay (tcpreplay -i lo dump.pcap) и таким способом отправить данные в коллектор.

За захват Netflow отвечает секция "capture" главного конфигурационного файла xenoeye.conf. Там перечислены сокеты, на которых коллектор будет слушать и pcap-интерфейсы c BPF-фильтрами.

Чтобы увидеть, что коллектор принимает и декодирует фловы, выставьте параметр "dump-flows" в секции "debug". Допускаются значения

  • "none" — фловы не логгируются, обычное состояние системы
  • "syslog" — декодированные фловы передаются в syslog и дополнительно печатаются в stderr
  • "/path/to/file.txt" — фловы в текстовом виде пишутся в файл

Выставьте этот параметр в "syslog"

	/*...*/
	"debug": {
		/* allowed values: "none", "syslog", "/path/to/file.txt" */
		"dump-flows": "syslog"
	},
	/*...*/

и запустите коллектор

$ xenoeye

или

$ xenoeye -c /path/to/xenoeye.conf

Если вы используете захват с помощью pcap, коллектор должен запускаться с правами суперпользователя

После того, как коллектор получит Netflow-пакеты с шаблонами, он сможет парсить фловы с данными. Они будут отправляться в syslog и одновременно выводиться в stderr приблизительно в таком текстовом виде:

IPv4 src addr: 1.2.3.4; IPv4 dst addr: 5.6.7.8; Src TOS: 0; Protocol: 6; Src port: 7878; Dst port: 8787; ICMP type: 0; Input SNMP index: 111; Src VLAN: 222; Src mask: 16; Dst mask: 24; Src AS: 12345; Dst AS: 0; IPv4 next hop: 0.0.0.0; TCP flags: 24; Output SNMP index: 333; Bytes: 65522; Packets: 185; Min TTL: 51; Max TTL: 52; Unknown field 152: 0x00 0x00 0x01 0x77 0x0d 0x0c 0x07 0x00 ; Unknown field 153: 0x00 0x00 0x01 0x77 0x0d 0x0c 0xee 0x00 ; Unknown field 136: 0x02 ; Unknown field 61: 0xff ; Unknown field 243: 0x00 0x00 ; Unknown field 245: 0x00 0x00 ; Unknown field 54: 0x00 0x00 0x00 0x00 ; *dev-ip: 9.10.11.12; *dev-id: 555500, *rate: 1 [flow_debug.c, ...]

Коллектор показывает все поля, которые есть в netflow-пакете. Часть полей он может не знать и показывает их как "Unknown field NNN: <байты с данными>".

Кроме полей Netflow, коллектор показывает "виртуальные" поля со звездочкой: *dev-ip: 1.2.3.4; *dev-id: 123456, *rate: 1.

  • dev-ip - IP-адрес сенсора (роутера)
  • dev-id - идентификатор сенсора
  • rate - sampling rate, по умолчанию 1

Если фловы приходят и успешно декодируются, можно вернуть старое значение параметра "dump-flows": "none"

Распределение нагрузки по нескольким CPU

В коллекторе используется простая модель распределения нагрузки: каждая запись в секции "capture" - это отдельный рабочий поток.

Вы можете запустить коллектор на нескольких UDP-портах и подавать на каждый порт Neflow разных роутеров.

Операционная система при необходимости распределит рабочие потоки по разным CPU.

Частота семплирования

Коллектор не берет (по крайней мере сейчас) частоту семплирования из options template.

Для установки частоты семплирования отредактируйте файл /etc/xenoeye/devices.conf. Это JSON-массив, каждый элемент - запись об устройстве-источнике Netflow (роутере). Как и в остальных конфигурационных файлах, допускаются комментарии

[
	{
		"ip": "1.2.3.4",
		"id": 123456,
		"sampling-rate": 10000
	},
	{
		"ip": "2.3.4.5",
		"sampling-rate": 1000
	}
]

Частоту можно установить для IP адреса устройства и, если нужно, то и для source ID (его можно увидеть в текстовом дампе флова, виртуальное поле *dev-id). Коллектор показывает частоту семплирования в каждом флове, в виртуальном поле *rate.

Внутри коллектора частота семплирования - это просто коэффициент, на который умножается количество октетов и пакетов.

Объекты мониторинга

Объект мониторинга - основная сущность в коллекторе. К объектам мониторинга привязаны отчеты и скользящие средние.

Объект мониторинга задается фильтром с BPF-подобным синтаксисом.

Например, фильтр dst net 10.11.12.0/24 выделяет весь трафик, который идет в сеть 10.11.12.0/24.

Фильтры могут определять не только сети, но и произвольные объекты, которые можно выделить из Netflow

Объект мониторинга src net 10.11.12.0/24 or 10.11.13.0/24 and proto tcp and dst port 80 or 443 определяет TCP-трафик, который выходит из сетей 10.11.12.0/24 и 10.11.13.0/24 на порты 80 или 443, то есть исходящий HTTP/HTTPS-трафик.

В фильтрах допускаются скобки, операторы and, or и not.

Кроме обычных полей в фильтрах могут использоваться виртуальные поля dev-ip и dev-id. Например, фильтр "dst net 10.11.12.0/24 and dev-ip 1.2.3.4" отберет только фловы, которые приходят с роутера 1.2.3.4. Подробнее о фильтрах смотрите в CONFIG.ru.md

Физически объект мониторинга - это каталог с файлом mo.conf, расположеный в специальном месте - каталоге объектов мониторинга.

Каталог объектов мониторинга задается в главном конфигурационном файле, параметр mo-dir. По умолчанию это /var/lib/xenoeye/mo.

Объект мониторинга "ingress"

Давайте создадим объект мониторинга c именем ingress. Он будет описывать весь входящий в наши сети трафик.

Для начала создайте каталог с таким названием:

$ mkdir -p /var/lib/xenoeye/mo/ingress

В этом каталоге создайте файл mo.conf с содержимым:

{
	/* какая-то из наших сетей */
	"filter": "dst net 10.11.12.0/24",

	"debug": {
		/* Для теста включим отладочный режим. Фловы, которые принадлежат этому объекту, будут отправляться в syslog и на stderr */
		"dump-flows": "syslog"
	}
}

Перезапустите коллектор и посмотрите что он выводит на экран. Он должен показывать только те фловы, у которых IPv4 dst addr находится в вашей сети.

Рабочий режим для коллектора - это режим без печати или сохранения каждого флова в файл. Но для небольших объектов мониторинга, и когда нужен каждый флов со всеми данными этой фичей можно пользоваться. Мы используем печать фловов в файл для того чтобы смотреть на трафик, который не должен появляться на роутере. Например, трафик bogon сетей или трафик у которых и src и dst IP не из наших сетей.

Объект мониторинга "egress"

Таким же способом можно создать объект мониторинга egress, который будет описывать трафик, исходящий из наших сетей.

$ mkdir -p /var/lib/xenoeye/mo/egress
{
	"filter": "src net 10.11.12.0/24"
	/* ... */
}

IP-cписки

В полях с IP адресами сети можно задавать цифровыми значениями (1.2.3.0/24), перечислением сетей (1.2.3.0/24 or 1.2.4.0/24) или именем списка. Когда сетей много, имеет смысл держать их в отдельном файле и в фильтрах использовать имя IP-списка.

Для создания списка сетей создайте файл в каталоге /var/lib/xenoeye/iplists/. Имя файла и будет именем списка. Например, можно создать файл /var/lib/xenoeye/iplists/bogon и поместить в него bogon-сети, по одной сети на строку:

# В файле с IP-сетями допускаются комментарии и пустые строки

10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/24
192.0.2.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/4
240.0.0.0/4
255.255.255.255/32

# В одном файле могут быть как IPv4-сети, так и IPv6
::/128
::1/128
::ffff:0:0/96
::/96
100::/64
2001:10::/28
2001:db8::/32
fc00::/7
fe80::/10
fec0::/10
ff00::/8

После этого в фильтре можно будет указывать bogon в качестве сети.

Фильтр "net bogon" отберет фловы, в которых IPV4_SRC_ADDR или IPV4_DST_ADDR принадлежат bogon-сетям.

Фильтр "src net bogon and dst net bogon" отберет фловы, в которых и IPV4_SRC_ADDR и IPV4_DST_ADDR одновременно относятся к bogon-сетям.

Для IPv6 нужно указывать поля с суффиксом 6, например "src net6 bogon and dst net6 bogon"

Если вы хотите мониторить много сетей, имеет смысл создать файл с перечислением ваших сетей:

$ mkdir -p /var/lib/xenoeye/iplists/
$ echo "1.2.3.0/24" > /var/lib/xenoeye/iplists/my-nets
$ echo "1.2.4.0/24" >> /var/lib/xenoeye/iplists/my-nets
...

Конфигурируем какие данные экспортировать в СУБД

Если коллектор показывает правильные фловы для объекта мониторинга, можно убрать печать фловов и добавить элементы для экспорта в СУБД.

/var/lib/xenoeye/mo/ingress/mo.conf:

{
	"filter": "dst net my-nets",

	"debug": {
		"dump-flows": "none"
	},

	/* Определяем данные для экспорта */
	"fwm": [
		{
			/* общее количество пакетов и байт на наши сети, по всему объекту мониторинга */
			"name": "all",
			"fields": ["packets", "octets"]
		},
		{
			/* dst IP и количество пакетов/байт по каждому адресу */
			"name": "octets_by_dst",
			"fields": ["packets", "octets", "dst host"]
		},
		{
			/* номера протоколов и количество пакетов/байт по каждому протоколу */
			"name": "octets_by_proto",
			"fields": ["packets", "octets", "proto"]
		},
		{
			/* src IP адрес и количество байт с каждого уникального IP */
			"name": "octets_by_src",
			"fields": ["octets desc", "src host"],
			"limit": 5,    /* ограничиваем количество строк экспорта */
			"time": 10     /* устанавливаем размер окна в 10 секунд (по умолчанию 30 сек) */
		}
	]
}

Каждый элемент в массиве "fwm" соответствует таблице в СУБД. Название таблицы получается из названия объекта мониторинга + _ + название элемента.

В каждом элементе должно быть название ("name") и список netflow-полей, которые будут использоваться ("fields"). Кроме этого можно изменять время, за которое агрегируются данные ("time", по умолчанию 30 секунд) и ограничивать размер экспортируемых строк (по умолчанию экспортируются все данные). Подробное описание файлов mo.conf находится в CONFIG.ru.md

Давайте посмотрим на каждый элемент.

{
	"name": "all",
	"fields": ["packets", "octets"]
}

Элемент с именем "all" инструктирует систему создать таблицу ingress_all. У таблицы будет 3 поля: time, packets и octets. Каждые 30 секунд в таблицу будет добавляться запись со временем и объемом трафика по всему объекту мониторинга.

{
	"name": "by_dst",
	"fields": ["packets", "octets", "dst host"]
}

Будет создана таблица ingress_by_dst с полями time, packets, octets, dst_host. Каждые 30 секунд в таблицу будет добавляться время, все IP-адреса, на которые шел трафик за эти 30 секунд и количество пакетов/байт, пришедших на каждый IP адрес.

{
	"name": "by_proto",
	"fields": ["packets", "octets", "proto"]
}

В таблицу ingress_by_proto будет добавляться номер протокола, количество входящих пакетов и байт по каждому протоколу

{
	"name": "octets_by_src",
	"fields": ["octets desc", "src host"],
	"limit": 5,
	"time": 10
}

В таблицу ingress_octets_by_src будут добавляться IP адреса источников и количество байт, которые пришли от них. Так как адресов источников может быть много, мы ограничиваем их количество. Каждые 30 секунд будет экспортироваться только топ-5 источников по количеству байт. "limit": 5 указывает системе количество записей. Спецификатор desc в названии поля ("packets desc") говорит о том, что нужно сортировать по количеству байт от большего количества к меньшему.

Данные будут экспортироваться в таком формате:

time                   octets       src_host
2023-01-09 22:14:33    223566128    1.2.3.4
2023-01-09 22:14:33    218947845    2.3.4.5
2023-01-09 22:14:33    204806183    1.2.4.3
2023-01-09 22:14:33    164083132    3.4.1.2
2023-01-09 22:14:33    121142482    4.2.3.1
2023-01-09 22:14:33   7049471049    NULL

Последняя строка с NULL вместо IP адреса - это сумма трафика (октетов) всех остальных хостов, которые не вошли в топ-5.

Для исходящего трафика можно создать конфиг-файл с таким набором собираемых данных:

/var/lib/xenoeye/mo/egress/mo.conf:

{
	"filter": "src net my-nets",

	"debug": {
		"dump-flows": "none"
	},

	/* данные для экспорта */
	"fwm": [
		{
			/* общее количество пакетов и байт из наших сетей, по всему объекту мониторинга */
			"name": "all",
			"fields": ["packets", "octets"]
		},
		{
			/* src IP и количество пакетов/байт по каждому адресу */
			"name": "by_src",
			"fields": ["packets", "octets", "src host"]
		},
		{
			/* номера протоколов и количество пакетов/байт по каждому протоколу */
			"name": "by_proto",
			"fields": ["packets", "octets", "proto"]
		},
		{
			/* dst IP и количество байт к каждому адресу */
			"name": "octets_by_dst",
			"fields": ["octets desc", "dst host"],
			"limit": 5,
			"time": 10
		}
	]
}

Перезапустите коллектор, и он начнет генерировать файлы с данными.

Экспорт в СУБД

Коллектор не отправляет данные непосредственно в СУБД, а генерирует файлы экспорта в виде SQL-сриптов. Эти файлы создаются в каталоге экспорта (по умолчанию '/var/lib/xenoeye/exp')

Для того чтобы данные появились в СУБД нужно эти файлы периодически отдавать утилите psql.

В каталоге scripts/ находится файл fill-db.sh, который это делает. Измените в нем параметры подключения (user:[email protected]:5432/database) и пути к каталогам если нужно. База данных должна существовать, пользователь должен иметь права на запись.

  • EXP_DIR — каталог, в котором скрипт ищет файлы экспорта
  • FAIL_DIR — каталог, куда скрипт складывает файлы если экспорт закончился неудачей (нет подключения к СУБД или что-то еще пошло не так)

Скрипт можно запускать из командной строки (в бесконечном цикле while true; do ./fill-db.sh ; sleep 10; done) или из крона.

Мы запускаем этот скрипт в бесконечном цикле в tmux-окне. В отдельном окне коллектор, в отдельном - скрипт экспорта.

Быстрая подсказка: как установить PostgreSQL на одном сервере с коллектором

$ sudo apt -y install postgresql
#
$ sudo su - postgres -c "createuser -P xenoeye"
Enter password for new role:
Enter it again:
$ sudo su - postgres -c "createdb xenoeyedb"
$ sudo su - postgres -c "psql -d xenoeyedb -c 'GRANT ALL PRIVILEGES ON DATABASE xenoeyedb TO xenoeye;'"
GRANT
$ vi scripts/fill-db.sh

Отредактируйте параметры подключения, если нужно:

psql postgresql://user:[email protected]:5432/database -f "$sqlscript"

Простые отчеты по IP-адресам

После всех манипуляций, описанных выше, у вас начнут заполняться таблицы СУБД. Можно начинать делать отчеты. Зная SQL даже на минимальном уровне это довольно просто:

Исходящий трафик в байтах по всему объекту мониторинга за последний час:

=> select sum(octets) from egress_all where time >= now() - interval '1 hour';
    sum
-------------
 14600823461
(1 row)

Входящий трафик по отдельным хостам назначения за последний час:

=> select sum(octets), dst_host from ingress_by_dst where time >= now() - interval '1 hour' group by dst_host order by sum desc;
    sum     |    dst_host
------------+----------------
 1335804665 | 1.2.3.4
  312434774 | 4.3.2.1
  166234656 | 2.1.1.4
...

Входящий трафик по отдельным хостам назначения за предыдущий месяц:

=> select sum(octets), dst_host from ingress_by_dst where time >= date_trunc('month', current_date - interval '1' month) and time < date_trunc('month', current_date) group by dst_host order by sum desc;

В таблицах может храниться и другая информация из Netflow (AS number, Interface Idx, VLAN, TCP/UPD порты и т.п.), отчеты по этим данным можно строить точно так же.

Определяем спам-ботов и ssh-сканеры

Если в вашем датацентре или в сети есть зараженные хосты, которые сканируют остальных по ssh, их можно определить таким объектом мониторинга:

/var/lib/xenoeye/mo/ssh_scanners/mo.conf:

{
	"filter": "src net my-nets and dst port 22",

	"fwm": [
		{
			"name": "hosts",
			"fields": ["packets", "src host", "dst host", "proto"]
		}
	]
}

После того, как данные станут экспортироваться в СУБД, можно получить список IP-адресов хостов таким запросом:

$ select src_host, count(src_host) from (select distinct src_host, dst_host from ssh_scanners where time >= now() - interval '1 day' order by src_host desc) as x group by src_host order by count desc;

IP-адреса с большим количеством исходящих ssh-соединений с большой вероятностью заражены и пытаются заразить остальных

Для зараженных хостов, которые рассылают SMTP-спам, можно создать такой объект мониторинга:

/var/lib/xenoeye/mo/mail_spam/mo.conf:

{
	"filter": "src net my-nets and not src net mail-servers and dst port 25 or 587 or 465 or 2525",

	"fwm": [
		{
			"name": "hosts",
			"fields": ["packets", "src host", "dst host", "proto"]
		}
	]
}

В файл /var/lib/xenoeye/iplists/mail-servers нужно записать IP-адреса настоящих почтовых серверов, чтобы они не попали в объект мониторинга.

Выбирать спам-хосты можно таким же запросом, как и ssh сканеры:

$ select src_host, count(src_host) from (select distinct src_host, dst_host from mail_spam where time >= now() - interval '1 day' order by src_host desc) as x group by src_host order by count desc;

Строим графики с помощью gnuplot

Один из самых простых способов строить графики временных рядов — с помощью программы gnuplot.

$ sudo apt -y install gnuplot-nox

Построим график входящего трафика за предыдущие сутки:

Для этого нужно получить данные в текстовом виде:

$ psql postgresql://xenoeye:password@localhost/xenoeyedb -c "\copy (select * from ingress_all where time >= now() - interval '1 day' order by time) to 'day-i.csv' with CSV delimiter ','"
COPY 2880

Строим график в файл day-i.png:

$ gnuplot

gnuplot> set terminal png size 1000,400
gnuplot> set output 'day-i.png'
gnuplot> set xdata time
gnuplot> set timefmt '%Y-%m-%d %H:%M:%S'
gnuplot> set xtics rotate
gnuplot> set datafile separator ','
gnuplot> set format y '%.02s%cB'
gnuplot> set style fill solid
gnuplot> set boxwidth 0.5
gnuplot> plot 'day-i.csv' using 1:2 notitle with boxes
gnuplot> ^D
$

gnuplot chart 1

Построим более сложный график — входящий трафик с разбивкой по IP протоколам:

Номера IP-протоколов передаются в netflow как числа. Для того, чтобы отображать названия протоколов в текстовом виде, возьмем данные IANA и создадим из них таблицу в СУБД.

$ wget https://www.iana.org/assignments/protocol-numbers/protocol-numbers-1.csv
# обрежем в файле ненужное
$ grep "^[0-9]*-" protocol-numbers-1.csv -v | tail -n +2 > iana.csv
# создадим таблицу
$ psql postgresql://xenoeye:password@localhost/xenoeyedb -c "create table iana_protocols (num int, name text, descr text, ipv6ext text, ref text);"
CREATE TABLE
# заполним ее данными
$ psql postgresql://xenoeye:password@localhost/xenoeyedb -c "\copy iana_protocols FROM 'iana.csv' DELIMITER ',' CSV"
COPY 149

Теперь можно сделать выборку с названием протокола:

$ echo "select time, iana_protocols.name, octets from ingress_proto join iana_protocols on ingress_proto.proto=iana_protocols.num where time >= now() - interval '1 day' order by time \crosstabview time name octets" | psql postgresql://xenoeye:password@localhost/xenoeyedb > day-i-prot.csv

Строим график в файл day-i-prot.png (предполагаем, что протоколов в результате не больше 20):

$ gnuplot
gnuplot> set terminal png size 1000,400
gnuplot> set output 'day-i-prot.png'
gnuplot> set key autotitle columnhead
gnuplot> set xdata time
gnuplot> set timefmt '%Y-%m-%d %H:%M:%S'
gnuplot> set format y '%.02s%cB'
gnuplot> set xtics rotate
gnuplot> set datafile separator '|'
gnuplot> set style fill solid
gnuplot> set boxwidth 0.5
gnuplot> plot 'day-i-prot.csv' using 1:2 with boxes, for [i=3:20] '' using 1:i with boxes

gnuplot chart 2

И еще один график — входящий трафик с разбивкой по IP-адресам назначения

IP-адресов в отчете может быть очень много (особенно в IPv6-сетях).

Поэтому SQL-запрос становится более сложным:

SELECT time, sum(octets)/30*8 AS ip, ips FROM
(
  WITH topips AS
  (SELECT  sum(octets) AS ip, COALESCE (src_host::text, 'Other') as ips FROM ingress_octets_by_src WHERE time >= now() - interval '1 day' GROUP BY ips ORDER BY ip desc limit 20)
  SELECT time, octets,  COALESCE (src_host::text, 'Other') as ips FROM ingress_octets_by_src WHERE time >= now() - interval '1 day' AND src_host::text IN (SELECT ips from topips)
  UNION ALL
  SELECT time, octets, 'Other'                             as ips FROM ingress_octets_by_src WHERE time >= now() - interval '1 day' AND src_host::text NOT IN (SELECT ips from topips)
  UNION ALL
  SELECT time, octets, 'Other'                             as ips FROM ingress_octets_by_src WHERE time >= now() - interval '1 day' AND src_host IS NULL
) AS report
GROUP BY time, ips ORDER BY time;

Краткое объяснение:

SELECT sum(octets) AS ip, COALESCE (src_host::text, 'Other') as ips FROM ingress_octets_by_src WHERE time >= now() - interval '1 day' GROUP BY ips ORDER BY ip desc limit 20 - выбираем топ-20 адресов, по убыванию количества байт

Следующие три SELECT выбирают время, количество байт и IP-адрес. Если адрес находится не в топ-20, он становится 'Other', байты по этим адресам суммируются

Константы в выборке самого верхнего уровня (SELECT time, sum(octets)/30*8 AS ip, ips FROM):

30 - количество секунд в окне, 8 - количество бит в байте, результат пересчитываем в BPS.

gnuplot chart 3

Скрипт для генерации такого графика находится здесь: scripts/mkchart-gnuplot.sh

Графики с помощью Python Matplotlib

Если вы работаете с Python, то, возможно, вам будет удобнее строить графики с помощью библиотеки Matplotlib.

Графики немного визуально отличаются от тех, которые генерирует gnuplot.

Установка нужных библиотек:

$ pip3 install matplotlib psycopg2-binary pandas

Скрипты для генерации кольцевых диаграмм, которые берет данные из СУБД и строят диаграмму в файлы:

scripts/mkchart-matplotlib-donut-prot.py

matplotlib chart 1

scripts/mkchart-matplotlib-donut-as.py

matplotlib chart 2

Скрипты для генерации графиков временных рядов:

scripts/mkchart-matplotlib-ts.py

matplotlib chart 3

График с агрегацией и разбивкой по IP-адресам. Обратите внимание, как сглаживаются всплески при агрегации

scripts/mkchart-matplotlib-ts-ip.py

matplotlib chart 4

Визуализация трафика в Grafana

Сетевые данные можно визуализировать с помощью grafana.

Для этого подключите источник данных PostgreSQL.

Grafana PostgreSQL data source

Создайте дашбоард и добавляйте к нему панели с графиками.

Простой временной ряд с общим трафиком по объекту мониторинга:

Grafana chart 1

SQL-запрос для такого графика:

SELECT
  time AS "time",
  octets/30*8 as octets
FROM ingress_all
WHERE
  $__timeFilter(time)
ORDER BY 1

Временной ряд с разбивкой по протоколам:

Grafana chart proto

SELECT
  time AS "time",
  sum(octets)/30*8 AS protocol,
  iana_protocols.name as proto
FROM ingress_proto
JOIN iana_protocols on ingress_proto.proto=iana_protocols.num
WHERE
  $__timeFilter(time)
GROUP BY time, iana_protocols.name
ORDER BY time

В новых версиях Grafana для того чтобы строить такие графики нужно добавить трансформацию:

Grafana tr

Grafana tr1

Grafana tr2

График с разбивкой по IP-адресам назначения:

Grafana chart 2

IP адресов может быть много, чтобы grafana и браузеру не стало плохо, SQL-запрос будет выбирать топ-15 адресов (по количеству байт) за период отображения и показывать только их. Остальные будут показаны как 'Other'.

Запрос для графиков такого типа:

SELECT time, sum(octets)/30*8 AS ip, ips FROM
(
  WITH topips AS
  (SELECT  sum(octets) AS ip, COALESCE (dst_host::text, 'Other') as ips FROM ingress_octets_by_dst WHERE $__timeFilter(time) GROUP BY ips ORDER BY ip desc limit 15)
  SELECT time, octets,  COALESCE (dst_host::text, 'Other') as ips FROM ingress_octets_by_dst WHERE $__timeFilter(time) AND dst_host::text IN (SELECT ips from topips)
  UNION ALL
  SELECT time, octets, 'Other'                             as ips FROM ingress_octets_by_dst WHERE $__timeFilter(time) AND dst_host::text NOT IN (SELECT ips from topips)
  UNION ALL
  SELECT time, octets, 'Other'                             as ips FROM ingress_octets_by_dst WHERE $__timeFilter(time) AND dst_host IS NULL
) AS report
GROUP BY time, ips ORDER BY time

Еще примеры графиков:

Grafana chart 3

Grafana chart 4

Для отображения круговых диаграмм используются такие же запросы, как и для временных столбчатых диаграмм. Чтобы данные в диаграмме суммировались за весь выбранный период, установите свойство Calculation в Total

Grafana pie option

Скользящие средние

Выше был описан подход к обработке netflow-данных (как временны́х рядов) с помощью временны́х окон фиксированного размера. Коллектор агрегирует данные внутри этих окон и экспортирует в СУБД. Для многих применений этого вполне достаточно.

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

  • Быстрые всплески могут "раствориться" в достаточно длинном окне, их будет не видно
  • Система сможет реагировать на изменение скорости только после того как закончится временно́е окно и экспортируются данные

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

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

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

Для одного объекта мониторинга может одновременно подсчитываться несколько скользящих средних с разными параметрами (значением порогов, размером окна). Идея была такая: при превышении одного порога оповещается администратор системы и включается сбор расширенной статистики. При превышении следующего порога у нас уже будут более подробные данные о трафике, можно оповещать администратора еще раз и предпринимать какие-то действия.

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

Для работы скользящих средних коллектору не нужна СУБД, конфигурация берется из файлов, все рассчеты производятся внутри коллектора.

Пример конфигурации скользящих средних:

/var/lib/xenoeye/mo/http_flood/mo.conf

	"mavg": [
		{
			"name": "mavg1",
			"time": 20,
			"fields": ["src host", "octets"],
			"overlimit": [
				{
					"name": "level1",
					"back2norm-time": 60,
					"default": [10000000],
					"limits": "/var/lib/xenoeye/mo/http_flood/limits1.csv",
					"action-script": "/var/lib/xenoeye/scripts/on-start.sh",
					"back2norm-script": "/var/lib/xenoeye/scripts/on-stop.sh"
				}
			]
		}
	]

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

Краткое описание параметров:

  • "time" - размер скользящего окна в секундах. Чем больше этот параметр, тем медленнее происходят изменения в скользящем окне
  • "name" - название
  • "back2norm-time" - время в секундах, после которого система считает что трафик вернулся в норму
  • "default" - значение порога по умолчанию
  • "limits" - файл с индивидуальными порогами для некоторых IP-адресов
  • "action-script" - скрипт, который исполняется при превышении порога
  • "back2norm-script" - скрипт, который исполняется при возврате трафика в норму

Когда объем трафика уходит ниже порога срабатывания, система не сразу совершает действия, а ждет некоторое время. Трафик может сразу же вырасти и опять пробить порог. Только после того как трафик будет ниже порога back2norm-time секунд, запускается пользовательский скрипт и отключается расширенная статистика.

Формат файла с индивидуальными порогами зависит от поля "fields". В нем должны быть через запятую перечислены индивидуальные значения в том же порядке, как и в этом поле.

Если в конфиге "fields": ["src host", "octets"], файл должен выглядеть как IP-адрес,порог:

1.2.3.4,1000000
1.2.3.5,2000000

Если "fields": ["src host", "proto", "packets"], то в файле должен быть еще номер протокола:

1.2.3.4,1,100000
1.2.3.4,17,200000
1.2.3.4,6,300000
1.2.3.5,6,200000

В такой конфигурации скользящее среднее считается будет считаться для уникальной комбинации "src host", "proto", в окне будет подсчитываться PPS.

Настройка и установка порогов

Можно использовать два источника для вычисления порогов: экспортировать данные в СУБД и на основании этих данных выставлять пороги или попросить систему показывать текущие значения скользящих средних.

Чтобы коллектор начал писать в файл текущие значения скользящих средних, нужно для этого скользящего окна установить параметр dump в количество секунд. Это будет время между дампами. Потом создайте в каталоге с mo.conf файл с таким же названием, как и название скользящего среднего + .d.

Если в конфиге "name": "mavg1", то:

$ touch /var/lib/xenoeye/mo/http_flood/mavg1.d

После этого коллектор начнет периодически записывать текущие значения скользящих средних в файл /var/lib/xenoeye/mo/http_flood/mavg1.dump.

Wed Jan 25 23:10:25 2023
mem used/avail: 2M/256M (1245505/268435456 bytes)
 '4.13.136.10'  6  :: 0 (10000000 )
 '4.13.136.11'  6  :: 0 (10000000 )
 '4.13.136.11'  17  :: 30659 (10000000 )
 ...

Если вы хотите посмотреть как изменяются скользящие средние в динамике, то можно создать файл с .a в конце:

$ touch /var/lib/xenoeye/mo/http_flood/mavg1.a

После этого каждый дамп будет добавляться к файлу /var/lib/xenoeye/mo/http_flood/mavg1.adump

Не забудьте удалить файл .a, даже на небольших сетях он может распухнуть до огромного размера

Кроме значений скользящих средних коллектор показывает дату, время и память, которая используется для хранения набора. По умолчанию выделяется 256М, можно изменить параметром "mem-m":

	/* ... */
	"name": "mavg1",
	"dump": 10,
	"mem-m": 10, /* 10 мегабайт */
	"fields": ["src host", "octets"],
	/* ... */

Используя эти значения, вы можете выставить общий порог для объекта и если нужно индивидуальные пороги по каждому отдельному IP (или по комбинации netflow-полей)

Скрипты и их параметры

Коллектор запускает два скрипта: первый при превышении порога и второй при возврате трафика в норму.

Скрипты запускаются с такими параметрами:

  1. Название объекта мониторинга
  2. Название скользящего среднего
  3. Название элемента с порогами
  4. Полное имя файла-уведомления
  5. Декодированные netflow-поля, в том порядке как записано в "fields"
  6. Значение, которое превысило порог
  7. Значение порога

При превышении порога, перед запуском скрипта коллектор создает файл-уведомление в специальном каталоге (по умолчанию /var/lib/xenoeye/notifications/). Имя файла состоит из всех этих элементов, кроме значений превышения и порога. Внутри файла находятся декодированные netflow-поля, значение, которое превысило порог и значение порога. Файл обновляется каждые 3 секунды.

После того как трафик приходит в норму, файл-уведомление удаляется.

С такой конфигурацией в файле /var/lib/xenoeye/mo/http_flood/mo.conf:

{
	"name": "mavg1",
	"fields": ["src host", "proto", "octets"],
	"overlimit": [
		{
			"name": "level1",
			"action-script": "/var/lib/xenoeye/scripts/on-start.sh",
			"back2norm-script": "/var/lib/xenoeye/scripts/on-stop.sh"
		}
	]
}

Будут запускаться скрипты с параметрами:

/var/lib/xenoeye/scripts/on-start.sh http_flood mavg1 level1 /var/lib/xenoeye/notifications/http_flood-mavg1-level1-15.22.13.99-6 15.22.13.99 6 1234567 1000000

Выглядит немного избыточно, но вы можете смело игногировать параметры которые вам не нужны. Скорее всего вас будет интересовать IP адрес ("src host"), протокол и количество байт, которые пробили порог. Это 4, 5 и 6 параметры, если считать от 0.

Пример скриптов можно посмотреть в телеграм-роботе

Расширенная статистика

Часто при превышении порогов хочется узнать более подробно, что за трафик вызвал это превышение. Можно при превышении запускать скрипт, который будет собирать сырой netflow-трафик (с помощью tcpdump/tshark), и потом анализировать этот трафик например с помощью wireshark.

Кроме такого способа, в коллекторе есть механизм "расширенной статистики". Это специальные элементы секции "fwm". Они помечаются параметром "extended" : true и при старте неактивны. Как только превышается порог, эти элементы становятся активными, в соответствующие таблицы добавляются расширенные данные. При возврате трафика в норму они опять становятся неактивными

Можно включить расширенную статистику для текущего объекта мониторинга или для какого-то другого. Таблицы из другого объекта мониторинга записываются как объект_мониторинга/таблица.

{
	"fwm": [
		{
			"extended": true,
			"name": "ext",
			"fields": ["octets", "src host", "dst host", "tcp-flags"]
		}
		/* ... */
	],

	"mavg": [
		{
			"name": "mavg1",
			"time": 20,
			"fields": ["src host", "octets"],
			"overlimit": [
				{
					"name": "level1",
					"default": [10000000],
					"action-script": "/var/lib/xenoeye/scripts/on-start.sh",
					"back2norm-script": "/var/lib/xenoeye/scripts/on-stop.sh",
					"ext": ["ext"]
					//"ext": ["ext" , "egress/ext"]
				}
			]
		}
		/* ... */
	]
}

После превышения порогов соответствующая таблица начнет заполняться данными.

Будьте внимательны - в расширенные таблицы попадает не только трафик который вызвал превышения, а весь, который принадлежит этому объекту мониторинга.

Оповещение об аномалиях с помощью Telegram-робота

С помощью скриптов, которые запускаются при превышении порога и при возвращении трафика в норму, можно оповещать пользователя используя мессенджеры. Мы пользуемся Telegram, скрипты для запуска робота и уведомлений находятся в каталоге ./scripts/telegram-bot/

Создайте каталог, через который скрипты будут обмениваться информацией:

$ mkdir -p /var/lib/xenoeye/telemsg/

Скопируйте скрипты в место, откуда они будут запускаться:

$ mkdir -p /var/lib/xenoeye/scripts/tgm/
$ cp scripts/telegram-bot/* /var/lib/xenoeye/scripts/tgm/

Отредактируйте объект мониторинга: mo.conf

{
	"name": "mavg1",
	"fields": ["src host", "proto", "octets"],
	"overlimit": [
		{
			"name": "level1",
			"action-script": "/var/lib/xenoeye/scripts/tgm/on-start.sh",
			"back2norm-script": "/var/lib/xenoeye/scripts/tgm/on-stop.sh"
		}
	]
}

Перезапустите коллектор. Теперь при превышении порогов будут исполняться скрипты для уведомлений.

Следующий шаг — настройка робота.

  1. Создайте Telegram-робота (в интернете есть много документации как это сделать)
  2. Добавьте API_TOKEN робота в код скрипта, вместо ... в строке API_TOKEN = '...'
  3. Запустите робота
  4. Начните с ним чат
  5. Наберите в чате с роботом команду /id, робот ответит какой у чата идентификатор
  6. Добавьте идентификатор чата в код скрипта CHATS = [<ваш-ид>]. Идентификаторов может быть несколько (должны перечисляться через запятую, CHATS = [ид1, ид2]), тогда робот будет посылать оповещения в несколько чатов
  7. Перезапустите робота

После этого робот должен начать рассылать уведомления

telegram-bot

Красным шариком помечается начало превышения порога, зеленым (в ответ на начало) - окончание.

Робот проверяет наличие аномалий раз в несколько секунд (по умолчанию 10). Если за это время трафик успел превысить порог и вернуться в норму, сообщение приходит с желтым шариком