Ubuntu 24.04 : Filtrage du chemin inverse — le réglage caché qui casse le multi-homing (cas n°54)

Cet article vous a aidé ?

Symptôme : une machine Ubuntu 24.04 multi‑homée « fonctionne pour la plupart » jusqu’à ce qu’elle ne fonctionne plus. Certaines connexions entrantes se bloquent. Les réponses disparaissent. La supervision indique que le lien est OK. Votre équipe applicative insiste pour dire que c’est DNS. Ce n’est pas ça.

Réalité : le noyau abandonne silencieusement des paquets parce que rp_filter a estimé que votre routage semble suspect. Et si vous avez deux NIC, deux uplinks, des VRF, du policy routing, Kubernetes, des VIP ou quoi que ce soit ressemblant à la vraie vie, la suspicion devient un mode de fonctionnement.

Cas n°54 : la panne multi‑homing qui vous a surpris

Nous allons présenter cela comme un incident réel, parce que c’est ainsi que vous le rencontrerez : une demi‑panne avec d’excellentes apparences.

Un nœud de service dispose de deux uplinks :

  • eth0 vers le réseau « interne » (trafic est‑ouest, cluster, stockage).
  • eth1 vers un réseau « dmz/edge » (trafic nord‑sud, partenaires, supervision, parfois VPN).

Les deux liens sont actifs. Les deux ont des adresses. Il existe un routage basé sur la politique pour faire que certaines adresses source préfèrent certaines passerelles. Il peut aussi y avoir un VIP pour basculement (VRRP/keepalived), ou une interface WireGuard qui devient la route par défaut pour un sous‑ensemble de trafic.

Puis quelqu’un met à jour vers Ubuntu 24.04 ou reconstruit un nœud et applique des sysctls de « durcissement » de sécurité. Soudain :

  • Des SYN entrants arrivent sur eth1, mais le SYN‑ACK ne sort jamais.
  • Le ping ICMP fonctionne depuis certains réseaux mais pas d’autres.
  • Les connexions depuis un ASN partenaire basculent toutes les quelques minutes.
  • Le trafic vers le stockage fonctionne ; le trafic vers le load balancer ne fonctionne pas ; les probes de vivacité Kubernetes sont aléatoires.

La première heure est consacrée à blâmer l’évident : règles de pare‑feu, MTU, un « mauvais port de switch », et—inévitablement—DNS. Pendant ce temps le noyau jette des paquets parce que la vérification du chemin inverse ne trouve pas de route de retour par la même interface sur laquelle le paquet est arrivé, donc il suppose une usurpation et supprime le paquet.

Une citation qui devrait figurer dans vos runbooks : « L’espoir n’est pas une stratégie. » — idée paraphrasée souvent attribuée aux ingénieurs en fiabilité

Voici la vérité crue : le multi‑homing sans conception explicite du routage est juste un jeu de hasard avec plus de câbles.

Ce que fait réellement le filtrage du chemin inverse (et pourquoi ça mord)

rp_filter est la fonctionnalité de validation d’origine de Linux. L’objectif est louable : rejeter les paquets qui prétendent provenir d’une adresse source vers laquelle le système ne routerait pas en retour. Cela bloque certaines usurpations et réduit le rayon d’action des routes défectueuses. Dans des petites installations mono‑homées, c’est généralement invisible et globalement utile.

Le modèle mental

Quand un paquet arrive sur l’interface X avec l’IP source S, le noyau se demande : « Si j’envoyais un paquet à S, sortirait‑il par l’interface X ? »

  • Si la réponse est « oui », accepter.
  • Si la réponse est « non », abandonner (ou le traiter différemment selon le mode).

Strict vs loose vs off

Linux implémente cela via net.ipv4.conf.*.rp_filter :

  • 0 (off) : ne pas effectuer de filtrage du chemin inverse.
  • 1 (strict) : l’interface entrante doit correspondre à la route de retour vers la source. Parfait pour une route par défaut unique. Dangereux pour l’asymétrie.
  • 2 (loose) : la source doit être joignable via quelque interface (une route existe), mais pas nécessairement l’interface entrante. C’est généralement le réglage sensé pour les hôtes multi‑homés.

