SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor

FTP : le mode passif bloque derrière le pare-feu

La connexion FTP réussit, puis l’affichage du contenu reste bloqué : déclarez la plage de ports passifs et la règle de pare-feu correspondante.

Pourquoi FTP accepte la connexion, mais bloque lors de l’affichage du contenu

Le mode passif FTP échoue derrière un pare-feu, car FTP utilise deux connexions TCP, et non une seule. La connexion au port 21 transporte l’authentification et les commandes. Le nom d’utilisateur et le mot de passe sont donc acceptés, et le pare-feu semble correctement configuré. La première ls nécessite ensuite une deuxième connexion sur un autre port. Le pare-feu n’autorise pas cette connexion. Le client reste alors bloqué jusqu’à l’expiration du délai.

La solution consiste à définir une plage fixe de ports pour ces connexions de données, puis à ajouter une règle de pare-feu qui autorise cette même plage. Un serveur situé derrière un NAT (network address translation) nécessite un réglage supplémentaire pour annoncer la bonne adresse. Les helpers de suivi des connexions se chargeaient autrefois de cette tâche. Ce n’est plus le cas. Il est important de comprendre pourquoi avant de reprendre une ancienne procédure.

Le canal de contrôle et le canal de données

FTP (File Transfer Protocol) est défini dans la RFC 959 et précède à la fois le NAT et le pare-feu stateful. Une session ouvre une connexion de contrôle vers le port TCP 21 et la conserve pendant toute la session. Les commandes sont transmises en texte brut. Les réponses se composent d’un code à trois chiffres et d’une ligne de texte. Cette connexion ne transporte jamais le contenu des fichiers.

Chaque donnée utilise sa propre connexion TCP : une pour une liste de répertoire (LIST), une pour chaque téléchargement (RETR) et une pour chaque envoi (STOR). La connexion est ouverte, utilisée une seule fois, puis fermée. L’authentification s’effectue entièrement sur le canal de contrôle. Un chemin de données défaillant se manifeste donc toujours de la même manière : la connexion est établie, puis le client se bloque. Si le client affiche une réponse 230, puis s’arrête pendant la liste, le problème concerne le canal de données et non les identifiants.

Mode actif : le serveur se reconnecte au client

En mode actif, le client choisit un port, se met à l’écoute sur ce port et indique au serveur où se connecter :

PORT 192,168,1,50,195,80

Les quatre premiers nombres correspondent à l’adresse IP du client. Les deux derniers représentent le port, encodé sur deux octets : 195 * 256 + 80 = 50000. Le serveur ouvre ensuite la connexion de données depuis son propre port 20 vers le port 50000 du client.

Du point de vue du client, cette connexion est entrante et non sollicitée. Le pare-feu du client la bloque donc. Si le client se trouve derrière un routeur domestique, l’adresse indiquée dans la commande PORT est une adresse privée que le serveur ne peut pas joindre. C’est le mode actif qui a donné au FTP sa réputation de protocole difficile à faire fonctionner.

Mode passif : le client ouvre les deux connexions

Le mode passif inverse le sens de la connexion de données. Le client envoie PASV et le serveur répond avec sa propre adresse et un port :

227 Entering Passive Mode (203,0,113,10,195,80)

L’encodage est le même. Le client se connecte donc à 203.0.113.10 sur le port 50000. Le client ouvre désormais les deux connexions. C’est pourquoi le mode passif fonctionne malgré le NAT côté client et pourquoi tous les clients actuels le demandent en premier.

Le problème a été déplacé, pas supprimé. La connexion entrante non sollicitée arrive maintenant sur votre serveur, sur un port élevé qui change à chaque transfert. Ce firewall vous appartient. Le problème vous revient donc.

EPSV (mode passif étendu, RFC 2428) applique le même principe avec une réponse plus simple :

229 Entering Extended Passive Mode (|||50000|)

Elle ne contient aucune adresse. Le client réutilise l’adresse déjà utilisée pour la connexion de contrôle. C’est ce qui permet au mode passif de fonctionner avec IPv6 et élimine toute une catégorie de problèmes de NAT. Le manuel de curl indique que curl essaie normalement EPSV avant PASV. Le port est toujours choisi à l’exécution. EPSV ne change donc rien à vos règles de firewall.

Pourquoi une règle de pare-feu classique ne peut pas autoriser le canal de données

