Installation de Docker et de Traefik sur un NAS ASUSTOR

Installation de Docker et de Traefik sur un NAS ASUSTOR

La problématique est la suivante. Je souhaite un reverse proxy permettant d’accéder de l’extérieur (internet) à toutes les applications internes, sans ouvrir de ports. La solution c’est le reverse proxy. Asus Data Master (ADM pour les intimes) a bien un outil appelé reverse proxy qui permet d’accéder en https à une application ou un service tournant en interne de façon non sécurisée (http), mais pour ceci il faut ouvrir un port « externe » qui sera associé au port interne de l’application visée, donc on revient quelque part au point de départ.

En fouillant un peu, j’ai trouvé l’outil qu’il me fallait : Traefik. Cet outil est nativement un reverse-proxy.

Cependant, pour l’installer de façon la plus pratique, il s’avère qu’il est préférable de passer par Docker. Docker est une sorte d’outil permettant de virtualiser des applications spécifiques, de façon qu’elles tournent sur leur propre système et ne dépendent pas du système sur lequel elles doivent tourner. Il se trouve que c’est extrêmement pratique pour installer sur des systèmes « propriétaires » comme ce qui fait tourner un NAS (e.g. ADM pour Asustor, pour Synology ou pour QNap). On utilise pour ça des distributions ultra-légères de GNU/Linux qui sont donc des systèmes indépendants.

Installation de Docker

Pour installer Docker sur ADM, rien de plus simple puisque l’application est supportée par AppCentral (le système de gestion des applications de ADM). Dans la foulée, j’ai également installé Portainer CE (Community Edition) qui est une interface graphique permettant de gérer l’installation et la gestion de docker divers, simple à prendre en main, surtout pour quelqu’un qui ne connai(ssai)t pas Docker (comme moi).

Installation de Traefik

Après avoir tenté d’aller au plus simple en installer Traefik directement de Portainer, je me suis finalement convaincu de mettre un peu la main à la pâte et l’installer manuellement. Dans mon installation, les répertoires Docker installés par ADM sont situés dans /volume1/ Docker. J’ai créé un répertoire « Traefik » dans celui-ci et créé les fichiers suivants. Portainer reste un outil puissant pour la gestion des dockers, pour les arrêter, les redémarrer, gérer les volumes etc.

Dernier détail, mais essentiel ! En raison de cette problématique du serveur apache du NAS qui ne lâche pas les ports 80 et 443, mais lance une instance d’un autre serveur (lighttpd en l’occurrence) il faut rediriger les ports 80 et 443 de la box internet, sur des ports libres sur le NAS, par ex. 89 et 449 respectivement, et c’est ces derniers qu’il faut rediriger dans le docker Traefik ce qui explique les lignes dans le fichier docker-compose.yml ci-dessous :

...
# les ports redirigés dans le docker
    - "449:443"
    - "89:80"
...

Avant de se lancer dans ce qui suit, il est important de créer en avance de phase les domaines. Personnellement j’ai acheté pour trois francs six sous un nom de domaine chez OVH : www.webdot.fr. Puis j’ai créé tous les sous-domaines que j’utilise en CNAME. Si le sous-domaine n’existe pas lancer traefik au terme de tout ce qui suit gérèrera une erreur.

Voici le fichier docker-compose.yml : j’ai mis quelques commentaires dans le texte

version: '3.4'

services:

  reverse-proxy:

    restart: always # politique de redémarrage
    image: traefik:latest # c'est l'image qui est téléchargée et sur laquelle va se baser votre propre installation
    container_name: Traefik # le nom du docker tel qu'il apparaîtra dans votre installation
