Ubuntu 24.04: Filtrado de ruta inversa — el ajuste oculto que rompe el multihoming (caso nº 54)

¿Te fue útil?

Síntoma: un equipo multi-homed con Ubuntu 24.04 “funciona en su mayoría” hasta que deja de hacerlo. Algunas conexiones entrantes se quedan colgadas. Las respuestas desaparecen. El monitoreo dice que el enlace está bien. El equipo de aplicaciones insiste en que es DNS. No lo es.

Realidad: el kernel está descartando paquetes silenciosamente porque rp_filter ha decidido que tu enrutamiento parece sospechoso. Y si tienes dos NIC, dos enlaces ascendentes, VRF, enrutamiento por políticas, Kubernetes, VIPs o cualquier cosa parecida a la vida real, la sospecha se convierte en estilo de vida.

Caso nº 54: el corte de multihoming que no viste venir

Lo enmarcaremos como un incidente real, porque así es como te lo encontrarás: medio corte con muy buen ambiente.

Un nodo de servicio tiene dos enlaces ascendentes:

  • eth0 hacia la red “interna” (tráfico este-oeste, clúster, almacenamiento).
  • eth1 hacia una red “dmz/edge” (tráfico norte-sur, partners, monitoreo, a veces VPN).

Ambos enlaces están activos. Ambos tienen direcciones. Hay enrutamiento basado en políticas para que ciertas IPs origen prefieran ciertas puertas de enlace. También puede haber un VIP para conmutación por error (VRRP/keepalived), o una interfaz WireGuard que se convierte en la ruta por defecto para un subconjunto del tráfico.

Entonces alguien actualiza a Ubuntu 24.04 o reconstruye un nodo y aplica sysctls de “endurecimiento de seguridad”. De repente:

  • Los SYN entrantes llegan por eth1, pero el SYN-ACK nunca sale.
  • El ping ICMP funciona desde algunas redes pero no desde otras.
  • Las conexiones desde un ASN partner se intermiten cada pocos minutos.
  • El tráfico hacia el almacenamiento funciona; el tráfico hacia el balanceador no; las comprobaciones de liveness de Kubernetes son una moneda al aire.

La primera hora se dedica a culpar a lo obvio: reglas de firewall, MTU, un “puerto de switch malo” y—inevitablemente—DNS. Mientras tanto el kernel está descartando paquetes porque la comprobación de ruta inversa no encuentra una ruta de regreso por la misma interfaz por la que llegaron, así que asume suplantación y los descarta.

Una frase que debería estar en tus runbooks: “La esperanza no es una estrategia.” —idea parafraseada atribuida a menudo a ingenieros en círculos de fiabilidad

Verdad sin rodeos: el multihoming sin un diseño explícito del enrutamiento es apostar con más cables.

Qué hace realmente el filtrado de ruta inversa (y por qué duele)

rp_filter es la función de validación de origen del kernel Linux. El objetivo es bueno: descartar paquetes que afirman venir de una dirección de origen a la que el sistema no enrutaria de vuelta. Esto bloquea cierta suplantación y reduce el radio de daño de rutas malas. En configuraciones pequeñas y con una sola interfaz, suele ser invisible y mayormente útil.

El modelo mental

Cuando un paquete llega por la interfaz X con IP de origen S, el kernel pregunta: “Si yo enviara un paquete a S, ¿saldría por la interfaz X?”

  • Si la respuesta es “sí”, aceptar.
  • Si la respuesta es “no”, descartar (o tratarlo de forma especial según el modo).

Estricto vs laxo vs desactivado

Linux implementa esto con net.ipv4.conf.*.rp_filter:

  • 0 (off): no realizar filtrado de ruta inversa.
  • 1 (strict): la interfaz entrante debe coincidir con la ruta de vuelta al origen. Genial para una única ruta por defecto. Peligroso para asimetría.
  • 2 (loose): la fuente debe ser alcanzable vía alguna interfaz (existe ruta), pero no necesariamente la misma por la que llegó. Normalmente es la configuración sensata para hosts multi-homed.

El multihoming crea asimetría a propósito. El tráfico puede legítimamente entrar por una interfaz y salir por otra debido a:

  • enrutamiento por políticas (ip rule)
  • múltiples puertas de enlace por defecto (incluso “accidentalmente”)
  • decisiones ECMP en redes ascendentes
  • NAT o cambios en la publicidad de VIP (VRRP/keepalived)
  • túneles (WireGuard, IPsec) donde la “ruta de retorno” no es la misma NIC física