Parce que le numéro de port n’existe pas encore lorsque vous écrivez la règle. Le serveur le choisit pour chaque transfert. Avec sa configuration par défaut, vsftpd documente pasv_min_port et pasv_max_port comme 0, c’est-à-dire « utiliser n’importe quel port ». La connexion de données peut donc arriver sur n’importe quel port supérieur à 1023. sudo ufw allow 21/tcp autorise le canal de contrôle, et rien d’autre. C’est exactement la configuration qui permet une connexion fonctionnelle, mais empêche l’affichage du contenu des répertoires. Si la notion de service à l’écoute sur un port fixe reste floue, le fonctionnement des ports et des sockets en écoute sous Linux fournit les explications nécessaires.

Un pare-feu stateful suit bien l’état des connexions, et le kernel peut autoriser une nouvelle connexion comme RELATED d’une connexion existante. Avec FTP, cela nécessite qu’un composant lise le flux de contrôle et extraie le port d’une ligne 227 ou PORT. Par défaut, rien ne le fait.

Pourquoi le helper de suivi des connexions FTP n’est plus la solution

Le module du noyau nf_conntrack_ftp est celui indiqué par les anciens guides. Il lit le canal de contrôle en clair, trouve le port annoncé et enregistre une attente. La connexion de données est ainsi autorisée sans règle indiquant son port. Quatre éléments ont changé depuis la rédaction de ces guides.

L’attribution automatique des helpers est désactivée. Sur les noyaux actuels, elle ne peut plus être réactivée. Le noyau l’a désactivée par défaut en 2016. Le commit de Pablo Neira Ayuso intitulé « netfilter: remove nf_conntrack_helper sysctl and modparam toggles » (août 2022) a ensuite supprimé complètement ce paramètre. Le commit précise que « les utilisateurs doivent désormais s’appuyer sur une configuration explicite des helpers conntrack via le ruleset ». Le sysctl nf_conntrack_helper est présent dans Linux 5.19 et a disparu depuis Linux 6.0. La documentation des sysctl conntrack du noyau ne le liste plus. Charger le module ne suffit donc pas.

Vous pouvez vérifier le type de noyau utilisé. Exécutez sysctl net.netfilter.nf_conntrack_helper. Une réponse sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_helper: No such file or directory signifie que le noyau ne permet plus d’activer l’attribution automatique des helpers. Si une valeur numérique est renvoyée, vous utilisez un noyau antérieur à 6.0. Le paramètre existe encore et sa valeur par défaut est 0.

Les frontends de pare-feu l’ont également rendu obsolète. man ufw-framework sur Ubuntu 24.04 indique à propos de la ligne IPT_MODULES dans /etc/default/ufw : « Le chargement inconditionnel des modules de suivi des connexions (nf_conntrack_*) de cette manière est obsolète », et ajoute que les règles de helper « doivent être gérées dans les RULES FILES ». firewalld documente AutomaticHelpers dans firewalld.conf comme suit : « Obsolète. Cette option est ignorée et n’est plus utilisée. » Attacher un helper nécessite désormais d’écrire manuellement une règle explicite avec la cible CT. C’est plus complexe que la correction présentée ci-dessous, et cela cesse de fonctionner dès que vous activez TLS. iptables et nftables sur Ubuntu explique où se trouvent réellement ces règles.

TLS met fin au débat. Un helper fonctionne en lisant le canal de contrôle comme du texte. Si vous chiffrez ce canal, le helper ne voit plus que du texte chiffré et ne peut pas trouver le port. Il n’existe pas de correction à ce problème, et c’est normal : un équipement intermédiaire capable de lire votre canal de contrôle est aussi capable de lire votre mot de passe.

Déclarer une plage de ports passifs sur le serveur

Tous les serveurs FTP peuvent être configurés pour choisir leurs ports passifs dans une plage définie par vos soins. Les noms des options varient. Consultez donc la documentation du serveur que vous utilisez réellement.

vsftpd, dans /etc/vsftpd.conf :

pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30099

pasv_enable est déjà défini par défaut sur YES. Les deux options de port valent par défaut 0, ce qui correspond au comportement « utiliser n’importe quel port » décrit plus haut. Appliquez la configuration avec sudo systemctl restart vsftpd, puis vérifiez que le service a redémarré avec systemctl status vsftpd. vsftpd refuse de démarrer lorsqu’il ne peut pas analyser une ligne de configuration, au lieu de l’ignorer. Si le redémarrage échoue, consultez journalctl -u vsftpd -n 20 pour trouver la ligne 500 OOPS: qui indique l’option que vous venez de saisir.

