Nginx Proxy Manager : quand le filtrage par adresse IP ne protège rien
Une liste d'adresses IP autorisées dans Nginx Proxy Manager semble protéger vos services sensibles. Mais par défaut, NPM croit l'adresse annoncée par les requêtes venues de Cloudflare, et n'importe qui peut en envoyer...
Beaucoup de petites structures protègent leurs services sensibles (ERP, gestionnaire de mots de passe, outils internes) avec Nginx Proxy Manager (NPM) et une liste d'adresses IP autorisées : celle des bureaux, celle du serveur, celles du réseau interne. Si l'adresse du visiteur est dans la liste, il passe ; sinon, il doit s'authentifier. Toute la protection repose donc sur une question : d'où vient vraiment le visiteur ?
Le problème
Certains sites sont placés derrière un CDN comme Cloudflare ou Amazon CloudFront. Toutes les visites arrivent alors depuis les serveurs du CDN, qui indique l'adresse du vrai visiteur dans une étiquette jointe à la requête : l'en-tête X-Real-IP.
Pour que ces sites fonctionnent, NPM est réglé par défaut, chez tout le monde, pour croire cette étiquette quand la requête vient d'une adresse Cloudflare ou CloudFront. Il télécharge lui-même la liste de ces adresses toutes les 6 heures (code source de NPM, configuration de NPM).
Le hic : n'importe qui peut ouvrir un compte Cloudflare gratuit et faire partir ses propres requêtes depuis les serveurs de Cloudflare, avec l'étiquette de son choix.
Un exemple
Un pirate envoie une requête depuis Cloudflare avec l'étiquette « je suis 192.168.1.10 ». NPM voit une requête venue de Cloudflare, croit l'étiquette, constate que 192.168.1.10 fait partie des adresses autorisées… et laisse passer.
Il suffit de deviner une adresse de la liste, et c'est souvent facile : le réseau du bureau (192.168.1.x), le réseau Docker par défaut (172.16 à 172.31), ou l'adresse publique du serveur, visible de tous.
Les conséquences dépassent le filtrage par IP. fail2ban se fait berner : l'attaquant annonce une nouvelle adresse à chaque essai, et les bannissements tombent à côté. Les applications, elles, voient une fausse adresse dans leurs protections contre les essais de mots de passe à répétition. Il ne reste alors que les mots de passe et la double authentification.
En image : un gardien laisse entrer les personnes inscrites sur sa liste, mais il a une consigne : « si quelqu'un arrive dans la camionnette d'un grand transporteur, crois le nom écrit sur l'étiquette sans vérifier ». Or n'importe qui peut louer la même camionnette.
La correction
Une seule ligne, à placer en tête de l'onglet Advanced de chaque hôte dans NPM :
set_real_ip_from 127.0.0.1/32;
Elle dit à NPM : « ne crois l'étiquette que si elle vient de toi-même ». Cela n'arrive jamais : l'étiquette est donc toujours ignorée, et NPM retient la vraie adresse de la connexion, qu'on ne peut pas falsifier. Le gardien vérifie de nouveau lui-même la pièce d'identité. Pour un visiteur normal, rien ne change.
Pourquoi ça marche : placée dans un hôte, cette ligne remplace la liste de confiance globale de NPM au lieu de s'y ajouter (module realip de nginx). Nous l'avons vérifié sur un nginx de test réglé comme NPM : avec la fausse étiquette « 192.168.1.10 », l'hôte sans la ligne voit 192.168.1.10 ; l'hôte avec la ligne voit la vraie adresse du visiteur.
À retenir
- Qui est concerné ? Toute installation NPM qui filtre par adresse IP sans être réellement derrière un CDN.
- Où mettre la ligne ? Sur chaque hôte, sans oublier ceux que vous créerez plus tard.
- Exception : un hôte vraiment placé derrière Cloudflare a besoin de l'ancien réglage. Ne lui mettez pas la ligne.
- Pourquoi pas un réglage global ? NPM re-télécharge lui-même la liste toutes les 6 heures : un nettoyage du fichier global serait écrasé. La ligne placée dans l'hôte, elle, fait partie de sa configuration et reste en place.
Vous utilisez NPM, ou un autre proxy, et vous voulez vérifier ce que vos règles d'accès laissent réellement passer ? Parlons-en.