Docker et Portainer part 2 – Stack vsftpd mono-image

Docker et Portainer

Salut à tous, dans le dernier TP on s’est penché sur docker et plus particulièrement son intégration avec Portainer. Aujourd’hui on va regarder comment déployer une Stack vsftpd dans Portainer. L’objectif étant d’avoir un serveur (VS)FTP accessible en read-only pour quelques utilisateurs à la fin du TP.

Rappel des articles de la série « Docker et Portainer » :

Les stacks docker

Les stacks sont une de mes fonctions préférées de docker, elles permettent de définir la configuration d’une ou plusieurs image dans un fichier docker-compose.yml. En effet, si un image docker permet de configurer un système, un logiciel ou encore un ensemble d’application dans un conteneur ; une image n’est pas adaptée à la configuration qui varie entre chaque utilisateur de l’image : création de comptes utilisateur, gestion des variables d’environnements, gestion du réseau, volumes, logging, etc.

C’est là qu’intervient la stack et le fichier docker-compose.yml, qui permettent de spécifier ces éléments pour une image (en accord avec ce qui est prévu par l’image).

Je vais beaucoup utiliser les snacks dans la suite des TP sur docker et Portainer car elles permettent :

  • d’industrialiser facilement les déploiements des conteneurs ;
  • de conserver la configuration de vos images dans un fichier simplifiant backup et mise à jour ;
  • de lier entre-elles plusieurs images dans un seul fichier de configuration.

Dans ce TP je vais vous présenter une stack « mono-conteneur » pour introduire le concept simplement. Vous verrez par la suite que c’est très utile (indispensable) lorsque vos images deviennent complexes (au hasard, un site WordPress avec un nginx, un php, une DB et un serveur de cache qui doivent tous se parler…).

La Stack VSFTPD

Un bon exemple valant toujours mieux qu’un long discours on va s’attaquer à notre Stack VSFTPD sans transition. Mon objectif était d’avoir à la fin un serveur FTP avec les caractéristiques suivantes :

  • Plusieurs utilisateurs ;
  • Accès FTPS sécurisé par certificats ;
  • accès aux logs ; et
  • un répertoire commun à tous les users, en lecture seule.

Trouver un image VSFTPD ou en construire une ?

La première chose à faire avant de construire notre stack c’est déjà de trouver l’image docker qu’on va faire tourner, deux options :

  1. on part de zéro et on construit notre propre image « from scratch » ; ou
  2. on repart d’un image existante sur le hub docker (en vérifiant dans les sources qu’elle ne fait pas n’importe nawak), et si nécessaire en la customisant pour nos besoins.

C’est cette seconde options que j’ai pris, donc direction hub.docker.com et rechercher vsftpd (382 images trouvées…). Après il faut prendre un peu de temps pour regarder ce que fait chaque image.

J’ai fini par m’arrêter sur celle-ci panubo/vsftpd qui répondait à mon besoin et pour lesquels j’ai trouvé que les sources étaient propre.

Construire une stack vsftpd.

Bon alors la base d’un fichier de stack dans portainer c’est :

version: 2
services:
  vsftpd:

Après il faut définir les éléments qu’on veut mettre en place dans notre Stack. on va commencer par le minimum, l’image du conteneur à utiliser, les noms et le comportement à avoir en cas d’arrêt :

version: "2"
services:
  vsftpd:
    image: panubo/vsftpd:latest
    container_name: vsftpd
    hostname: vsftpd
    restart: always

Maintenant, on peut passer aux aspects réseaux, le bloc ports nous permet de spécifier les ports qui seront exposés à l’extérieur du réseau privée docker :

  ports:
    - 21:21
    - 4559-4564:4559-4564

On passe ensuite à la persistance des données maintenant en définissant nos volumes (rappels sur les volumes dans la partie 1) :

  volumes:
    - "vsftpd_cert:/etc/ssl:ro"
    - "shared_ftp:/srv:ro"
    - "vsftpd_logs:/var/log"
    - "vsftpd_conf:/etc/vsftpd"

Notez qu’on a pas besoin de créer les volumes s’ils n’existent pas, docker/portainer s’en occupera tout seul à l’instanciation si besoin. Remarquez aussi le :ro en fin de ligne pour spécifier que le volume est en read only pour le conteneur.