Le multi‑homing crée de l’asymétrie volontairement. Le trafic peut légitimement entrer par une interface et sortir par une autre à cause de :

  • routage basé sur la politique (ip rule)
  • plusieurs passerelles par défaut (même « accidentellement »)
  • décisions ECMP dans les réseaux en amont
  • NAT ou changements d’annonce de VIP (VRRP/keepalived)
  • tunnels (WireGuard, IPsec) où le « chemin de retour » n’est pas le même NIC physique

Le mode strict de rp_filter traite ces paquets légitimes comme des faux. Vous obtenez une perte de paquets « aléatoire » qui corrèle avec le chemin choisi en amont, c’est pourquoi vous pouvez regarder la machine pendant une heure et jurer qu’elle est hantée.

Blague n°1 : Le filtrage du chemin inverse est comme un videur qui vérifie si vous partiriez par la même porte que celle par laquelle vous êtes entré. Parfait pour la sécurité incendie, terrible pour un bâtiment avec plus d’une sortie.

Faits intéressants et contexte (pourquoi ça revient)

  1. rp_filter existe parce que l’usurpation d’adresse était autrefois courante. Dans les premières années moins filtrées d’Internet, les adresses source falsifiées étaient suffisamment répandues pour que la validation côté hôte ait du sens.
  2. Il fait partie d’une famille plus large appelée « validation d’adresse source ». Les réseaux implémentent des concepts similaires en périphérie, mais la validation hôte‑side reste utile quand le filtrage en amont est incohérent.
  3. Le mode loose a été conçu spécifiquement pour tolérer l’asymétrie. Le multi‑homing et le routage complexe ne sont plus des « cas limites » ; ce sont des conditions normales.
  4. Le réglage est par interface et aussi « all »/« default ». Les gens changent un réglage en pensant qu’il affecte les autres. Ce n’est pas toujours le cas, et pas de la façon dont vous pensez.
  5. Les conteneurs et Kubernetes ajoutent des interfaces que vous n’avez pas demandées. Les plugins CNI créent des paires veth, des bridges et des routes qui peuvent changer la décision de chemin inverse de façon inattendue.
  6. Les VIP VRRP/keepalived peuvent déclencher des suppressions rp_filter lors d’un basculement. Un VIP peut arriver sur une interface alors que la meilleure route de retour est ailleurs pendant la convergence.
  7. Le réseau cloud produit souvent des chemins de retour asymétriques. NIC secondaires, vérifications source/destination, et routage en overlay font que des paquets peuvent arriver « depuis » des endroits que votre table principale ne choisirait pas.
  8. rp_filter interagit avec le routage basé sur la politique de façon non triviale. La recherche du chemin inverse peut ne pas consulter la table de routage que vous attendiez à moins que les règles et la sélection d’origine soient correctes.
  9. Les gens confondent rp_filter avec un pare‑feu. Le rejet se produit avant que vous le voyiez dans les logs iptables/nftables à moins que vous n’activiez explicitement la journalisation des martiens du noyau.

Guide de diagnostic rapide

Si vous êtes de permanence et que vous avez 10 minutes avant que quelqu’un n’escalade, faites cela dans l’ordre. L’objectif est de prouver ou d’éliminer rp_filter rapidement, puis décider si vous avez besoin de corrections de routage ou de changements sysctl.

1) Confirmer que le symptôme est directionnel et spécifique à une interface

  • Pouvez‑vous voir les paquets entrants sur l’interface attendue ?
  • Les réponses sortent‑elles, et si oui, par quelle interface ?
  • La panne est‑elle liée à un réseau source particulier ?

2) Vérifier les valeurs rp_filter (all, default et l’interface)

Si rp_filter=1 est présent n’importe où de pertinent, considérez‑le coupable jusqu’à preuve du contraire.

3) Faire une recherche de route pour l’IP source dans le contexte de l’interface

Utilisez ip route get et assurez‑vous que l’interface de sortie correspond à l’interface d’entrée si le mode strict est activé. Si ça ne correspond pas, le mode strict va abandonner.

4) Chercher les logs du noyau : martians et plaintes de chemin inverse

Activez la journalisation temporaire si nécessaire, mais ne la laissez pas activée en permanence sur des systèmes à fort trafic à moins d’aimer remplir des disques.