ports: # les ports redirigés dans le docker
    - "449:443"
    - "89:80"
    network_mode: bridge # le nom du "réseau" propre aux images docker qui tournent

    environment: # tout ce bloc est destiné à gérer les certificats de Let's Encrypt des différents sous-domaines en une seule passe. 

    - "TZ=Europe/Paris"
    - "OVH_ENDPOINT=ovh-eu"
    - "OVH_APPLICATION_KEY=xxxxxxxxxxxxxxxxxxx"
    - "OVH_APPLICATION_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxx"
    - "OVH_CONSUMER_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxx"

    volumes: # les volumes montés permettent de conserver les modifications que les fichiers de configuration ou dans des répertoires. On peut faire un lien (bind) ou monter un vrai répertoire du système hôte sur le répertoire interne du docker. 

    - /volume1/Docker/Traefik/traefik.log:/traefik.log
    - /volume1/Docker/Traefik/traefik.toml:/etc/traefik/traefik.toml
    - /volume1/Docker/Traefik/services.toml:/etc/traefik/services.toml
    - /volume1/Docker/Traefik/acme.json:/acme.json
    - /var/run/docker.sock:/var/run/docker.sock

    labels:

    - "traefik.enable=true"

    # http to https redirection
    - "traefik.protocols=https"
    - "traefik.http.middlewares.https_redirect.redirectscheme.scheme=https"
    - "traefik.http.middlewares.https_redirect.redirectscheme.permanent=true"
    - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"  
    - "traefik.http.routers.redirs.entrypoints=http"  
    - "traefik.http.routers.redirs.middlewares=https_redirect"  

    # Traefik dashboard ==> c'est le bloc qui permet d'accéder à l'interface traefik pour voir son fonctionnement et déterminer les bugs éventuels
    - "traefik.http.routers.api.rule=Host(`traefik.webdot.fr`)"
    - "traefik.http.routers.api.service=api@internal"
    - "traefik.http.routers.api.entrypoints=https,http"
    - "traefik.http.routers.api.middlewares=https_redirect"
    - "traefik.http.routers.api.tls=true"
    - "traefik.http.routers.api.tls.certresolver=letsencrypt"
    #- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$Wkc.0oXU$$wd24kJ89JhbcGoMruIynk1"

    # Docker interface ==> pour accéder à l'interface de Portainer
    - "traefik.http.routers.docker.entrypoints=https,http"
    - "traefik.http.routers.docker.middlewares=https_redirect"
    - "traefik.http.routers.docker.rule=Host(`docker.webdot.fr`)"
    - "traefik.http.routers.docker.service=docker@file"
    - "traefik.http.routers.docker.tls=true"
    - "traefik.http.routers.docker.tls.certresolver=letsencrypt"

    # Apache2 reverse proxy ==> Permet de remplacer le serveur Apache du NAS, j'y reviendrai
    - "traefik.http.routers.www.entrypoints=https,http"
    - "traefik.http.routers.www.middlewares=https_redirect"
    - "traefik.http.routers.www.rule=Host(`www.webdot.fr`)"
    - "traefik.http.routers.www.service=www@file"
    - "traefik.http.routers.www.tls=true"
    - "traefik.http.routers.www.tls.certresolver=letsencrypt"

    # Nextcloud service ==> pour l'accès à Nextcloud
    - "traefik.http.routers.cloud.rule=Host(`cloud.webdot.fr`)" # ligne 1
    - "traefik.http.routers.cloud.entrypoints=https" # ligne 2
    - "traefik.http.routers.cloud.middlewares=https_redirect" # ligne 3
    - "traefik.http.routers.cloud.service=cloud@file" # ligne 4
    - "traefik.http.routers.cloud.tls=true" # ligne 5
    - "traefik.http.routers.cloud.tls.certresolver=letsencrypt" # ligne 6

.......... Et vous pouvez mettre plein d'autres blocs sur le schéma du bloc pour Nextcloud ci-dessous.