El rp_filter estricto trata esos paquetes legítimos como falsificaciones. Obtienes pérdida de paquetes “aleatoria” que se correlaciona con la elección de ruta que hizo el upstream, por eso puedes mirar la máquina durante una hora y jurar que está encantada.

Broma #1: El filtrado de ruta inversa es como un portero que comprueba si te irías por la misma puerta por la que entraste. Genial para códigos de incendio, terrible para edificios con más de una salida.

Hechos interesantes y contexto (por qué sigue ocurriendo)

  1. rp_filter existe porque la suplantación solía ser habitual. En los primeros días de Internet, con menos filtrado, las direcciones de origen falsificadas eran lo bastante comunes como para que las comprobaciones en el host importaran.
  2. Forma parte de una familia más amplia llamada “validación de dirección de origen”. Las redes implementan conceptos similares en los bordes, pero la validación en el host todavía ayuda cuando el filtrado ascendente es inconsistente.
  3. El modo laxo fue creado específicamente para tolerar la asimetría. El multihoming y el enrutamiento complejo ya no son “casos límites”; son un martes cualquiera.
  4. La configuración es por interfaz y también “all”/“default”. La gente cambia una y asume que afecta a las otras. No siempre lo hace, y no de la forma que crees.
  5. Contenedores y Kubernetes añaden interfaces que no pediste. Los plugins CNI crean pares veth, bridges y rutas que pueden cambiar la decisión de ruta inversa inesperadamente.
  6. VIPs VRRP/keepalived pueden desencadenar descartes por rp_filter durante la conmutación. Un VIP puede llegar por una interfaz mientras la “mejor” ruta de retorno está en otra durante la convergencia.
  7. La red en la nube a menudo produce caminos de retorno asimétricos. NICs secundarias, comprobaciones source/destination y enrutamiento overlay hacen que los paquetes puedan llegar “desde” lugares que la tabla principal no elegiría.
  8. rp_filter interactúa con el enrutamiento por políticas de formas no obvias. La búsqueda de ruta inversa podría no consultar la tabla de enrutamiento que esperabas a menos que las reglas y la selección de origen sean correctas.
  9. La gente confunde rp_filter con firewalling. El descarte ocurre antes de que lo veas en los logs de iptables/nftables a menos que registres explícitamente los martianos del kernel.

Guía de diagnóstico rápido

Si estás de guardia y tienes 10 minutos antes de que alguien escale, haz esto en orden. El objetivo es probar o eliminar rp_filter rápidamente, y luego decidir si necesitas arreglos de enrutamiento o cambios en sysctl.

1) Confirma que el síntoma es direccional y específico de interfaz

  • ¿Puedes ver paquetes entrantes en la interfaz esperada?
  • ¿Las respuestas salen, y si es así, por qué interfaz?
  • ¿La falla está ligada a una red de origen concreta?

2) Comprueba los valores de rp_filter (all, default y la interfaz)

Si rp_filter=1 en cualquier lugar relevante, asúmelo culpable hasta que se demuestre lo contrario.

3) Haz una búsqueda de ruta para la IP de origen desde el contexto de la interfaz

Usa ip route get y asegúrate de que la interfaz de salida coincida con la de entrada si el modo estricto está activado. Si no coincide, el modo estricto descartará.

4) Busca logs del kernel: martianos y quejas de ruta inversa

Activa el registro temporal si es necesario, pero no lo dejes encendido para siempre en sistemas de alto tráfico a menos que te guste llenar discos.

5) Si es multi-homed por diseño, pasa a modo laxo (2) o arregla el enrutamiento por políticas

El modo estricto es una buena configuración de seguridad para servidores con una sola interfaz. Los servidores multi-homed no son servidores single-homed. Trátalos diferente.

Tareas prácticas: comandos, salidas, decisiones (12+)

Estas son las tareas que realmente ejecuto cuando una máquina Ubuntu multi-homed empieza a perder tráfico “sin motivo”. Cada una incluye: comando, salida de ejemplo, qué significa y qué decisión tomar.

Tarea 1: Listar interfaces y direcciones (detectar multihoming y VIPs)

cr0x@server:~$ ip -br addr
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             10.10.10.20/24
eth1             UP             172.20.5.20/24
wg0              UP             10.99.0.2/32

Significado: tienes al menos dos dominios L3 más un túnel. La asimetría no es hipotética.

Decisión: trata este host como multi-homed; rp_filter estricto probablemente sea incorrecto salvo que el enrutamiento esté cuidadosamente fijado.

Tarea 2: Mostrar rutas por defecto y métricas (detectar “dos defaults” accidentales)

