
Garder un VPS en ligne sous DDoS
Trois attaques bien différentes partagent le nom DDoS, et presque tout ce que vous lirez à ce sujet échoue parce que cela les traite comme une seule et même chose : on répond à une liaison montante saturée par une directive nginx, ou à un flood de couche 7 habile par un serveur plus gros. La bonne question n’est jamais « comment je bloque ça » mais « à quelle couche est-ce physiquement arrêtable, et qui possède cette couche ». Cette page y répond pour une machine Linux louée — ce que le réseau au-dessus de vous absorbe avant même que vous ne le voyiez, ce qui aide vraiment à l’intérieur de la machine, et pourquoi le geste le plus efficace consiste presque toujours à cesser d’être adressable, purement et simplement.
Sur ce réseau, l’atténuation volumétrique est toujours active et il n’y a rien à activer de votre côté : les floods sous 10 Gbps sont absorbés en silence, de 10 à 100 Gbps sont nettoyés chez l’opérateur de transit, et tout ce qui dépasse est annoncé sur un chemin de nettoyage dédié — les seuils sont écrits noir sur blanc dans la documentation plutôt que laissés à l’état d’adjectif marketing. C’est la limite honnête de ce qu’un hébergeur peut vous offrir. C’est aussi, pour le trafic qui met réellement à terre les petits serveurs, la moitié la moins intéressante du problème.
Parce que les attaques qui tuent à coup sûr un VPS à 5 $ ne sont pas les monstres à 340 Gbps qui font les gros titres. Ce sont quarante mille paquets par seconde de petits SYN qui remplissent une table d’état que vous n’avez jamais regardée, ou trois cents requêtes par seconde vers la seule URL de votre site qui lance une requête en base — du trafic qui arrive sur une interface parfaitement saine, via un tuyau loin d’être plein, et qu’aucun nettoyeur en amont ne peut distinguer de vos utilisateurs. Ceux-là sont à vous de gérer, et ils se gèrent avec une vingtaine de lignes de configuration, à condition de déterminer d’abord laquelle des trois vous avez sous les yeux.
Ce qui suit suppose Debian 13 ou Ubuntu 24.04, nftables et nginx, et suppose que vous avez déjà effectué le durcissement de la première heure — un serveur qui répond encore sur des ports qu’il ne sert pas n’est pas prêt à être défendu.
Trois attaques, un seul nom
« DDoS » décrit une intention, pas un mécanisme, et les mécanismes n’ont presque rien en commun. Les trier correctement n’est pas de la pédanterie : c’est tout le travail, parce que chacun ne s’arrête qu’à une seule couche précise et reste invisible à toutes les autres.
Les floods volumétriques visent votre bande passante. L’amplification UDP — DNS, NTP, memcached, et depuis peu tout ce qui répond à une petite question par une grande réponse — permet à un attaquant de transformer 1 Gbps de sa propre capacité en 50 Gbps dirigés contre vous. La cible, c’est le lien, pas le serveur. Votre CPU s’ennuiera du début à la fin.
Les attaques de protocole et d’état visent une table finie dans votre noyau. Un SYN flood cherche à épuiser la file d’acceptation ; un flood générique de petits paquets cherche à épuiser le suivi de connexions. Les deux se mesurent en paquets par seconde, pas en bits par seconde, et les deux peuvent tuer une machine dont le lien est inactif à quatre-vingt-dix-sept pour cent. C’est la catégorie qui met à terre les petits serveurs, et celle que la plupart des guides sautent.
Les floods de couche applicative visent votre CPU ou votre base de données, avec des requêtes indiscernables de vraies requêtes parce que ce sont de vraies requêtes. Cent requêtes par seconde vers un endpoint de recherche, ce n’est rien pour un réseau et fatal pour une application PHP. Aucun nettoyeur en amont ne peut filtrer cela pour vous : de l’extérieur, ça ressemble exactement à un succès.
Il existe une quatrième chose, qui se présente déguisée en les trois autres et qui n’est pas une attaque du tout : un lien qui a bien marché quelque part, un client mal élevé chez vous, ou un crawler sans manières. L’écarter en premier ne coûte rien, et il est gênant de voir à quel point c’est souvent la bonne réponse.
La partie que vous ne pouvez pas corriger depuis la machine
Si 60 Gbps visent votre adresse et que votre port est un port 1 Gbps, la décision concernant ces paquets se prend dans un routeur en amont de vous, plusieurs sauts avant tout ce que vous administrez. Votre pare-feu ne les voit jamais. Il ne peut pas : ils ont été jetés pour protéger un lien qui ne vous appartient pas. C’est le fait structurel le plus important à propos des attaques volumétriques, et la raison pour laquelle « durcissez votre pare-feu contre le DDoS » relève surtout du non-sens.
Les seules questions qui comptent sont donc ce que votre hébergeur fait automatiquement, et où se situent ses seuils. Les nôtres sont publiés plutôt que promis : en dessous de 10 Gbps, c’est absorbé sans effet visible ; de 10 à 100 Gbps, c’est nettoyé chez l’opérateur de transit et vous remarquerez peut-être une hausse de latence ; au-delà de 100 Gbps, le préfixe est annoncé sur un chemin de nettoyage dédié — la latence augmente plus nettement, et le service reste joignable. Le null-routing d’une adresse n’est envisagé qu’en dernier recours, pour des attaques soutenues qui menacent l’ensemble du PoP, et nous vous prévenons dans les minutes qui suivent si cela arrive. Il n’y a rien à acheter et rien à activer.
Deux choses valent la peine d’être sues sur le nettoyage, que les fournisseurs révèlent rarement d’eux-mêmes. La première, c’est que vous êtes protégé par le voisinage, pas seulement par vos propres défenses : une attaque sur un client qui partage votre /20 en amont est absorbée avant d’atteindre le préfixe de qui que ce soit — un flood d’environ 340 Gbps visant une plage voisine à Paris est passé sans impact mesurable sur la nôtre, ce qui est exactement le genre de résultat que personne ne remarque. La seconde, c’est que les nettoyeurs sont des heuristiques, et que les heuristiques se trompent parfois : sur ce même réseau, un nettoyeur a un jour passé neuf minutes à envoyer des TCP reset contre des connexions légitimes en filtrant une attaque visant un voisin. Les deux incidents figurent dans le journal d’incidents public. Si vos sessions meurent d’une façon qui ressemble à un reset actif plutôt qu’à un timeout pendant l’attaque de quelqu’un d’autre, c’est un mode de défaillance réel, qu’il vaut mieux signaler que déboguer localement pendant une heure.
Quatre-vingt-dix secondes de mesure, avant de toucher à quoi que ce soit
Toute mauvaise réaction à un DDoS commence par un changement effectué avant que quiconque ait compris ce qui se passait. Récupérez d’abord quatre chiffres. Ils tiennent sur un seul écran et ils désignent la couche à votre place.
# 1. bits vs packets — which axis is saturated?
sar -n DEV 1 5 # or: ifstat -i eth0 1
# 2. socket states — SYN-RECV piling up means a SYN flood
ss -s
ss -tan state syn-recv | wc -l
# 3. kernel drop counters (these are the ones that matter)
nstat -az | grep -E 'ListenDrops|ListenOverflows|SyncookiesSent|TCPReqQFullDrop'
dmesg -T | tail -20 # look for: nf_conntrack: table full, dropping packet
# 4. connection tracking headroom
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_maxLisez-les ensemble. Bits par seconde élevés, paquets par seconde faibles, CPU faible : c’est une attaque volumétrique, et votre travail consiste à le confirmer puis à arrêter de taper des commandes. Paquets par seconde élevés, bits par seconde faibles — beaucoup de tout petits paquets — c’est une attaque d’état ; regardez immédiatement SyncookiesSent et le compteur conntrack. Trafic modeste sur les deux axes avec le CPU bloqué à fond, c’est de la couche 7, et la réponse se trouve dans votre serveur web et votre base de données, pas dans votre pare-feu.
Une lecture contre-intuitive mérite d’être intériorisée : pendant un vrai flood volumétrique, votre interface peut sembler presque calme. Vous voyez les survivants — ce qui passe à travers un lien déjà plein, ou ce que le nettoyeur en amont a laissé passer. Une interface qui affiche 900 Mbps sur un port 1 Gbps pendant que les utilisateurs signalent une injoignabilité totale n’est pas une preuve contre une attaque. C’en est la signature.
Vérifiez ensuite que le trafic n’est pas tout simplement réel. Faites un tail -f sur votre journal d’accès pendant dix secondes : un seul chemin répété par des milliers d’adresses distinctes sans référent est une attaque ; un éventail de chemins normaux depuis des navigateurs normaux est une audience, et la rate-limiter fera le travail de l’attaquant à sa place.
Réduisez la cible avant l’attaque, pas pendant
Chaque port ouvert est une file qu’un attaquant peut remplir, et chaque paquet que votre invité doit examiner vous coûte du CPU et une entrée conntrack, même quand vous le jetez. Le paquet le moins cher est celui que votre machine ne reçoit jamais, ce qui rend le filtrage au-dessus de l’invité plus précieux que le filtrage à l’intérieur.
Il y a ici deux couches, et ce n’est pas la même chose. Le filtre en périphérie est optionnel, configuré par serveur, à état et à refus par défaut, et il tourne sur l’hyperviseur — le trafic que vous y rejetez n’atteint jamais votre carte réseau virtuelle, donc il ne vous coûte ni CPU, ni mémoire, ni entrée de table d’état. Le pare-feu invité — votre jeu de règles nftables — est entièrement le vôtre, et nous n’y touchons jamais. Le schéma qui survit au contact d’une attaque consiste à exprimer en périphérie la vérité grossière et stable de la couche 4 (« cette machine sert les ports 80, 443 et SSH depuis ces adresses, et rien d’autre n’existe ») et à garder le jeu de règles invité pour le travail fin qui change avec votre application. Les deux sont décrits dans la documentation du pare-feu.
Réduisez ensuite ce qui reste. Restreindre SSH aux adresses depuis lesquelles vous administrez réellement n’est pas ici un raffinement de durcissement — cela retire entièrement de votre machine toute une classe d’attaque par épuisement de connexions. Une base de données liée à 127.0.0.1 ou à une adresse WireGuard ne peut absolument pas être floodée depuis internet. Et si vous exposez un plan de contrôle purement interne, placez-le derrière une interface WireGuard plutôt que derrière un port public protégé par un mot de passe.
SYN floods et file d’acceptation
Un SYN flood exploite la poignée de main : l’attaquant envoie un flux de demandes de connexion et ne les termine jamais, et chacune occupe un emplacement dans la file SYN du noyau jusqu’à expiration. Remplissez la file et les poignées de main légitimes sont jetées — votre service est actif, à l’écoute, et injoignable.
Linux dispose d’une réponse propre depuis les années 1990, et elle est active par défaut. Les SYN cookies permettent au noyau de cesser complètement d’allouer de la mémoire pour les connexions à moitié ouvertes : il encode l’état de la connexion dans le numéro de séquence qu’il renvoie, et le reconstruit si le client termine la poignée de main. Vérifiez-le plutôt que de le supposer, et laissez aux files assez de marge pour encaisser une rafale :
# /etc/sysctl.d/99-flood.conf
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_abort_on_overflow = 0
sysctl --systemtcp_synack_retries = 2 compte plus qu’il n’y paraît : la valeur par défaut de cinq signifie qu’une poignée de main forgée occupe le noyau pendant environ trois minutes, et la ramener à deux fait tomber cela à environ sept secondes. Augmenter somaxconn ne fait que la moitié du travail — la profondeur de la file est le minimum entre cette valeur et ce que l’application a demandé, donc nginx a besoin de listen 443 ssl backlog=8192; et d’un reload avant que le réglage du noyau ne signifie quoi que ce soit. C’est le cas classique d’un sysctl qui a l’air d’avoir été appliqué et qui ne fait rien.
Si vous voulez arrêter le flood avant même qu’il n’atteigne l’application, nftables peut terminer les poignées de main pour le compte du noyau et ne transmettre que les connexions qui se révèlent réelles :
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
tcp dport { 80, 443 } ct state new synproxy mss 1460 wscale 7 timestamp sack-perm
tcp dport { 80, 443 } accept
ip protocol icmp icmp type echo-request limit rate 5/second accept
}
}Testez synproxy sur une machine que vous pouvez encore atteindre par la console avant d’en dépendre ; mal configuré, c’est un excellent moyen de vous bannir vous-même de votre propre serveur avec votre pare-feu. Pour la plupart des gens, le bloc sysctl ci-dessus suffit, et le classement honnête est le suivant : syncookies d’abord, backlog ensuite, synproxy seulement si vous avez mesuré que les deux premiers ne suffisaient pas.
La table que personne ne regarde jusqu’à ce qu’elle soit pleine
C’est celle qui piège les gens expérimentés. Un pare-feu à état doit se souvenir de chaque flux qu’il a vu, et cette mémoire, c’est nf_conntrack, une table de hachage de taille fixe choisie au démarrage. Une fois qu’elle est pleine, le noyau jette les nouvelles connexions — toutes, attaquant comme client — et écrit une seule ligne dans dmesg, que personne ne surveille :
nf_conntrack: table full, dropping packetSi c’est aussi efficace, c’est une question d’arithmétique. Une table par défaut sur un petit VPS tient quelque part dans les basses dizaines de milliers d’entrées, et chaque paquet venant d’une nouvelle adresse source en crée une — y compris les paquets UDP, y compris les paquets que vous jetez, y compris le flood lui-même. Vingt mille paquets par seconde depuis des sources usurpées la remplissent en moins de deux secondes, sur un lien qui ne transporte que quelques mégabits. Votre graphique de bande passante n’affichera strictement rien.
# /etc/sysctl.d/99-conntrack.conf
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_buckets = 65536
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
net.netfilter.nf_conntrack_udp_timeout = 20Les timeouts font plus de bien que la taille. La durée de vie par défaut d’un flux TCP établi est de cinq jours, ce qui signifie qu’un serveur en ligne depuis une semaine conserve l’état de connexions terminées depuis mardi ; une heure suffit largement pour tout ce qui n’est pas une session SSH inactive, et net.ipv4.tcp_keepalive_time = 600 les garde en vie proprement plutôt que par accident. Comptez environ 300 octets de mémoire noyau par entrée pour le dimensionnement : 262 144 entrées, ça fait autour de 80 Mo, ce qui va très bien sur une machine à 4 Go et beaucoup moins bien si vous réglez ça à dix millions parce qu’un forum l’a suggéré.
Si vous faites tourner quelque chose de réellement sans état et à fort volume — un serveur DNS faisant autorité, un serveur de jeu public, un relais Tor — la meilleure réponse est d’arrêter complètement de le suivre. Le suivi de connexions pour un service qui n’a pas de connexions au sens propre est un coût pur :
table ip raw {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
udp dport 51820 notrack
tcp dport 9001 notrack
}
}Gardez à l’esprit qu’un tel trafic contourne alors vos règles ct state established, et qu’il lui faut donc des règles d’acceptation explicites dans la chaîne filter. C’est le compromis : vous abandonnez le suivi d’état pour ce port et vous obtenez en échange une table qu’on ne peut pas remplir.
Couche 7 : le flood qui ressemble exactement à vos clients
L’attaque la plus efficace contre un petit serveur n’est pas du tout un flood. Ce sont quelques centaines de requêtes HTTP par seconde soigneusement choisies, chacune parfaitement valide, chacune touchant l’unique endpoint qui exécute une requête sans index ou qui rend une page non cachée. L’économie est brutale : la requête coûte à l’attaquant quelques centaines d’octets et vous coûte 200 millisecondes de CPU et une connexion à la base de données. Vous perdez cette course à n’importe quel volume que vous avez les moyens de servir.
Le réflexe, c’est de bloquer l’attaquant. Face à un botnet réparti sur des milliers d’adresses résidentielles, chacune envoyant deux requêtes par seconde, bloquer par IP relève du théâtre — aucune limite de débit par adresse qui laisse passer vos vrais utilisateurs ne se déclenchera jamais, et vous passerez toute la panne à ajouter des règles pendant que le site reste hors service. Le coup gagnant consiste à changer le coût, pas le compte.
Mettez en cache en premier, et mettez en cache le miss. Un cache de page complète transforme une requête coûteuse en une lecture mémoire, et la directive qui compte le plus en cas d’attaque est celle qui empêche mille miss simultanés de devenir mille requêtes simultanées en base :
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app:64m max_size=2g inactive=60m;
server {
location / {
proxy_cache app;
proxy_cache_valid 200 301 302 10m;
proxy_cache_lock on; # one origin request per key, not a thousand
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on; # serve stale, refresh behind it
add_header X-Cache $upstream_cache_status always;
proxy_pass http://127.0.0.1:8080;
}
}proxy_cache_lock et use_stale sont les deux lignes qui décident si un pic de trafic est survivable. Sans elles, à l’instant où une clé chaude expire, chaque requête en vol devient une requête vers l’origine — la ruée qui transforme une attaque gérable en panne. Avec elles, votre backend sert une requête par clé et par intervalle, et tous les autres reçoivent une page légèrement périmée, ce qui est le bon compromis dans toute situation où l’alternative est de n’avoir aucune page du tout.
Limitez ensuite spécifiquement le débit des chemins coûteux, généreusement, et d’une manière qui échoue de façon visible :
limit_req_zone $binary_remote_addr zone=general:16m rate=20r/s;
limit_req_zone $binary_remote_addr zone=costly:16m rate=2r/s;
limit_conn_zone $binary_remote_addr zone=conn:16m;
limit_req_status 429;
limit_conn_status 429;
server {
limit_req zone=general burst=40 nodelay;
limit_conn conn 24;
location /search { limit_req zone=costly burst=5 nodelay; proxy_pass http://127.0.0.1:8080; }
location /login { limit_req zone=costly burst=3 nodelay; proxy_pass http://127.0.0.1:8080; }
}burst sans nodelay met les requêtes excédentaires en file au lieu de les rejeter, ce qui, sous attaque, revient à voir nginx maintenir poliment des milliers de connexions ouvertes en votre nom — vous avez converti un flood de requêtes en flood de connexions. Utilisez nodelay, renvoyez 429 immédiatement, et laissez le client se débrouiller avec ça. Et derrière n’importe quel proxy, rappelez-vous que $binary_remote_addr est celle du proxy tant que vous n’avez pas correctement configuré set_real_ip_from et real_ip_header ; une limite de débit indexée sur l’adresse de votre propre frontal bloquera tous les utilisateurs d’un coup, dès son premier déclenchement.
Les attaques façon Slowloris — de nombreuses connexions, chacune distillant une requête un octet à la fois — dérangent à peine le modèle événementiel de nginx, mais les timeouts valent quand même la peine d’être resserrés : client_header_timeout 8s; client_body_timeout 8s; send_timeout 10s; reset_timedout_connection on;. Combiné à limit_conn, c’est toute la défense.
Pourquoi fail2ban n’est pas un outil anti-DDoS
fail2ban est réellement utile, et il vise un problème différent. Il lit des fichiers journaux, décide après N échecs en M minutes, et insère une règle de pare-feu. Chacun de ces aspects est la mauvaise forme pour un flood.
C’est trop lent : une rafale qui dure quarante secondes est terminée avant même que la fenêtre de bannissement ne se referme. Il lit des journaux, donc l’attaque vous a déjà coûté le plein prix de chaque requête — l’analyse se fait après les dégâts. Il bannit des adresses individuelles, donc face à dix mille sources, il ne fait soit rien, soit insère dix mille règles linéaires, moment où le pare-feu lui-même devient le goulot d’étranglement et où vous avez terminé l’attaque à la place de votre attaquant. Et il dépend de vos journaux, donc un flood assez gros pour remplir un disque de lignes de log peut mettre le serveur à terre par un chemin auquel vous n’aviez jamais pensé.
Si vous voulez du blocage dynamique, faites-le dans le plan de données, là où la recherche est un hachage et où l’expiration est automatique. Un ensemble dynamique nftables est en O(1) quelle que soit sa taille, et s’oublie tout seul :
table inet filter {
set flooders {
type ipv4_addr
flags dynamic, timeout
timeout 10m
size 65535
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
ip saddr @flooders drop
tcp dport { 80, 443 } ct state new add @flooders { ip saddr limit rate over 50/second burst 100 packets } drop
tcp dport { 80, 443 } accept
}
}Lisez la règle attentivement, car la négation piège les gens : l’adresse est ajoutée à l’ensemble et jetée seulement quand le débit est supérieur à la limite. Gardez fail2ban pour ce qu’il fait bien — le devinage lent d’identifiants contre SSH et les connexions applicatives, là où un bannissement à l’échelle humaine sur une adresse précise est exactement ce qu’il faut.
Ne soyez pas une arme : l’amplification que vous hébergez
Chaque grosse attaque volumétrique est alimentée par des serveurs dont les opérateurs ignoraient qu’ils y participaient. Le mécanisme est un service UDP qui répond à une petite requête forgée par une grosse réponse : un résolveur DNS ouvert renvoie cinquante fois ce qu’on lui a demandé, NTP et memcached mal configurés font pire, et tout protocole qui répond avant de pouvoir vérifier qui demande est un candidat.
Les conséquences vous frappent avant de frapper la victime. Votre liaison montante se remplit de vos propres réponses sortantes, votre hébergeur voit un trafic abusif soutenu quitter votre port, et les fournisseurs de transit qui le reçoivent commencent à null-router les plages d’où il vient — c’est pourquoi la politique d’utilisation acceptable trace une ligne dure sur l’origination d’attaques et l’amplification, tout en autorisant essentiellement tout le reste. Être un réflecteur involontaire est le moyen le plus rapide de perdre une adresse qui n’a rien à voir avec ce que vous vouliez faire tourner.
# bind a recursive resolver to localhost, never 0.0.0.0
# unbound: interface: 127.0.0.1 + access-control: 0.0.0.0/0 refuse
# what is actually listening on a public address?
ss -ulpn
ss -tlpn
# from another machine, confirm you do not answer strangers
dig @your.server.ip example.com +short # must time out
ntpq -c rv your.server.ip # must failSi vous faites tourner un service UDP public volontairement — un serveur de jeu, un serveur de noms faisant autorité, un endpoint WireGuard — la règle est la même que partout ailleurs : limitez le débit de la réponse, jamais celui de la requête. bind et knot implémentent tous les deux la limitation de débit des réponses ; utilisez-la, car c’est toute la différence entre servir vos utilisateurs et livrer l’attaque de quelqu’un d’autre.
La défense la plus efficace, c’est de ne pas être trouvable
Tout ce qui précède est du contrôle des dégâts pour une attaque qui a déjà trouvé votre adresse. Cela vaut la peine d’être fait, et c’est le second meilleur choix. Une origine que personne ne peut nommer n’est absolument pas attaquable en couche 3 ou 4, et séparer l’adresse qui sert le trafic de l’adresse que le monde connaît est la chose la plus rentable de toute cette page.
Les adresses d’origine ne fuitent presque jamais à cause d’attaques habiles. Elles fuitent par l’histoire et par la négligence, selon une liste courte et bien connue. Les vieux enregistrements DNS sont le coupable habituel : les archives de DNS passif se souviennent pour toujours de l’enregistrement A que vous aviez avant de mettre en place un frontal. Les journaux de certificate transparency sont publics et permanents, si bien qu’un certificat émis pour un nom d’hôte qui pointait directement vers l’origine est une trace signée et horodatée de l’endroit où vous viviez autrefois. Le mail sortant imprime l’adresse du serveur d’envoi dans les en-têtes de chaque message. Et la plus discrète : votre serveur web qui répond sur son IP nue, ce qui permet à quiconque scanne l’espace d’adressage à la recherche du HTML de votre site de vous retrouver par le contenu en un après-midi.
Ce dernier point se corrige en deux lignes et presque personne ne le fait. Faites en sorte que le serveur par défaut refuse tout ce qui n’arrive pas avec un nom d’hôte que vous servez :
server {
listen 80 default_server;
listen 443 ssl default_server;
ssl_reject_handshake on; # nginx 1.19.4+: no certificate, no fingerprint
return 444; # close without a response
}Choisissez ensuite un frontal. Un CDN commercial est la réponse évidente et vient avec un coût réel qui compte ici : vous ajoutez une entreprise qui termine votre TLS, voit votre trafic en clair, connaît votre origine, et peut se voir signifier une procédure légale dans une juridiction que vous n’avez pas choisie — ce qui annule une bonne partie de la raison pour laquelle le serveur a été payé en crypto sans nom attaché. Si vous le faites quand même, comprenez que l’origine ne doit jamais avoir été publique, et que tout l’arrangement échoue dès que quelqu’un retrouve un vieil enregistrement.
L’alternative qui préserve ce pour quoi vous avez payé, c’est d’être votre propre frontal. Une instance à 5 $ dans une seconde région, faisant tourner nginx en reverse proxy, avec le filtre en périphérie de l’origine qui n’accepte le trafic que de cette seule adresse, vous donne une adresse sacrifiable que vous pouvez renuméroter en cinq minutes, et un plan de contrôle qui ne répond qu’à vous. C’est la même architecture, moins le tiers. Et là où l’audience peut s’en servir, un service onion Tor retire complètement l’IP de l’équation — il n’y a aucune adresse à flooder, même si les services onion ont leur propre surface d’attaque au niveau des points d’introduction, ce qui explique pourquoi le Tor moderne embarque une défense par preuve de travail exactement pour cela.
Bloquer des pays, des ASN, et la forme du trafic
Tôt ou tard, quelqu’un suggère de bloquer un pays. C’est brutal, occasionnellement juste, et surtout une façon de se donner l’impression d’agir.
C’est défendable quand votre service a une audience réellement bornée — un serveur de jeu régional, un outil interne, un panneau d’administration — et que vous êtes prêt à assumer les faux positifs, ce qui inclut vos propres utilisateurs en voyage et n’importe qui routé bizarrement. C’est à peu près inutile contre un botnet moderne, réparti sur des connexions résidentielles dans tous les pays, y compris le vôtre, et c’est activement nuisible pour tout ce qui est public : vous perdrez silencieusement de vrais gens et ne verrez jamais ceux qui sont partis.
Bloquer par ASN est plus incisif. Le trafic d’attaque qui provient d’une poignée d’hébergeurs — des plages de VPS bon marché loués à l’heure — peut être jeté par préfixe avec bien moins de dégâts collatéraux qu’un blocage par pays, parce que les utilisateurs ordinaires ne naviguent pas depuis un datacenter. Les plages de datacenters sont aussi, commodément, d’où viennent le scraping et le credential stuffing.
Mais la version durable de cette idée n’a rien à voir avec la géographie : c’est le filtrage sur la forme du trafic plutôt que sur son origine. Une attaque partage généralement quelque chose de structurel — le même user agent, le même header manquant, la même URL avec le même paramètre de requête, la même empreinte TLS, une absence complète de la seconde requête qu’un vrai navigateur fait toujours. Trouvez la propriété partagée et vous écrivez une seule règle qui ne coûte rien et qui survit au changement d’adresse de l’attaquant, ce qu’il fera plus vite que vous ne pourrez les lister.
# in an attack: what do these requests have in common?
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 # addresses
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 # user agents
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 # pathsSi un seul user agent représente quatre-vingt-dix pour cent des requêtes, vous êtes à quatre-vingt-dix secondes d’un correctif. Si les vingt premières adresses ne représentent chacune que deux pour cent du trafic, arrêtez de chercher des adresses — c’est une attaque distribuée, et la réponse, c’est le cache et les limites de débit.
Quand l’adresse est grillée
Parfois l’attaque est ciblée, persistante, dirigée contre vous personnellement plutôt que contre une adresse au hasard, et la bonne réponse consiste à cesser de défendre l’adresse et à l’abandonner. Ce n’est pas une défaite ; pour un petit service, c’est souvent l’issue la moins coûteuse possible, et ce n’est douloureux que si vous ne l’avez jamais répété.
Ce qui rend l’opération rapide se décide à l’avance. Gardez des TTL DNS à 300 secondes en posture permanente — le coût est négligeable et l’avantage, c’est de pouvoir bouger en cinq minutes plutôt qu’en une journée. Gardez votre déploiement reproductible, parce qu’un serveur que vous pouvez reconstruire est un serveur que vous pouvez déplacer ; si reconstruire signifie se rappeler ce que vous avez fait en mars, vous n’avez pas un plan, vous avez un otage. Gardez une sauvegarde restaurable dans une autre région, et sachez, grâce à un vrai exercice, combien de temps prend une restauration.
Le déplacement proprement dit est ensuite bref : déployez dans une autre région, restaurez, vérifiez sur la nouvelle adresse par surcharge de nom d’hôte avant de toucher au DNS, basculez l’enregistrement, et gardez l’ancien serveur en service assez longtemps pour que les derniers résolveurs se mettent à jour. Le guide de migration traite correctement de l’ordonnancement, et c’est la même séquence que vous déménagiez pour la performance ou parce que quelqu’un est en colère.
Le modèle de facturation rend la répétition à peu près gratuite : les frais sont calculés au prorata du jour sur votre solde, donc une seconde machine que vous déployez, testez et détruisez en une après-midi coûte quelques centimes, et il n’y a ni contrat, ni carte, ni renouvellement à annuler. Rien ne justifie que votre premier changement d’adresse ait lieu pendant une attaque.
Ce que rien de tout cela ne vous garantit
Deux remarques honnêtes pour finir, parce que le reste de cette page est optimiste par construction.
La première, c’est qu’aucun hébergeur n’absorbe tout, et tout fournisseur qui prétend le contraire est en train de vendre. La capacité est finie, la capacité de nettoyage l’est encore plus, et à une échelle suffisamment grande, l’acte économiquement rationnel pour un réseau qui protège des milliers de clients est de cesser d’annoncer une adresse. Nous traitons cela comme un dernier recours contre des attaques soutenues qui menacent l’ensemble du PoP, et nous vous le disons plutôt que de vous laisser le découvrir en déboguant — mais une promesse que cela ne peut jamais arriver serait un mensonge, et vous devriez vous méfier de tout hébergeur qui en fait une.
La seconde, c’est que les représailles ne sont pas au menu. Les services de booter et de stresser sont des attaques à louer déguisées en outils de test, ils sont illégaux dans la plupart des juridictions qui comptent, ils sont surveillés de près, et ils feront résilier votre serveur ici, sous une politique qui autorise pourtant presque tout le reste. L’asymétrie qui rend le DDoS séduisant pour un attaquant le rend sans valeur pour un défenseur : vous ne pouvez pas surpasser en flood quelqu’un qui n’a rien à perdre. Absorbez, cachez, déplacez-vous, et laissez la situation devenir ennuyeuse — ce qui, pour quiconque paie pour le trafic qu’il envoie, finit toujours par arriver.
- Nommez la couche en quatre-vingt-dix secondes
Avant tout changement de configuration, récupérez les deux axes et les deux compteurs. Bits élevés et paquets faibles, c’est volumétrique ; paquets élevés et bits faibles, c’est une attaque d’état ; les deux modestes avec le CPU bloqué à fond, c’est de la couche 7.
sar -n DEV 1 5 # bits/s and packets/s per interface ss -s # socket summary; watch synrecv nstat -az | grep -E 'ListenDrops|ListenOverflows|SyncookiesSent' cat /proc/sys/net/netfilter/nf_conntrack_count dmesg -T | grep -i conntrack | tailÉcartez ensuite le succès et l’auto-sabotage : survolez le journal d’accès pendant dix secondes. Si les chemins ressemblent à un site web qu’on utilise, vous avez une audience ou un bug, pas un attaquant.
- Faites en sorte qu’une attaque du noyau ne vous coûte rien
Deux fichiers, un reload. C’est sans risque sur n’importe quel serveur généraliste, et cela élimine les deux façons les plus courantes dont une petite machine s’effondre.
cat > /etc/sysctl.d/99-flood.conf <<'EOF' net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 8192 net.core.somaxconn = 8192 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_keepalive_time = 600 net.netfilter.nf_conntrack_max = 262144 net.netfilter.nf_conntrack_buckets = 65536 net.netfilter.nf_conntrack_tcp_timeout_established = 3600 net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20 EOF sysctl --system sysctl net.ipv4.tcp_syncookies net.netfilter.nf_conntrack_max # verify, do not assumeFaites ensuite correspondre l’application : nginx a besoin de
listen 443 ssl backlog=8192;, sans quoi la file du noyau que vous venez d’agrandir reste plafonnée par le plus petit nombre que le processus a demandé. - Fermez tout ce que vous ne servez pas, au-dessus de l’invité
Recensez ce qui écoute réellement, décidez ce qui a sa place sur une adresse publique, et remontez les règles grossières jusqu’au filtre en périphérie de l’hyperviseur pour que le trafic n’atteigne jamais votre carte réseau virtuelle.
ss -tlpn; ss -ulpn # every public listener, with the process that owns itDans Server → Network → Firewall, réglez le filtre en périphérie sur un refus par défaut et n’autorisez que les ports que vous servez, avec SSH restreint aux adresses depuis lesquelles vous administrez. Gardez le jeu de règles
nftablesinvité comme seconde couche — ceinture et bretelles, pas l’un ou l’autre — et liez bases de données et interfaces d’administration à127.0.0.1ou à une adresse WireGuard plutôt que de mettre un pare-feu devant un port public. - Mettez un cache devant le chemin coûteux
C’est le changement le plus rentable de tous pour tout ce qui rend des pages. Ajoutez
proxy_cacheavecproxy_cache_lock onetproxy_cache_use_stale, pour que mille miss simultanés deviennent une seule requête vers l’origine, et que tous les autres reçoivent une page légèrement ancienne.nginx -t && systemctl reload nginx curl -sI https://example.com/ | grep -i x-cache # MISS, then HITVérifiez le taux de hit avant d’y croire. Un cache qui ne fait jamais de hit à cause d’un
Set-Cookiesur chaque réponse est le faux sentiment de sécurité le plus courant de tout cet exercice — vérifiez avec deuxcurl -sIet lisezX-Cache. - Limitez le débit des chemins qui vous coûtent cher, pas celui du visiteur
Appliquez une limite globale généreuse et une limite serrée sur les endpoints qui touchent une base de données. Toujours
nodelay, toujours avec un code de statut, jamais une file silencieuse.limit_req_zone $binary_remote_addr zone=general:16m rate=20r/s; limit_req_zone $binary_remote_addr zone=costly:16m rate=2r/s; limit_req_status 429;Si nginx est derrière un proxy, configurez d’abord
set_real_ip_frometreal_ip_header. Une limite indexée sur l’adresse de votre frontal ne freine pas un attaquant — elle freine tout le monde en même temps, dès son premier déclenchement. - Retirez l’adresse d’origine de la vue publique
Rendez l’IP nue inutile, puis décidez de ce qui la met en façade. Le refus tient en deux lignes et ferme pour de bon la voie qui consiste à scanner internet à la recherche du HTML de votre site.
server { listen 80 default_server; listen 443 ssl default_server; ssl_reject_handshake on; return 444; }Auditez ensuite les fuites dans l’ordre où elles se produisent d’habitude : anciens enregistrements A dans les archives de DNS passif, noms d’hôtes dans les journaux de certificate transparency qui ont autrefois pointé vers l’origine, et en-têtes de mail sortant. Si l’un d’eux nomme encore l’adresse, un frontal ne vous sauvera pas — renumérotez d’abord, mettez un frontal ensuite.
- Prévenez l’opérateur réseau, avec des chiffres
L’atténuation volumétrique est automatique et ne nécessite aucun ticket, mais un signalement qui inclut des preuves permet à un humain de confirmer ce qu’a fait l’automatisation, et de repérer les modes de défaillance qu’elle ne peut pas voir — un nettoyeur qui réinitialise des sessions légitimes, par exemple.
Envoyez l’adresse de destination, l’heure de début en UTC, le protocole et les ports de destination, le débit sur les deux axes tel que vous l’avez mesuré, et si vos propres compteurs montrent des paquets qui arrivent ou qui disparaissent en amont. Cinquante lignes de
tcpdump -ni eth0 -c 200valent mieux qu’un paragraphe de description. « Le site est lent » n’est pas exploitable ; « 185.x.x.x, 14:02 UTC, UDP vers le port 443, ~1,2 Mpps, l’interface montre 40 Kpps en entrée » l’est. - Répétez le changement d’adresse pendant que tout va bien
Réglez dès aujourd’hui les TTL DNS à 300 secondes et laissez-les ainsi. Faites ensuite l’exercice une fois, de bout en bout, sur une machine que vous détruirez une heure plus tard.
# deploy a second instance in another region, restore, verify by hostname override curl --resolve example.com:443:<new-ip> https://example.com/ -sINotez ce que cela vous a coûté en minutes. Ce chiffre est votre vrai plan anti-DDoS — davantage que n’importe quelle règle de ce guide — parce que c’est lui qui indique combien de temps une attaque ciblée peut vous garder hors ligne. Les frais sont calculés au prorata du jour sur votre solde, donc toute la répétition coûte quelques centimes.
Où chaque attaque peut réellement être arrêtée
| Couche | Arrête | N’arrête pas | Ce que ça vous coûte | Qui la contrôle |
|---|---|---|---|---|
| Nettoyage en amont | Floods volumétriques — absorbés sous 10 Gbps, nettoyés jusqu’à 100, déviés par BGP au-delà | Tout ce qui reste dans les volumes de trafic normaux : attaques d’état, couche 7 | Rien. Toujours actif, sans ticket, une latence occasionnelle pendant un détournement | Nous, automatiquement |
| Filtre en périphérie de l’hyperviseur | Tout ce qui vise un port fermé, avant que ça n’atteigne votre carte réseau virtuelle — ni CPU, ni entrée d’état | Les attaques sur les ports que vous devez garder ouverts | Rien à part les règles que vous écrivez ; à état, refus par défaut | Vous, par serveur |
| Pare-feu invité (nftables) | SYN floods avec synproxy, débits de paquets par source, protocoles indésirables | Le trafic qui a déjà saturé le tuyau au-dessus de vous — il n’arrive jamais | Du CPU et une entrée conntrack pour chaque paquet, y compris ceux que vous jetez | Vous, entièrement |
| Réglage du noyau (sysctl) | Épuisement de la file d’acceptation et de conntrack — le tueur classique des petits serveurs | Toute connexion valide et menée à son terme | Environ 80 Mo de RAM pour une taille de conntrack raisonnable. Deux fichiers | Vous, entièrement |
| Application (cache + limite de débit) | Floods de couche 7, ruées de requêtes, endpoints coûteux | Les attaques au niveau paquet — elles n’atteignent jamais le serveur web | Des pages légèrement périmées, et des 429 pour les utilisateurs que vous avez mal jugés | Vous, entièrement |
| Frontal en reverse proxy | Les attaques directes sur l’origine — il n’y a aucune adresse publique à viser | Les attaques sur le frontal lui-même, et tout ce qui suit une fuite de votre IP d’origine | 5 $/mois et une machine de plus à maintenir à jour | Vous, si vous le gérez vous-même |
| CDN commercial | Les grosses attaques de couche 3/4 et de couche 7, à grande échelle, avec une file de support | Une origine qui a un jour été publique, ou qui répond sur son IP nue | Une terminaison TLS par un tiers qui sait qui vous êtes et peut se voir signifier une procédure | Eux |
| Service onion Tor | Toute attaque fondée sur l’IP — il n’y a pas d’adresse dans le protocole | Le flood des points d’introduction, ce pour quoi le Tor moderne embarque une défense par preuve de travail | De la latence, et une audience prête à utiliser Tor | Vous et le réseau |
Questions qui méritent une réponse
Dois-je activer une protection DDoS sur mon serveur ?
Non. L’atténuation volumétrique se situe en amont des PoP et elle est toujours active — il n’y a aucun produit à acheter, aucun interrupteur à actionner. En dessous de 10 Gbps, c’est absorbé sans effet visible ; de 10 à 100 Gbps, c’est nettoyé chez l’opérateur de transit, où vous pouvez observer une brève hausse de latence ; au-delà de 100 Gbps, le préfixe est annoncé sur un chemin de nettoyage dédié, la latence augmente plus nettement et le service reste joignable. Les seuils sont publiés dans la documentation. Ce qui n’est pas automatique, c’est tout ce qui se situe au-dessus de la couche 4 : l’épuisement de table d’état et les floods de couche applicative ressemblent à du trafic ordinaire vus depuis l’amont, et doivent être gérés sur votre machine.
Allez-vous null-router mon IP si je me fais attaquer ?
Seulement en dernier recours, et seulement pour des attaques soutenues qui menacent l’ensemble du PoP plutôt que votre seul serveur — et nous vous prévenons dans les minutes qui suivent si cela arrive, plutôt que de vous laisser le découvrir. Tout hébergeur qui promet que cela ne peut jamais arriver décrit du marketing, pas un réseau : à une échelle suffisante, protéger des milliers de clients signifie finir par retirer une adresse. La protection réaliste consiste à vous rendre vous-même peu attrayant comme cible — gardez l’adresse d’origine non publiée, gardez un plan de changement d’adresse répété, et connaissez votre temps de restauration.
Mon site est en panne mais le graphique de bande passante a l’air normal. Que se passe-t-il ?
Presque certainement de l’épuisement d’état — le cas classique étant une table nf_conntrack pleine. Vingt mille petits paquets par seconde depuis des sources usurpées remplissent une table par défaut en quelques secondes tout en n’utilisant que quelques mégabits, si bien que le graphique n’affiche rien et que chaque nouvelle connexion est jetée. Vérifiez dmesg -T | grep conntrack à la recherche de « table full, dropping packet » et comparez nf_conntrack_count à nf_conntrack_max. L’autre possibilité, c’est la lecture inverse : le tuyau au-dessus de vous est déjà plein, et votre interface ne vous montre que les survivants, pas l’attaque.
Une offre plus grosse survivra-t-elle à une attaque qu’une petite ne peut pas encaisser ?
Parfois, et pas pour la raison à laquelle on s’attend. Davantage de vCPU et de RAM aident réellement contre les floods de couche 7 et l’épuisement d’état, parce que ces attaques épuisent le CPU, la mémoire et l’espace des tables. Contre un flood volumétrique, l’offre est presque hors sujet — les paquets sont jetés en amont de votre port quoi qu’il y ait derrière, et un port à 2,5 Gbps ne vous sauve pas de 40 Gbps. Corrigez le cache et le dimensionnement de conntrack avant de monter en gamme ; c’est moins cher, et il s’avère généralement que c’était le vrai problème.
Puis-je mettre Cloudflare ou un autre CDN devant un VPS no-KYC ?
Techniquement oui, et ça marche bien. Comprenez le compromis avant de le faire : le CDN termine votre TLS et voit votre trafic en clair, connaît votre adresse d’origine, détient un compte qui vous identifie par e-mail et souvent par moyen de paiement, et peut se voir signifier une procédure légale dans une juridiction que vous n’avez pas choisie. Pour un serveur acheté sans nom attaché, cela réintroduit exactement le tiers que l’arrangement était censé éviter. Si votre modèle de menace, c’est l’interruption de service plutôt que l’exposition, c’est un choix raisonnable — mais l’origine ne doit jamais avoir été publique, sinon un vieil enregistrement DNS ruine tout. Si votre modèle de menace inclut qui sait où vous êtes, faites plutôt tourner votre propre reverse proxy sur une seconde instance, et laissez l’origine n’accepter de trafic que depuis lui.
fail2ban suffit-il à arrêter un DDoS ?
Non, et c’est le mauvais outil plutôt qu’un outil faible. Il réagit à l’échelle de temps humaine après avoir analysé des journaux, donc les requêtes vous ont déjà coûté tout ce qu’elles allaient coûter ; il bannit une adresse à la fois, ce qui ne fait rien contre dix mille sources et transforme votre pare-feu en balayage linéaire si vous le laissez essayer ; et il dépend de journaux qu’un attaquant peut noyer sous le flood. Utilisez-le pour ce qu’il fait excellemment — le devinage lent d’identifiants contre SSH et les formulaires de connexion — et utilisez un ensemble dynamique nftables avec timeout pour tout ce qui se passe à la vitesse d’un flood.
Dois-je bloquer des pays entiers ou des ASN ?
Bloquer un pays n’est défendable que si votre audience est réellement bornée et que vous acceptez de perdre des utilisateurs en voyage et ceux routés bizarrement. C’est à peu près inutile contre un botnet moderne, réparti sur des connexions résidentielles partout, y compris dans votre propre pays. Bloquer les ASN de datacenters est plus incisif — les gens ordinaires ne naviguent pas depuis une plage d’hébergeur — et attrape en prime le scraping et le credential stuffing. Le meilleur réflexe consiste à filtrer sur la forme du trafic : un user agent partagé, un header manquant, un chemin répété. Cette règle continue de fonctionner après que l’attaquant a changé d’adresses, ce qu’il fera en quelques minutes.
Faire tourner un service onion Tor me rend-il immunisé ?
Immunisé contre les attaques fondées sur l’IP, oui — il n’y a pas d’adresse dans le protocole sur laquelle diriger un flood, ce qui est une posture de sécurité réellement différente, pas une simple amélioration incrémentale. Ce n’est pas immunisé en général : les services onion peuvent être attaqués au niveau de leurs points d’introduction, ce pour quoi le Tor moderne embarque une défense par preuve de travail qui rend le flood coûteux pour le client. Cela coûte aussi de la latence et limite l’audience à ceux prêts à utiliser Tor. Beaucoup de gens font tourner les deux à la fois — un frontal clearnet pour la portée, une adresse onion qui continue de fonctionner quand le clearnet est sous attaque.
Keep exploring
Durcir un nouveau VPS pendant la première heure
Le prérequis de cette page : fermer les ports que vous ne servez pas retire toute une classe d’attaque avant même de régler quoi que ce soit.
Migrer un VPS sans interruption de service
L’exercice complet de changement d’adresse, avec l’ordonnancement DNS qui décide combien de temps une adresse grillée vous laisse hors ligne.
VPS pour serveurs de jeu
La charge de travail qui attire le plus ce genre d’attaque, et le réglage spécifique à l’UDP qui va avec un port de jeu public.
Deploy your offshore server.
Choisissez une région. Choisissez un plan. Collez une clé. Payez. Les 47 prochaines secondes sont pour nous.