Accès FTP
Spektrum héberge un service FTP/FTPS dédié pour ses clients qui ont besoin de déposer des médias (images, documents) consommés par leurs applications. Le cas d'usage typique: un ERP ou un service externe pousse des fichiers en FTP, et l'application Spektrum les récupère pour les intégrer.
Architecture
Pour les mêmes raisons d'isolation que le reste de l'infrastructure (cf. Cloud Layout), chaque environnement (staging, production) a sa propre VM FTP dédiée avec une IP publique propre.
| Serveur | Hostname | IP publique | Sous-réseau |
|---|---|---|---|
| FTP-Staging | ftp.staging.spektrum-suisse.ch | 84.234.27.226 | internal |
| FTP-Prod | ftp.prod.spektrum-suisse.ch | 83.228.249.248 | production |
Pourquoi pas derrière le bastion NGINX ?
Le protocole FTP ne peut pas être reverse-proxifié par NGINX (ni par NPM). FTP n'a pas de header Host et négocie IP+port dans la connexion de contrôle pour le mode passif. Donc chaque serveur FTP est exposé directement sur sa propre IP, sans passer par les bastions HTTP/S.
Stack technique
| Composant | Choix |
|---|---|
| OS | Ubuntu 24.04 LTS |
| Container | delfer/alpine-ftp-server (vsftpd Alpine) |
| Mode | FTPS Explicit (TLS sur port 21 via AUTH SSL) |
| Cert | Let's Encrypt via certbot HTTP-01 standalone |
| Mode connexion | Passive uniquement, plage 21000-21100 |
| Renouvellement | certbot.timer + deploy hook qui restart le container |
| TODO DNS Challenge régler l'issue |
Ports
| Port | Usage | SG / UFW |
|---|---|---|
21/tcp | FTP control (TLS négocié dessus via AUTH) | Ingress public |
21000-21100/tcp | FTPS passive data | Ingress public |
80/tcp | Cert Let's Encrypt (renouvellement HTTP-01) | Ingress public |
Port 80 ouvert sans risque
Le port 80 reste ouvert en permanence pour les renouvellements certbot, mais rien n'écoute dessus entre les renouvellements. La connexion est refusée par le kernel - pas de surface d'attaque.
Stockage - 1 volume par client
Chaque client qui consomme le FTP a son propre volume cloud Infomaniak rattaché à la VM FTP, monté directement sur son dossier de données. Cela permet:
- D'isoler les données entre clients (un client ne peut pas remplir l'espace d'un autre)
- D'agrandir/restaurer indépendamment chaque volume
- De détacher proprement le volume si on déplace un client ailleurs
Layout
/ ← rootfs (sda1, ~20G)
└── data/ftp/ ← rootfs
├── docker-compose.yml ← rootfs
├── .env (chmod 600, contient les users + mots de passe)
├── certs/{fullchain,privkey}.pem ← rootfs (copiés depuis Let's Encrypt)
└── data/
├── the-swiss-collector/ ← /dev/sdb (volume dédié client A)
├── hopital-vs/ ← /dev/sdc (volume dédié client B)
└── client-suivant/ ← /dev/sdd (volume dédié client C)Persistance
Les volumes sont montés via LABEL dans /etc/fstab:
LABEL=tsc-ftp-prod /data/ftp/data/the-swiss-collector ext4 defaults,nofail 0 2
LABEL=hvs-ftp-prod /data/ftp/data/hopital-vs ext4 defaults,nofail 0 2Ordre du chown
Toujours faire le chown APRÈS le mount. Sinon, les permissions sont sur le dossier sous-jacent du rootfs, pas sur le filesystem du volume - et elles disparaissent au montage.
Authentification
Les utilisateurs FTP sont déclarés dans le fichier .env du serveur (chmod 600):
FTP_ADDRESS=ftp.spektrum-suisse.ch
FTP_USERS=the-swiss-collector|<password>|/home/vsftpd/the-swiss-collector hopitalvs|<password>|/home/vsftpd/hopital-vsFormat: username|password|home_path, séparés par des espaces. Chaque user est confiné en chroot dans son home_path, qui correspond au volume monté pour ce client.
Mot de passe par client
Génère un mot de passe robuste pour chaque client:
openssl rand -base64 24Ne jamais réutiliser un mot de passe entre staging et production.
Ajouter un nouveau client
- Créer / attacher un volume dans Manager Infomaniak Public Cloud (taille selon besoin)
- Sur la VM FTP, formater et monter:bash
sudo mkfs.ext4 -L <client>-ftp-<env> /dev/sdX sudo mkdir -p /data/ftp/data/<client> sudo mount /dev/sdX /data/ftp/data/<client> echo "LABEL=<client>-ftp-<env> /data/ftp/data/<client> ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab sudo mount -a - Ajouter le user dans
/data/ftp/.env:envFTP_USERS=...précédents... <client>|<password>|/home/vsftpd/<client> - Mettre à jour le
docker-compose.ymlpour binder le nouveau volume:yamlvolumes: - /data/ftp/data/<client>:/home/vsftpd/<client> - Redémarrer:bash
docker compose up -d - Communiquer au client: hostname, port (21), user, password, mode FTPS Explicit + Passif.
Connexion administrateur (SSH)
L'accès admin à la VM FTP se fait via le serveur Bastion correspondant, comme toute autre VM
Username + clé privée
Les accès sont les mêmes que partout: username LDAP + clé privée. Cf. Accès.
Renouvellement automatique du certificat
Le hook /etc/letsencrypt/renewal-hooks/deploy/ftp.sh recopie les certs renouvelés et redémarre le container:
#!/bin/bash
cp /etc/letsencrypt/live/<hostname>/fullchain.pem /data/ftp/certs/
cp /etc/letsencrypt/live/<hostname>/privkey.pem /data/ftp/certs/
docker restart ftp-serverLe timer certbot.timer tourne 2x par jour et déclenche le renouvellement quand il reste <30 jours sur le cert. Vérification:
sudo systemctl list-timers certbot.timer
sudo certbot renew --dry-runCôté application qui consomme le FTP
Côté NopCommerce (et autres clients .NET), utiliser FluentFTP avec:
new FtpClient(host, new NetworkCredential(user, pass), port: 21) {
Config = {
EncryptionMode = FtpEncryptionMode.Explicit,
DataConnectionEncryption = true,
DataConnectionType = FtpDataConnectionType.AutoPassive,
ConnectTimeout = 15_000,
DataConnectionConnectTimeout = 15_000,
SocketKeepAlive = true
}
};Mode passif obligatoire
En mode actif, le serveur FTP essaie de se connecter au client sur un port qu'il a annoncé - bloqué par tous les NAT modernes. Toujours AutoPassive.
Test de connexion (debug)
Depuis n'importe quel poste:
curl -v --ssl-reqd \
--user "<user>:<password>" \
ftp://<hostname>/Si tu vois 230 Login successful + un listing → tout fonctionne.
Sur Windows / cmd.exe
Utilise des guillemets doubles ("foo:bar"), pas simples - cmd.exe ne traite pas les ' comme délimiteurs de chaîne et te file un user/pass invalide:
curl -v --ssl-reqd --user "client:password" ftp://ftp.spektrum-suisse.ch/Migration depuis un ancien serveur FTP
Voir rsync via ProxyJump - le pattern réutilisable pour transférer les données existantes vers le nouveau serveur sans recopier via le FTP lui-même (beaucoup plus rapide).

