Brute force SSH en 2026 : au-delà de fail2ban, que mettre en place ?
Le brute force SSH reste l'une des premières portes d'entrée exploitées par les attaquants ciblant les serveurs Linux exposés sur Internet. En 2026, les campagnes automatisées génèrent des dizaines de milliers de tentatives par heure, souvent coordonnées depuis des botnets distribués, et même une dizaine de logs suspects quotidiens dans /var/log/auth.log témoigne d'un balayage permanent. Fail2ban demeure l'outil de prédilection pour réagir après coup, mais son architecture userspace – lecture de logs, parsing, écriture de règles iptables – introduit une latence incompatible avec les volumes modernes et laisse passer les premières vagues. Cet article détaille pourquoi la protection au niveau kernel via XDP et nftables s'impose désormais comme standard opérationnel, et comment déployer une défense en profondeur sans conflit avec vos stacks existantes.
Pourquoi Fail2ban montre ses limites face au brute force SSH moderne
Fail2ban fonctionne en surveillant les fichiers journaux (par défaut /var/log/auth.log sous Debian/Ubuntu) via des expressions régulières (filtres), puis bannit temporairement les adresses IP dépassant un seuil en injectant une règle iptables -I INPUT -s <IP> -j DROP. Ce modèle présente trois faiblesses structurelles :
- Latence multi-seconde : entre l'arrivée du paquet SSH, son traitement par sshd, l'écriture dans syslog, la lecture par fail2ban et l'insertion iptables, 2 à 5 secondes s'écoulent – largement suffisant pour qu'un botnet teste 100 combinaisons user/pass sur 10 threads.
- Consommation CPU en userspace : chaque ligne lue, chaque regex exécutée sollicite le processeur ; à 50 000 tentatives/heure, le daemon fail2ban peut représenter 5 à 10 % de CPU sur une VM modeste, au détriment de votre application métier.
- Pollution des logs : sshd enregistre chaque connexion échouée, générant des gigaoctets de journaux inutiles qui compliquent l'audit et l'analyse forensique post-incident.
Surtout, fail2ban n'agit qu'après que le paquet ait été accepté par le kernel, traversé netfilter, remonté la stack TCP/IP et atteint le démon applicatif. À l'ère des attaques distribuées multi-sources et des tentatives low-and-slow espacées sous le radar, cette réactivité se révèle insuffisante.
XDP : bloquer au niveau de la carte réseau avant toute allocation mémoire
eXpress Data Path (XDP) est un hook eBPF exécuté avant l'allocation du skb (socket buffer) dans le driver réseau, au stade le plus bas du kernel Linux accessible en espace utilisateur. Un programme XDP décide en quelques nanosecondes si le paquet doit être passé (XDP_PASS), rejeté (XDP_DROP), redirigé (XDP_REDIRECT) ou transmis au TX (XDP_TX) pour un bounce immédiat.
Architecture du PAKKT Engine
PAKKT.io déploie un programme XDP unique par interface réseau (typiquement eth0), capable de gérer jusqu'à 256 règles simultanées pilotées par des BPF maps de type BPF_MAP_TYPE_HASH et BPF_MAP_TYPE_ARRAY. Chaque règle définit :
port_range: port unique (ex. 22), plage (1024-65535) ou wildcard (*) ;protocol: TCP / UDP / ICMP / any ;rule_type:block(drop immédiat),rate_limit(max_pps global et max_port_pps),allow_only(whitelist inverse) ;min_packet_size/max_packet_sizepour filtrer les anomalies protocolaires (SYN flood à 40 octets, fragments géants).
Pour SSH, une règle type block ou rate_limit sur le port 22 TCP bloque ou bride les paquets SYN avant même qu'ils n'atteignent sshd. Le taux de traitement dépasse plusieurs millions de paquets par seconde sur un CPU moderne, avec une latence ajoutée sub-microseconde par paquet – trois ordres de grandeur plus rapide que fail2ban.
# Exemple de structure BPF map (pseudo-code)
struct pakkt_rule {
__u16 port_start;
__u16 port_end;
__u8 protocol; // IPPROTO_TCP = 6
__u8 rule_type; // BLOCK = 1, RATE_LIMIT = 2
__u32 max_pps;
__u32 max_port_pps;
__u16 min_size;
__u16 max_size;
};
Limites stateless du XDP et passage de relais à nftables
Le XDP étant stateless (pas d'accès au conntrack), il ne distingue pas une nouvelle connexion d'un paquet d'une session établie. Pour SSH, cela signifie que si vous appliquez un rate_limit strict, vous briderez aussi les paquets légitimes d'une session active (saisie interactive, transfert SCP). La bonne pratique consiste donc à :
- Utiliser XDP pour bloquer les sources blacklistées (BPF map synchronisée avec la blacklist IP du panel PAKKT) ou rate-limiter les SYN globalement sur le port 22 afin d'absorber les pics brutaux.
- Passer le relais à nftables stateful pour affiner le filtrage connexion par connexion (voir section suivante).
Le PAKKT Engine expose les métriques par port et par règle (compteurs XDP_DROP, XDP_PASS) dans une BPF map interrogeable via bpftool map dump name pakkt_stats, et remontées au panel central toutes les 30 secondes par l'agent mTLS.
Firewall nftables stateful : protéger SSH avec conntrack et rate-limit par connexion
Nftables (successeur d'iptables fusionné dans le kernel depuis la version 3.13) offre un moteur de règles performant et flexible, couplé au connection tracking (conntrack). PAKKT déploie automatiquement une table isolée inet pakkt (famille inet = IPv4 + IPv6 simultanés) avec priorité filter de base 0, garantissant zéro conflit avec Docker (table nat priority -100), fail2ban (chaînes f2b-* dans la table filter par défaut), ou iptables-persistent.
Règle SSH anti-brute-force stateful
nft add rule inet pakkt input \
tcp dport 22 ct state new \
limit rate 3/minute burst 5 \
accept comment "SSH: max 3 nouvelles connexions/min par IP source"
nft add rule inet pakkt input \
tcp dport 22 ct state established,related \
accept comment "SSH: sessions établies OK"
nft add rule inet pakkt input \
tcp dport 22 \
drop comment "SSH: drop tout le reste (rate exceeded)"
Cette chaîne de trois règles autorise jusqu'à 5 tentatives initiales (burst), puis bride à 3 nouvelles connexions par minute par IP source (le meter implicite dans limit rate isole chaque source). Les sessions déjà établies (ct state established) passent sans limitation, préservant l'usage interactif légitime. Toute tentative supplémentaire est rejetée silencieusement (drop), épargnant sshd et les logs.
Complément : validation des flags TCP et detection SYN flood
nft add rule inet pakkt input \
tcp flags syn tcp flags \& \(fin\|syn\|rst\|ack\) == syn \
ct state new \
limit rate 40/second burst 100 \
accept comment "SYN flood mitigation: max 40 SYN/s global"
nft add rule inet pakkt input \
tcp flags \& \(fin\|syn\|rst\|psh\|ack\|urg\) == fin\|syn \
drop comment "Drop FIN+SYN (invalid flag combo)"
La première règle protège contre les SYN flood massifs en limitant le nombre de nouveaux SYN acceptés globalement (indépendamment du port) ; la seconde rejette les combinaisons de flags invalides (FIN+SYN, NULL scan, Xmas scan) souvent employées en reconnaissance.
Synchronisation blacklist/whitelist avec le panel PAKKT
Le panel PAKKT permet d'ajouter manuellement ou via API une IP à la blacklist ou whitelist de l'agent. L'agent Go synchronise immédiatement deux couches :
- Une BPF map
pakkt_blacklist(typeBPF_MAP_TYPE_LPM_TRIEpour supporter CIDR) consultée dans le hook XDP ; - Un set nftables
@blacklist_ipsdans la tableinet pakkt, utilisé par une règle prioritaireip saddr @blacklist_ips drop.
Ainsi, une IP blacklistée est rejetée avant toute analyse protocolaire (XDP) et au niveau conntrack (nftables), offrant une défense en profondeur. La whitelist fonctionne symétriquement avec un accept prioritaire, utile pour exempter des adresses d'administration fixes.
Déployer une stack anti-brute-force SSH complète avec PAKKT
Prérequis système
- Kernel Linux 5.x ou supérieur avec support XDP (vérifiez
CONFIG_XDP_SOCKETS=ydans/boot/config-$(uname -r)) ; - Nftables installé (
nft --version≥ 0.9.3) ; - Interface réseau avec driver XDP natif (la plupart des drivers récents :
ixgbe,i40e,mlx5,virtio_netdepuis kernel 5.9).
Installation de l'agent PAKKT (30 secondes)
curl -sSL https://install.pakkt.io | sudo bash
# L'installeur détecte l'OS, télécharge le binaire garble-obfusqué,
# vérifie le checksum SHA256, installe le service systemd,
# et démarre l'agent en mode heartbeat mTLS toutes les 30s.
Connectez-vous au panel PAKKT.io, créez un premier agent (essai gratuit 7 jours), copiez le token d'enrôlement et collez-le dans le prompt de l'installeur. L'agent s'enregistre, charge le programme XDP sur eth0 (détecté automatiquement) et applique la table nftables inet pakkt.
Configuration d'une règle SSH dans le panel
- Depuis l'onglet Rules, cliquez Add Rule.
- Port :
22; Protocol :TCP; Rule Type :rate_limit. - Max PPS global :
1000(absorbe les bursts sans affecter les sessions légitimes) ; Max Port PPS :50(limite spécifique au port 22). - Sauvegardez : l'agent reçoit la configuration via mTLS, met à jour la BPF map en moins de 100 ms, et le rate-limit XDP prend effet immédiatement.
Complétez avec une règle nftables stateful (le panel PAKKT génère automatiquement la configuration nft basée sur vos paramètres de rate-limit par connexion) :
# Généré automatiquement dans /etc/pakkt/nftables.conf
table inet pakkt {
chain input {
type filter hook input priority filter; policy accept;
ip saddr @blacklist_ips drop
ip saddr @whitelist_ips accept
tcp dport 22 ct state new limit rate 3/minute burst 5 accept
tcp dport 22 ct state established,related accept
tcp dport 22 drop
# Règles applicatives (ports de jeu, etc.)
}
}
Monitoring temps réel et alertes
Le dashboard PAKKT affiche :
- Carte monde GeoIP des IPs sources (données MaxMind GeoLite2, mise à jour hebdomadaire) ;
- Top 10 IPs tentatrices, top 10 pays, graphes de paquets dropped/passed par port (données TimescaleDB, rétention 30 jours sur le plan standard) ;
- Audit log des modifications de règles, ajouts/retraits blacklist, self-updates agent.
Intégrez PAKKT à votre stack existante via l'API publique (clé API générée dans le panel) pour automatiser le blacklistage d'IPs détectées par d'autres sondes (honeypot, IDS Suricata) ou pour récupérer les métriques dans Prometheus/Grafana.
Exemple de workflow complémentaire avec fail2ban (optionnel)
Si vous souhaitez conserver fail2ban pour d'autres services (HTTP, SMTP), configurez-le pour ne pas toucher SSH (commentez la jail [sshd]) et faites-le alimenter la blacklist PAKKT via un script action personnalisé :
# /etc/fail2ban/action.d/pakkt-blacklist.conf
[Definition]
actionban = curl -X POST https://api.pakkt.io/v1/agents/<AGENT_ID>/blacklist \
-H "Authorization: Bearer <API_KEY>" \
-d '{"ip":"<ip>","duration":3600}'
actionunban = curl -X DELETE https://api.pakkt.io/v1/agents/<AGENT_ID>/blacklist/<ip> \
-H "Authorization: Bearer <API_KEY>"
Ainsi, fail2ban détecte les abus sur d'autres ports (80, 443, 25) et bannit au niveau kernel via PAKKT, centralisant la blacklist et évitant les doublons de règles iptables.
Performances, impact et compatibilité
Charge système de l'agent PAKKT
L'agent Go tourne en arrière-plan avec un footprint mémoire < 5 MB et une consommation CPU < 1 % en moyenne (pics à 2 % lors du self-update SHA256 vérifié toutes les 24 h). Le programme XDP lui-même s'exécute dans le contexte kernel et ne consomme quasiment pas de CPU en dehors du traitement des paquets – un serveur traitant 100 000 pps verra une charge XDP négligeable (quelques pourcents de CPU mono-cœur, selon l'architecture du processeur et la complexité des règles).
Latence réseau ajoutée
Les benchmarks internes (non publiés publiquement, NDA client) montrent une latence ajoutée sub-microseconde par paquet pour un programme XDP de complexité moyenne (8 à 16 règles actives). En pratique, cela se traduit par un delta imperceptible sur le ping RTT (typiquement +0,01 ms), largement compensé par la réduction du bruit réseau (suppression des paquets illégitimes avant qu'ils ne polluent la stack TCP/IP).
Compatibilité avec Docker, Kubernetes, fail2ban, iptables-persistent
| Technologie | Table/chaîne nftables | Priorité | Conflit PAKKT ? |
| Docker | ip nat DOCKER |
-100 (nat) | ❌ Non (famille et priorité différentes) |
| fail2ban (iptables-nft) | ip filter f2b-sshd |
0 (filter) | ❌ Non (chaînes isolées, table filter vs inet pakkt) |
| Kubernetes (kube-proxy iptables) | ip nat KUBE-SERVICES |
-100 (nat) | ❌ Non |
| iptables-persistent | ip filter INPUT |
0 (filter) | ❌ Non (PAKKT utilise inet, pas ip) |
La table inet pakkt est isolée par conception (famille inet = IPv4 + IPv6, vs ip ou ip6 pour iptables legacy) et n'interfère avec aucune stack standard. Les règles PAKKT sont prioritaires au niveau XDP (avant netfilter), puis au niveau filter (priorité 0), sans modifier les chaînes INPUT, FORWARD ou OUTPUT existantes.
Références techniques
Pour approfondir les mécanismes XDP et eBPF, consultez la documentation officielle du kernel kernel.org BPF et le projet eBPF.io. La RFC 4987 de l'IETF (TCP SYN Flooding Attacks and Common Mitigations) décrit les attaques SYN flood et les contre-mesures au niveau protocole, contexte utile pour dimensionner vos seuils de rate-limit.
En résumé, blinder SSH au niveau kernel via XDP et nftables n'est plus une option de niche réservée aux CDN ou aux hyperscalers : c'est une nécessité opérationnelle pour tout serveur exposé en 2026. PAKKT démocratise cette approche en fournissant un agent clé-en-main, un panel centralisé et une tarification transparente (3 €/agent/mois, essai gratuit 7 jours). Vous conservez votre hébergeur préféré, votre panel de gestion (Pterodactyl, cPanel, Plesk), et ajoutez simplement la couche de protection kernel par-dessus – sans migration, sans refonte infrastructure, sans impact utilisateur.
FAQ
Puis-je combiner PAKKT avec fail2ban sur le même serveur sans conflit ?
Oui, absolument. La table nftables inet pakkt est isolée de la table filter utilisée par fail2ban (iptables-nft backend). Nous recommandons de désactiver la jail [sshd] dans fail2ban (puisque SSH est protégé au niveau kernel par PAKKT) et de conserver fail2ban pour d'autres services (HTTP, SMTP). Vous pouvez même configurer un script action fail2ban pour alimenter automatiquement la blacklist PAKKT via l'API, centralisant ainsi toutes vos bannissements au niveau XDP.
Le rate-limit XDP sur le port 22 ne risque-t-il pas de couper mes sessions SSH légitimes ?
Non, si vous configurez correctement les deux couches. Le rate-limit XDP (max_port_pps) borne le nombre total de paquets/seconde sur le port 22, mais ne distingue pas les états de connexion (XDP est stateless). C'est pourquoi vous devez compléter avec une règle nftables stateful (ct state established,related accept) qui laisse passer sans limite les sessions déjà établies. Seules les nouvelles tentatives de connexion sont bridées (typiquement 3 par minute), ce qui stoppe le brute force sans gêner l'usage interactif d'une session ouverte (saisie clavier, SCP, rsync).
Mon hébergeur (OVH, Hetzner, AWS) propose déjà un anti-DDoS : PAKKT est-il redondant ?
Non, PAKKT est complémentaire. Les solutions de scrubbing cloud (OVH VAC, AWS Shield, CloudFlare) filtrent les attaques volumétriques (dizaines de Gbps) en amont, avant que le trafic n'atteigne votre serveur. PAKKT intervient sur le serveur, au niveau kernel, pour affiner la protection couche 3/4 avec des règles métier précises (port, protocole, rate-limit par port, blacklist IP) et pour absorber les attaques de faible volume (quelques milliers de pps) qui passent sous le radar du scrubbing générique. En combinant scrubbing cloud + PAKKT kernel-level, vous obtenez une défense en profondeur optimale.
Déployez PAKKT en 30 secondes
Protection kernel double couche XDP + nftables, pilotable depuis un panel centralisé. Essai gratuit 7 jours.