Prenons l’exemple d’un bloc : Nextcloud. Le bloc permet de déterminer le sous-domaine qui permettra d’accéder au service (ligne #1), de spécifier qu’on souhaite passer par un accès sécurisé (lignes #2 et #3). Pour ceci on définit un « service » qui relie au fichier service.toml dont je parle ci-dessous et qui relie le sous-domaine à l’accès non sécurisé du service ou de l’application (ligne #4). Les deux dernières lignes (lignes #5 et #6) s’occupent de la gestion des certificats Let’s Encrypt en passant par un « resolver » défini dans le fichier traefik.toml ci-dessous. Il faut noter que Traefik gère automatiquement la regénération des certificats avant qu’ils ne soient caduques (tous les trois mois).

services.toml : dans ce fichier on fait le lien entre un bloc sous-domaine du fichier docker-compose.yml et l’accès non sécurisé en interne de l’application ou du service : par exemple, on associe le service « cloud » à l’url http://192.168.1.140:32680. Le sous-domaine qui permet d’y accéder est défini dans le bloc correspondant (cloud) dans le fichier docker-compose.yml.

Le dernier bloc renvoie simplement le flux web (80/443) sur le serveur Apache du NAS.

[http]
  [http.services]
    [http.services.docker]
      [http.services.docker.loadBalancer]
        [[http.services.docker.loadBalancer.servers]]
          url = "http://192.168.1.140:19900"
    [http.services.cloud]
      [http.services.cloud.loadBalancer]
        [[http.services.cloud.loadBalancer.servers]]
          url = "http://192.168.1.140:32680"
    [http.services.www]
      [http.services.www.loadBalancer]
        [[http.services.www.loadBalancer.servers]]
          url = "http://192.168.1.140"

.......... Et à chaque bloc du fichier docker-compose.yml il faut associer un bloc de 4 lignes comme pour le service "cloud"

Enfin, il faut un troisième fichier qui permet de spécifier ports, fichiers, gestionnaire de certificats et niveau de log le cas échéant. C’est le fichier traefik.toml.

[entryPoints.https]
  address = ":443"

[serversTransport]
  insecureSkipVerify = true

[api]
   
[providers.docker]
  endpoint = "unix:///var/run/docker.sock"
          
[providers.file]
  filename = "/etc/traefik/services.toml"

[certificatesResolvers.letsencrypt.acme]
  email = "xxx@webdot.fr"
  storage = "/acme.json"
#  caserver = "https://acme-staging-v02.api.letsencrypt.org/directory"
  [certificatesResolvers.letsencrypt.acme.httpChallenge]
    entryPoint = "http"
#  [certificatesResolvers.letsencrypt.acme.dnsChallenge]
#    provider: ovh
#    delayBeforeCheck: 0

[log]
  filePath = "/traefik.log"
  level = "ERROR" # on peut fixer le niveau de log à DEBUG, PANIC, FATAL, ERROR, WARN ou INFO

Attention : la ligne

#  caserver = "https://acme-staging-v02.api.letsencrypt.org/directory"

est très importante. Il est indispensable de décommenter cette ligne pour faire des tests. Une fois décommentée, la génération des certificats Let’s Encrypt se fait sur un serveur de test qui n’a pas de limitation (ou alors très élevée), si elle n’est pas décommentée, on attaque directement le serveur de Let’s Encrypt et s’il y a des soucis dans la configuration, on peut consommer TRES vite la quantité de certificats autorisée en une semaine (aujourd’hui, cinquante). Une fois qu’on est certain de la configuration, on peut commenter cette ligne, relancer Traefik, et les certificats sont générés et enregistrés dans un fichier acme.json, dans la racine du répertoire /volume1/Docker/Traefik, cf. la ligne :

- /volume1/Docker/Traefik/acme.json:/acme.json

dans le fichier docker-compose.yml.

Les lignes commentées

#  [certificatesResolvers.letsencrypt.acme.dnsChallenge]
#    provider: ovh
#    delayBeforeCheck: 0

Sont relatives à la gestion des certificats Let’s Encrypt via les SANS, mais je ne suis pas encore arrivé à utiliser ces particularités. J’ai quand même conservé ces lignes, quand j’aurai le temps.

Avant tout essai, il faut bien créer le fichier acme.json, et lui attribuer les droits d’accès « 600 »:

root@astorus:/volume1/Docker/Traefik# touch acme.json && chmod 600 acme.json

Normalement, une fois que tout est bien configuré, on peut lancer le docker Traefik avec la commande :

root@astorus:/volume1/Docker/Traefik# docker-compose up -d

Le docker se lance, va alimenter le fichier de certificats acme.json, on peut suivre les erreurs éventuelles dans le fichier traefik.log. Les sous-domaines doivent bien entendu être actifs comme expliqué ci-dessus.

Maintenant, on aimerait accéder aux certificats d’une façon différente que de la façon dont ils sont présentés dans le fichier acme.json. Pour ceci j’ai trouvé un outil très utile : traefik-certs-dumper. Cet outil permet d’extraire les certificats d’un fichier .json. Avec la commande :

root@astorus:/volume1/Docker/Traefik#./traefik-certs-dumper file --version v2 --clean --crt-name cert --key-name private --source ./acme.json --dest ./dump/

On extrait tous les certificats pour les ré-écrire dans le répertoire ./dump. Attention, l’option –version v2 est indispensable, sinon ça ne marchera pas.

Du coup, j’ai créé un petit script qui me permet de récupérer les certificats et de les ranger là où je veux 🙂 . Cela m’est utile essentiellement pour gérer les certificats pour le serveur de mail (dans mon cas : mail.webdot.fr). Je les recopie (voir les lignes cp…) dans des répertoires persistants de postfix et dovecot dont je parlerai dans un prochain post.

J’ai mis ce script dans crontab et je le fais tourner une fois par mois, et je suis certain d’avoir dans ce répertoire dump les certificats en cours (vu que les certificats Let’s Encrypt sont générés tous les trois mois).

#!/bin/sh

cd /volume1/Docker/Traefik

# Extract the certificates from the acme.json file
./traefik-certs-dumper file --version v2 --clean --crt-name cert --key-name private --source ./acme.json --dest ./dump/

# Copy mail hostname certificates to Postfix docker volume ssl directory and apply r/w rights
cp ./dump/certs/mail.webdot.fr.crt /usr/local/AppCentral/docker-ce/docker_lib/volumes/postfix_conf/_data/ssl/
cp ./dump/private/mail.webdot.fr.key  /usr/local/AppCentral/docker-ce/docker_lib/volumes/postfix_conf/_data/ssl/
chmod 644 /usr/local/AppCentral/docker-ce/docker_lib/volumes/postfix_conf/_data/ssl/*.crt
chmod 600 /usr/local/AppCentral/docker-ce/docker_lib/volumes/postfix_conf/_data/ssl/*.key

# Copy mail hostname certificates to Dovecot docker volume ssl directory and apply r/w rights
cp ./dump/certs/mail.webdot.fr.crt /usr/local/AppCentral/docker-ce/docker_lib/volumes/a73949915e9e61ca225db3388b315f490be498483ac0e9f87ed621a37768d18b/_data/ssl/cert.pem
cp ./dump/private/mail.webdot.fr.key  /usr/local/AppCentral/docker-ce/docker_lib/volumes/a73949915e9e61ca225db3388b315f490be498483ac0e9f87ed621a37768d18b/_data/ssl/key.pem
chmod 644 /usr/local/AppCentral/docker-ce/docker_lib/volumes/a73949915e9e61ca225db3388b315f490be498483ac0e9f87ed621a37768d18b/_data/ssl/cert.pem
chmod 600 /usr/local/AppCentral/docker-ce/docker_lib/volumes/a73949915e9e61ca225db3388b315f490be498483ac0e9f87ed621a37768d18b/_data/ssl/key.pem

Et voilà, normalement, les sous-domaines ont maintenant accès à tout les services correspondants (sous réserve que ces derniers soient installés avant !).

3 Comments

  1. Pieter

    Bonjour Eric,

    Avant tout merci pour toutes ces informations.

    J’utilise Traefik sur un serveur Debian chez Kimsufi depuis environ un an et je dois dire que c’est vraiment top en termes d’utilisation. Je galère depuis pas mal de temps pour avec le reverse proxy intégré d’Asustor… Ça manque pas mal de fonctionnalités.

    Pour l’instant je n’ai pas eu le temps de m’y coller, mais je vais trouver le temps!

    Petite question, faut il commencer par la création du serveur Apache (évoqué dans le billet suivant)? Ou cela n’a pas d’importance?

    Merci encore pour le partage!

    Bien cordialement

    1. Bonjour Pieter. Je suis d’accord avec toi que Traefik est un outil exceptionnel vraiment dédié au reverse proxy.
      Concernant l’Asustor, également d’accord pour affirmer que c’est limite en termes de souplesse d’utilisation des outils intégrés. En l’occurrence, leur système de reverse proxy n’est tout simplement pas utilisable si l’on souhaite faire le lien entre un port interne en http (appli en docker) et un sous-domaine accessible en https sur le serveur web, sur le port standard. C’est pour ça que personnellement j’ai carrément laissé tomber les deux : le serveur Apache du NAS, et le système de reverse proxy du NAS.
      Concernant plus précisément ta question, elle coule de source. Il est préférable de mettre en place le serveur Apache en docker, avant de mettre en place Traefik, puisque dans la configuration que je décris, on redirige les appels externes aux ports http/https au docker Apache, c’est mieux si celui-ci est déjà actif 🙂 . Si le post est venu après, c’est parce que j’ai fait toute mon installation et seulement après que tout fonctionnait que j’ai écrit mes posts, et du coup pas dans le bon ordre, désolé pour ça.
      Ceci dit il y a une chose que je n’ai pas précisée et qui me revient en tête, une chose que je n’ai toujours pas comprise. Lors d’un redémarrage du NAS, Traefik (en docker) ne peut pas se lancer spontanément. Il faut que j’aille dans les « services » du NAS, que je désactive le serveur Apache du NAS, une fois arrêté je le réactive : je retourne alors dans Portainer et je démarre Traefik sans problème. Après cette étape de démarrage, on peut arrêter et redémarrer Traefik sans retoucher au serveur Apache du NAS. Je n’ai toujours pas compris pourquoi, mais c’est … pénible. C’est également pour ça qu’il est préférable de garder un accès de l’extérieur :
      1) à l’interface du NAS, via le système « ezconnect » (avec le nom de domaine de type « monNAS.ezconnect.to ») ==> arrêter/démarrer le serveur Apache du NAS,
      2) au NAS par ssh ==> relancer le docker de Traefik par ligne de commande.
      Pour l’instant c’est le seul contournement que j’ai trouvé, pour me permette de relancer Traefik sur le NAS si celui-ci redémarre pour une raison ou pour une autre. Après il est remarquablement stable, pour l’instant les seuls redémarrages auxquels j’ai assistés sont ceux que j’ai déclenchés 🙂 .

      1. Je me réponds à moi-même : Il n’y a pas besoin de serveur Apache en Docker !!!!!!!
        J’ai modifié le post sur la mise en place d’un serveur Apache en Docker en conséquence, et celui-là aussi bien sûr ! L’explication est dans le post. Au lieu du workaround fumeux, je l’avoue, que je proposais, il est beaucoup plus simple de renvoyer les ports externes 80 et 443 de la box sur les ports internes 49 et 449 du NAS (en tous cas, des ports non utilisés), et, dans la configuration de reroutage des ports pour le docker Traefik, de router ces ports 89 et 449 sur ses ports internes 80 et 443. Il suffit alors, dans la configuration de Traefik, de renvoyer les ports 80 et 443 sur le serveur Apache natif du NAS, et ça fonctionne parfaitement bien.

Répondre à Pieter Annuler la réponse

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