"El arte de filtrar paquetes." - El Ing. Felipe Evans nos llevara por un recorrido de "aceptacion y dropeo de paquetes" en GNU/Linux, con iptables, nftables, tc y eBFP.
El objetivo de la charla ver las distintas formas de filtrado de paquetes en GNU/Linux.
Video: https://www.youtube.com/watch?v=ZmbomaWTaho&t=405s
6. netfilter → iptables y nftables
Netfilter project es la organización que provee una solución de filtrado de paquetes para
GNU/Linux. Desarrollan la herramienta iptables y su sucesora nftables.
http://www.netfilter.org/
Estas herramientas proveen:
● Filtrado de paquetes de red
● Auditoría y logueo de paquetes
● Network Address Translation (NAT)
● Poder tratar los paquetes en user space
● Marcar paquetes para su filtrado o tratamiento
7. netfilter → historia de los comandos
● ipchains
● iptables
○ arptables
○ ebtables
○ ip6tables
○ ipset
● nftables
8. iptables vs nftables
● Reemplaza los comandos ipset, iptables, ip6tables, arptables and ebtables por nft. Simplificando la
sintaxis y un mejor manejo del dual stack ipv4/ipv6
● Remueve código duplicado en todos los módulos sobretodo entre ipv4 y ipv6.
○ agrega el módulo nftables.
● Fue agregado en el kernel 3.13 (Enero 2014)
● Reusa las estructuras existentes de bloques: hooks, conntrack, NAT, logging y userspace queueing
● Reutiliza las tablas existentes xtables mediante el módulo nft compat
● Permite reglas combinadas y acciones múltiples
● Trae soporte propio de sets y diccionarios.
● Permite mayor flexibilidad y mejor exportación de datos.
● Mejora de performance.
● Permite monitoreo y logging
10. Repaso de iptables
Está integrado en el kernel y está organizado en una colección de tablas, cada una con un
propósito específico. Las tablas están formadas por un conjunto de cadenas predefinidas o
cadenas definidas por el usuario , y las cadenas contienen reglas que se recorren en orden
consecutivo. Cada regla consiste en una condición asociada a una eventual acción (llamada
target). En pocas palabras si se cumple, se ejecuta la regla.
Términos Importantes:
● Tablas
● Cadenas
● Reglas == Condición + Acción/Target
11. Repaso de iptables
Tablas:
iptables cuenta con cinco tablas, que son zonas en
las que una cadena de reglas se puede aplicar:
raw filtra los paquetes antes que cualquier otra tabla.
Se utiliza principalmente para configurar exenciones
de seguimiento de conexiones en combinación con
el target NOTRACK.
filter es la tabla por defecto (si no se pasa la opción
-t).
nat se utiliza para la traducción de dirección de red
(por ejemplo, el redirección de puertos). Debido a las
limitaciones en iptables, el filtrado no se debe hacer
aquí.
mangle se utiliza para la alteración de los paquetes
de red especializados (véase Mangles packet).
security se utiliza para reglas de conexión de red
Mandatory Access Control.
Cadenas:
Las tablas contienen cadenas, que son listas de
reglas que ordenan los paquete de red. Por defecto,
la tabla filter contiene tres cadenas integradas:
INPUT, OUTPUT, FORWARD. En iptables existen 5 por
default: INPUT, OUTPUT, FORWARD, PREROUTING Y
POSTROUTING pero depende de la tablas si la
contiene o no.
Target:
Los targets se especifican mediante la opción -j o
--jump. Los targets pueden ser tanto las cadenas
definidas por el usuario, como uno de los targets
integrados especiales, o una extensión de target. Los
targets integrados son ACCEPT, DROP, QUEUE y
RETURN; las extensiones de target son, por ejemplo,
REJECT y LOG.
13. Ahora de nftables
Tablas:
Tablas son completamente configurables.
Las tablas no tienen propósito predefinido. Este es
uno de los cambios más fuertes.
Cadenas:
Por defecto no existe cadenas, las cadenas se
agrupan en tablas. Las cadenas tiene tipos (types) y
se aplica en un hooks. También tienen prioridad.
Tipos de cadenas:
● filter
● nat
● route (mangle)
Rule(Match)/Target:
● No hay distinción entre matches y targets.
● Puedes agregar varios target juntos.
Hooks:
Las cadenas bases son todas aquellas que están
agregadas en los hooks.
Hooks disponibles: ingress, input, output forward,
prerouting, postrouting
Otros
● No hay contadores por defecto por cadena o por regla.
● Para actualizar no es necesario actualizar el kernel.
● Permite una infraestructura de maps y serts.
14. Comandos nftables
Listar
# nft list ruleset → Lista todas las reglas (ruleset = tables, chain and rules)
# nft list [<tables>|<chains> ]
Tablas
nft {add | create} table [family] <nombre table> [{ flags flags ; }]
nft {delete | list | flush} table [family] <nombre table>
Ejemplo
# Crear una tabla llamada mytable.
nft create table inet mytable
# Crear una nueva cadena base con nombre myin en el hook input
nft add chain inet mytable myin { type filter hook input priority 0; }
# Le agrego el contador a la cadena myin
nft add rule inet mytable myin counter
# Deshabilitar la tabla mytable
nft add table inet mytable { flags dormant; }
# vuelvo a activar la tabla
nft add table inet mytable
family
ip → familias de direcciones IPv4.
ip6 → familias de direcciones IPv6.
inet → Ambas (IPv4/IPv6)
arp → Direcciones ARP
bridge → Maneja los paquetes que
atraviesan los bridges
netdev → Familia de direcciones
Netdev. Maneja los paquetes
desde ingress.
NOTA: Hay muchos ejemplos con
nombres de tablas con nombre de de
hooks. Eso suele generar confusión
15. Comandos nftables
CHAINS
{add | create} chain [family] <table> <chain> [{ type <type> hook <hook> [device <device>] priority <priority> ; [policy <policy> ;] }]
{delete | list | flush} chain [family] <table> <chain>
list chains [family]
Ejemplos:
# Nueva cadena llamada my_chain
nft add chain inet my_table my_chain '{ type filter hook input priority 0; }'
# Cambio de política de my_chain
nft chain inet my_table my_chain '{ policy drop ; }'
# Borrar la cadena
nft delete chain inet my_table my_chain
# limpiar las reglas de la cadena
nft flush chain inet my_table my_chain
16. Comandos nftables
Reglas
{add | insert} rule [family] <table> <chain> [handle handle | index index] statement ... [comment comment]
replace rule [family] <table> <chain> handle handle statement ... [comment comment]
NOTA: La regla al momento crearla se agrega al final de la cadena. Si se quiere en otro lugar hay que utilizar la opción handle o index
Ejemplos:
#Agregar a la tabla my_fillter cadena my_output permitir salir a 192.168.0.0/24
nft add rule inet my_filter my_output ip daddr 192.168.0.0/24 accept
# Listar todas las reglas
# nft -a list ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy accept;
ct state established,related accept # handle 4
ip saddr 10.1.1.1 tcp dport ssh accept # handle 5
...
# Borrar la regla con el handle 5
nft delete rule inet filter input handle 5
17. Comandos nftables
Statement Comunes
● Venrdict
○ accept, drop, queue, continue, return, jump chain_name, and goto chain_name
● Meta (meta properties, e.g. interfaces)
○ oif <output interface INDEX>, iif <input interface INDEX>, oifname <output interface NAME>,
iifname <input interface NAME>
● ip :
○ daddr <destination address>, saddr <source address>
● ip6:
○ daddr <destination address>, saddr <source address>
● tcp:
○ dport <destination port>, sport <source port>
● udp
○ dport <destination port>, sport <source port>
● ct:
○ state <new | established | related | invalid>
18. nftables en ejemplos
Ejemplos matches
Con nftables
# nft add rule filter FORWARD tcp dport 21 log drop
Con iptables
# iptables -A FORWARD -p tcp --dport 22 -j LOG
# iptables -A FORWARD -p tcp --dport 22 -j DROP
Con nftables
nft add rule ip6 filter INPUT tcp dport {telnet, http, https} accept
nft add rule ip6 filter INPUT icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert }
accept
Con Iptables
ip6tables -A INPUT -p tcp -m multiport --dports 23,80,443 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT
19. nftables en ejemplos
table inet my_table {
chain web {
tcp dport http accept
tcp dport 8080 accept
}
chain my_input {
type filter hook input priority 0;
ip saddr 10.0.2.0/24 jump web
drop
}
}
table inet my_nat {
chain my_masquerade {
type nat hook postrouting priority 100;
ip saddr 192.168.100.0/24 oifname "enp2s0" masquerade
}
}
table ip my_nat {
chain my_prerouting {
type nat hook prerouting priority -100;
tcp dport { ssh, http } dnat to destination_ip
}
chain my_postrouting {
type nat hook postrouting priority 100;
ip daddr destination_ip masquerade
}
}
20. Momento show me the code...
Vamos a visualizar:
1. Tables y Chain nft
2. Tables y Chain Iptables
3. Relación nft y iptables
4. ¿Qué cosas me pierdo si
uso los dos?
21. Comandos nftables
SET
Son arrays de elementos. Permite dos tipos: anónimos y nombrados.
Ejemplo anónimo
nft add rule filter input ip saddr { 10.0.0.0/8, 192.168.0.0/16 } tcp dport { 22, 443 } accept
Ejemplo nombrados
nft add set ip filter blackhole { type ipv4_addr;}
nft add element ip filter blackhole { 192.168.3.4 }
nft add element ip filter blackhole { 192.168.1.4, 192.168.1.5 }
nft add rule ip filter input ip saddr @blackhole drop
nft delete element ip filter blackhole { 192.168.1.5 }
Ejemplo con tiempo
nft add set ip filter ports {type inet_service ; timeout 1h10s ;} → en 1h 10s se sale del set
22. Comandos nftables
Diccionarios
Son conocidos como mapas de acciones.
Ejemplo
nft add map filter mydict { type ipv4_addr : verdict; }
nft add element filter mydict { 192.168.0.10 : drop, 192.168.0.11 : accept }
nft add rule filter input ip saddr vmap @mydict
Mapas
Son mapas claves valor.
Sin nombrar
nft add rule ip nat prerouting dnat tcp dport map { 80 : 192.168.1.100, 8888 : 192.168.1.101 }
Nombrados
nft add map nat porttoip { type inet_service: ipv4_addr; }
nft add element nat porttoip { 80 : 192.168.1.100, 8888 : 192.168.1.101 }
nft add rule ip nat postrouting snat tcp dport map @porttoip
Nota: no tienen muchas diferencias en
opciones con el SET
23. nftables
Otras cosas interesantes:
● Flowtable (o fastpath network): La posibilidad de acelerar paquetes con
el uso de conntrack. Una vez clasificado el flujo se lo puede derivar de
una interfaz a otra salteando el pipeline de nftable.
● Operaciones Matemática: Es una opción muy útil sobre todo para hacer
cálculos para el balanceo de carga.
● Actualizar sets: Como acción de una regla.
● meters: Mejora al módulo de hashlimit. Esto es más flexible mediante
Concatenations.
● Concatenations: Permite combinar dos o más selectores.
● Comments: Agregar comentarios a las reglas
● Tracing: Monitoreo o rastreo de reglas.
24. RECREO: nftables - queue
Redireccionar paquetes a userspace
Esto permite pasarle a un programa la posibilidad de elegir qué hacer con el programa o cambiarlos.
nft add filter input counter queue num 3 → Envía todo al canal 3
Con el parámetro bypass se le agrega accept a los paquetes si el programa no está escuchando el número de cola.
Por defecto, si no hay programa, hace drop.
nft add filter input counter queue num 0 bypass
Permite balanceo de carga (Se recomienda 1 cola por CPU)
nft add filter input counter queue num 0-3
cat /proc/net/netfilter/nfnetlink_queue
3 2400 0 2 4016 0 0 0 1
Primer valor: número de queue
Segundo valor: pid del procesos asociado a queue
Quinto valor: tamaño total del paquete que pasa a user space.
Sexto valor: cantidad de paquetes dropeados por la cola
Séptimo valor: cantidad de paquetes dropeados por usuario
25. RECREO: nftables - queue
from netfilterqueue import NetfilterQueue
from scapy.all import *
import os
domain = 'facebook.com'
localIP='127.100.0.1'
os.system('nft create table ip myqueue 2>/dev/null')
os.system('nft create chain ip myqueue OUTPUT {type filter hook output priority 10;
policy accept;}')
os.system('nft add ip myqueue OUTPUT udp dport 53 counter queue num 3 ')
def callback(payload):
data = payload.get_payload()
pkt = IP(data)
if not pkt.haslayer(DNSQR):
payload.accept()
else:
if domain in str(pkt[DNS].qd.qname):
spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)/
UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport)/
DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd,
an=DNSRR(rrname=pkt[DNS].qd.qname, ttl=10, rdata=localIP))
spoofed_pkt[UDP].len = len(bytes(spoofed_pkt[UDP]))
del spoofed_pkt[UDP].chksum
del spoofed_pkt[IP].chksum
payload.set_payload(bytes(spoofed_pkt))
print ('[+] spoofed packet %s' % domain)
#spoofed_pkt.show2()
else:
print ('[+] Consulta %s' %
pkt[DNS].qd.qname)
payload.accept()
def main():
nfqueue = NetfilterQueue()
nfqueue.bind(3, callback)
try:
nfqueue.run() # Main loop
except KeyboardInterrupt:
nfqueue.unbind()
os.system('nft delete table ip myqueue')
sys.exit('cerrando...')
main()
Nota: para python >3.7 el NetfilterQueue hay
que instalarlo de github, no de pip.
#idem con iptables
os.system('iptables -A OUTPUT -p
udp --dport 53 -j NFQUEUE')
26. Momento show me the code...
Vamos a visualizar:
1. Set en acción
2. nfqueue
27. Otros ejemplos
Contar las conexiones por IP
Cuenta las nuevas conexiones por HTTPS
/etc/newHTTPSconect.nft
table inet filter {
set https {
type ipv4_addr;
flags dynamic;
size 65536;
timeout 60m;
}
chain input {
type filter hook input priority 0
ct state new tcp dport 443 update @https { ip saddr
counter }
}
}
Para imprimir los contadores, nft list set inet filter https.
Habilitar el rastreo
# nft add chain ip filter trace_chain { type filter hook
prerouting priority -10; }
# nft add rule ip filter trace_chain meta nftrace set 1
# nft monitor trace
Limitando
nft add rule filter input icmp type echo-request limit rate over
10/second drop
nft add rule filter input limit rate over 10 mbytes/second drop
29. Filtrando con TC
Control de tráfico.
El comando TC permite controlar las colas de paquetes de salida de las interfaces. Se puede
crear un dispositivo intermedio llamado FIB para controlar las cola de entrada de los dispositivos.
Este comando permite armar las QOS de paquetes, como cambiar los algoritmos de control de
colas o configurar los mismos.
Aunque este esquema no fue diseñado para filtrar paquetes, se han desarrollado filtros para
mejorar los QOS. El filtro desarrollado para tc se denomina u32.
Algunos criterios que permite el u32 son:
● Dirección IP de origen/destino del paquete
● Protocolo utilizado: tcp, udp, icmp, gre, ...
● Puertos de origen y destino utilizados
● Valor del campo TOS, etc
31. Filtrado con u32 y tc
Dropear los paquetes que van de de la red 1.2.3.0/24 a la red 4.3.2.0/24.
tc qdisc add dev eno1 root handle 1: prio
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32
match ip src 1.2.3.0/24
u32 match ip dst 4.3.2.0/24
flowid 1:1
action drop
Dropear los paquetes que vienen de la red 4.3.2.0/24 con puerto destino 80
tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32
match ip src 4.3.2.0/24
match ip dport 80 0xffff
action drop
32. Momento show me the code...
Vamos a visualizar:
● Ejemplo de dropeo en TC
con u32 en el ingress
33. Ejemplo con u32 y tc
Dropear los paquetes que salen de la red 192.168.100.0/24 al puerto 80.
tc qdisc add dev ens6 ingress
tc filter add dev ens6 parent ffff: protocol ip prio 1 u32
match ip src 192.168.100.0/24
match ip dport 80 0xffff
action drop
tc -d filter add dev ens6 parent ffff:
#tc filter del dev ens6 parent ffff:
#wget http://www.fi.mdp.edu.ar
#wget https://www.fi.mdp.edu.ar
34. eBPF, Que es?
eBPF es una tecnología que permite ejecutar programas de espacio en un sandbox dentro del kernel de
Linux sin cambiar el código fuente o cargar módulos.
Estos programas pueden ser adheridos a distintos puntos del kernel y estos se ejecutan por eventos.
Estos programas pueden ser utilizados para dejar pasar, filtrar o modificar los datos que pasen por ese
hook. También permiten comunicarse con programas en user space.
eBPF
36. eBPF
eBPF was described by Ingo Molnár as:
One of the more interesting features in this cycle is the ability to attach eBPF programs (user-defined,
sandboxed bytecode executed by the kernel) to kprobes (dynamic tracing of a kernel function call). This
allows user-defined instrumentation on a live kernel image that can never crash, hang or interfere with
the kernel negatively.
Traducción: Una de las características más interesantes de este ciclo es la
capacidad de adjuntar programas eBPF (código de bytes en espacio aislado,
definido por el usuario, ejecutado por el kernel) a kprobes (rastreo dinámico de
una llamada de función del kernel). Esto permite la instrumentación definida por
el usuario en una imagen de kernel en vivo que nunca puede fallar, colgarse o
interferir negativamente con el kernel.
37. eBPF
linux/bpf.h ofrece más de 30 tipos distintos de programas (31 la última vez que vi ;-).
Para filtrado de paquetes podemos observar estos:
● BPF_PROG_TYPE_SOCKET_FILTER: program to perform socket filtering;
● BPF_PROG_TYPE_SCHED_CLS: program to perform traffic classification at the TC
● BPF_PROG_TYPE_SCHED_ACT: program to add actions to the TC layer
● BPF_PROG_TYPE_XDP: program to be attached to the eXpress Data Path hook;
● BPF_PROG_TYPE_LWT_{IN, OUT or XMIT}: programs for Layer-3 tunnels;
● BPF_PROG_TYPE_SOCKET_OPS: program to catch and set socket operations such as retrans mission timeouts,
passive/active connection establishment, and so on;
● BPF_PROG_TYPE_SK_SKB: program to access socket buffers and socket parameters (IP addresses, ports, etc)
and to perform packet redirection between sockets;
● BPF_PROG_TYPE_FLOW_DISSECTOR: program to do flow dissection, i.e., to find important data in network
packet headers
El resto de los tipos de programas sirve para realizar monitoreo o seguridad. Sugiero:
https://github.com/iovisor/bcc
http://www.brendangregg.com/blog/2019-01-01/learn-ebpf-tracing.html
38. XDP permite adherir un programa eBPF en un hook dentro del driver de
red dentro de la función de ingress. Usualmente usan el método NAPI
poll() antes que SKB (socket buffer) cuando es efectivamente es alocado
en memoria fuera del driver el actual paquete.
eBPF - XDP
39. eBPF - XDP
Modos de operación
Para incrementar la performance los programas eBPF son ingresados en el pipeline de procesos del driver para esto se requiere
que el driver puede aceptar el programa. La mayoría de los driver de interfaces de alta velocidad lo soportan.
● XDP Native: se ejecuta en el pipeline del driver. Drivers que lo soportan: ixgbe, virtio_net, e1000, i40e ,
nfp, etc.
● XDP Generic: lo emula el kernel.
● XDP Offload: Se ejecuta en el hardware. Ejemplo: Netronome SmartNICs.
Listado de drivers: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#xdp
40. eBPF - XDP
XDP REDIRECT or user space socket via AF_XDP address family
Para más datos sobre XDP_REDIRECT o AF_XDP
● http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf
● https://www.kernel.org/doc/html/latest/networking/af_xdp.html
42. eBPF - XDP
Ahora que funcione
clang-10 -target bpf -O2 -I /usr/src/linux-headers-5.9.0-4-amd64/arch/x86/include/generated/uapi -I
/usr/src/linux-headers-5.9.0-4-common/arch/x86/include/uapi/ -I /usr/include/bpf/ -c xdp_dummy.c -o xdp_dummy.o
ip link set dev lo xdp object xdp_dummy.o sec proc
ip link set dev lo xdp off
Nota:
ip link set dev lo xdp object xdp_dummy.o → para ejecutar así debe existir la sección prog. Alias SEC("prog")
# ip link list dev lo
lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 173 tag 3b185187f1855c4c jited
# bpftool net list dev lo
xdp:
lo(1) generic id 173
tc:
flow_dissector:
ip link permite
● xdp → modo automático
● xdpgeneric → modo genérico
● xdpdrv → modo por driver, si no puede fallar
● xdpoffload → modo por hardware, si no puede fallar
● off → Quita los programas xdp
43. eBPF - XDP
BPF Compiler Collection (BCC)
BCC es una herramienta que permite crear en forma más simple y eficiente
programas eBPF. Para poder usarlo se requiere Linux 4.1 o superior.
#define KBUILD_MODNAME "helloworld"
#include <linux/bpf.h>
int hello(struct xdp_md *ctx)
{
return XDP_PASS;
}
from bcc import BPF
from bcc.utils import printb
device = "lo"
b = BPF(src_file="helloworld.c")
fn = b.load_func("hello", BPF.XDP)
b.attach_xdp(device, fn, 0)
try:
b.trace_print()
except KeyboardInterrupt:
print("Termino..")
b.remove_xdp(device, 0)
44. eBPF - XDP
Hechos
● eBPF - XDP corre cuando el dispositivo recibe el paquete
● eBPF - XDP puede modificar los contenidos de los
paquetes
● eBPF - XDP no necesita hacer alocación de memoria
● eBPF - XDP es sin estado (hace que la cosas vayan rápido)
Razones para usar eBPF - XDP
● Prevención de DDoS
● Load balancing
● Recolección de estadísticas de paquetes
● Algunas otras cuestiones locas
Algunos mitos
Algunas respuesta que son NO
● ¿XDP es solo una moda? “NO”
● ¿XDP es inseguro porque permite que un usuario
ejecute código en el kernel? “En principio no, el JIT
de eBPF verifica que el código sea seguro”
● ¿Es XDP un reemplazo a netfilter/tc? No, hay
algunos solapamientos pero pero es sin estado. Es
difícil implementar conntrack en eBFP
https://jvns.ca/blog/2017/04/07/netdev-conference-day-2/
45. eBPF - XDP
Gráficas de Cloudflare
https://blog.cloudflare.com/how-to-drop-10-million-packets/
46. Momento show me the code...
Vamos a visualizar:
● Hello world in
eBPF-XDP
● Something "util" with
eBPF-XDP
47. Proyectos locos
● Acceleration of IPTABLES Linux Packet Filtering using GPGPU
● Filtros en user Space NFQUEUE and python
● eBPF-based Networking, Observability, and Security https://cilium.io/
● Información sobre EBPF https://ebpf.io/
● Recopilación de documentos sobre ebpf
https://github.com/zoidbergwill/awesome-ebpf
● Redirect eBPF socket AF_XDP
https://archive.fosdem.org/2018/schedule/event/af_xdp/
● XDP as tcpdump. https://github.com/cloudflare/xdpcap