ProFTPD, dans proftpd.conf :

PassivePorts 30000 30099

La documentation de ProFTPD ne définit aucune valeur par défaut ici : sans cette directive, le noyau choisit le port. Elle précise également que, lorsqu’aucun port de votre plage n’est disponible, le serveur utilise un port attribué par le noyau et journalise un message. Une plage trop petite provoque donc des échecs intermittents au lieu d’un échec explicite, ce qui complique fortement le diagnostic. Utilisez des ports non privilégiés, à partir de 1024.

Pure-FTPd utilise l’option -p first:last, documentée dans man pure-ftpd comme « utiliser uniquement les ports compris entre le premier et le dernier pour les téléchargements en mode passif ». Cette option « améliore la compatibilité de pure-ftpd avec les filtres de paquets ». Les paquets distribués encapsulent généralement cette option dans un fichier de configuration. Consultez donc la documentation de votre distribution pour connaître le nom du fichier, au lieu de le deviner.

De combien de ports avez-vous besoin ? D’un port par connexion de données active. Un port TCP fermé reste dans TIME_WAIT pendant quelques minutes avant de pouvoir être réutilisé. Prévoyez donc plusieurs fois votre pic d’activité attendu. Cent ports conviennent à quelques utilisateurs, mais un serveur public très sollicité en nécessite beaucoup plus. Dans ce domaine, vsftpd est moins tolérant que ProFTPD : son code source essaie quelques ports aléatoires dans la plage. Si tous sont occupés, il termine la session avec 500 OOPS: vsf_sysutil_bind au lieu d’utiliser un port hors plage. Avec l’un ou l’autre serveur, une plage trop petite interrompt les transferts au moment où l’activité est la plus élevée.

Où placer la plage ? Exécutez d’abord sysctl net.ipv4.ip_local_port_range. Sur une installation Ubuntu standard, la commande renvoie 32768 60999, c’est-à-dire les ports que le noyau attribue aux connexions sortantes. Une plage passive située dans cette fenêtre peut entrer en conflit avec une connexion sortante qui utilise déjà le port. Placez-la donc en dessous. Sur une installation par défaut, 30000 à 30099 est disponible. Vérifiez la configuration de votre propre serveur au lieu de vous fier à cette valeur.

Ouvrir la même plage dans le pare-feu

ufw utilise deux-points pour écrire une plage. Sa page de manuel indique qu’une plage ou une liste « peut également servir à spécifier plusieurs ports, auquel cas le protocole est obligatoire » :

sudo ufw allow 21/tcp
sudo ufw allow 30000:30099/tcp
sudo ufw status verbose

ufw status verbose doit maintenant afficher les deux entrées. La même commande sans /tcp est rejetée avec une erreur demandant de préciser tcp ou udp, car ufw ne devine pas le protocole. syntaxe des règles ufw sur un VPS couvre le reste.

firewalld utilise un trait d’union pour écrire la plage et nécessite un reload :

sudo firewall-cmd --permanent --add-service=ftp
sudo firewall-cmd --permanent --add-port=30000-30099/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

--add-service=ftp ouvre 21/tcp et demande le helper ftp défini par le service fourni. Cette commande n’ouvre pas votre plage passive. À elle seule, elle vous laisse donc exactement dans la situation initiale. zones et services firewalld sur un VPS présente le fonctionnement général.

Avec nftables directement, dans votre chaîne input :

tcp dport { 21, 30000-30099 } accept

Il faut également tenir compte d’un autre pare-feu. La plupart des fournisseurs exécutent un pare-feu réseau dans le control panel, en dehors du système d’exploitation. Si les règles du serveur semblent correctes mais que les paquets n’arrivent toujours pas, ouvrez-y aussi la même plage.

Indiquer l’adresse publique du serveur derrière un NAT

Exécutez ip -4 addr show sur le serveur. Si l’adresse de l’interface est celle à laquelle les clients se connectent, ignorez cette section. Si l’interface possède une adresse privée (10.x, 172.16 à 172.31.x, 192.168.x) et que la plate-forme lui associe une adresse publique, le serveur ne connaît pas sa propre adresse publique. La documentation de vsftpd indique que la valeur par défaut de pasv_address est « l’adresse est obtenue à partir de la socket connectée entrante ». La réponse 227 contient donc l’adresse privée, et le client est redirigé vers une adresse qu’il ne peut pas joindre.