5) Si c’est multi‑homing par conception, passez en mode loose (2) ou corrigez le routage basé sur la politique

Le mode strict est un bon paramètre de sécurité pour les serveurs mono‑homés. Les serveurs multi‑homés ne sont pas des serveurs mono‑homés. Traitez‑les différemment.

Tâches pratiques : commandes, sorties, décisions (12+)

Voici les tâches que j’exécute réellement lorsqu’une machine Ubuntu multi‑homée commence à perdre du trafic « sans raison ». Chaque tâche inclut : commande, sortie exemple, ce que cela signifie, et la décision à prendre.

Task 1: List interfaces and addresses (spot multi-homing and 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

Signification : vous avez au moins deux domaines L3 plus un tunnel. L’asymétrie n’est pas hypothétique.

Décision : considérez cet hôte comme multi‑homé ; rp_filter strict est probablement inapproprié sauf si le routage est soigneusement verrouillé.

Task 2: Show default routes and metrics (spot accidental “two defaults”)

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

Signification : deux routes par défaut existent. Linux préfèrera la métrique la plus basse, mais le trafic entrant peut arriver par l’une ou l’autre interface selon le routage en amont.

Décision : implémentez du routage basé sur la source/interface, ou attendez‑vous à ce que rp_filter strict jette des paquets.

Task 3: Check rp_filter globally and per-interface (the usual smoking gun)

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

Signification : mode strict partout.

Décision : si cet hôte est vraiment multi‑homé (et non « une interface inutile »), basculez en mode loose (2) pour les interfaces concernées ou redessinez le routage pour que la validation stricte tienne.

Task 4: Confirm the problem with a route lookup to a failing source (reverse-path logic)

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

Signification : le noyau enverrait du trafic vers 203.0.113.55 via eth0. Si des paquets de 203.0.113.55 arrivent sur eth1, rp_filter strict les rejettera.

Décision : corrigez le routage pour que le chemin de retour utilise eth1 pour cette source (routage basé sur la source), ou relâchez rp_filter.

Task 5: Capture on the ingress interface (prove packets arrive)

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

Signification : des SYN arrivent sur eth1. Si votre service écoute, vous devriez voir les SYN‑ACK sortir. Si vous ne les voyez pas, suspectez un rejet du noyau ou un pare‑feu local.

Décision : capturez aussi en sortie ; si rien ne sort, passez aux vérifications rp_filter/martian.

Task 6: Capture on the expected egress interface (see if replies leave elsewhere)

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

Signification : aucune réponse ne sort sur eth0 pour ce flux (au moins pendant la capture). Combiné avec la tâche 5, vous avez probablement des suppressions avant que la pile TCP réponde, ou des réponses bloquées ailleurs.

Décision : vérifiez les logs du noyau et rp_filter ; vérifiez que le service écoute ; contrôlez nftables/iptables seulement après avoir éliminé rp_filter.

Task 7: Inspect kernel logs for martians and rp_filter drops

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

Signification : le noyau vous indique qu’il considère cette source comme invalide sur cette interface. C’est la validation du chemin inverse en action (ou des vérifications martiennes associées).

Décision : passez en mode rp_filter loose ou corrigez le routage basé sur la politique pour que le chemin de retour corresponde.

Task 8: Temporarily set rp_filter to loose mode (quick test, not yet permanent)

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

Signification : eth1 utilise maintenant le mode loose.

Décision : retestez le client en échec. Si le problème disparaît immédiatement, vous avez confirmé la cause. Ensuite, faites un changement permanent et documenté (et considérez les implications en matière de sécurité).

Task 9: Make rp_filter persistent with a dedicated sysctl file

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

Signification : les réglages survivront au redémarrage et s’appliqueront de façon prévisible.

Décision : engagez cela dans votre gestion de configuration et notez pourquoi ; « durcissement de sécurité » sans exceptions est la façon d’obtenir des appels à minuit.

Task 10: Validate policy routing rules (if you prefer strict rp_filter)

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

Signification : le trafic issu de chaque IP utilise une table différente. C’est le début de la cohérence.

Décision : inspectez les tables 100 et 200 pour vous assurer que chacune a la bonne passerelle par défaut et les routes on‑link. Si une table est incomplète, les vérifications de chemin inverse et le routage de réponse resteront incorrects.

Task 11: Inspect the custom routing tables (are defaults and connected routes present?)

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

Signification : chaque table est autonome (par défaut plus le réseau connecté). C’est ce qu’il faut.

Décision : si vous voulez rp_filter strict, vous devez toujours garantir que les routes de retour correspondent à l’entrée. Le routage basé sur la politique aide, mais vous devez aussi vous assurer que la sélection de l’adresse source et les binds applicatifs se comportent comme attendu.

Task 12: Route lookup with explicit source (validate your policy routing)

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

Signification : si la réponse est sourcée depuis 172.20.5.20, elle sortira par eth1. Cela correspond aux attentes du filtrage strict pour des flux arrivant sur eth1.

Décision : si cela ne correspond pas, corrigez ip rule et les tables de routage plutôt que de désactiver rp_filter à l’aveugle.

Task 13: Check for “martian logging” settings (helpful during incident)

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

Signification : le noyau peut rejeter sans vous le dire.

Décision : activez temporairement la journalisation sur l’interface suspecte pendant le diagnostic ; désactivez ensuite pour éviter le spam de logs.

Task 14: Temporarily enable martian logging (targeted, time-boxed)

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

Signification : vous verrez désormais des preuves dans journalctl -k quand la logique du chemin inverse rejette du trafic.

Décision : reproduisez le problème une fois, capturez les logs, puis désactivez‑la.

Task 15: Verify ARP flux-related settings (often co-troubles with 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

Signification : le comportement ARP par défaut peut être « créatif » sur des hôtes multi‑homés (répondre sur la « mauvaise » interface). Ce n’est pas rp_filter, mais cela crée des symptômes étranges similaires.

Décision : si vous utilisez des VIP ou plusieurs sous‑réseaux, envisagez de resserrer le comportement ARP en même temps que rp_filter, surtout sur du bare metal ou des réseaux L2 adjacents.

Trois mini‑histoires d’entreprise du terrain

Mini‑histoire 1 : la panne causée par une mauvaise hypothèse

L’entreprise disposait d’une paire de nœuds API « identiques » dans deux data centers. Chaque nœud avait deux NIC : une pour les services internes, une pour le trafic partenaire. Le document de conception disait « le trafic partenaire utilise eth1 ». Cette ligne a été traitée comme une loi physique.

Un changement réseau est arrivé en amont — maintenance de routine, ajustement de préférence BGP, rien de dramatique. Le chemin de retour d’un partenaire a commencé à arriver via un autre edge, et les paquets ont commencé à atterrir sur eth0 sur un site en raison d’un nouveau saut L3. Le nœud API répondait toujours via eth1 à cause du routage basé sur la source lié à l’adresse qu’il choisissait pour répondre.

rp_filter strict était activé dans le cadre d’un pack de durcissement. Il avait toujours été activé. Il avait aussi toujours été chanceux. Quand l’asymétrie est arrivée, le noyau a commencé à abandonner des paquets entrants en tant que « martians ». L’équipe applicative a constaté des erreurs 5xx intermittentes. Le SRE de garde n’a vu ni erreurs d’interface, ni rejets de pare‑feu, ni pics CPU. Juste un flux lent de sessions échouées.

La mauvaise hypothèse était subtile : « le trafic partenaire utilise eth1 » signifiait pour eux que cela arriverait sur eth1. Dans des réseaux routés, le « devrait » est une aspiration, pas une garantie.

La correction n’a pas été héroïque. Ils sont passés en mode loose sur l’interface côté partenaire et ont mis à jour le routage basé sur la politique pour être cohérent. Puis ils ont ajouté un test de régression : recherches de route depuis chaque IP source vers des préfixes partenaires représentatifs. La ligne importante du postmortem : « Nous avons supposé la symétrie ; le réseau n’a pas signé ce contrat. »

Mini‑histoire 2 : une optimisation qui s’est retournée contre eux

Une équipe plateforme voulait réduire le risque de mouvement latéral. Ils ont poussé un nouveau bundle sysctl sur les flottes : rp_filter strict, journalisation martian désactivée (pour réduire le « bruit »), et quelques autres options qui semblaient correctes dans une feuille de conformité.

Ça a fonctionné sur la couche web : NIC unique, route par défaut unique, prévisible. Ils l’ont donc intégré dans l’image de base utilisée pour tout, y compris des systèmes stateful avec réseaux de réplication, réseaux de sauvegarde, et parfois une « migration NIC » qui apparaissait et disparaissait.

Le retour de flammes a été différé. Un mois plus tard, un service adjacent au stockage a commencé à échouer uniquement pendant les fenêtres de sauvegarde. Pourquoi ? Le système de sauvegarde utilisait un réseau différent, et pendant ces heures le service originait du trafic avec l’adresse source « backup » à cause d’un bind applicatif trop large. Les réponses prenaient une interface différente que les requêtes entrantes. rp_filter strict a interprété certains flux entrants comme usurpés et les a abandonnés, mais seulement quand certaines routes étaient présentes.

L’optimisation — durcir partout — a créé un bug intermittent qui s’alignait sur des horaires opérationnels. Le pire type d’intermittent : reproductible seulement quand vous êtes fatigué et sans café.

Ils ont récupéré en séparant les profils sysctl : un pour les couches stateless mono‑homées, un pour les systèmes multi‑homés et complexes en routage. Ils ont aussi modifié l’approche des logs « silencieux ». Vous n’avez pas besoin des martians toute la journée, mais vous devez avoir un interrupteur facile pour les activer pendant les incidents. Les systèmes silencieux sont agréables ; les pannes silencieuses sont coûteuses.

Mini‑histoire 3 : la pratique ennuyeuse mais correcte qui a sauvé la mise

Une entreprise régulée exploitait des bastions multi‑homés : une NIC vers le réseau d’administration, une NIC vers un segment partenaire contrôlé. L’environnement était rempli de demandes de changement, et les gens adoraient les « corrections rapides ». Ces hôtes refusaient les corrections rapides.

L’équipe avait une règle : chaque nœud multi‑homé doit avoir un petit auto‑test de routage qui s’exécute au démarrage et lors des recharges réseau. Il exécutait ip route get pour quelques destinations représentatives, depuis chaque adresse source, et comparait les sorties à une attente connue. Si ça divergeait, une alerte était déclenchée avant que les utilisateurs ne s’en rendent compte.

Un jour, après une mise à jour du noyau et un changement netplan, le test a échoué : le chemin de retour depuis l’IP partenaire a commencé à préférer la route par défaut admin. Personne n’avait remarqué en activité normale parce que le flux affecté était peu volumineux. Mais le test l’a remarqué, bruyamment.

La correction a été une ligne ip rule et un choix délibéré : garder rp_filter=2 sur l’interface partenaire parce que des arrivées asymétriques pouvaient encore se produire. Le point clé est la partie ennuyeuse : le problème a été détecté par un contrôle déterministe, pas par du debugging héroïque en production.

Blague n°2 : Le système le plus fiable est celui qui échoue en staging. Le second le plus fiable est celui qui signale immédiatement.

Concevoir du multi‑homing qui survit à rp_filter

Choisissez votre philosophie : correction stricte ou tolérance opérationnelle

Vous avez deux approches défendables. Les mélanger sans réfléchir est la recette pour obtenir « cas n°54 ».

Approche A : Garder rp_filter strict, concevoir pour la symétrie

Cela peut fonctionner, mais demande du travail :

  • Assurez‑vous que le trafic entrant sur l’interface X route toujours le retour vers la source via X.
  • Implémentez le routage basé sur la politique : ip rule indexé par adresse source (et parfois fwmarks) pour sélectionner une table de routage par interface/uplink.
  • Rendez chaque table de routage complète : routes connectées plus la route par défaut (et toutes routes spécifiques requises).
  • Assurez‑vous que les applications se lient aux adresses source correctes ou utilisent SO_BINDTODEVICE/bind() quand nécessaire.

Cela fonctionne bien lorsque vous contrôlez réellement les deux côtés du chemin (WAN privé, upstream prévisible). C’est moins adapté sur l’Internet public ou dans les clouds avec un routage « aidant ».

Approche B : Utiliser rp_filter loose pour le multi‑homing, se concentrer sur la disponibilité et la journalisation

C’est ce que je recommande pour la plupart des hôtes multi‑homés exposés à des chemins imprévisibles :

  • Définissez rp_filter=2 sur les interfaces concernées (ou globalement si l’hôte est systématiquement multi‑homé).
  • Conservez quand même du routage basé sur la politique, car vous voulez un egress stable et une sélection d’origine correcte.
  • Activez les journaux martian uniquement pendant le dépannage, ou redirigez‑les vers des pipelines de logs avec limitation de débit.
  • Comptez sur le filtrage anti‑usurpation en amont, plus les pare‑feu hôtes, plus le principe du moindre privilège. rp_filter n’est pas votre seul contrôle.

Spécificités Ubuntu 24.04 : où les gens sont surpris

Ubuntu 24.04 lui‑même n’est pas malveillant. La surprise vient généralement de ce que vous avez changé en même temps :

  • nouvelles images de base
  • nouveaux ensembles sysctl de durcissement
  • réécritures netplan qui ajoutent involontairement une seconde route par défaut ou changent les métriques
  • mises à jour du noyau qui modifient le nommage d’interface ou le comportement des pilotes et réordonnent les routes
  • mises à jour du réseau de conteneurs (versions CNI) qui ajoutent des règles et des routes

Le mode de panne donne l’impression que « le réseau Ubuntu est instable ». Ce n’est pas le cas. Votre politique de routage est incohérente avec la validation source stricte. Le noyau applique simplement ce que vous avez demandé.

Comment décider : une grille pratique

  • Si le serveur est mono‑homé et le restera : strict (1) est correct et souvent souhaitable.
  • Si le serveur a deux uplinks actifs : partez sur loose (2) sauf si vous avez une forte raison et une discipline de routage stricte.
  • Si le serveur utilise des VIP, VRRP, direct server return de load balancer, ou des astuces anycast : supposez l’asymétrie ; utilisez loose (2) et testez les basculements.
  • Si le serveur exécute Kubernetes avec plusieurs CNI ou routage spécial : le mode loose (2) est généralement plus sûr ; validez ensuite avec ip route get par réseau de pod si nécessaire.
  • Si la politique de sécurité exige strict : imposez la symétrie avec du routage basé sur la politique et des binds applicatifs explicites, et ajoutez des tests continus pour éviter la dérive.

Erreurs courantes : symptôme → cause → correction

1) « Les connexions entrantes expirent, mais seulement depuis certains réseaux »

Symptôme : certains sous‑réseaux clients ne peuvent pas se connecter ; d’autres fonctionnent. Des SYN arrivent, pas de SYN‑ACK.

Cause : rp_filter strict jette des paquets arrivant sur une interface qui n’est pas l’interface de retour préférée pour cette source (chemin asymétrique).

Correction : définir net.ipv4.conf.<if>.rp_filter=2 ou implémenter du routage basé sur la source pour que le chemin de retour corresponde à l’entrée.

2) « Ça a cassé juste après l’ajout d’une seconde route par défaut pour la résilience »

