Erreurs SFTP : diagnostiquer connexion, authentification et permissions serveur

Diagnostiquez rapidement les erreurs SFTP en identifiant la couche impactée (réseau, SSH, SFTP, système de fichiers) et en suivant un playbook reproductible de logs et commandes.

Table des matières :

  1. Cartographier une erreur SFTP : à quel étage ça casse ?
  2. Connexion : DNS, port, firewall, NAT, bannissement automatique
  3. Négociation SSH : empreinte, algorithmes, compatibilité OpenSSH
  4. Authentification : mot de passe vs clés, droits sur .ssh et erreurs piégeuses
  5. Sous-système SFTP : internal-sftp, chroot, “Subsystem request failed”
  6. Permissions serveur : POSIX, ACL, umask, SELinux/AppArmor (et cas PrestaShop)
  7. Méthode de diagnostic reproductible : logs, traces, et playbook en 10 minutes

Cartographier une erreur SFTP : à quel étage ça casse ?

Une erreur SFTP n’est jamais « une seule erreur ». Vous empilez au minimum : TCP (connectivité), SSH (négociation + authentification), SFTP (sous-système côté serveur), puis système de fichiers (droits, ACL, SELinux/AppArmor). Diagnostiquer efficacement consiste à identifier la couche impactée avant de toucher à sshd_config ou aux permissions du webroot.

Point de vocabulaire qui évite des malentendus (notamment entre équipes infra/dev et prestataires) :

  • SFTP ≠ FTPS : SFTP passe dans SSH, tandis que FTPS est FTP + TLS (autre port, autre serveur, autre logique).
  • SFTP n’est pas un service séparé dans la majorité des cas : c’est un subsystem lancé par sshd. Donc “SFTP en panne” est souvent “SSH en panne” ou “sshd mal configuré”.

Définition utile (à rappeler aux équipes qui mélangent tout) : le SFTP est le SSH File Transfer Protocol, il passe dans le tunnel SSH. Le manuel OpenSSH le formule sans ambiguïté : “sftp is a file transfer program, similar to ftp, which performs all operations over an encrypted ssh transport.” (man page sftp(1), OpenSSH). Conséquence directe : si ssh ne marche pas, SFTP ne marchera pas non plus.

Pour aller vite, classez le symptôme avec ce mapping (les libellés varient selon FileZilla/WinSCP/CLI) :

Message côté client Couche probable Première action utile
Connection timed out / No route to host Réseau / firewall nc -vz host 22, vérifier SG/NACL, fail2ban
Connection refused Service SSH down / port ss -lntp | grep :22, systemctl status ssh
Host key verification failed SSH (known_hosts) valider l’empreinte, gérer known_hosts
No matching host key type found / no matching key exchange method SSH (algos) mettre à jour client, ajuster algos côté serveur
Permission denied (publickey,password) Auth ssh -vvv, logs auth.log
Subsystem request failed on channel 0 SFTP subsystem Subsystem sftp ..., internal-sftp
Permission denied lors d’un put/get FS/ACL/SELinux namei -l, getfacl, context SELinux

Astuce “gain de temps” : si vous ne savez pas où vous en êtes, testez d’abord ssh, puis sftp. Un ssh utilisateur@host qui échoue vous évite de chercher côté permissions de répertoire alors que le problème est réseau, clé hôte, ou algorithmes.

Si vous êtes en train de déployer un thème/module PrestaShop et que vous confondez un 403 HTTP avec un Permission denied SFTP, ne mélangez pas les incidents : le 403 est côté serveur web. Pour le diagnostic 403, voir l’article dédié : Erreur 403 Forbidden : causes et dépannage étape par étape.

Connexion : DNS, port, firewall, NAT, bannissement automatique

Commencez par confirmer que vous parlez au bon hôte et au bon port. Sur votre poste :

getent hosts sftp.exemple.tld
nc -vz sftp.exemple.tld 22
# ou si port non standard
nc -vz sftp.exemple.tld 2222