FileZilla affiche exactement ce message :

Server sent passive reply with unroutable address. Using server address instead.

FileZilla corrige le problème et poursuit le transfert. Beaucoup d’autres clients ne le font pas. Ils se connectent à 10.0.0.5, puis restent bloqués.

curl masque également le problème, ce qui est important si vous l’utilisez pour vos tests. Sa documentation indique que --ftp-skip-pasv-ip « est activé par défaut (ajouté dans 7.74.0) ». curl ignore donc l’adresse présente dans la réponse 227 et réutilise l’adresse de la connexion de contrôle. Un transfert qui fonctionne avec curl peut tout de même échouer dans un client graphique pour cette seule raison.

Définissez explicitement l’adresse. vsftpd accepte pasv_address=203.0.113.10, ainsi que pasv_addr_resolve=YES (valeur par défaut : NO) si vous préférez indiquer un nom d’hôte. ProFTPD accepte MasqueradeAddress, qui peut contenir une adresse, un nom DNS ou un nom d’interface. Pure-FTPd accepte -P, documenté pour le cas où « le serveur se trouve derrière un équipement de masquage (NAT) ». EPSV évite entièrement le problème, car sa réponse ne contient pas de champ d’adresse. Vous ne pouvez toutefois pas compter dessus, car c’est le client qui choisit la commande à envoyer.

Ce que TLS change

FTPS est le protocole FTP sur TLS (Transport Layer Security). Le client se connecte au port 21 comme d’habitude, envoie AUTH TLS pour sécuriser le canal de contrôle, puis envoie PROT P pour chiffrer également le canal de données. Le FTP en clair transmet le mot de passe sur le réseau sous forme de texte lisible. Si vous devez conserver FTP, utilisez FTPS. vsftpd est livré avec ssl_enable défini par défaut sur NO.

Deux conséquences en découlent. Aucun helper de suivi des connexions ne peut fonctionner. C’est le même problème que précédemment, vu sous un autre angle. De plus, sudo tcpdump -nAi any 'tcp port 21' ne vous montrera plus la réponse 227. Lorsque vous devez connaître l’adresse et le port annoncés par le serveur, consultez plutôt le journal du serveur que le trafic réseau.

Tester la modification depuis l’extérieur

Exécutez ces commandes depuis une autre machine. Un test effectué depuis le serveur contourne le pare-feu que vous essayez de corriger.

sudo ss -ltnp | grep :21
curl -v --disable-epsv --user ftpuser:secret ftp://example.com/
nc -vz example.com 30000

ss doit afficher le démon FTP à l’écoute sur le port 21. Aucun processus n’écoute sur la plage passive lorsque le serveur est inactif, car ces sockets sont créées pour un transfert puis fermées après celui-ci.

--disable-epsv force curl à utiliser le chemin PASV, qui expose le problème d’adresse. La trace affiche la réponse du serveur, puis l’adresse et le port auxquels curl se connecte :

< 227 Entering Passive Mode (203,0,113,10,117,52)

117 * 256 + 52 = 30004, ce qui se trouve dans la plage déclarée. Une adresse privée sur cette ligne signifie que pasv_address n’est pas défini. Un port situé en dehors de votre plage signifie que le serveur n’a jamais lu la modification de configuration. Vérifiez donc que vous avez modifié le fichier utilisé par le service en cours d’exécution.

nc répond à lui seul à la question du pare-feu. Un Connection refused immédiat signifie que le paquet a atteint le serveur, mais qu’aucun processus n’écoutait, ce qui est le résultat attendu pour un port passif inactif : votre règle fonctionne. Une attente jusqu’à l’expiration de nc signifie qu’un élément a supprimé le paquet silencieusement. Il s’agit d’un pare-feu, soit celui du serveur, soit celui configuré dans le panneau de votre fournisseur. Cette distinction est la même que celle décrite dans connexion SSH refusée ou arrivée à expiration, et elle s’applique à tous les ports.

Devez-vous encore utiliser FTP ?

