Registro del tráfico en GNU/Linux mediante conntrack, Loki y Grafana

per Victor Carceler darrera modificació 2023-12-17T18:06:40+02:00

Ahora que contamos con nuestro servidor Loki para consolidar logs (y journal) de manera eficiente, lo podemos utilizar para registrar metadatos de las conexiones del gateway del centro (cirdan-2204) y visualizar en un dashboard de Grafana información sobre el funcionamiento de nuestra red.

Con esta visualización mostramos:

Grafana-Loki-conntrack.png

  • Total de conexiones por protocolo: TCP, UDP, ICMP.
  • Contadores de tráfico (paquetes y bytes) en los dos sentidos.
  • Total de equipos (direcciones) únicos.
  • Proporción de conexiones por protocolo y línea de tiempo.
  • Proporción de conexiones de los 25 orígenes con más conexiones y línea de tiempo por origen (todos).
  • Proporción de conexiones de los 25 destinos con más conexiones y línea de tiempo por destino (todos).
  • Proporción de conexiónes de los 25 puertos de destino con más conexiones y línea de tiempo por puerto de destino (todos).
  • Tabla con los datos de las conexiones. Con posibilidad de ordenador por cualquier campo y de establecer filtros multivalor.
  • Panel con los datos registrados.

Grafana permite definir la ventana de tiempo que nos interesa dentro de la política de retención, que en nuestro caso son 30 días, y además contamos con una variable (text) que nos permitirá filtrar por cualquier campo.

Así podremos discriminar tráfico filtrando:

  • El protocolo que queramos.
  • La dirección de origen o destino que nos interese, su prefijo de red o simplemente una subcadena.
  • El puerto de destino o de origen en cualquiera de los dos sentidos.

Todo esto permite auditar el funcionamiento de nuestra red y ayudar en el diagnóstico y solución de problemas. Naturalmente lo único que registramos en Loki son los metadatos de las conexiones (extraídos de conntrack) y no el tráfico real que requeriría una capacidad de almacenamiento exagerada.

Aunque a lo largo de un día cirdan-2204 atiende a cerca de 1800 equipos que realizan cerca de 5 millones de conexiones. Loki no tarda más que unos 20 segundos en procesar todas esas líneas y actualizar los paneles con los datos agregados de todo un día.

Si se realizan consultas utilizando una ventana de tiempo menor o se filtran las conexiones todo es prácticamente inmediato.

Registro de las conexiones con conntrack

El proyecto netfilter implementa las funciones de filtrado de paquetes en el núcleo Linux. Los comandos iptables y nftables (o nft) permiten configurar las reglas del cortafuegos del núcleo. Los primeros cortafuegos únicamente podían examinar cada paquete de manera individual para aceptar o rechazar el tráfico pero no mantenían ningún estado que permitiera relacionar los paquetes de una misma conexión.

Pero netfilter es un cortafuegos con estado y tiene la noción de conexión que permite relacionar paquetes. Por ejemplo, cuando se hace una petición HTTP cabe esperar una respuesta y tanto el tráfico de la petición como el de la respuesta formará parte de una misma conexión para el cortafuegos. Las conexiones permiten establecer reglas de filtrado mucho más interesantes como: "permitir conexiones nuevas al puerto 443 y su tráfico relacionado".

Para netfilter una secuencia de pings a un mismo destino (y sus respuestas) también son una conexión aunque el protocolo ICMP no esté orientado a conexión y lo mismo ocurrirá con UDP.

El sistema de seguimiento de conexiones de netfilter se llama conntrack y mantendrá una conexión con dos flujos (de origen a destino y viceversa) para el tráfico intercambiado.

Los posibles estados de las conexiones son:

  • NEW: Cuando se ha recibido el primer paquete válido (por ejemplo el paquete SYN inicial de una nueva conexión TCP) y todavía no se ha visto tráfico de respuesta.
  • ESTABLISHED: El cortafuegos ya ha visto tráfico en los dos sentidos.
  • RELATED: Es una conexión esperada. Por ejemplo una nueva transferencia de una sesión FTP activa.
  • INVALID: Tráfico que no sigue las expectativas.