Symptôme : connectivité instable après avoir ajouté une passerelle de secours.

Cause : deux routes par défaut plus rp_filter strict créent un décalage quand le trafic arrive sur l’interface à métrique plus élevée.

Correction : ne comptez pas seulement sur une route par défaut de secours ; utilisez du routage basé sur la politique et/ou des protocoles de routage. Si vous gardez des doubles defaults, préférez rp_filter en mode loose.

3) « Les logs du pare‑feu ne montrent rien, mais les paquets disparaissent »

Symptôme : les compteurs nftables/iptables ne bougent pas ; tcpdump voit les paquets entrants ; l’appli ne répond pas.

Cause : le rejet rp_filter se produit avant les hooks de pare‑feu que vous regardez, et la journalisation martian est désactivée.

Correction : vérifiez sysctl net.ipv4.conf.*.rp_filter, activez brièvement log_martians, puis mettez rp_filter en loose ou corrigez le routage.

4) « Le basculement VRRP provoque une brève coupure, puis ça revient »

Symptôme : un VIP se déplace, et certains clients échouent pendant 10–60 secondes.

Cause : pendant la convergence, la « meilleure route de retour » change plus vite/plus lentement que l’annonce du VIP ; rp_filter strict jette les paquets arrivant sur la « mauvaise » interface.