Maintenant il nous reste un peu de configuration à faire pour créer les utilisateurs et mettre en place le chiffrement des communications. Pour les utilisateurs, l’image de panubo passe par des variables d’environnement dans le docker.

  environment:
    - FTP_USER_1=toto:$$6$$R3h09ywl$$QBf0HtgHgwDejoruGviab1Ogo4PiGrXbXSSM4Hx4zu3ZnfreHeDEjxxSPMexod0G/ekznFmSyswVwQvKDTcO/
    - FTP_USER_2=tata:$$6$$ZE.GMXwY9mFaCDQ$$b5vfJ5ThglxZmjZbyibsaQulIS0rxszUp9DHZ8mQi2zwrfYFGhtg.x7QKP/ANdx.FQiY8UMGzKCHPN1lnGvq1

Et le hash du mot de passe est à générer avec la commande suivante :

mkpasswd -m sha-512 monSuperMot2Passe
$6$HsHpgRecjOX13/$wc5ExJnkcwEaIshtgp5OmRXqT7ORTrC0ZSsvAxPV0kqAK4kUmdZBsEWFFvG9BqxnLgZRc/c4rNs4rA3aedikF.

Notez qu’il faut doubler les $ (pour les échapper) dans le fichier par rapport au retour de la commande.

Mettre en place le chiffrement FTPS

Il nous reste ce dernier point à mettre en place. Pour ça il faut d’abord générer un certificat et sa clé privée :

openssl genrsa -out vsftpd.key 2048
openssl rsa -in vsftpd.key -pubout -out vsftpd.crt

Ici vous allez avoir besoin de créer le volume vsftpd_certs avant de lancer votre stack pour placer les certificats dedans. Pour cela rendez-vous dans Volume-> Create dans Portainer.

Stack vsftpd

A partir de là, sur votre hôte vous trouverez dans /var/lib/docker/volumes/vsftpd_cert/_data/ votre volume. IL vous faut alors y créer les dossiers suivants et déplacer vos certificats dedans ainsi.

mkdir /var/lib/docker/volumes/vsftpd_cert/_data/certs
mkdir /var/lib/docker/volumes/vsftpd_cert/_data/private
mv vsftpd.crt /var/lib/docker/volumes/vsftpd_cert/_data/certs
mv vsftpd.key /var/lib/docker/volumes/vsftpd_cert/_data/private

Il faut ensuite changer la commande de démarrage de notre image vsftpd pour utiliser le fichier de configuration « ssl » comme documenté par le projet. Et pour cela vous devez rajouter la ligne suivante dans votre docker compose.

   command: "vsftpd /etc/vsftpd_ssl.conf"

Stack vsftpd : docker-compose.yml

Au final vous devriez vous retrouver avec un fichier comme ça :

version: "2"
services:
  vsftpd:
    image: panubo/vsftpd:latest
    container_name: vsftpd
    hostname: vsftpd
    command: "vsftpd /etc/vsftpd_ssl.conf"
    restart: always
    environment:
      - FTP_USER_1=toto:$$6$$R3h09ywl$$QBf0SHtgHgwDejoSBDviab1Ogo4PiGrXbXSSM4Hx4zu3Znfdl2eDEjxxSPMexod0G/ekznFmSyswVwQvKDTcO/
      - FTP_USER_2=tata:$$6$$ZE.GMXwY9mFaCDQ$$b5vfJ5rVBlxZmjZbyibsaQulIS0rxszUp9DHZ8tmQi2zwrfYFarqg.x7QKP/ANdx.FQiY8UMGzKCHPN1lnGvq1
    ports:
      - 21:21
      - 4559-4564:4559-4564
    volumes:
      - "vsftpd_cert:/etc/ssl:ro"
      - "shared_ftp:/srv:ro"
      - vsftpd_logs:/var/log
      - vsftpd_conf:/etc/vsftpd

Il faudra copier cette Stack dans Portainer dans Stack-> Add Stack.

Stack vsftpd

Et c’est tout, vous cliquez sur Deploy the stack et vous devriez avoir un beau serveur vsftpd tout bien configuré qui sera accessible sur le port 21 et en mode « passif » sur les ports 4559 à 4564.

Vous pouvez tester avec un filezilla par exemple en configurant dans le gestionnaire de site :

Conclusion

Voilà c’est tout pour aujourd’hui, on a eufleré ici la construction d’une stacks docker. J’aurais aussi pu vous parler du networking de vos conteneurs ou de la manière dont on peut remonter les logs des conteneurs dans un Splunk, mais ce sera pour la suite.