cr0x@server:~$ ip route show default
default via 10.10.10.1 dev eth0 proto dhcp src 10.10.10.20 metric 100
default via 172.20.5.1 dev eth1 proto static src 172.20.5.20 metric 200

Significado: existen dos rutas por defecto. Linux preferirá la métrica menor, pero el tráfico entrante puede llegar por cualquiera de las interfaces según el enrutamiento ascendente.

Decisión: implementa enrutamiento por políticas por origen/interfaz, o espera que rp_filter estricto descarte paquetes.

Tarea 3: Comprobar rp_filter global y por interfaz (la habitual pista inequívoca)

cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.default.rp_filter net.ipv4.conf.eth0.rp_filter net.ipv4.conf.eth1.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1

Significado: modo estricto en todas partes.

Decisión: si este host es verdaderamente multi-homed (y no “una interfaz es peso muerto”), cambia a modo laxo (2) para las interfaces relevantes o rediseña el enrutamiento para que la validación estricta sea válida.

Tarea 4: Confirmar el problema con una búsqueda de ruta hacia una fuente fallida (lógica inversa)

cr0x@server:~$ ip route get 203.0.113.55
203.0.113.55 via 10.10.10.1 dev eth0 src 10.10.10.20 uid 0
    cache

Significado: el kernel enviaría tráfico a 203.0.113.55 vía eth0. Si paquetes de 203.0.113.55 llegan por eth1, rp_filter estricto los descartará.

Decisión: o arregla el enrutamiento para que la ruta de retorno use eth1 para esa fuente (enrutamiento por políticas), o relaja rp_filter.

Tarea 5: Capturar en la interfaz de ingreso (probar que los paquetes llegan)

cr0x@server:~$ sudo tcpdump -ni eth1 host 203.0.113.55 and tcp port 443 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:10:01.100001 IP 203.0.113.55.51512 > 172.20.5.20.443: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 1 ecr 0], length 0
12:10:02.100002 IP 203.0.113.55.51512 > 172.20.5.20.443: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 2 ecr 0], length 0
^C
2 packets captured

Significado: los SYNs llegan por eth1. Si tu servicio está escuchando, deberías ver el SYN-ACK saliendo. Si no lo ves, sospecha de un descarte del kernel o del firewall local.

Decisión: captura también en la interfaz de salida; si no sale nada, pasa a comprobar rp_filter/martianos.

Tarea 6: Capturar en la interfaz de egress esperada (ver si las respuestas salen por otra parte)

cr0x@server:~$ sudo tcpdump -ni eth0 host 203.0.113.55 and tcp port 443 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
0 packets captured

Significado: no hay respuestas saliendo por eth0 para ese flujo (al menos durante la captura). Combinado con la Tarea 5, probablemente hay descartes antes de que la pila TCP responda, o las respuestas están bloqueadas en otro sitio.

Decisión: revisa logs del kernel y rp_filter; verifica que el servicio esté escuchando; revisa nftables/iptables solo después de haber descartado rp_filter.

Tarea 7: Inspeccionar logs del kernel por martianos y descartes de rp_filter

cr0x@server:~$ sudo journalctl -k --since "10 min ago" | tail -n 20
Dec 30 12:09:58 server kernel: IPv4: martian source 203.0.113.55 from 203.0.113.55, on dev eth1
Dec 30 12:09:58 server kernel: ll header: 00000000: 00 11 22 33 44 55 66 77 88 99 aa bb 08 00
Dec 30 12:09:58 server kernel: IPv4: martian source 203.0.113.55 from 203.0.113.55, on dev eth1

Significado: el kernel te está diciendo que considera esa fuente inválida en esa interfaz. Eso es validación de ruta inversa en acción (o comprobaciones de martianos relacionadas).

Decisión: cambia a rp_filter laxo o arregla el enrutamiento por políticas para que la ruta inversa coincida.

Tarea 8: Establecer temporalmente rp_filter en modo laxo (prueba rápida, no permanente aún)

cr0x@server:~$ sudo sysctl -w net.ipv4.conf.eth1.rp_filter=2
net.ipv4.conf.eth1.rp_filter = 2

Significado: eth1 ahora usa modo laxo.

Decisión: vuelve a probar con el cliente fallido. Si el problema desaparece inmediatamente, has confirmado la causa raíz. Después haz un cambio permanente y documentado (y considera las implicaciones de seguridad).

Tarea 9: Hacer rp_filter persistente con un archivo sysctl dedicado