Pour un nouveau projet, non. SFTP (protocole de transfert de fichiers SSH) fonctionne au sein d’une seule connexion SSH sur le port 22. Il n’y a ni second canal, ni plage passive, ni configuration NAT, ni daemon supplémentaire à sécuriser, car OpenSSH fournit déjà ces fonctions. sftp user@example.com fonctionne sur un serveur où aucun service de transfert de fichiers n’a été configuré. Pour donner à quelqu’un accès à des fichiers, et à rien d’autre, sshd_config accepte ForceCommand internal-sftp avec ChrootDirectory. Ce répertoire doit appartenir à root et ne doit pas être accessible en écriture par l’utilisateur. Sinon, sshd refuse la session et journalise une ligne bad ownership or modes for chroot directory.

FTP reste utile lorsque l’autre partie ne peut pas changer de protocole. Les scanners et les imprimantes multifonctions sont livrés avec un firmware qui prend en charge FTP, et rien d’autre. Les équipements de laboratoire et les systèmes industriels utilisent souvent une image figée que personne ne fera recertifier. Des partenaires commerciaux publient un point de dépôt FTPS et ne veulent pas ajouter un protocole pour un seul fournisseur. Dans chacun de ces cas, la plage passive et la règle de firewall correspondante constituent l’essentiel du travail. Il faut utiliser FTPS plutôt que FTP en clair. La conception à deux canaux date de 1980. Elle figurait déjà dans la RFC 765, cinq ans avant que la RFC 959 ne la reprenne. Elle fonctionne aujourd’hui dans un environnement qu’elle n’avait pas prévu, comme l’explique l’histoire des protocoles de transfert de fichiers.

FAQ

Pourquoi FTP se connecte-t-il, mais le listing du répertoire reste-t-il bloqué ?

La connexion utilise uniquement la connexion de contrôle sur le port 21, que votre pare-feu autorise. Le listing du répertoire nécessite une deuxième connexion TCP sur un autre port, et cette connexion est bloquée. Déclarez une plage de ports passifs sur le serveur FTP, ouvrez la même plage dans le pare-feu, puis le listing s’effectue correctement. Un listing bloqué indique un problème de canal de données, jamais un problème de mot de passe.

Quels ports dois-je ouvrir pour le mode passif FTP ?

Le port 21 pour le canal de contrôle, ainsi que la plage configurée pour les connexions de données passives. Il n’existe pas de plage standard, car vous la choisissez. Une plage de 30000 à 30099 convient, par exemple : dimensionnez-la en fonction du nombre maximal de transferts simultanés et évitez la plage de ports sortants du noyau, que vous pouvez lire avec sysctl net.ipv4.ip_local_port_range. Si votre fournisseur propose un pare-feu réseau dans son panneau de contrôle, ouvrez également cette plage à cet endroit.

Ai-je encore besoin de nf_conntrack_ftp ?

Non, et vous ne pouvez pas compter dessus avec un noyau récent. L’affectation automatique des helpers est désactivée par défaut et, depuis Linux 6.0, le commutateur net.netfilter.nf_conntrack_helper a été supprimé ; sysctl indique donc que le fichier n’existe pas. Le manuel d’ufw considère le chargement inconditionnel de ces modules comme obsolète, et firewalld ignore complètement AutomaticHelpers. Un helper doit également lire le canal de contrôle en texte brut ; il cesse donc de fonctionner dès que vous activez FTPS. Déclarez plutôt une plage de ports passifs.

Pourquoi mon client FTP indique-t-il que la réponse passive contient une adresse non routable ?

Le serveur a répondu à PASV avec l’adresse qu’il voit sur sa propre interface, et cette adresse est privée. Cela se produit lorsque la plateforme associe une adresse publique à une adresse privée. Définissez explicitement l’adresse publique : pasv_address dans vsftpd, MasqueradeAddress dans ProFTPD ou -P dans Pure-FTPd. FileZilla contourne ce problème en réutilisant l’adresse à laquelle il s’est déjà connecté et journalise « Using server address instead ». C’est pourquoi certains clients fonctionnent malgré cette mauvaise configuration, tandis que d’autres restent bloqués.

Dois-je utiliser FTPS ou SFTP ?

Utilisez SFTP lorsque vous contrôlez les deux extrémités : une seule connexion SSH sur le port 22, aucun canal de données à ouvrir, et le service est déjà disponible. FTPS est du FTP sur TLS ; il conserve donc la conception à deux canaux et tous les problèmes de pare-feu qui en découlent. Choisissez-le lorsque l’autre extrémité ne prend en charge aucun autre protocole. N’utilisez pas FTP en clair sur Internet, car le mot de passe traverse le réseau sous forme de texte lisible.