nc (netcat) vous dit en 1 seconde si le port répond. Si ça time-out : firewall réseau, security group cloud, ACL OVH, IP non autorisée, ou bannissement. Si c’est refused : service SSH arrêté, ou écoute sur un autre port. Côté serveur, vérifiez :

ss -lntp | grep -E ':(22|2222)\b'
systemctl status ssh  # Debian/Ubuntu
journalctl -u ssh -S -10min

Compléments utiles quand le diagnostic “réseau” est ambigu :

  • DNS : un mauvais enregistrement (ou une migration) peut pointer vers une ancienne IP.
  • dig +short sftp.exemple.tld (si disponible) ou resolvectl query sftp.exemple.tld (systemd).
  • Vérifiez aussi IPv6 : un client peut tenter AAAA en priorité ; si l’IPv6 est mal filtré, ça ressemble à un time-out “mystère”.
  • Chemin réseau : un traceroute -T -p 22 host (ou mtr -T -P 22 host) peut révéler un filtrage en amont.
  • Windows (si vos équipes opèrent depuis un poste Windows) : Test-NetConnection sftp.exemple.tld -Port 22 donne rapidement l’état de la connectivité TCP.

Un point qui fait perdre des heures : fail2ban (ou un WAF/IDS du fournisseur) peut bannir votre IP après quelques essais. Dans ce cas la connexion peut alterner entre time-out et reset. Cherchez des lignes type Ban / Unban dans /var/log/fail2ban.log ou via fail2ban-client status sshd. C’est un “faux problème SFTP” : vous devez traiter la cause (mauvais login, rotation de clé) et/ou mettre l’IP en liste blanche (temporairement).

Mini-scénario (fréquent en agence / équipe distribuée) : vous administrez une boutique hébergée chez OVHcloud, Scaleway ou en région Paris (eu-west-3) ; vous testez depuis un coworking ou une box grand public. Le port 22 peut être bloqué en sortie par une politique réseau (entreprise, proxy) ou par un filtrage local. Dans ce cas, un bastion sur un port autorisé (ou un VPN d’entreprise) est souvent plus réaliste que “ouvrir partout”.

Enfin, sur des architectures modernes (reverse-proxy, bastion, Kubernetes), le « serveur SFTP » n’est parfois qu’un jump host. Si vous êtes en migration ou en GitOps, documentez clairement le chemin réseau (bastion -> nœud) et évitez les accès directs depuis des IP “de bureau”. Le check-list infra et les pièges d’archi sont très proches de ceux décrits dans Migration PrestaShop vers Kubernetes : checklist GitOps et pièges courants (même si ici on parle SSH/SFTP plutôt qu’Ingress).

Négociation SSH : empreinte, algorithmes, compatibilité OpenSSH

Quand le port répond mais que SFTP échoue, passez en mode verbeux. Côté client (Linux/macOS) :

ssh -vvv [email protected] -p 22
sftp -vvv -P 22 [email protected]

Le -vvv vous montre exactement où ça casse : échange de clés (KEX), choix du cipher, validation de l’hôte, etc. Si vous tombez sur Host key verification failed, ce n’est pas “un bug”. C’est known_hosts qui protège contre l’homme-du-milieu. En CI/CD, on gère ça proprement (pinning d’empreinte), pas en désactivant la vérification.

Deux commandes pratiques pour “objectiver” une empreinte (utile en runbook, et quand un prestataire vous envoie une empreinte à la main) :

ssh-keyscan -p 22 sftp.exemple.tld | ssh-keygen -lf -
# ou vérifier une clé hôte fournie (fichier):
ssh-keygen -lf /chemin/vers/ssh_host_ed25519_key.pub

Les erreurs du type no matching key exchange method ou no matching host key type found sont fréquentes quand :

  • le serveur a durci sa config (désactivation de RSA SHA-1, DSA, vieux KEX)
  • votre client est trop vieux (VM Windows, appliance, outil legacy)