cr0x@server:~$ printf '%s\n' \
'net.ipv4.conf.all.rp_filter=2' \
'net.ipv4.conf.default.rp_filter=2' \
'net.ipv4.conf.eth0.rp_filter=2' \
'net.ipv4.conf.eth1.rp_filter=2' | sudo tee /etc/sysctl.d/60-multihome-rpfilter.conf
net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.default.rp_filter=2
net.ipv4.conf.eth0.rp_filter=2
net.ipv4.conf.eth1.rp_filter=2
cr0x@server:~$ sudo sysctl --system | tail -n 8
* Applying /etc/sysctl.d/60-multihome-rpfilter.conf ...
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.eth0.rp_filter = 2
net.ipv4.conf.eth1.rp_filter = 2

Significado: las configuraciones sobrevivirán al reinicio y se aplicarán de forma predecible.

Decisión: incorpora esto en tu gestión de configuración y anota el motivo; “endurecimiento de seguridad” sin excepciones es como recibir llamadas medianoche.

Tarea 10: Validar reglas de enrutamiento por políticas (si prefieres rp_filter estricto)

cr0x@server:~$ ip rule show
0:      from all lookup local
100:    from 172.20.5.20 lookup 200
110:    from 10.10.10.20 lookup 100
32766:  from all lookup main
32767:  from all lookup default

Significado: el tráfico originado por cada IP usa una tabla diferente. Eso es el comienzo de la cordura.

Decisión: inspecciona las tablas 100 y 200 para asegurarte de que cada una tenga la puerta de enlace por defecto correcta y rutas on-link. Si una tabla está incompleta, las comprobaciones de ruta inversa y el enrutamiento de respuesta seguirán siendo incorrectos.

Tarea 11: Inspeccionar las tablas de enrutamiento personalizadas (¿están presentes defaults y rutas conectadas?)

cr0x@server:~$ ip route show table 100
default via 10.10.10.1 dev eth0 src 10.10.10.20
10.10.10.0/24 dev eth0 proto kernel scope link src 10.10.10.20
cr0x@server:~$ ip route show table 200
default via 172.20.5.1 dev eth1 src 172.20.5.20
172.20.5.0/24 dev eth1 proto kernel scope link src 172.20.5.20

Significado: cada tabla es autocontenida (default más subred conectada). Eso es lo que quieres.

Decisión: si quieres rp_filter estricto, aún debes garantizar que las rutas de retorno coincidan con la entrada. El enrutamiento por políticas ayuda, pero también debes asegurarte de la selección de fuente y de cómo las aplicaciones se enlazan.

Tarea 12: Búsqueda de ruta con fuente explícita (validar tu enrutamiento por políticas)

cr0x@server:~$ ip route get 203.0.113.55 from 172.20.5.20
203.0.113.55 via 172.20.5.1 dev eth1 src 172.20.5.20 uid 0
    cache

Significado: si la respuesta se origina desde 172.20.5.20, sale por eth1. Esto se alinea con las expectativas de ruta inversa estricta para flujos que llegan por eth1.

Decisión: si esto no coincide, arregla ip rule y las tablas de enrutamiento en lugar de desactivar rp_filter a ciegas.

Tarea 13: Comprobar las configuraciones de “log martiano” (útil durante el incidente)

cr0x@server:~$ sysctl net.ipv4.conf.all.log_martians net.ipv4.conf.eth1.log_martians
net.ipv4.conf.all.log_martians = 0
net.ipv4.conf.eth1.log_martians = 0

Significado: el kernel puede estar descartando sin avisarte.

Decisión: activa temporalmente el registro en la interfaz sospechosa durante el diagnóstico; luego desactívalo para evitar spam en los logs.

Tarea 14: Activar temporalmente el registro de martianos (dirigido, con tiempo limitado)

cr0x@server:~$ sudo sysctl -w net.ipv4.conf.eth1.log_martians=1
net.ipv4.conf.eth1.log_martians = 1

Significado: ahora verás evidencia en journalctl -k cuando la lógica de ruta inversa rechace tráfico.

Decisión: reproduce el problema una vez, captura los logs y luego vuelve a apagarlo.

Tarea 15: Verificar configuraciones relacionadas con ARP flux (a menudo coinciden con problemas de multihoming)

cr0x@server:~$ sysctl net.ipv4.conf.all.arp_ignore net.ipv4.conf.all.arp_announce
net.ipv4.conf.all.arp_ignore = 0
net.ipv4.conf.all.arp_announce = 0

Significado: el comportamiento ARP por defecto puede ser “creativo” en hosts multi-homed (responder por la interfaz “equivocada”). No es rp_filter, pero crea síntomas igualmente extraños.

