Tester en local avec HTTPS

HébergementContainers06 Mar 2022

Mes services sont hébergés sur un serveur de containers propulsé par docker compose. L'exposition des services HTTP et HTTPS est géré par le reverse-proxy Traefik qui s'occupe aussi de renouveler les certificats SSL délivrés par Let's Encrypt ; pour cela il s'interface avec la plupart des registrars (les entités qui gère les noms de domaine). Cela permet de bénéficier de la méthode dnsprovider et de demander un certificat wildcard (*.mondomaine.fr) plutôt que des certificats pour chaque hôte du domaine (soit chaque container exposé).

Un serveur à base de containers est facilement portable d'une machine à l'autre et on en vient vite à monter un environnement de test local sur sa machine pour tester / valider avant de déployer sur le serveur. Ce qui est plus touchy c'est de se placer dans des configurations similaires au serveur en production et notamment d'avoir du HTTPS.

Après avoir considéré la complication à gérer des certificats auto-signés, j'ai opté pour une solution basée sur la résolution de noms fournie par Traefik.me. Cette géniale idée permet de résoudre n'importe quel nom de machine du domaine traefik.me en localhost. Contrairement à ce que le nom du site laisse supposer, ce n'est pas une solution officielle fournie par Traefik mais je crois que son auteur Pyrou est un utilisateur (et peut-être un fan) de Traefik. En tout cas on peut mettre en œuvre traefik.me avec n'importe quelle autre solution de reverse-proxy, les certificats PEM à jour sont fournis sur la page d'accueil du site.

Voici donc ma configuration locale pour résoudre les nom de mes containers en HTTPS sur le domaine traefik.me :

Fichier docker-compose.traefik-local.yml :

services:
  traefik-local:
    container_name: traefik-local
    image: traefik:v2.5.3
    profiles: ["testing"]   
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    expose:
      - 8080
    labels:
      - traefik.enable=true
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik.yml:/etc/traefik/traefik.yml
      - tls.yml:/etc/traefik/tls.yml
      - certs:/etc/ssl/traefik

  traefik-reverse-proxy-https-helper:
    container_name: traefik-reverse-proxy-https-helper
    image: alpine
    profiles: ["testing"]   
    command: sh -c "cd /etc/ssl/traefik
      && wget traefik.me/cert.pem -O cert.pem
      && wget traefik.me/privkey.pem -O privkey.pem"
    volumes:
      - certs:/etc/ssl/traefik

volumes:
  certs:

Fichier tls.yml :

tls:
  stores:
    default:
      defaultCertificate:
        certFile: /etc/ssl/traefik/cert.pem
        keyFile: /etc/ssl/traefik/privkey.pem
  certificates:
    - certFile: /etc/ssl/traefik/cert.pem
      keyFile: /etc/ssl/traefik/privkey.pem

Fichier traefik.yml :

logLevel: INFO

api:
  insecure: true
  dashboard: true

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"

providers:
  file:
    filename: /etc/traefik/tls.yml
  docker:
    endpoint: unix:///var/run/docker.sock
    watch: true
    exposedByDefault: false
    defaultRule: "HostRegexp(`{{ index .Labels \"com.docker.compose.service\"}}.traefik.me`,`{{ index .Labels \"com.docker.compose.service\"}}-{dashed-ip:.*}.traefik.me`)"

http:
  # global redirect to https
  routers:
    http-catchall:
      rule: "hostregexp(`{host:.+}`)"
      entrypoints:
        - http
      middlewares:
        - redirect-to-https

  # middleware redirect
  middlewares:
    redirect-to-https:
      redirectscheme:
        scheme: https
        permanent: true

On notera le container compagnon "traefik-reverse-proxy-https-helper" qui s'occupe de rapatrier une version à jour des certificats et de les stocker sur le volume certs partagé avec le container "traefik-local".

La configuration de traefik pour le serveur est classique. Dans mon cas elle s'interface avec le registrar infomaniak qui me loue mon nom de domaine.

Fichier docker-compose.traefik-infomaniak.yml :

services:
  traefik-infomaniak:
    container_name: traefik-infomaniak
    image: traefik:v2.5.3
    profiles: ["production"]
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false      
      - --api.dashboard=false
      - --entrypoints.http.address=:80      
      - --entrypoints.https.address=:443
      - --certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}
      - --certificatesresolvers.letsencrypt.acme.storage=/acme.json
      - --certificatesResolvers.letsencrypt.acme.dnsChallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=infomaniak
    environment:
      - INFOMANIAK_ACCESS_TOKEN=${LETSENCRYPT_DNSPROVIDER_TOKEN}
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.entrypoints=http
      - traefik.http.routers.api.entrypoints=https
      - traefik.http.routers.api.service=api@internal
      # middleware auth
      - traefik.http.routers.api.middlewares=auth
      - traefik.http.middlewares.auth.basicauth.users=${BASIC_AUTH}          
      # request widlcard certificate
      - traefik.http.routers.api.tls.certresolver=letsencrypt
      - traefik.http.routers.api.tls.domains[0].main=${DOMAIN}
      - traefik.http.routers.api.tls.domains[0].sans=*.${DOMAIN}
      # global redirect to https
      - traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.http-catchall.entrypoints=http
      - traefik.http.routers.http-catchall.middlewares=redirect-to-https
      # middleware redirect
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      - traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true     
    ports:
      - 80:80
      - 443:443
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - acme.json:/acme.json

Le support des profils de docker-compose permet de choisir la bonne configuration au démarrage des containers.

En mode "production" sur le serveur :

docker-compose --env-file .env --profile production up -d

En mode "test" sur la machine locale :

docker-compose --env-file .env --profile testing up    

Les configurations docker-compose de l'article sont un peu simplifiées pour faciliter la compréhension. Les sources complètes sont sur GitHub.

Votre commentaire