Correction : utilisez rp_filter loose sur les interfaces portant des VIP, et validez le comportement ARP (arp_ignore/arp_announce) pour éviter les confusions d’interface.

5) « WireGuard fonctionne en sortie, mais le handshake entrant est peu fiable »

Symptôme : le tunnel se monte parfois ; le pair distant voit des retries.

Cause : les paquets vers le point de terminaison du tunnel arrivent sur une NIC, le chemin de retour en préfère une autre à cause des routes par défaut/métriques ; rp_filter strict jette.

Correction : rp_filter loose, et assurez‑vous que le routage vers l’endroit du pair est fixé sur l’uplink correct si vous avez besoin d’un comportement déterministe.

6) « Nous avons mis rp_filter=2 dans /etc/sysctl.conf mais c’est toujours 1 après reboot »

Symptôme : les valeurs runtime reviennent en arrière.

Cause : un autre fichier sysctl.d l’écrase plus tard, ou cloud‑init/durcissement applique après votre fichier.

Correction : placez un fichier dédié avec un ordre lexical qui l’emporte (nom plus élevé), puis vérifiez avec sysctl --system.

7) « Une seule interface est affectée »

Symptôme : eth1 jette des paquets, eth0 non.

Cause : rp_filter par interface diffère, ou seule une interface reçoit du trafic asymétrique.