SSH évolue et c’est normal. Le cadre général est celui des RFC : “The Secure Shell (SSH) Protocol provides secure remote login and other secure network services over an insecure network.” (RFC 4251, IETF, https://www.rfc-editor.org/rfc/rfc4251). Quand serveur et client n’ont plus d’intersection d’algorithmes, la connexion s’arrête avant toute authentification.

Côté serveur OpenSSH, vérifiez la version et la politique d’algos :

sshd -T | egrep -i 'kexalgorithms|ciphers|macs|hostkeyalgorithms'
ssh -Q kex
ssh -Q key

Précaution : si vous “réactivez” des algorithmes obsolètes pour dépanner un client legacy, faites-le de manière temporaire et ciblée (par IP ou par utilisateur, via un Match Address / Match User), et documentez un plan de sortie (mise à jour client, remplacement outil). En environnement e-commerce exposé, la bonne trajectoire est généralement : mettre à jour le client (WinSCP, FileZilla, libssh2) ou passer par un bastion compatible.

Pour la doc officielle OpenSSH (référence pratique quand on doute d’une option), la page sshd_config d’OpenBSD est très fiable : https://man.openbsd.org/sshd_config

Authentification : mot de passe vs clés, droits sur .ssh et erreurs piégeuses

L’erreur la plus répandue reste Permission denied (publickey,password). Avant de réinitialiser le mot de passe « au hasard », lisez les logs serveur. Selon distro :

# Debian/Ubuntu
sudo tail -n 200 /var/log/auth.log
# RHEL/Alma/Rocky
sudo tail -n 200 /var/log/secure
# systemd
sudo journalctl -u ssh -S -30min

Cherchez des motifs clairs : Failed password, Invalid user, Too many authentication failures, User ... not allowed because listed in DenyUsers, ou encore l’incontournable : Authentication refused: bad ownership or modes for directory .... Cette dernière indique que les permissions POSIX du home (ou de ~/.ssh) sont trop permissives. Typiquement :

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~
chown -R user:user ~/.ssh

Pensez aussi aux causes “non SSH” qui finissent en échec SSH :

  • Compte verrouillé / mot de passe expiré (courant après une politique de rotation) : passwd -S user et chage -l user (selon distributions).
  • Shell interdit / compte sans shell : si vous forcez du SFTP-only, c’est OK, mais si vous attendez un shell et qu’il est désactivé (/usr/sbin/nologin), certains outils peuvent se comporter différemment.
  • Méthodes d’auth : un serveur peut accepter publickey mais refuser password (PasswordAuthentication no), ou exiger du MFA / keyboard-interactive via PAM. Le client affichera parfois un message générique ; les logs serveur tranchent.

Si vous utilisez des clés, validez la chaîne complète : clé privée chargée dans l’agent (ssh-add -l), clé publique présente dans authorized_keys, et le bon utilisateur ciblé (une erreur classique est de pousser la clé sur root puis de tester avec deploy). En CI/CD, vous devez aussi gérer known_hosts et éviter les prompts interactifs. Sur ce point, le pattern “déploiement par job” est très proche de ce qui est détaillé dans PrestaShop CI/CD : générer des démos à la demande en minutes : le SFTP devient une dépendance à stabiliser (clés, secrets, rotation, logs).

Dernier piège : Too many authentication failures peut apparaître même avec la bonne clé si votre agent présente trop de clés et que le serveur coupe après MaxAuthTries. Forcer la clé côté client règle le problème :

ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes user@host

Sous-système SFTP : internal-sftp, chroot, “Subsystem request failed”

Si ssh fonctionne mais sftp échoue avec Subsystem request failed on channel 0, vous n’êtes plus dans l’auth : le serveur SSH ne lance pas (ou n’autorise pas) le sous-système SFTP. Dans OpenSSH, c’est déclaré dans sshd_config via Subsystem. Par défaut on voit souvent :

Subsystem sftp /usr/lib/openssh/sftp-server
# ou (recommandé)
Subsystem sftp internal-sftp

internal-sftp évite des dépendances de chemin et simplifie le chroot. Il permet aussi (utile en exploitation) d’ajouter des options de log et d’umask. Exemple fréquent :

Subsystem sftp internal-sftp -f AUTH -l INFO

Ce réglage remonte les événements SFTP dans syslog/journal (selon distro), ce qui aide à différencier un échec “auth” d’un échec “permissions” quand les clients ne remontent pas les détails.

La doc OpenSSH rappelle aussi une contrainte non négociable dès que vous chroot-ez un utilisateur : “At session startup sshd checks that all components of the pathname are root-owned directories which are not writable by group or others.” (man page sshd_config(5), OpenSSH). C’est exactement la source de beaucoup de “ça marchait hier” après un chown malheureux.

Checklist simple quand vous suspectez le chroot :

  • sshd -T | grep -i chroot pour voir la config effective (y compris les Match).
  • vérifier que chaque composant du chemin de ChrootDirectory est root:root et non group-writable/other-writable.
  • s’assurer qu’un dossier interne (ex. upload/) est, lui, writable par l’utilisateur.

Exemple minimal de configuration SFTP-only (attention : à tester sans fermer votre session SSH active, et valider sshd -t avant reload) :

Match User sftpuser
  ForceCommand internal-sftp
  ChrootDirectory /var/sftp/%u
  PasswordAuthentication yes
  PermitTunnel no
  AllowAgentForwarding no
  X11Forwarding no
  PermitTTY no

Puis, côté filesystem : le répertoire de chroot doit être root:root et non writable, et vous créez un sous-dossier writable pour l’utilisateur :

mkdir -p /var/sftp/sftpuser/upload
chown root:root /var/sftp/sftpuser
chmod 755 /var/sftp/sftpuser
chown sftpuser:sftpuser /var/sftp/sftpuser/upload
chmod 755 /var/sftp/sftpuser/upload

Si vous faites du SFTP pour échanger des flux (ERP, EDI), ce modèle est plus robuste qu’un accès shell complet. Pour le contexte “SFTP dans des échanges inter-systèmes”, voir Intégration ERP : standards EDI, EDIFACT, AS2, SFTP et PEPPOL.

Permissions serveur : POSIX, ACL, umask, SELinux/AppArmor (et cas PrestaShop)

Les erreurs Permission denied au moment d’un put/rename sont 90% du temps du POSIX pur : mauvais owner, groupe incorrect, bit d’écriture absent sur un répertoire parent. La commande la plus rentable est namei, parce qu’elle déroule toute la chaîne de dossiers :

namei -l /var/www/prestashop/img
ls -ld /var/www/prestashop /var/www/prestashop/img

Sur une boutique PrestaShop (8.1.x ou 9.x) sous PHP-FPM (PHP 8.2/8.3), vous avez souvent deux identités en jeu : l’utilisateur SFTP (deploy, sftpuser) et l’utilisateur d’exécution web (www-data, php-fpm). Si vous uploadez des fichiers via SFTP en deploy:deploy mais que PHP écrit en www-data:www-data, vous créez vite des conflits sur var/cache/, img/, modules/ ou themes/.

Repère concret (à adapter à votre stack et à vos pratiques de déploiement) : les dossiers qui posent le plus souvent problème lors d’une mise à jour / install de module sont :

  • var/ (cache, logs)
  • img/ (générations d’images, uploads selon modules)
  • modules/ (installation / upgrade)
  • themes/ (si vous déployez un thème par SFTP)
  • download/ et upload/ (selon configuration et modules)

Le correctif propre n’est pas chmod -R 777 (c’est une régression sécurité), mais une stratégie de groupe :

  • un groupe commun (ex. shop)
  • chgrp -R shop sur les répertoires devant être modifiables
  • chmod -R g+rwX et setgid sur les dossiers (bit 2) pour conserver le groupe
groupadd shop
usermod -aG shop deploy
usermod -aG shop www-data
chgrp -R shop /var/www/prestashop/var /var/www/prestashop/img
chmod -R g+rwX /var/www/prestashop/var /var/www/prestashop/img
find /var/www/prestashop/var /var/www/prestashop/img -type d -exec chmod g+s {} \;

Deux détails “perms” qui expliquent des comportements incohérents :

  • umask : selon le serveur SFTP (et sa config), les fichiers uploadés peuvent sortir en 0644 ou 0664. Avec internal-sftp, vous pouvez contrôler l’umask côté serveur via -u (ex. -u 0002 pour favoriser l’écriture groupe). À utiliser si votre modèle “groupe commun + setgid” est votre standard.
  • rename / atomicité : certains clients uploadent en fichier temporaire puis font un rename. Si le répertoire autorise l’écriture mais pas la suppression/rename (droits sur le dossier), vous verrez des erreurs lors de la finalisation plutôt qu’au début de transfert.

Si malgré des perms correctes vous avez encore des refus, regardez les ACL et les contrôles Mandatory Access Control. ACL :

getfacl -p /var/www/prestashop/img | sed -n '1,120p'

SELinux (RHEL-like) : un contexte bloquant donne souvent un “permission denied” sans logique POSIX. Vérifiez getenforce et les logs (ausearch -m avc -ts recent). AppArmor (Ubuntu) : profils sshd ou php-fpm peuvent aussi limiter.

Enfin, n’écartez pas les “faux Permission denied” : un système de fichiers monté en lecture seule (incident stockage), un quota dépassé, ou un disque plein se traduisent souvent par des erreurs côté client qui ressemblent à des refus d’écriture. Vérifications rapides :

df -h
mount | grep -E ' /var| /var/www'

Ce sont des sujets de durcissement : si vous cherchez une méthode plus globale, l’article Symfony : sécuriser un serveur PHP avec hardening et gestion CVE donne des repères utiles (même si l’exécution n’est pas “Symfony-only”).

Méthode de diagnostic reproductible : logs, traces, et playbook en 10 minutes

Côté client, ne restez pas sur l’UI. Les logs FileZilla/WinSCP sont utiles, mais ssh -vvv et sftp -vvv sont la source de vérité pour comprendre la négociation et l’auth. Conservez ces traces dans votre ticket (sans secrets). Ajoutez systématiquement : IP source, hostname, port, user, type d’auth (password/key), et horodatage.

Côté serveur, standardisez un playbook minimal :

1) journalctl -u ssh -S -15min (ou auth.log) pour corréler la tentative
2) sshd -T | less pour voir la config effective (incluant Match)
3) ss -lntp pour confirmer l’écoute
4) namei -l + getfacl sur le chemin cible si c’est un problème d’écriture

