Comment mettre le HTTPS en local avec docker-compose et mkcert ?

Publié le

27 mars 2023

Docker et docker-compose sont maintenant largement utilisés dans le monde du développement web. Depuis notre premier hackathon en 2015, Docker est devenu un standard dans tous nos projets.
Cela nous aide énormément pour maintenir nos différentes stacks et pour gérer des configurations spécifiques pour les environnements de développement, staging ou production.

Néanmoins, beaucoup de configurations Docker pour des environnements de développement sont assez éloignés des environnements de staging ou de production.Cet article explique pourquoi les différents environnements doivent être proches les uns des autres. C'est le meilleur moyen de fail fast en local pour éviter de déployer des bugs en production.

La plupart du temps, la configuration locale de Docker donne accès aux services en utilisant des ports spécifiques. Le Saint Graal est d'utiliser un reverse-proxy (comme Traefik) même en développement. C'est pourquoi aujourd'hui, on se concentre sur un sujet très spécifique : Le cryptage TLS en environnement local.

Prérequis

Cet article a été créé en utilisant ces versions de Docker et Mkcert

  • docker: 20.10.15
  • docker-compose: 1.28.5
  • mkcert: 1.4.3-1

La configuration présentée peut fonctionner avec des versions plus anciennes mais nous n'avons pas vérifié.

Traefik

Avant de commencer, nous devons nous attarder un minimum sur Traefik si vous n'en avez jamais entendu parler.

Fondamentalement, Traefik est un reverse proxy, concrètement c'est la porte d'entrée de votre plateforme.
Il intercepte et redirige toutes les requêtes entrantes : il connaît toute la logique et toutes les règles qui déterminent quel service traite quelles requêtes (basé sur le chemin, l'hôte, le header etc.).

traefik_concepts_1_bd4b04df2f-1

Ce qui est cool avec Traefik c'est que vous n'avez pas beaucoup de configuration à écrire pour le faire marcher.
Avec seulement quelques labels docker-compose sur vos service, vous avez un puissant reverse-proxy pour traiter le traffic de votre stack.

Mkcert

mkcert est un outil pour générer des certificats de développement localement fiables qui pourront ensuite être partagés à Traefik pour effectuer un cryptage TLS.

Mkcert peut aussi etre utilisé en environnement local. Les domaines publiques demanderont de leur fournir un certificat approuvé par une authorité externe.

Vous pouvez suivre le guide d'installation sur la documentation de mkcert.

Pour commencer

Nous allons utiliser l'image Docker containous/whoami pour faire une démonstration de l'utilisation d'un certificat TLS sur un service docker-compose.

Certificats Mkcert

Premièrement nous devons créer et stocker les certificats TLS créé par mkcert dans le dossier ./certs.
Ces certificats sont localement fiables c'est à dire qu'ils sont valides uniquement sur votre machine. Par conséquent, vous devez les ajouter à votre gitignore.

mkdir -p certs
mkcert -cert-file certs/local-cert.pem -key-file certs/local-key.pem "app.localhost" "*.app.localhost" "domain.local" "*.domain.local"

Configuration Traefik

Maintenant créez les dossiers : ./docker/traefik

mkdir -p docker/traefik

Créez ensuite le fichier ./docker/traefik/dynamic_conf.yaml qui permettra la configuration de tous les sous-domaines servis par Traefik.
Il indique aussi le chemin vers les certificats TLS (générés par Mkcert) qui seront ensuite partagés avec Traefik via un volume Docker.

http:
  routers:
    traefik:
      rule: "Host(`traefik.app.localhost`)"
      service: "api@internal"
      tls:
        domains:
          - main: "app.localhost"
            sans:
              - "*.app.localhost"
          - main: "domain.local"
            sans:
              - "*.domain.local"

tls:
  certificates:
    - certFile: "/etc/certs/local-cert.pem"
      keyFile: "/etc/certs/local-key.pem"

Puis nous allons créer le fichier de configuration ./docker/traefik/traefik.yaml.

global:
  sendAnonymousUsage: false

api:
  dashboard: true
  insecure: true

providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    watch: true
    exposedbydefault: false

  file:
    filename: /etc/traefik/dynamic_conf.yaml
    watch: true

log:
  level: DEBUG
  format: common

entryPoints:
  web:
    address: ':80'
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: ':443'

Je ne vais pas expliquer chaque partie de ce fichier mais je vais un peu expliciter les sections providers et entryPoints:

providers

Cette section indique à Traefik quels services sont accessibles de l'extérieur et comment ils sont accessibles. C'est globalement la configuration du routeur.

Nous avons ici deux providers différents :

  • docker : Utilise des labels sur les conteneurs pour récupérer les configurations de routage (plus d'information dans la prochaine section).
  • file : Charge un fichier de configuration, ici le fichier dynamic_config.yaml que l'on vient de créer.

entryPoints

Cette section est plutôt simple, elle permet de rediriger toutes les requêtes entrantes de http (port 80) en https (port 443).
Maintenant nous pouvons être sûr que Traefik ne transmettra aucune requête http à vos services.

Fichier docker-compose

Remplir le fichier docker-compose.dev.yml avec :

version: '3.8'

services:
  reverse-proxy:
    image: traefik:v2.4.7
    container_name: traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik=true"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.services.traefik.loadbalancer.server.port=8080"
    ports:
      - 80:80
      - 443:443
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - ./docker/traefik/dynamic_conf.yaml:/etc/traefik/dynamic_conf.yaml:ro
      - ./docker/traefik/traefik.yaml:/etc/traefik/traefik.yaml:ro
      - ./certs:/etc/certs:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro

  whoami:
    image: containous/whoami:latest
    container_name: whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.app.localhost`)"
      - "traefik.http.routers.whoami.tls=true"
    restart: unless-stopped

networks:
  proxy:
    external: true

Vous trouverez ainsi les déclarations des services de votre stack. Voici quelques explications sur cette configuration :

reverse-proxy

  • ports : Relie les ports 80 et 443 avec l'hôte. A noter que le service whoami n'expose aucun port. Toutes les requêtes entrantes de votre stack seront gérées par Traefik.
  • volumes : Nous partageons les fichiers de configurations, les certificats et le socket Docker (Traefik a besoin d'un accès au socket Docker pour avoir accès à sa configuration dynamique).

whoami

Nous définissons trois labels:

  • traefik.enable=true : On définie explicitement à Traefik d'exposer publiquement ce conteneur.
  • traefik.http.routers.whoami.rule=Host('whoami.app.localhost') : Le domaine rattaché au service.
  • traefik.http.routers.whoami.tls=true : Active le HTTPS sur cette route.

Lancer la stack

docker-compose -f docker-compose.dev.yml up -p

Et voilà! Vous pouvez maintenant accéder à : https://whoami.app.localhost.

Des questions ou des retours ? N'hésitez pas à nous envoyer un message sur Twitter ! https://twitter.com/KNPLabs

Publié par

Antoine Lelaisant
Antoine Lelaisant

Caen

Front and backend developer with a pref for mobile apps. Loves to share his XP with clients & KNPeers during our trainings.

Commentaires