La herramienta del shell conntrackpermite consultar la tabla de conexiones, manipularla o registrar eventos como la creación, actualización o destrucción de una conexión.

Con el comando conntrack -E -e DESTROY se imprimirá una línea cada vez que se cierre una conexión:

root@cirdan-2204:/home/vcarceler# conntrack -E -e DESTROY
[DESTROY] udp 17 src=192.168.248.56 dst=192.168.240.1 sport=62998 dport=53 packets=1 bytes=82 src=192.168.240.1 dst=192.168.248.56 sport=53 dport=62998 packets=1 bytes=118 delta-time=30
[DESTROY] udp 17 src=192.168.247.189 dst=192.168.240.1 sport=34458 dport=53 packets=2 bytes=146 src=192.168.240.1 dst=192.168.247.189 sport=53 dport=34458 packets=2 bytes=178 [ASSURED] delta-time=125
[DESTROY] udp 17 src=192.168.245.157 dst=192.168.240.1 sport=49111 dport=53 packets=1 bytes=75 src=192.168.240.1 dst=192.168.245.157 sport=53 dport=49111 packets=1 bytes=203 delta-time=30
[DESTROY] udp 17 src=192.168.245.157 dst=192.168.240.1 sport=54014 dport=53 packets=1 bytes=81 src=192.168.240.1 dst=192.168.245.157 sport=53 dport=54014 packets=1 bytes=109 delta-time=30
[DESTROY] tcp 6 CLOSE_WAIT src=192.168.246.21 dst=216.58.209.78 sport=59443 dport=443 packets=11 bytes=2864 src=216.58.209.78 dst=192.168.100.10 sport=443 dport=59443 packets=10 bytes=6245 [ASSURED] delta-time=301
[DESTROY] udp 17 src=192.168.249.14 dst=192.168.255.255 sport=137 dport=137 packets=6 bytes=468 [UNREPLIED] src=192.168.255.255 dst=192.168.249.14 sport=137 dport=137 packets=0 bytes=0 delta-time=32
[DESTROY] tcp 6 TIME_WAIT src=192.168.253.193 dst=83.247.151.197 sport=62867 dport=443 packets=12 bytes=9483 src=83.247.151.197 dst=192.168.100.10 sport=443 dport=62867 packets=15 bytes=2089 [ASSURED] delta-time=132
[DESTROY] tcp 6 119 CLOSE src=192.168.246.21 dst=216.58.209.78 sport=59443 dport=443 packets=2 bytes=135 [UNREPLIED] src=216.58.209.78 dst=192.168.100.10 sport=443 dport=59443 packets=1 bytes=40 delta-time=0
[DESTROY] udp 17 src=192.168.100.10 dst=193.108.91.125 sport=49852 dport=53 packets=1 bytes=85 src=193.108.91.125 dst=192.168.100.10 sport=53 dport=49852 packets=1 bytes=121 delta-time=30
[DESTROY] udp 17 src=192.168.100.10 dst=205.251.199.154 sport=59377 dport=53 packets=1 bytes=99 src=205.251.199.154 dst=192.168.100.10 sport=53 dport=59377 packets=1 bytes=272 delta-time=30
[DESTROY] tcp 6 120 CLOSE src=192.168.246.27 dst=10.241.14.195 sport=61371 dport=8080 packets=1 bytes=52 [UNREPLIED] src=10.241.14.195 dst=192.168.0.10 sport=8080 dport=61371 packets=1 bytes=40 delta-time=0
^Cconntrack v1.4.6 (conntrack-tools): 11 flow events have been shown.
root@cirdan-2204:/home/vcarceler#

Para obtener esta información ha sido necesario añadir dos claves a /etc/sysctl.conf que activan la contabilidad de tráfico y el timestamp que permite calcular la duración de las conexiones.

net.netfilter.nf_conntrack_acct = 1
net.netfilter.nf_conntrack_timestamp = 1