En pratique, vous pouvez “industrialiser” ce playbook avec 3 recherches rapides dans les logs (sans forcément ajouter d’outils) :

  • Auth échoue : grep -E "Failed password|Invalid user|Authentication refused|Too many authentication failures" /var/log/auth.log
  • SFTP subsystem/chroot : grep -E "subsystem request for sftp|internal-sftp|chroot" /var/log/auth.log
  • Bannissement : grep -E "Ban|Found|Unban" /var/log/fail2ban.log

Ce playbook s’intègre bien à une démarche plus large de dépannage prod. Si vous avez déjà un référentiel d’incidents, vous pouvez l’adosser à Dépannage boutique en ligne : 30 pannes classées par symptôme pour éviter que “SFTP en panne” devienne le fourre-tout de tous les problèmes d’exploitation.

Enfin, évitez le diagnostic en aveugle : centralisez les logs SSH (auth + fail2ban) dans un moteur de recherche de logs si vous avez du volume et des accès distribués. Si vous êtes déjà équipé sur l’écosystème OVH, l’approche décrite dans OVHcloud Managed OpenSearch : moteur NoSQL managé pour logs et recherche s’applique directement : un dashboard “Failed password / Invalid user / Subsystem failed” réduit drastiquement le MTTR, et vous permet de distinguer incident réel vs bruit (scans bots sur le port 22).



À lire aussi