Correction : inspectez les réglages par interface. Ne supposez pas que all implique que chaque interface est identique.

Listes de vérification / plan pas à pas

Checklist A : arrêter l’hémorragie (mode incident)

  1. Prouver l’ingress : tcpdump sur l’interface suspecte pour l’IP/port client en échec.
  2. Vérifier rp_filter : lire net.ipv4.conf.all/default/<if>.rp_filter.
  3. Vérifier la route de retour : ip route get <client-ip> et comparer l’interface de sortie attendue.
  4. Chercher les martians : journalctl -k. Si rien, activez temporairement log_martians=1 sur l’interface.
  5. Mitigation rapide : mettre rp_filter en mode loose (2) sur les interfaces affectées, retester.
  6. Annuler les changements risqués : si un bundle de durcissement a été poussé globalement, arrêtez le déploiement et isolez les rôles multi‑homés.

Checklist B : corriger correctement (après l’incident)

  1. Décidez votre modèle : symétrie stricte ou tolérance avec policy‑routing.
  2. Supprimez les doubles defaults accidentels : si les deux defaults sont nécessaires, documentez pourquoi et assurez‑vous que métriques et règles sont intentionnelles.
  3. Implémentez le routage basé sur la politique : une table par uplink/IP source. Gardez les tables complètes.
  4. Validez avec des sondes de route : exécutez ip route get depuis chaque IP source vers des destinations représentatives.
  5. Rendez les sysctls persistants : fichier dédié dans /etc/sysctl.d/ ; vérifiez l’ordre d’application.
  6. Documentez les exceptions : les hôtes multi‑homés ne sont pas « moins sécurisés » ; ils nécessitent des contrôles différents.
  7. Ajoutez un détecteur de dérive : un script simple qui alerte quand rp_filter change ou quand les tables de routage perdent des routes requises.

