guest@6admin.io:~$

Le trafic entrant : exposer moins et garder ce qui reste

Chaque port sur lequel une machine écoute est une promesse de répondre à un inconnu et la plupart des incidents commencent par une promesse que personne ne voulait faire: une base liée à 0.0.0.0, un panneau d’administration sur une IP publique, un service oublié qu’un scanner a trouvé avant vous. Internet est scanné de bout en bout en continu, donc un service exposé n’est pas un risque qui pourrait être remarqué, c’est un risque qui le sera, en général en quelques heures. Le durcissement de l’entrée est donc deux disciplines: exposer le moins possible et placer le peu qui doit l’être derrière des contrôles qui supposent qu’il sera attaqué.

L’exposition minimale est tout le jeu

Avant toute règle de pare-feu, la question est ce qui écoute et pourquoi. L’essentiel de ce qui apparaît ne devrait pas être joignable de l’extérieur et souvent ne devrait pas écouter sur une interface externe du tout:

ss -tlnp        # chaque socket TCP en écoute, et le processus derrière

Les constats courants sont une base sur 0.0.0.0:5432, un cache (redis, memcached) sans authentification qui répond au monde, un point de métriques ou d’administration sur une interface publique, un port de gestion qui appartient au réseau interne. La première correction n’est pas une règle de pare-feu, c’est de lier le service à l’interface à laquelle il devrait répondre: une base qui ne sert que des apps locales se lie à 127.0.0.1 ou à l’adresse interne et alors aucune erreur de pare-feu ne peut l’exposer parce qu’elle n’écoute pas là où internet peut l’atteindre. Liez étroit d’abord; pare-feu ensuite.

Default-deny au bord

La posture de pare-feu pour l’entrée est celle, incontestée, qui reste à moitié implémentée partout: refuser par défaut, permettre les services précis censés être publics et n’autoriser ceux de gestion que depuis les réseaux qui les administrent:

iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -A INPUT -p tcp --dport 443 -j ACCEPT               # le service public
iptables -A INPUT -s 10.0.10.0/24 -p tcp --dport 22 -j ACCEPT # SSH depuis le réseau admin seulement

Le détail qui compte, c’est que l’exposition publique et l’exposition de gestion sont deux questions différentes. Un serveur web répond 443 au monde; son SSH ne répond qu’à la plage admin, parce qu’il n’y a aucune raison qu’internet atteigne la porte administrative. Confondre les deux, exposer SSH ou un panneau de contrôle à tous parce que le service web est public, est la sur-exposition la plus courante et c’est une correction d’une ligne.

Placez les services web derrière un reverse proxy

Une application web publique ne devrait pas être ce qui fait face à internet directement. Un reverse proxy devant elle termine le TLS, présente une seule porte d’entrée durcie et patchable et vous donne l’endroit où appliquer les limites de débit, le filtrage de requêtes et les timeouts sensés que l’application elle-même ne peut souvent pas:

server {
    listen 443 ssl;
    ssl_protocols TLSv1.2 TLSv1.3;             # rien de plus ancien
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
    limit_req zone=app burst=20 nodelay;        # émousse le brute force / l'abus
    location / { proxy_pass http://127.0.0.1:8080; }   # l'app se lie à localhost
}

L’application se lie à localhost, de sorte que la seule voie vers elle passe par le proxy et le TLS est terminé sur un jeu de protocoles actuel avec HSTS pour qu’un downgrade ne soit pas sur la table. Le proxy est aussi là où vit un WAF (ModSecurity ou celui de la plateforme) si vous en faites tourner un et où les limites de taille et de timeout de requête transforment un abus par requête lente en connexion coupée plutôt qu’en pool de workers épuisé.

Le brute force et les services qui l’invitent

Tout ce qui s’authentifie et fait face au réseau se fait brute-forcer, SSH et tout formulaire de connexion en premier. La vraie réponse pour SSH est les clés seules (traitée pour elle-même), qui retire le mot de passe qu’il y aurait à deviner; fail2ban par-dessus assèche le bruit des logs et laisse tomber les récidivistes évidents au pare-feu. Pour les connexions web, l’équivalent est la limitation de débit au proxy et la logique de verrouillage dans l’application. Le cadrage honnête, comme pour fail2ban en général, c’est que cela réduit le bruit et arrête les flots automatisés bêtes; cela n’arrête pas un attaquant distribué et patient et ce n’est pas un substitut à une authentification forte derrière la connexion.

Le DDoS, au niveau que vous pouvez vraiment traiter

Les flots volumétriques qui saturent le lien ne se résolvent pas sur l’hôte, parce que les paquets ont déjà consommé la bande passante au moment où ils arrivent; c’est un problème amont ou de prestataire de scrubbing et prétendre qu’une règle côté serveur le règle est l’erreur à éviter. Ce que l’hôte peut faire, c’est survivre aux attaques d’épuisement de ressources plus petites: les SYN cookies contre les SYN floods, le suivi de connexions et des limites de connexions par source pour qu’une source ne consomme pas la table d’états et les timeouts de requête ci-dessus pour qu’un goutte-à-goutte à la Slowloris ne tienne pas les workers ouverts indéfiniment:

# /etc/sysctl.d/ingress.conf
net.ipv4.tcp_syncookies = 1
net.netfilter.nf_conntrack_max = 1000000

Soyez clair avec vous-même sur le niveau d’une attaque donnée, parce que les contrôles ne se transfèrent pas: une limite de conntrack ne fait rien contre un flot qui sature le lien et un contrat de scrubbing ne fait rien contre une attaque par requête lente qui reste sous le seuil de volume.

Où atterrir

Commencez par écouter moins: ss -tlnp, liez chaque service à l’interface la plus étroite qui sert ses vrais clients et sortez les bases et les points d’administration des interfaces externes entièrement. Puis default-deny en entrée, en exposant les services publics au monde et les services de gestion au seul réseau admin, sans jamais confondre les deux. Placez les applications web derrière un reverse proxy qui termine un TLS moderne, limite le débit et met en façade une app liée à localhost. Asséchez le brute force avec des clés et fail2ban en vous rappelant que c’est de la réduction de bruit, pas de l’authentification. Et dimensionnez vos attentes DDoS au niveau que vous pouvez traiter sur l’hôte, en laissant le problème volumétrique en amont où il vit. L’entrée est nécessaire et non suffisante à elle seule: c’est le mur et les contrôles de sortie sont ce qui attrape ce qui passe par-dessus.