Dans les prochains TP on verra comment modifier une image docker à partir d’une image existante, et surtout comment déclarer et lier plusieurs conteneurs dans un même Stack.

A bientôt pour la suite et geekez bien (et chez vous donc) d’ici là.

5 commentaires on “Docker et Portainer part 2 – Stack vsftpd mono-image

  1. Bonjour,

    Merci pour cette série de tuto qui va me servir à me lancer dans une utilisation plus réfléchie et mieux organisée.
    est-ce que cela demande de l’adaptation pour la mise en place sur un raspberry ?
    j’ai adapté l’installation de docker / portainer, c’est fonctionnel. Tout semble s’être bien passé également pour vsftpd mais j’ai une erreur à la connexion :
    Statut : Échec de la tentative de connexion avec « ECONNREFUSED – Connexion refusée par le serveur ».
    Erreur : Impossible d’établir une connexion au serveur

    tout est en local, pas de nom de domaine, filezilla résout bien le nom (j’avais essayé via l’IP aussi)
    Les tests avec l’assistant de configuration réseau ne remonte pas de soucis côté filezilla…

    c’est la première fois que j’essaie de créer un ftp sur le raspberry, je ne sais pas où peux se situer le soucis ni comment troubleshooter mieux que ça

    Est-ce que vous auriez une idée ?

    • Hello Corly,
      Je n’ai jamais testé Docker sur ARM et RasPi mais si c’est supporté il n’y a pas de raisons que ça fonctionne différemment.
      Pour ton « ECONNREFUSED » : ça signifie que la connexion TCP n’arrive pas à s’établir (TCP SYN en « reject »). Tu peux avoir plusieurs causes qu’il faut contrôler :

      • Déjà, est-ce que ton client interroge le serveur en IPv4 ou en IPv6, ça dépendra de ton enregistrement DNS notamment si tu ne tape pas l’adresse IPv4 en dur dans ton client FTP.
      • Ensuite, est-ce que le port est ouvert localement (en ipv4 ou en IPv6), tu peux le vérifier sur ton hôte avec un « netstat -tunlp« , le port 21 devrait être en écoute.
      • Si le port est ouvert, tu as encore le firewall à contrôler aussi : j’ai écrit un article sur iptables si besoin (ici), mais en gros c’est pareil : le port 21 (et les ports passif) que tu as configuré doit être ouvert.
      • Méfie toi au passage du iptables restore et de docker qui jardine beaucoup dans les règles iptables pour naté les connexions pour les conteneurs créés il faut vérifier que ton port 21 est bien routé vers ton hôte docker vsftpd.
      • Après tu as la conf vsftpd et notamment la partie IPV6, dès que ton conteneur est « up » il faut contrôler les paramètres suivants dans le fichier vsftpd.conf, (ici une conf ipv4) :
        listen=YES
        listen_ipv6=NO
        Note que vsftpd ne supporte pas les 2 piles IP simultanément : il faut choisir entre V4 et V6.
      • enfin tu as le même support pour ipv6 à activer ou non pour docker, en fonction de ton infra dans /etc/docker/daemon.json:
        « ipv6 »: true,

      Voilà pour mes pistes à froid, je me rappel avoir un peu galérer avec le support ipv6 sur le serveur mais rien d’insurmontable dans mes souvenirs. Hésites pas à nous dire ce que c’était quand du aura trouvé. @+

      • Salut,
        merci pour la réponse.
        j’avoue avoir laissé un peu tomber pour le moment mais je me note ça, je vérifie au plus vite et je fais un retour 🙂

  2. Bonjour,
    Merci pour cette série d’articles très bien faits.
    Suite à une légère baisse de glycémie lors de la rédaction, vous avez glissé ce magnifique lapsus: « Je vais beaucoup utiliser les snacks dans la suite des TP sur docker « … savoureux.
    Et le lien vers la partie 3 renvoie sur une erreur 404, ce qui est bien dommage.

    Encore merci et bravo

    • Hello,
      Merci pour les infos, je crois que je vais laisser le snack finalement : on ne sait jamais si j’ai un petit creux je ferai un coup de : docker snack compose-salade.yml qui sait ? 🙂
      Je corrige les liens de ce pas (edit: c’est fait), merci pour l’info et @+

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.