Checklist C : durcir sans se saboter

  1. Classifiez les hôtes : mono‑homés, multi‑homés, porteurs de VIP, points de terminaison de tunnel, nœuds Kubernetes.
  2. Appliquez rp_filter par classe : strict pour mono‑homés ; loose pour multi‑homés et rôles VIP/tunnel sauf si la symétrie est imposée.
  3. Journalisez de manière tactique : martians désactivés par défaut, mais avec un interrupteur à la demande et une limitation de débit.
  4. Testez les modes de panne : événements de basculement, lien down/up, renouvellement DHCP, netplan apply, redémarrage CNI.

FAQ

1) rp_filter est‑il un pare‑feu ?

Non. C’est une validation d’origine dans le chemin de routage du noyau. Il peut rejeter des paquets avant que vos règles ou logs de pare‑feu ne rendent la situation évidente.

2) Devrais‑je mettre rp_filter à 0 partout pour « réparer le réseau » ?

Seulement si vous aimez échanger un type de panne contre une régression de sécurité. Pour les hôtes multi‑homés, utilisez 2 (loose) dans la plupart des cas, et gardez une politique de routage cohérente.

3) Quel est le réglage le plus sûr pour des serveurs Ubuntu multi‑homés ?

rp_filter=2 est généralement le meilleur compromis. Associez‑le à du routage basé sur la politique pour que la sortie soit déterministe. Si vous pouvez imposer la symétrie de manière fiable, le mode strict peut être viable—mais ne prétendez pas que c’est gratuit.