Estas líneas contienen los metadatos de cada conexión en un formato compacto que podremos registrar en journal para que promtail las envíe a Loki. Además prácticamente están en el formato que el parser logfmt de Loki puede interpretar automáticamente. Pero como hay claves duplicadas (src, dst, sport ...) podremos utilizar sed para renombrar los campos duplicados y añadir prot= al campo protocolo para simplificar las consultas en Grafana.

Así preparamos la unidad /etc/systemd/system/conntrack-to-journal.service

[Unit]
Description=Send to journal DESTROY events for nat flows
After=network.target

[Service]
Type=simple
# Needs:
#
# echo 1 >/proc/sys/net/netfilter/nf_conntrack_acct
# echo 1 >/proc/sys/net/netfilter/nf_conntrack_timestamp
#
# Increased buffer-size
ExecStart=/bin/sh -c '/usr/sbin/conntrack -E -e DESTROY --buffer-size 21299200 | /usr/bin/sed "s/\[DESTROY\] /\[DESTROY\] prot=/;s/src=/srca=/;s/src=/srcb=/;s/dst=/dsta=/;s/dst=/dstb=/;s/sport=/sporta=/;s/sport=/sportb=/;s/dport=/dporta=/;s/dport=/dportb=/;s/packets=/packetsa=/;s/packets=/packetsb=/;s/bytes=/bytesa=/;s/bytes=/bytesb=/"'
TimeoutSec = 2
Restart = on-failure
RestartSec = 10

[Install]
WantedBy=multi-user.target

Acceso a los datos desde Grafana

El acceso a los datos desde Grafana es muy sencillo, como estamos utilizando journal para registrar los datos cada stream tendrá las etiquetas job, nodename y unit.

Podremos seleccionar las conexiones de nuestro gateway con el selector

{nodename="cirdan-2204", unit="conntrack-to-journal.service"}

para obtener los datos registrados:

2023-12-14 18:18:49.439	[DESTROY] prot=udp      17 srca=192.168.10.108 dsta=192.168.10.10 sporta=59798 dporta=53 packetsa=1 bytesa=105 srcb=192.168.10.10 dstb=192.168.10.108 sportb=53 dportb=59798 packetsb=1 bytesb=121 delta-time=30
2023-12-14 18:18:49.439	[DESTROY] prot=tcp      6 120 CLOSE srca=127.0.0.1 dsta=127.0.0.1 sporta=52304 dporta=5778 packetsa=1 bytesa=60 [UNREPLIED] srcb=127.0.0.1 dstb=127.0.0.1 sportb=5778 dportb=52304 packetsb=1 bytesb=40 delta-time=0
2023-12-14 18:18:49.439	[DESTROY] prot=udp      17 srca=192.168.252.114 dsta=192.168.255.255 sporta=137 dporta=137 packetsa=6 bytesa=468 [UNREPLIED] srcb=192.168.255.255 dstb=192.168.252.114 sportb=137 dportb=137 packetsb=0 bytesb=0 delta-time=32
2023-12-14 18:18:49.439	[DESTROY] prot=udp      17 srca=192.168.254.194 dsta=142.250.200.110 sporta=65216 dporta=443 packetsa=12 bytesa=7226 srcb=142.250.200.110 dstb=192.168.100.10 sportb=443 dportb=65216 packetsb=13 bytesb=4214 delta-time=31
2023-12-14 18:18:49.439	[DESTROY] prot=udp      17 srca=192.168.247.140 dsta=192.168.240.1 sporta=51839 dporta=53 packetsa=1 bytesa=73 srcb=192.168.240.1 dstb=192.168.247.140 sportb=53 dportb=51839 packetsb=1 bytesb=89 delta-time=30

Filtrar líneas que contengan la cadena 'tcp' añadiendo |= "tcp" después del selector (LogQL permite otros filtros de línea)

{nodename="cirdan-2204", unit="conntrack-to-journal.service"} |= "tcp"

Y contar las líneas en el rango de tiempo para obtener un contador de las conexiones TCP utilizando la función count_over_time() (LogQL permite otras funciones para agregar datos)

count_over_time({nodename="cirdan-2204", unit="conntrack-to-journal.service"} |= `tcp` [$__range])

Que podremos representar en un panel Stat u otra de las visualizaciones de Grafana.

tcp.png

Más información: