Debian 13 : nf_conntrack table full — pourquoi les connexions échouent et comment régler en toute sécurité

Cet article vous a aidé ?

Si votre hôte Debian 13 commence soudainement à « couper l’accès internet » alors que le CPU est correct et que les disques sont inactifs, vérifiez le journal du noyau. Lorsque vous voyez nf_conntrack: table full, dropping packet, ce n’est pas un avertissement vague. C’est une limite de capacité stricte dans le sous‑système de suivi des connexions du noyau, et les nouveaux flux en payent le prix.

C’est l’un de ces modes de défaillance qui pousse les gens intelligents à dire des choses discutables comme « mais TCP devrait réessayer ». Certes. Il le fait. Pendant que votre load balancer panique, que vos utilisateurs actualisent, que vos probes passent au rouge, tout le monde apprend que la pile réseau est une ressource finie.

Ce qu’est conntrack (et pourquoi Debian 13 s’en soucie)

nf_conntrack est le système de suivi des connexions de Linux. Il conserve l’état des flux réseau pour que Netfilter puisse effectuer du filtrage stateful et du NAT. Cet « état » est stocké dans une table du noyau : des entrées pour des flux comme TCP, UDP et certaines conversations ICMP. Ce n’est pas optionnel si vous faites des choses courantes comme :

  • Règles de pare‑feu stateful (ct state established,related / -m conntrack --ctstate)
  • NAT / masquerade (typique pour les conteneurs, les nœuds Kubernetes, les routeurs domestiques et les « hacks » d’entreprise temporaires qui deviennent permanents)
  • Load balancers ou reverse proxies sur un hôte qui fait aussi du NAT pour autre chose
  • Tout ce qui s’appuie sur le suivi des réponses (helpers FTP, certains comportements SIP, et autres joies du passé)

Quand la table se remplit, le noyau ne peut pas allouer une nouvelle entrée pour un nouveau flux. Il laisse donc tomber les paquets qui créeraient un nouvel état. Les flux existants peuvent continuer tant bien que mal, mais le comportement visible par les utilisateurs ressemble à des échecs « aléatoires » : certaines connexions fonctionnent, d’autres bloquent, certaines se retransmettent, d’autres expirent.

Faits et contexte intéressants (parce que ce bazar a une histoire)

  1. Conntrack est apparu à l’ère de Netfilter au début des années 2000 pour permettre le filtrage stateful et le NAT sous Linux sans appliances externes.
  2. Historiquement, beaucoup d’incidents « mon pare‑feu est OK » étaient en réalité des épuisements de conntrack — parce que les règles de filtrage étaient correctes, mais la table d’état n’était pas assez grande.
  3. Dans les stacks modernes de conteneurs, conntrack est devenu une ressource partagée : un seul nœud Kubernetes peut suivre le trafic pour de nombreux pods et services, donc les pics frappent plus fort.
  4. La taille par défaut de conntrack est volontairement conservatrice pour éviter de grandes réservations mémoire sur des petites machines. Bon pour les laptops. Mauvais pour les gateways occupés.
  5. Le NAT consomme agressivement des entrées conntrack, car chaque traduction doit être suivie ; ajoutez des connexions éphémères et le churn explose.
  6. Les « connexions » UDP sont factices mais sont tout de même suivies, et leurs timeouts peuvent silencieusement garder des entrées longtemps après que l’application n’en ait plus besoin.
  7. nf_conntrack_buckets (buckets de la table de hachage) affecte la performance de recherche ; une grande table avec trop peu de buckets, c’est comme un bottin téléphonique avec un seul onglet pour toutes les lettres.
  8. Les tests de charge manquent souvent la pression sur conntrack parce qu’ils se concentrent sur le débit applicatif, pas sur le churn de flux et les timeouts.
  9. Certaines pannes attribuées à un « DDoS » étaient en réalité du trafic normal plus un changement de timeout qui gardait les entrées vivantes plus longtemps.

Une règle opérationnelle : les problèmes de conntrack apparaissent rarement pendant les périodes calmes. Ils surviennent lors de déploiements, basculements, flash crowds, ou quand quelqu’un change des timeouts sans prévenir. C’est pourquoi vous devriez traiter conntrack comme de la planification de capacité, pas comme un bouton d’urgence.

Pourquoi les connexions échouent quand la table est pleine

Lorsqu’un paquet arrive et nécessite un état conntrack (nouveau SYN TCP, nouvelle « session » UDP, une réponse nécessitant une correspondance NAT), le noyau tente d’allouer une entrée conntrack. S’il ne le peut pas, vous obtenez un message dans les logs et un paquet abandonné. Les nouvelles connexions échouent de façons qui ressemblent à :

  • Des SYN TCP retransmis jusqu’à l’abandon par le client (ressemblant à de la perte de paquets)
  • Timeout DNS (requêtes UDP abandonnées, retransmissions, pics de latence)
  • Appels HTTP sortants bloqués (surtout depuis des nœuds faisant du NAT intensif)
  • Échecs intermittents de découverte de services dans Kubernetes

Les flux TCP déjà établis continuent souvent parce que leurs entrées conntrack existent déjà. Cela crée le symptôme classique « la moitié du monde fonctionne » : votre session SSH reste active, mais les nouvelles connexions échouent. Le canal d’incident se remplit de superstitions.

Blague n°1 : L’épuisement de conntrack est le seul moment où le réseau « stateful » devient « stateless » à dessein, et personne n’apprécie la pureté.

Le vrai goulot d’étranglement : les entrées, pas la bande passante

La capacité de conntrack se mesure en nombre de flux suivis, pas en bits par seconde. Un hôte peut avoir beaucoup de bande passante et échouer quand même parce qu’il a épuisé ses entrées à cause de :

  • Beaucoup de connexions éphémères (microservices avec clients HTTP bavards)
  • Fort fan‑out (un service appelant des dizaines d’autres par requête)
  • Workloads UDP (DNS, QUIC, télémétrie) avec des timeouts trop élevés
  • Gateways NAT pour de nombreux clients (les connexions de chaque client s’accumulent)
  • Trafic d’attaque, scans, ou bruit de bots (même quand il y a des limites ailleurs)

Une citation, parce que l’exploitation a aussi ses philosophes

paraphrased idea — « La fiabilité vient de la conception pour la défaillance, pas de faire comme si elle n’arriverait pas. » Attribué à : John Allspaw (fiabilité/exploitation).

Playbook de diagnostic rapide

Voici l’ordre qui permet de trouver le goulot rapidement, sans se perdre dans 40 compteurs et un terrier de lapin « peut‑être c’est le DNS ».

  1. Premièrement : confirmez qu’il s’agit d’un épuisement de conntrack, pas seulement de perte de paquets.

    • Vérifiez les logs du noyau pour « table full ».
    • Vérifiez l’utilisation actuelle vs le max configuré.
  2. Deuxièmement : identifiez ce qui crée les entrées.

    • Comptez les entrées conntrack par protocole/état.
    • Cherchez de gros volumes UDP, ou des tas de SYN_SENT/SYN_RECV.
    • Vérifiez si l’hôte fait du NAT (containeurs, Kubernetes, VPN, masquerade classique).
  3. Troisièmement : décidez s’il faut augmenter le max, raccourcir les timeouts, ou arrêter le suivi.

    • Si vous êtes régulièrement près du max : augmentez la capacité et les buckets, puis validez l’impact mémoire.
    • Si c’est seulement des pics : réduisez les timeouts pour le coupable et/ou corrigez le fan‑out ou les tempêtes de retry.
    • Si vous suivez du trafic dont vous n’avez pas besoin : désactivez le suivi pour ces flux (avec prudence).

Tout le reste — profiling CPU, tableaux de bord fancy, accuser le pare‑feu — vient après avoir confirmé la pression sur la table. Conntrack est soit plein soit il ne l’est pas.

Tâches pratiques : commandes, sorties, décisions

Ce sont des tâches réelles que vous pouvez exécuter sur Debian 13. Chaque étape inclut ce que signifie la sortie et la décision à prendre. Exécutez-les en root ou avec les privilèges appropriés.

Task 1: Confirm the kernel is dropping due to conntrack

cr0x@server:~$ sudo journalctl -k -g "nf_conntrack" -n 20 --no-pager
Dec 29 10:41:12 server kernel: nf_conntrack: table full, dropping packet
Dec 29 10:41:12 server kernel: nf_conntrack: table full, dropping packet
Dec 29 10:41:13 server kernel: nf_conntrack: table full, dropping packet

Signification : C’est la preuve évidente. Le noyau refuse de nouvelles allocations conntrack et abandonne des paquets.

Décision : Arrêtez de deviner. Passez à la mesure de l’utilisation et à l’identification de ce qui remplit la table.

Task 2: See current usage vs configured max

cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 262144
net.netfilter.nf_conntrack_max = 262144

Signification : Vous êtes au plafond. Count égale max, et les nouveaux flux seront rejetés.

Décision : Vous avez besoin d’un soulagement : soit augmenter le max (après vérification mémoire), soit réduire les timeouts, soit réduire les flux suivis.

Task 3: Check conntrack memory consumption (slab)

cr0x@server:~$ grep -E 'nf_conntrack|conntrack' /proc/slabinfo | head -n 3
nf_conntrack         262144 262144   320  256    8 : tunables    0    0    0 : slabdata   1024   1024      0
nf_conntrack_expect       0      0    96   42    1 : tunables    0    0    0 : slabdata      0      0      0

Signification : Le slab nf_conntrack montre combien d’objets sont alloués et leur taille. Ici, les entrées sont entièrement allouées.

Décision : Estimez l’impact mémoire avant d’augmenter le max. Si la mémoire est serrée, les timeouts ou la réduction des flux peuvent être plus sûrs.

Task 4: If conntrack-tools is installed, get a quick summary

cr0x@server:~$ sudo conntrack -S
cpu=0 found=1483921 invalid=121 insert=927314 insert_failed=8421 drop=8421 early_drop=0 error=0 search_restart=0

Signification : insert_failed et drop qui augmentent indiquent des échecs d’allocation — généralement table pleine, parfois pression sur le hash.

Décision : Si insert_failed augmente, vous abandonnez activement de nouveaux flux. Atténuez maintenant ; optimisez après.

Task 5: Count entries by protocol (quick heat map)

cr0x@server:~$ sudo conntrack -L | awk '{print $1}' | sort | uniq -c | sort -nr | head
210944 tcp
50877 udp
323 icmp

Signification : TCP domine ici. Si UDP domine, vous vous concentrerez davantage sur DNS/QUIC/télémétrie et les timeouts UDP.

Décision : Choisissez votre prochaine investigation : distribution des états TCP ou churn de timeout UDP.

Task 6: For TCP, find which states are ballooning

cr0x@server:~$ sudo conntrack -L -p tcp | awk '{for (i=1;i<=NF;i++) if ($i ~ /^state=/) {split($i,a,"="); print a[2]}}' | sort | uniq -c | sort -nr | head
80421 ESTABLISHED
61211 TIME_WAIT
45433 SYN_SENT
12912 CLOSE_WAIT

Signification : Beaucoup de SYN_SENT peut signifier des tempêtes de connexions sortantes ou un non‑réponse en amont. Beaucoup de TIME_WAIT peut refléter des connexions de courte durée et du churn.

Décision : Si SYN_SENT est élevé, cherchez des retries et la reachabilité en amont ; si TIME_WAIT est élevé, concentrez‑vous sur la réutilisation des connexions et les timeouts applicatifs.

Task 7: Identify top talkers by destination (spot a runaway dependency)

cr0x@server:~$ sudo conntrack -L -p tcp | awk '{for(i=1;i<=NF;i++){if($i ~ /^dst=/){split($i,a,"="); print a[2]}}}' | sort | uniq -c | sort -nr | head
64221 10.10.8.21
33110 10.10.8.22
12044 192.0.2.50

Signification : Une ou deux destinations consomment une grande part de la table. C’est généralement une dépendance en panne, une tempête de retries, ou un point d’agrégation NAT.

Décision : Consultez les logs/métriques des clients parlant à ces IP. Envisagez de limiter le débit ou d’appliquer un circuit breaker avant de toucher aux sysctls.

Task 8: Check whether NAT is in play (nftables)

cr0x@server:~$ sudo nft list ruleset | sed -n '/table ip nat/,/}/p'
table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    oifname "eth0" masquerade
  }
}

Signification : L’hôte fait du masquerade NAT. Chaque flux traduit nécessite un suivi, et des clients en rafale peuvent remplir rapidement la table.

Décision : Traitez cet hôte comme une passerelle. Dimensionnez conntrack pour le comportement agrégé des clients, pas pour un seul service.

Task 9: Check bucket count and max (hash sizing)

cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_buckets net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_buckets = 65536
net.netfilter.nf_conntrack_max = 262144

Signification : Une règle pratique courant est nf_conntrack_max autour de 4× buckets (pas une loi, mais un bon point de départ). Ici le ratio est exactement 4.

Décision : Si vous augmentez fortement le max, revoyez aussi les buckets. Sinon vous paierez le CPU avec des longues chaînes de hachage sous charge.

Task 10: Validate timeouts currently set (the hidden capacity lever)

cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_established net.netfilter.nf_conntrack_udp_timeout net.netfilter.nf_conntrack_udp_timeout_stream
net.netfilter.nf_conntrack_tcp_timeout_established = 432000
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180

Signification : Les flux TCP établis sont conservés pendant 5 jours ici. Ce n’est pas « faux », mais sur des gateways NAT occupés cela peut être catastrophique si les clients churnent et ne ferment pas proprement.

Décision : Si vous voyez beaucoup d’entrées établies qui sont obsolètes (client disparu), envisagez de réduire le timeout des établis — avec précaution et en tenant compte des connexions longues légitimes.

Task 11: Inspect per-namespace behavior (containers/Kubernetes)

cr0x@server:~$ sudo lsns -t net | head
        NS TYPE NPROCS   PID USER   NETNSID NSFS COMMAND
4026531993 net     245     1 root unassigned       /lib/systemd/systemd
4026532458 net      12  3112 root unassigned       /usr/bin/containerd

Signification : Plusieurs namespaces réseau existent, mais conntrack reste principalement une ressource globale du noyau (avec un certain accounting par netns selon la configuration). Les conteneurs peuvent générer du churn de flux de façon inattendue.

Décision : Si c’est un nœud, incluez le trafic pod/service dans le dimensionnement. Ne basez pas le dimensionnement uniquement sur « l’appli sur l’hôte ».

Task 12: Measure connection churn at the socket layer (correlate with conntrack)

cr0x@server:~$ ss -s
Total: 10648 (kernel 0)
TCP:   7342 (estab 1249, closed 5621, orphaned 0, synrecv 18, timewait 5621/0), ports 0

Transport Total     IP        IPv6
RAW       0         0         0
UDP       211       198       13

Signification : timewait est énorme au niveau des sockets aussi, ce qui correspond au churn observé par conntrack. C’est souvent un problème de comportement applicatif (pas de keepalive, pas de pooling) plus qu’un problème noyau.

Décision : Si vous pouvez corriger le churn au niveau appli/proxy, faites‑le. Le tuning noyau n’est pas un substitut à une réutilisation sensée des connexions.

Task 13: Quick mitigation—raise max immediately (temporary)

cr0x@server:~$ sudo sysctl -w net.netfilter.nf_conntrack_max=524288
net.netfilter.nf_conntrack_max = 524288

Signification : Le plafond est maintenant plus haut. Les abandons devraient cesser si l’épuisement était le seul problème.

Décision : Traitez ceci comme un garrot. Suivez avec le dimensionnement des buckets, la revue mémoire, et une analyse de cause racine pour savoir pourquoi les entrées ont augmenté.

Task 14: Make the change persistent (Debian sysctl.d)

cr0x@server:~$ sudo sh -c 'cat > /etc/sysctl.d/99-conntrack-tuning.conf <
cr0x@server:~$ sudo sysctl --system | tail -n 5
* Applying /etc/sysctl.d/99-conntrack-tuning.conf ...
net.netfilter.nf_conntrack_max = 524288

Signification : Le paramètre survivra au redémarrage et est appliqué maintenant.

Décision : Persistez seulement après avoir confirmé la marge mémoire et que vous ne masquez pas une tempête de trafic.

Task 15: Adjust hash buckets safely (boot-time module option)

cr0x@server:~$ sudo sh -c 'cat > /etc/modprobe.d/nf_conntrack.conf <
cr0x@server:~$ sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-6.12.0-amd64

Signification : hashsize est défini au moment du chargement du module ; sur beaucoup de systèmes c’est effectivement une décision prise au démarrage. Mettre à jour initramfs permet que ce soit appliqué tôt.

Décision : Planifiez une fenêtre de reboot si vous devez changer les buckets. Ne prétendez pas pouvoir « sysctl » ça au milieu d’une panne.

Task 16: Spot obvious scanning/attack patterns (SYNs, invalids)

cr0x@server:~$ sudo conntrack -S | sed -n '1p'
cpu=0 found=1483921 invalid=121 insert=927314 insert_failed=8421 drop=8421 early_drop=0 error=0 search_restart=0

Signification : Si invalid commence à monter avec des abandons, vous pourriez suivre des déchets : paquets mal formés, routage asymétrique, ou un DDoS partiel.

Décision : Envisagez un filtrage amont et du rate limiting local ; augmenter simplement conntrack donne aux attaquants un plus grand bac à jouets.

Stratégie de dimensionnement qui ne vous mordra pas plus tard

Il y a trois réglages vers lesquels on se tourne :

  • nf_conntrack_max : nombre maximal d’entrées suivies.
  • nf_conntrack_buckets (ou option module hashsize) : buckets de la table de hachage pour les recherches.
  • Timeouts : durée de vie des entrées, surtout pour UDP et les états TCP semi‑ouverts.

L’état d’esprit du tuning sûr est : capacité (max), efficacité (buckets), et rétention (timeouts). Vous pouvez « résoudre » une table pleine en augmentant le max, mais vous pourriez juste stocker plus de déchets plus longtemps, consommer plus de mémoire, et ralentir les recherches si vous ignorez les buckets.

Étape 1 : Décidez quel type d’hôte c’est

  • Serveur pur (pas de NAT, état du pare‑feu minimal) : conntrack peut à peine compter. S’il est plein, quelque chose cloche (règle NAT inattendue, sidecar proxy, ou réseau de conteneurs local).
  • Nœud passerelle/NAT : conntrack est central. Dimensionnez‑le selon les clients agrégés et leur comportement. Ce n’est pas l’endroit pour être radin.
  • Nœud Kubernetes : c’est une passerelle déguisée. Attendez‑vous à des rafales pendant les rollouts, l’autoscaling et les drains de nœuds.

Étape 2 : Estimez le nombre d’entrées requis (math pratique, pas fantaisie)

Ne modélisez pas à outrance. Utilisez les pics observés et ajoutez une marge :

  • Mesurez nf_conntrack_count pendant les périodes chargées normales.
  • Mesurez‑le pendant les déploiements/basculements (la référence du « mauvais jour »).
  • Réglez nf_conntrack_max au moins à 2× le 99e centile des pics pour des gateways de production, parfois 3× si vous avez des tempêtes de retries.

Si vous ne pouvez pas mesurer les pics (nouvel environnement), commencez prudemment haut pour les gateways puis validez la mémoire. Le coût d’être trop bas, ce sont des outages. Le coût d’être trop haut, c’est la mémoire utilisée et une éventuelle surcharge de recherche (que les buckets peuvent atténuer).

Étape 3 : Validez la marge mémoire

Les entrées conntrack vivent en mémoire noyau (slab). Augmenter nf_conntrack_max augmente l’utilisation mémoire potentielle du noyau. La taille par entrée varie selon le noyau et les fonctionnalités, donc considérez‑la comme « quelques centaines d’octets », puis confirmez avec slabinfo.

Sur une machine déjà proche d’un OOM, monter conntrack, c’est comme agrandir un entrepôt en emménageant dans votre cuisine.

Timeouts : le multiplicateur silencieux

Les timeouts déterminent la durée de vie des entrées. Cela compte parce que beaucoup de workloads créent des flux plus vite que vous ne le pensez, et un petit changement de rétention peut doubler ou tripler le nombre d’entrées à l’état stable.

Quels timeouts causent habituellement des problèmes

  • Timeout UDP trop élevé : fréquent quand on « veut être sûr ». Il garde des flux UDP morts et grignote la table.
  • Timeout TCP established trop élevé sur les gateways NAT : peut garder de l’état pour des clients disparus (clients mobiles, Wi‑Fi instable, applis qui plantent).
  • Timeouts TCP demi‑ouverts : si vous subissez des scans ou des patterns de SYN flood, les entrées demi‑ouvertes peuvent dominer.

Blague n°2 : Augmenter les timeouts « pour être sûr » revient à garder toutes les invitations de réunion pour toujours parce que peut‑être un jour vous y assisterez rétroactivement.

Une approche sensée des timeouts

Ne réglez pas les timeouts au hasard d’après des billets de blog. Faites plutôt :

  1. Identifiez le protocole/état qui domine conntrack.
  2. Confirmez qu’il se corrèle à une vraie valeur utilisateur (sessions TCP longues) ou au churn/bruit (rafales DNS, retries, scans).
  3. Ajustez le plus petit ensemble de timeouts qui réduit la rétention des déchets sans casser les flux légitimes de longue durée.

Dans beaucoup d’environnements, la meilleure correction n’est pas un tweak de timeout mais d’arrêter le churn : keepalive HTTP, pooling de connexions, et budgets de retry sensés. Conntrack doit suivre de vrais flux, pas le sous‑produit d’un thundering herd.

Buckets de hachage et performance : nf_conntrack_buckets

Même si vous n’atteignez jamais le max, une table de hachage mal dimensionnée peut nuire. Les recherches conntrack se produisent dans le chemin chaud du traitement des paquets ; de longues chaînes de hachage signifient plus de CPU par paquet, ce qui se traduit par de la latence et du softirq.

Ce que vous pouvez contrôler

  • nf_conntrack_buckets est souvent en lecture seule à l’exécution et fixé au démarrage du module.
  • Option module hashsize= est la manière courante de le définir de façon persistante.

Règle pratique qui marche souvent

Un ratio opérationnel courant est :

  • nf_conntrack_max ≈ 4 × nf_conntrack_buckets

Cela maintient des longueurs de chaîne moyennes raisonnables. Ce n’est pas magique. C’est un point de départ. Si votre trafic a des distributions de tuple étranges ou si vous subissez une attaque, il faudra observer plus finement.

Spécificités Debian 13 : sysctl, systemd et persistance

Debian 13 se comporte comme le Debian moderne : paramètres noyau via sysctl, configurations persistantes sous /etc/sysctl.d/, options de module sous /etc/modprobe.d/, et démarrage géré tôt par initramfs.

Que faire sur Debian 13

  • Mettez les sysctls conntrack dans un fichier dédié comme /etc/sysctl.d/99-conntrack-tuning.conf.
  • Pour hashsize, mettez options nf_conntrack hashsize=... dans /etc/modprobe.d/nf_conntrack.conf et lancez update-initramfs -u.
  • Redémarrez pendant une fenêtre planifiée pour appliquer les changements de buckets de manière fiable.

En outre : si votre hôte utilise des conteneurs, vérifiez que votre « pare‑feu simple » ne fait pas en secret du NAT pour les réseaux bridge. Beaucoup le font. Conntrack se fiche que ce soit « juste pour dev ».

Trois mini‑histoires d’entreprise tirées de la vie réelle

Mini‑histoire 1 : L’incident causé par une fausse hypothèse

L’entreprise avait une couche edge basée sur Debian : une poignée d’hôtes exécutant un reverse proxy, plus quelques règles de pare‑feu pour le confort. Pas de NAT, pas de VPN, rien de sophistiqué. Ils étaient convaincus que conntrack était sans importance parce que « nous ne sommes pas un routeur ».

Puis une nouvelle baseline de sécurité a débarqué : une politique stateful default‑deny avec de larges autorisations « established/related ». C’était correct du point de vue des règles, et cela a stoppé du garbage. Ça a aussi rendu conntrack indispensable pour pratiquement tous les flux entrants.

Quelques semaines plus tard, une campagne marketing a frappé. Le churn de connexions a augmenté — beaucoup de handshakes TLS courts dus à des clients agressifs et un keepalive mal configuré. Au pic, le journal noyau a commencé à hurler « conntrack table full ». Le proxy semblait « sain » côté CPU, et l’équipe réseau accusait une perte de paquets en amont. Pendant ce temps, les nouveaux utilisateurs ne pouvaient pas se connecter, mais les sessions existantes restaient, ce qui donnait l’impression d’une panne partielle.

La fausse hypothèse n’était pas « conntrack est mauvais ». La fausse hypothèse était « conntrack c’est juste pour le NAT ». Sur un pare‑feu stateful, conntrack est le produit.

Correction : ils ont augmenté nf_conntrack_max avec une marge raisonnable, ajusté le hashsize en conséquence, et — plus important — corrigé le keepalive et la réutilisation des connexions dans le proxy. Après cela, le compte conntrack s’est stabilisé à une fraction du max, et les échecs « aléatoires » ont disparu.

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

Une équipe plateforme voulait une latence plus faible pour la télémétrie basée sur UDP et a décidé « d’éviter les drops » en augmentant les timeouts UDP conntrack. Le changement a été déployé sur un ensemble de nœuds Debian 13 servant de gateways NAT pour des sites distants et des workloads conteneurisés. Personne n’a fait de revue de capacité parce que les nœuds avaient beaucoup de RAM et « c’est juste un timeout ».

Pendant un temps, tout semblait aller. Puis un déploiement de routine a causé une petite dégradation chez un collecteur en aval. Les clients ont réessayé. Les flux UDP ont churné. Le timeout plus long a gardé les entrées plus longtemps, donc la table s’est remplie de conversations mortes qui ne recevraient jamais de réponses. Les nouveaux flux ont commencé à échouer, déclenchant plus de retries, ce qui a créé plus de flux. Boucle de rétroaction positive classique. Le graphe d’incident ressemblait à une courbe exponentielle bien propre.

Ils ont essayé d’augmenter nf_conntrack_max pendant l’incident, ce qui a aidé brièvement mais augmenté le CPU à cause d’un mauvais hashsize et de longues chaînes de recherche. Le résultat net était « moins cassé mais plus lent », ce qui n’est pas un succès à annoncer.

Le retour de bâton n’était pas l’idée de tuner. C’était tuner sans mesurer le churn et sans comprendre que le temps de rétention fixe l’occupation à l’état stable. La correction correcte a été : revenir sur l’augmentation du timeout UDP, réduire les retries côté client, et ajouter du backpressure dans la pipeline. Ensuite, ils ont augmenté modérément le max avec des buckets assortis, basés sur des pics mesurés, pas des impressions.

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

Une autre organisation avait une politique : chaque hôte de type gateway disposait d’un dashboard « capacité noyau » avec trois lignes ennuyeuses : nf_conntrack_count, nf_conntrack_max et l’usage slab pour nf_conntrack. Pas de math SLO fancy. Juste les bases et une alerte à 70% soutenu.

Pendant une panne fournisseur, leurs services ont commencé à réessayer plus agressivement des appels HTTPS sortants. C’est un problème de comportement client, mais ça se manifeste comme du churn de flux sur le nœud NAT. Leur alerte conntrack s’est déclenchée avant que les clients ne remarquent quoi que ce soit. L’on‑call a vu le compte monter, confirmé la domination de SYN_SENT, et a limité la classe de jobs responsables des retries. Ils ont aussi augmenté temporairement le max parce qu’ils avaient pré‑calculé la marge mémoire et avaient une fourchette « safe bump » documentée.

Pas de héros, pas de salle de crise. Le ticket d’incident était presque embarrassant : « Prévenu l’épuisement de conntrack en limitant les retries ; ajusté le max dans la plage prévue. » C’est l’ennui que vous voulez en production.

La pratique salvatrice n’était pas un drapeau noyau secret. C’était l’observabilité de capacité, des limites d’ajustement pré‑approuvées, et une culture qui traite les retries comme du trafic de production, pas comme une thérapie gratuite pour des dépendances défaillantes.

Erreurs courantes : symptômes → cause racine → correctif

1) « Certaines connexions marchent, les nouvelles échouent » → conntrack plein → augmenter le max et stopper le churn

Symptômes : Un SSH existant reste actif, mais les nouvelles connexions SSH/HTTP bloquent. Les logs noyau montrent des abandons.

Cause racine : Table à nf_conntrack_max. Les nouveaux flux ne peuvent pas être suivis.

Fix : Immédiat : augmenter nf_conntrack_max (temporaire). Ensuite, trouvez la source du churn et corrigez keepalive/retries ; ajustez les buckets si vous conservez le max plus élevé.

2) « On a augmenté le max mais c’est toujours lent » → buckets trop petits → définir hashsize et reboot

Symptômes : Les abandons cessent, mais la latence augmente et le CPU en softirq augmente.

Cause racine : Vous avez augmenté le max sans augmenter les buckets, provoquant de longues chaînes de hachage et des recherches plus lentes.

Fix : Définissez l’option module hashsize (ou ajustez les buckets si supporté) pour que le nombre de buckets suive le max, puis redémarrez pendant une fenêtre.

3) « Ça n’arrive que pendant les déploiements » → tempêtes de retries + flux courts → corriger le comportement client

Symptômes : Le compte conntrack monte lors des rollouts ou basculements.

Cause racine : Churn de connexions dû aux retries, checks de santé, ou discovery qui flap.

Fix : Réduisez les retries, ajoutez du jitter, activez le pooling de connexions, et rendez les checks moins agressifs. Puis dimensionnez conntrack pour un pic réaliste.

4) « UDP bouffe tout » → timeouts UDP trop élevés ou trafic bruyant → tuner UDP et filtrer le bruit

Symptômes : Conntrack dominé par des entrées UDP ; des échecs DNS sont fréquents.

Cause racine : Churn UDP élevé avec timeouts qui retiennent les entrées, plus éventuellement du scanning ou de la télémétrie bruyante.

Fix : Gardez les timeouts UDP raisonnables, réduisez le fan‑out de la télémétrie, et filtrez le garbage évident à la périphérie quand c’est approprié.

5) « Ça a commencé après l’activation de Docker/Kubernetes » → NAT caché et règles iptables → traiter le nœud comme une passerelle

Symptômes : Un « serveur normal » atteint soudainement les limites conntrack après l’installation du runtime de conteneurs.

Cause racine : Le networking des conteneurs ajoute NAT/forwarding et des règles stateful, augmentant les flux suivis.

Fix : Inventoriez les règles NAT, puis dimensionnez conntrack en conséquence. Envisagez des gateways dédiés pour l’egress des workloads lourds pour isoler le rayon d’impact.

6) « conntrack invalid est énorme » → routage asymétrique ou offload bizarre → corriger les chemins/routing

Symptômes : Comptes invalid élevés, abandons étranges, parfois seulement une direction cassée.

Cause racine : Le trafic revient par un chemin différent de celui qu’il a pris au départ (routage asymétrique), ou des paquets contournent le suivi à cause de l’architecture/offload.

Fix : Corrigez la symétrie du routage ou ajustez le design pour que le suivi stateful reste sur un chemin cohérent. Évitez les pansements qui se contentent d’agrandir conntrack.

Checklists / plan étape par étape

Checklist : pendant un incident actif (15–30 minutes)

  1. Confirmez que les logs montrent nf_conntrack: table full et que le count égale le max.
  2. Obtenez rapidement une répartition protocole/état (TCP vs UDP ; SYN_SENT vs ESTABLISHED).
  3. Vérifiez si cet hôte fait du NAT (chaînes nftables/iptables NAT).
  4. Appliquez une augmentation temporaire du max si vous avez de la marge mémoire.
  5. Limitez la source de churn la plus importante (jobs qui réessayent, clients abusifs, checks de santé défaillants).
  6. Capturez un bundle de preuves court pour plus tard : valeurs sysctl, résumé conntrack, principales destinations.

Checklist : après stabilisation (même jour)

  1. Déterminez le rôle de l’hôte : gateway/NAT, nœud k8s, ou serveur pur.
  2. Définissez de façon persistante nf_conntrack_max basé sur les pics mesurés plus une marge.
  3. Planifiez l’ajustement des buckets/hashsize pour correspondre au nouveau max ; programmez un reboot.
  4. Revue des timeouts ; revenez sur les augmentations « par sécurité » qui gonflent la rétention.
  5. Corrigez le churn à la source : keepalive, pooling, budgets de retry, backoff/jitter.
  6. Ajoutez de l’alerte à 70–80% à usage soutenu et un panneau dashboard pour count/max/slab.

Checklist : durcissement long terme (ce trimestre)

  1. Testez la capacité côté churn de connexions, pas seulement le throughput. Incluez scénarios de déploiement/basculement.
  2. Documentez une plage d’urgence sûre pour nf_conntrack_max basée sur la marge mémoire.
  3. Si vous exécutez Kubernetes, traitez conntrack comme une dépendance SLO au niveau nœud ; dimensionnez par type de nœud.
  4. Envisagez de déplacer le NAT/egress lourd vers des gateways dédiés pour isoler le rayon d’impact.
  5. Auditez les règles de pare‑feu pour le suivi inutile (avec prudence ; ne cassez pas la sécurité stateful).

FAQ

1) « nf_conntrack table full » signifie‑t‑il que mes règles de pare‑feu sont erronées ?

Non. Cela signifie généralement que vos règles fonctionnent comme prévu (stateful), mais que la table d’état est trop petite pour le pattern de trafic ou les timeouts.

2) Puis‑je juste mettre nf_conntrack_max à une valeur énorme et l’oublier ?

Vous pouvez, mais vous payerez en mémoire noyau et potentiellement en CPU (surtout si les buckets ne suivent pas). De plus, une table plus grande permet aux scans/attaques de consommer plus d’état avant que vous ne remarquiez.

3) Comment savoir si le NAT est la cause ?

Cherchez des règles masquerade/SNAT dans nftables ou les tables NAT d’iptables, et confirmez que l’hôte forwarde le trafic pour d’autres clients ou conteneurs. Le NAT augmente presque toujours la pression sur conntrack.

4) Pourquoi les pannes DNS apparaissent‑elles en premier ?

Le DNS utilise UDP, est à courte durée de vie et sensible à la latence. Quand de nouveaux flux UDP sont abandonnés, les clients réessayent et amplifient le churn. C’est souvent la première fissure visible.

5) Quelle est la différence entre nf_conntrack_buckets et nf_conntrack_max ?

nf_conntrack_max est la capacité (combien d’entrées). Les buckets déterminent la performance des recherches. Un grand max avec trop peu de buckets augmente le coût CPU par paquet.

6) Les connexions TCP établies tombent‑elles quand la table est pleine ?

En général non immédiatement, car elles ont déjà des entrées. La douleur concerne surtout les nouvelles connexions et le trafic nécessitant de nouvelles mappings NAT.

7) Puis‑je désactiver conntrack pour certains trafics ?

Parfois, oui (pour des flux spécifiques où le suivi est inutile). Mais c’est facile de casser le NAT, les attentes du pare‑feu stateful, et le trafic de réponse. Ne le faites que si vous comprenez bien le chemin et que vous avez des tests.

8) Est‑ce un problème iptables ou nftables ?

Ni l’un ni l’autre, ou les deux. iptables/nftables sont des frontends de règles ; conntrack est un sous‑système noyau sur lequel ils s’appuient. Migrer vers nftables ne supprimera pas magiquement la pression sur conntrack.

9) Que faire si conntrack_count n’est pas près du max mais que je vois quand même des abandons ?

Regardez conntrack -S pour les insert failures, vérifiez le dimensionnement des buckets, et considérez si vous avez des rafales provoquant un épuisement transitoire ou une pression mémoire. Vérifiez aussi que les logs sont actuels et pas historiques.

10) Augmenter nf_conntrack_max nécessite‑t‑il un reboot ?

Non, vous pouvez changer nf_conntrack_max à chaud via sysctl. Les changements de buckets/hashsize nécessitent généralement le rechargement du module ou un reboot pour être appliqués en toute sécurité.

Prochaines étapes réalisables cette semaine

Faites ces actions dans l’ordre, et vous transformerez conntrack d’une panne surprise en une métrique de capacité gérée :

  1. Ajoutez de la visibilité : graphez nf_conntrack_count, nf_conntrack_max, et l’usage slab conntrack. Alertez à 70–80% soutenu.
  2. Classez vos nœuds : tout ce qui fait du NAT ou exécute du networking conteneurisé est une gateway. Traitez‑le comme tel.
  3. Choisissez un max sensé : basez‑le sur les pics observés avec marge ; persistez via /etc/sysctl.d/.
  4. Adaptez les buckets au max : définissez l’option module hashsize, mettez à jour initramfs, et planifiez un redémarrage.
  5. Corrigez le churn : réduisez les retries, ajoutez du jitter, activez keepalive/pooling, et arrêtez de transformer des échecs transitoires en vagues de trafic.
  6. Revue des timeouts : revenez sur les augmentations « au cas où » ; tunez seulement avec des preuves et en connaissant les connexions longue durée légitimes.

Conntrack est une table partagée du noyau. Traitez‑la comme l’espace disque d’un serveur de base de données : fini, mesurable, et capable de ruiner votre après‑midi si vous l’ignorez.

← Précédent
ZFS « Pas d’espace » : récupérer de l’espace sans suppressions aléatoires
Suivant →
systemd « Dépendance échouée » : Trouver le service qui bloque le démarrage

Laisser un commentaire