Decisión: si usas VIPs o múltiples subredes, considera endurecer el comportamiento ARP junto con las configuraciones de rp_filter, especialmente en metal físico o redes adyacentes L2.

Tres micro-historias corporativas desde el campo

Micro-historia 1: el corte causado por una suposición errónea

La empresa tenía un par de nodos API “idénticos” en dos centros de datos. Cada nodo tenía dos NICs: una para servicios internos y otra para tráfico de partners. El documento de diseño decía “el tráfico de partners usa eth1”. Esa línea se trató como física.

Un cambio de red upstream ocurrió—mantenimiento rutinario, un ajuste de preferencia BGP, nada dramático. La ruta de retorno de un partner empezó a llegar por un borde diferente, y los paquetes comenzaron a aterrizar en eth0 en uno de los sitios debido a un nuevo salto L3. El nodo API seguía respondiendo por eth1 debido al enrutamiento por políticas basado en la dirección origen que elegía para las respuestas.

rp_filter estricto estaba activado como parte de un paquete de endurecimiento base. Siempre había estado activado. También siempre había tenido suerte. Cuando llegó la asimetría, el kernel empezó a descartar paquetes entrantes como “martianos”. El equipo de apps vio errores 5xx intermitentes. El SRE de guardia no vio errores de interfaz, ni denegaciones de firewall, ni picos de CPU. Solo un goteo lento de sesiones fallidas.

La suposición errónea fue sutil: que “el tráfico de partners usa eth1” significaba que llegaría por eth1. En redes ruteadas, “debería” es una aspiración, no una garantía.

La solución no fue heroica. Cambiaron a modo laxo en la interfaz hacia partners y actualizaron el enrutamiento por políticas para ser consistentes. Luego añadieron una prueba de regresión: búsquedas de ruta desde cada IP origen hacia prefijos representantes de partners. La línea del postmortem que importó: “Asumimos simetría; la red no firmó ese contrato.”

Micro-historia 2: una optimización que salió mal

Un equipo de plataforma quiso reducir el riesgo de movimiento lateral. Aplicaron un nuevo bundle de sysctl a las flotas: rp_filter estricto, martian logging apagado (para reducir “ruido”) y algunas otras vueltas que quedaban bien en una hoja de cumplimiento.

Funcionó en la capa web: una sola NIC, una sola ruta por defecto, predecible. Así que lo incorporaron a la imagen base usada para todo, incluidos sistemas con estado con redes de replicación, redes de backup y ocasionalmente una “NIC de migración” que aparecía y desaparecía.

El fallo llegó con retraso. Un mes después, un servicio adyacente al almacenamiento empezó a fallar solo durante ventanas de backup. ¿Por qué? El sistema de backup usaba otra red, y durante esas horas el servicio originaba algo de tráfico con la dirección “backup” por una bind de aplicación demasiado amplia. Las respuestas tomaban otra interfaz distinta a la de las solicitudes entrantes. rp_filter estricto interpretó algunos flujos entrantes como suplantados y los descartó, pero solo cuando ciertas rutas estaban presentes.

La optimización—endurecer todo por igual—creó un bug intermitente que se alineaba con horarios operativos. El peor tipo de intermitente: reproducible solo cuando estás cansado y sin café.

Se recuperaron separando perfiles sysctl: uno para capas sin estado y single-homed, otro para sistemas multi-homed y con enrutamiento complejo. También cambiaron la estrategia de “logs silenciosos”. No necesitas martian logs todo el día, pero sí un interruptor fácil para activarlos durante incidentes. Los sistemas silenciosos son agradables; las fallas silenciosas son caras.

Micro-historia 3: la práctica aburrida pero correcta que salvó el día

Una empresa regulada operaba hosts bastión multi-homed: una NIC a la red de administración, otra NIC a un segmento controlado de partners. El entorno estaba lleno de cambios, y a la gente le encantaban los “arreglos rápidos”. Estos hosts rechazaban los arreglos rápidos.

El equipo tenía una regla: cada nodo multi-homed debía tener una pequeña auto-prueba de enrutamiento que se ejecutara al arranque y en cada recarga de red. Ejecutaba ip route get para varios destinos representativos, desde cada dirección origen, y comparaba las salidas con una expectativa conocida. Si divergía, generaba una alerta antes de que los usuarios lo notaran.

Un día, tras una actualización de kernel y un cambio de netplan, la prueba falló: la ruta de retorno desde la IP partner empezó a preferir la ruta por defecto de administración. Nadie lo notó en actividad normal porque el flujo afectado era de bajo volumen y para callbacks de monitoreo. Pero la prueba lo notó, y fuerte.

La solución fue una corrección de orden en ip rule y una decisión deliberada: mantener rp_filter=2 en la interfaz partner porque la entrada asimétrica aún podía ocurrir. El punto clave es lo aburrido: el problema fue detectado por una comprobación determinista, no por una depuración heroica en producción.

Broma #2: El sistema más fiable es el que falla en staging. El segundo más fiable es el que chiva inmediatamente.

Diseñar multihoming que sobreviva a rp_filter

Elige tu filosofía: corrección estricta u tolerancia operacional

Tienes dos enfoques defendibles. Mezclarlos sin pensar es como obtener el “caso nº 54”.

Enfoque A: Mantener rp_filter estricto, diseñar para simetría

Esto puede funcionar, pero requiere trabajo:

  • Asegurar que el tráfico que entra por la interfaz X siempre enrutará de vuelta al origen vía X.
  • Implementar enrutamiento por políticas: ip rule basado en la IP origen (y a veces en fwmarks) para seleccionar una tabla de enrutamiento por interfaz/enlace ascendente.
  • Hacer que cada tabla de enrutamiento sea completa: rutas conectadas más default (y cualquier ruta específica requerida).
  • Asegurar que las aplicaciones se enlacen a las direcciones origen correctas o usen el comportamiento adecuado de SO_BINDTODEVICE/bind() cuando sea necesario.

Esto es genial cuando controlas verdaderamente ambos lados del camino (WAN privada, upstream predecible). Es menos genial en Internet pública o en nubes con enrutamiento “amable”.

Enfoque B: Usar rp_filter laxo para multi-homed, centrarse en alcance y registro

Esto es lo que recomiendo para la mayoría de hosts multi-homed que enfrentan rutas impredecibles:

  • Configurar rp_filter=2 en las interfaces relevantes (o globalmente si el host es consistentemente multi-homed).
  • Mantener enrutamiento por políticas de todos modos, porque aún quieres comportamiento de egress estable y selección de origen correcta.
  • Habilitar martian logs solo durante resolución de problemas, o enviarlos a pipelines de logging con limitación de tasa.
  • Confiar en el anti-spoofing upstream en los bordes de la red, además de firewall en el host y principio de menor privilegio. rp_filter no es tu único control.

Específicos de Ubuntu 24.04: dónde la gente se sorprende

Ubuntu 24.04 en sí no es malintencionado. La sorpresa suele venir de qué cambiaste al mismo tiempo:

  • nuevas imágenes base
  • nuevos conjuntos sysctl de hardening
  • re-escrituras de netplan que añaden accidentalmente una segunda ruta por defecto o cambian métricas
  • actualizaciones de kernel que cambian nombres de interfaz o comportamiento de drivers y reordenan rutas
  • actualizaciones de red de contenedores (versiones de CNI) que añaden reglas y rutas

El modo de fallo se siente como “la red de Ubuntu es inestable”. No lo es. Tu política de enrutamiento es inconsistente con la validación estricta de origen. El kernel simplemente está haciendo cumplir lo que pediste.

Cómo decidir: una rúbrica práctica

  • Si el servidor es single-homed y permanecerá así: estricto (1) está bien y a menudo es deseable.
  • Si el servidor tiene dos enlaces activos: por defecto a laxo (2) a menos que tengas una razón fuerte y disciplina de enrutamiento rigurosa.
  • Si el servidor usa VIPs, VRRP, direct server return de balanceadores o trucos tipo anycast: asume asimetría; usa laxo (2) y prueba las conmutaciones.
  • Si el servidor ejecuta Kubernetes con múltiples CNIs o enrutamiento especial: laxo (2) suele ser más seguro operativamente; luego valida con ip route get por cada red de pods según sea necesario.
  • Si la política de seguridad exige estricto: aplica simetría con enrutamiento por políticas y binding de aplicaciones explícito, y añade pruebas continuas para que no derive.

Errores comunes: síntoma → causa raíz → solución

1) “Las conexiones entrantes caducan, pero solo desde algunas redes”

Síntoma: ciertas subredes de clientes no pueden conectar; otras están bien. Los SYNs llegan, no hay SYN-ACK.

Causa raíz: rp_filter estricto descarta paquetes que llegan por una interfaz que no es la interfaz de retorno preferida para esa fuente (camino asimétrico).

Solución: establecer net.ipv4.conf.<if>.rp_filter=2 o implementar enrutamiento basado en origen para que la ruta de retorno coincida con la entrada.

2) “Se rompió justo después de añadir una segunda ruta por defecto para resiliencia”

Síntoma: la conectividad es inestable tras añadir una puerta de enlace de backup.

Causa raíz: dos defaults más rp_filter estricto crean un desfase cuando el tráfico llega por la interfaz de mayor métrica.

Solución: no te fíes solo de una “ruta por defecto de respaldo”; usa enrutamiento por políticas y/o protocolos de enrutamiento. Si mantienes defaults duales, prefiere rp_filter modo laxo.

3) “Los logs de firewall no muestran nada, pero los paquetes desaparecen”

Síntoma: los contadores de nftables/iptables no cambian; tcpdump ve paquetes entrantes; la app no responde.

Causa raíz: el descarte por rp_filter ocurre antes de los hooks de firewall que estás mirando, y el registro de martianos está apagado.

Solución: comprueba sysctl net.ipv4.conf.*.rp_filter, habilita brevemente log_martians, y luego ajusta rp_filter a laxo o arregla el enrutamiento.

4) “La conmutación VRRP causa una breve interrupción, luego se recupera”

Síntoma: un VIP se mueve y algunos clientes fallan durante 10–60 segundos.

Causa raíz: durante la convergencia, la “mejor ruta de retorno” cambia más rápido/lento que la publicidad del VIP; rp_filter estricto descarta paquetes que llegan por la interfaz “equivocada”.

Solución: usar rp_filter laxo en las interfaces que llevan VIPs, y validar comportamiento ARP (arp_ignore/arp_announce) para evitar confusión de interfaces.

5) “WireGuard funciona saliente, pero el handshake entrante es intermitente”

Síntoma: el túnel se levanta a veces; el peer remoto ve reintentos.

Causa raíz: los paquetes al endpoint del túnel llegan por una NIC, la ruta de retorno prefiere otra por la ruta por defecto/métricas; rp_filter estricto descarta.

Solución: rp_filter laxo, además asegurar que el enrutamiento hacia el endpoint del peer esté fijado en el uplink correcto si necesitas comportamiento determinista.

6) “Pusimos rp_filter=2 en /etc/sysctl.conf pero sigue siendo 1 tras reiniciar”

Síntoma: los valores en tiempo de ejecución revierten.

Causa raíz: otro archivo en sysctl.d lo sobrescribe más tarde, o cloud-init/hardening se aplica después de tu archivo.

Solución: coloca un archivo dedicado con un orden lexicográfico que gane (nombre con orden superior), luego verifica con la salida de sysctl --system.

7) “Solo una interfaz está afectada”

Síntoma: eth1 descarta, eth0 no.

Causa raíz: el rp_filter por interfaz difiere, o solo una interfaz recibe tráfico asimétrico.

Solución: inspecciona las configuraciones por interfaz. No asumas que all implica que cada interfaz sea igual.

Listas de verificación / plan paso a paso

Checklist A: detener el sangrado (modo incidente)

  1. Probar la entrada: tcpdump en la interfaz sospechosa para la IP/puerto del cliente que falla.
  2. Comprobar rp_filter: leer net.ipv4.conf.all/default/<if>.rp_filter.
  3. Comprobar ruta de vuelta: ip route get <client-ip> y comparar la interfaz de salida esperada.
  4. Buscar martianos: journalctl -k. Si no aparece nada, activa temporalmente log_martians=1 en la interfaz.
  5. Mitigación rápida: establecer rp_filter a laxo (2) en la(s) interfaz(es) afectada(s), volver a probar.
  6. Revertir cambios arriesgados: si se aplicó un paquete de hardening a toda la flota, detener el despliegue y aislar roles multi-homed.

Checklist B: dejarlo correcto (después del incidente)

  1. Decidir tu modelo: estricto-con-simetría o laxo-con-enrutamiento-por-políticas.
  2. Eliminar defaults duales accidentales: si ambos defaults son necesarios, documenta por qué y asegura métricas y reglas intencionales.
  3. Implementar enrutamiento por políticas: una tabla por uplink/IP origen. Mantén las tablas completas.
  4. Validar con sondas de ruta: ejecutar ip route get desde cada IP origen hacia destinos representativos.
  5. Hacer persistentes los sysctls: archivo dedicado en /etc/sysctl.d/; verificar el orden.
  6. Documentar excepciones: los hosts multi-homed no son “menos seguros”; requieren controles diferentes.
  7. Añadir un detector de deriva: un script simple que avise cuando rp_filter cambie o cuando las tablas de enrutamiento pierdan rutas requeridas.

Checklist C: endurecimiento sin autoboicot

  1. Clasificar hosts: single-homed, multi-homed, con VIP, endpoints de túnel, nodos Kubernetes.
  2. Aplicar rp_filter por clase: estricto para single-homed; laxo para multi-homed y roles con VIP/túnel salvo que la simetría esté asegurada.
  3. Registrar tácticamente: mantener martian logs apagados por defecto, pero ofrecer un interruptor bajo demanda y límites de tasa.
  4. Probar modos de fallo: eventos de failover, enlace down/up, renovación DHCP, netplan apply, reinicio de CNI.

Preguntas frecuentes

1) ¿Es rp_filter un firewall?

No. Es validación de origen en la ruta de enrutamiento del kernel. Puede descartar paquetes antes de que tus reglas o logs de firewall hagan la situación obvia.

2) ¿Debería poner rp_filter a 0 en todas partes para “arreglar la red”?

Sólo si disfrutas intercambiar una clase de fallos por una regresión de seguridad. Para hosts multi-homed, usa 2 (loose) en la mayoría de los casos y mantén tu política de enrutamiento sensata.

3) ¿Cuál es la configuración más segura para servidores Ubuntu multi-homed?

rp_filter=2 suele ser el mejor equilibrio. Combínalo con enrutamiento por políticas para que el egress sea determinista. Si puedes garantizar simetría, el modo estricto puede ser viable—pero no pretendas que eso sea gratis.

4) ¿Por qué aparece esto tras una actualización a Ubuntu 24.04?

Porque las actualizaciones a menudo coinciden con nuevos baselines sysctl, cambios de netplan, métricas de ruta por defecto diferentes o nuevas interfaces (túneles, contenedores). rp_filter no se volvió más cruel; tu entorno se volvió más complejo.

5) ¿Cómo confirmo que rp_filter es el culpable en minutos?

Comprueba sysctl net.ipv4.conf.<if>.rp_filter, ejecuta ip route get <source-ip> y busca martian logs. Cambia la interfaz a modo laxo temporalmente y vuelve a probar. Si lo arregla, tienes la respuesta.

6) ¿El modo laxo (2) permite suplantación?

Relaja la coincidencia de interfaz, no la alcanzabilidad. La fuente aún debe ser enrutable en algún lugar. Es menos estricto, así que sí, es un control anti-spoofing más débil que el modo estricto—usa filtrado upstream y firewalls en el host como controles primarios.

7) ¿Por qué veo “martian source” en los logs?

Eso es el kernel indicando que recibió un paquete con una dirección de origen que falla las comprobaciones de coherencia para la interfaz, a menudo por rp_filter en modo estricto o por inconsistencias en las rutas.

8) ¿Puedo mantener rp_filter estricto y aun así hacer enrutamiento por políticas?

Sí, pero debes ser disciplinado: orden correcto de ip rule, tablas de enrutamiento completas y selección de dirección origen predecible. Si algo de eso deriva, rp_filter estricto te castigará rápido.

9) ¿Los nodos Kubernetes deberían usar rp_filter estricto?

Usualmente no salvo que tu CNI y enrutamiento estén diseñados para ello. Nodos con múltiples interfaces, redes overlay y VIPs de servicio hacen que la asimetría sea común. El modo laxo es más seguro operativamente.

10) ¿Dónde configurar esto persistentemente en Ubuntu 24.04?

Crea un archivo en /etc/sysctl.d/ (por ejemplo 60-multihome-rpfilter.conf), luego aplica con sysctl --system y verifica los valores finales.

Conclusión: siguientes pasos que no te despertarán a las 03:00

El filtrado de ruta inversa es una de esas funciones que parece “seguridad gratis” hasta que ejecutas una red real. Multihoming, VIPs, túneles y enrutamiento por políticas son normales en producción. El rp_filter estricto trata lo “normal” como “criminal”.

Haz esto:

  1. Clasifica los hosts por complejidad de red. Deja de aplicar un perfil sysctl único a todo.
  2. Para roles multi-homed, configura rp_filter=2 (loose) a menos que puedas probar simetría extremo a extremo.
  3. Implementa enrutamiento por políticas para que el egress sea determinista y la depuración sea humana.
  4. Añade una auto-prueba de enrutamiento (unos pocos ip route get) para detectar deriva antes que los clientes.
  5. Mantén un interruptor de martian logging disponible para incidentes y apágalo tras obtener evidencia.

Si no te llevas nada más: el multihoming es un diseño, no una casilla para marcar. rp_filter solo hace cumplir si realmente lo diseñaste.

← Anterior
ZFS Readonly: el truco anti-ransomware que puedes implementar hoy
Siguiente →
ARC de ZFS por TB: Dimensionamiento de RAM sin mitos ni dogmas de foros

Deja un comentario