Rendre un service accessible publiquement
Préambule
Quand un conteneur Docker doit être accessible depuis l’extérieur de la machine, il y a deux solutions :
- Mapper un port de l’hôte sur un port du conteneur
- Utiliser Traefik
Important:
La règle générale est la suivante : on utilise un mapping de port pour les services non-web (e.g. LDAP, mail, SFTP…) et Traefik pour tous les services web. Une explication se trouve sur la page dédiée à Traefik.
Mapping de port
C’est la technique la plus simple. Supposons qu’un conteneur écoute sur son port interne 8080, et qu’on veuille le rendre accessible depuis l’extérieur. On va choisir arbitrairement un port de l’hôte que l’on va mapper dessus, par exemple :
- snippet.yaml
services: example: image: <image> ports: - 2000:8080
Dans cet exemple, tout ce qui arrive sur le port 2000
de la machine sera redirigé sur le port 8080
du conteneur.
Attention:
Par contre, pour tout ce qui concerne la gestion de TLS ou d’autres fonctionnalités de Traefik, on n’en profite pas, car on ne passe pas du tout par lui.
Utiliser Traefik
Pour les services web, on préfère largement passer par Traefik, car on pourra utiliser un port standard (80
ou 443
) et profiter de toutes ses fonctionnalités (HTTPS automatique, etc).
On configure le fait de router vers un conteneur grâce aux labels Docker. Voici un extrait commenté permettant de router toutes les requêtes arrivant sur Traefik à destination de app.picasoft.net
vers le conteneur exemple
, sur son port 8080
:
- snippet.yaml
networks: proxy: external: true services: exemple: container_name: example networks: - proxy labels: # Traefik va prendre ce conteneur en compte traefik.enable: true # websecure correspond au port 443 de la machine (config Traefik) # Remplacer <exemple> par le nom du conteneur traefik.http.routers.<exemple>.entrypoints: websecure # Il redirigera vers ce port, exposé par le conteneur # Remplacer <exemple> par le nom du conteneur # Remplacer app.picasoft.net par l'URL souhaitée traefik.http.routers.<exemple>.rule: Host(`app.picasoft.net`) # Lorsque l'utilisateur consulte cette URL # Remplacer <exemple> par le nom du conteneur # Remplacer 8080 par le port du service traefik.http.services.<exemple>.loadbalancer.server.port: 8080
Important:
Il est essentiel que le conteneur soit dans le même réseau que Traefik, qui est fixé à proxy
! C’est le sens des directives networks
.
websecure
désigne, dans la configuration Traefik, le port 443. Tout ce qui arrive vers Traefik est redirigé vers cet entrypoint, il faut donc se coller dessus.
Note:
Il est possible d’être plus fin sur l’URL à laquelle répondra le conteneur, et même d’utiliser des chemins spécifiques ou des expressions régulières. Voir la documentation officielle.
Sécuriser une sous-partie du service par authentification
Il y a pas mal de cas d’utilisation où une sous-partie d’un site doit être sécurisée par authentification :
- La partie
/admin
d’un service exposé sur internet mais sans authentification intégrée au service - La partie
/metrics
d’un service qui doit être accessible à la solution de monitoring mais pas aux internautes…
La solution consiste à créer un second point d’entrée à l’aide des labels. En reprenant l’exemple de la section précédente, on rajouterait ces labels :
- snippet.yaml
# Toujours le même point d'entrée : HTTPS traefik.http.routers.<exemple>-metrics.entrypoints: websecure # Ce point d'entrée sera sélectionné uniquement pour les requêtes # vers app.picasoft.net/metrics et ses dérivés. traefik.http.routers.<exemple>-metrics.rule: "Host(`app.picasoft.net`) && PathPrefix(`/metrics`)" # Indique que ce point d'entrée passe par un middleware nommé <exemple>-metrics-auth et qui est # configuré via les labels Docker (voir juste en dessous) traefik.http.routers.<exemple>-metrics.middlewares: "<exemple>-metrics-auth@docker" # Ce middleware est simplement une authentification, qui vient se mettre entre le point d'entrée # et l'application. traefik.http.middlewares.<exemple>-metrics-auth.basicauth.users: "test:$2y$10$7Gv5tUZC1UBsTyQs/ZTwmu0jamzBJFnNEY4SMxqdfIYfTyLBDTINm"
La chaîne $2y$10$7Gv5tUZC1UBsTyQs/ZTwmu0jamzBJFnNEY4SMxqdfIYfTyLBDTINm
correspond au mot de passe test
hashé avec bcrypt.
Important:
Pour éviter de versionner la chaîne d’identification, on préfère plutôt utiliser une variable pour le label basicauth.users
, qui vaudra “${METRICS_AUTH}”. La valeur sera placée dans un fichier .env
sur la machine de production, sous le format suivant :
METRICS_AUTH=test:$2y$10$7Gv5tUZC1UBsTyQs/ZTwmu0jamzBJFnNEY4SMxqdfIYfTyLBDTINm
En effet, Docker Compose lit les fichiers .env
et remplace les variables par leur valeur dans le fichier Compose. De plus, Git ignore les fichiers cachés par défaut, on ne risque pas de commiter les secrets par erreur.
A Savoir:
Pour générer la chaîne d’authenticiation Basic Auth qui est donnée au label Traefik, on commence par définir un nom d’utilisateur (ici test
) et par générer un mot de passe (par exemple avec pwgen -sB 64
). On place ces identifiants dans le vaultwarden.
On utilise ensuite la commande suivante pour générer la chaîne d’authentification qu’il faut renseigner dans le label.
$ echo $(htpasswd -nb USER PASSWORD) USER:$apr1$4WpORg/6$.bUMzTnrDPivUXfu7.vvP0