4) Pourquoi cela apparaît‑il après une mise à jour Ubuntu 24.04 ?

Parce que les mises à jour coïncident souvent avec de nouveaux baselines sysctl, des changements netplan, des métriques de route par défaut différentes, ou l’apparition de nouvelles interfaces (tunnels, conteneurs). rp_filter n’est pas devenu méchant ; votre environnement est devenu plus complexe.

5) Comment confirmer en quelques minutes que rp_filter est coupable ?

Vérifiez sysctl net.ipv4.conf.<if>.rp_filter, lancez ip route get <source-ip>, et cherchez des logs martian. Basculez temporairement l’interface en mode loose et retestez. Si ça règle le problème, vous avez votre réponse.

6) Le mode loose (2) permet‑t‑il l’usurpation ?

Il assouplit la correspondance d’interface, pas la joignabilité. La source doit toujours être routable quelque part. C’est moins strict, donc oui, c’est un contrôle anti‑usurpation plus faible que le mode strict — comptez sur le filtrage en amont et le pare‑feu hôte comme contrôles principaux.

7) Pourquoi je vois « martian source » dans les logs ?

C’est le noyau qui vous dit qu’il a reçu un paquet avec une adresse source qui échoue aux vérifications de cohérence pour l’interface, souvent à cause de rp_filter en mode strict ou d’incohérences de routage.

8) Puis‑je garder rp_filter strict et quand même faire du routage basé sur la politique ?

Oui, mais vous devez être discipliné : ordre correct des ip rule, tables de routage complètes, et sélection d’adresse source prévisible. Si l’un de ces éléments dérive, rp_filter strict vous punira rapidement.

9) Les nœuds Kubernetes doivent‑ils utiliser rp_filter strict ?

Généralement non sauf si votre CNI et votre routage sont conçus pour cela. Les nœuds multi‑interface, les réseaux overlay et les VIP de service rendent l’asymétrie courante. Le mode loose est plus sûr opérationnellement.

10) Où définir cela de façon persistante sur Ubuntu 24.04 ?

Créez un fichier dans /etc/sysctl.d/ (par exemple 60-multihome-rpfilter.conf), appliquez avec sysctl --system et vérifiez les valeurs finales.

Conclusion : prochaines étapes qui ne vous réveilleront pas à 03:00

Le filtrage du chemin inverse est une de ces fonctionnalités qui ressemble à de la « sécurité gratuite » jusqu’à ce que vous gériez un vrai réseau. Le multi‑homing, les VIP, les tunnels et le routage basé sur la politique sont normaux en production. Le mode strict traite le « normal » comme du « criminel ».

Faites ceci :

  1. Classez les hôtes selon la complexité réseau. Arrêtez d’appliquer un profil sysctl unique à tout le monde.
  2. Pour les rôles multi‑homés, mettez rp_filter=2 (loose) sauf si vous pouvez prouver la symétrie de bout en bout.
  3. Implémentez le routage basé sur la politique pour que l’egress soit déterministe et le debugging supportable.
  4. Ajoutez un auto‑test de routage (quelques ip route get) pour détecter la dérive avant que les clients ne l’aient.
  5. Gardez un interrupteur de journalisation martian disponible pour les incidents, puis désactivez‑le après collecte des preuves.

Si vous ne retenez rien d’autre : le multi‑homing est une conception, pas une case à cocher. rp_filter ne fait qu’appliquer si vous l’avez effectivement conçue.

← Précédent
ZFS Readonly : l’astuce anti-ransomware que vous pouvez déployer aujourd’hui
Suivant →
ARC ZFS par To : dimensionner la RAM sans mythes ni religion de forum

Laisser un commentaire