Sommaire

Traefik avec support HTTPS

Introduction

Dans la continuité de mon introduction à Traefik v2, nous allons voir ici comment j’ai fait pour passé en HTTPS. Pour cela, j’ai généré des certificats Let’s Encrypt en utilisant le défi DNS dont j’ai parlé dans un autre article.

Après la mise en place des éléments du protocole ACME (résolveur), nous verrons comment appliquer cela à notre dashboard (configuration dynamique par fichier) et ensuite la démarche pour des conteneurs Web externes (configuration dynamique avec les labels).

Configuration du résolveur

Organisation

Je travaille en root (sudo -i) dans le dossier /root/docker/traefik/. Depuis l’autre article, j’ai ordonné les fichiers en les mettant dans un dossier config.

Voici d’avance la liste des fichiers de cette partie :

/root/docker/traefik/docker-compose.yml
/root/docker/traefik/config/traefik.yml
/root/docker/traefik/config/dynamique/dashboard.yml
/root/docker/traefik/config/acme.json
/root/docker/traefik/config/.ovh-api.env

Fichier Docker Compose

Voici donc le contenu du fichier docker-compose.yml :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: '3.7'

services:
  reverse-proxy:
    # Traefik v2.2 officiel
    image: traefik:v2.2
    ports:
      # Ports d'écoute
      - "80:80"
      - "443:443"
      #- "8080:8080"
    # Variables d'environnement
    env_file:
      - ./config/.ovh-api.env
    volumes:
      # Mapping de la configuration statique
      - ./config/traefik.yml:/etc/traefik/traefik.yml:ro
      # Mapping du dossier de conf dynamique
      - ./config/dynamique/:/etc/dyn_config/
      # Mapping du fichier de stockage des certificats
      - ./config/acme.json:/acme.json
      # Pour que traefik puisse écouter les events Docker
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy

networks:
  proxy:
    driver: bridge
    name: traefik-net

Vous remarquerez les spécificités suivantes :

  • J’ai commenté la ligne d’exposition du port du dashboard vu que l’on va y accéder en HTTPS (donc sur le port 443)
  • J’ai ajouté un fichier de variable d’environnement : il contiendra les clés nécessaires pour la connexion à l’API d’OVH
  • J’ai mappé un fichier acme.json dans lequel seront stockés les éléments constituant les certificats

Fichier de configuration de Traefik

Le fichier config/traefik.yml :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
entryPoints:
  oueb:
    address: ":80"
  superoueb:
    address: ":443"
  dashboard:
    address: ':8080'
log:
  # Default: "ERROR"
  level: INFO
api:
  dashboard: true
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    # Expose containers by default in traefik
    exposedByDefault: true
  # Enable the file provider to define routers / middlewares / services in file
  file:
    directory: "/etc/dyn_config/"
    watch: true


certificatesResolvers:
  tomandjerry:
    # Enable ACME (Let's Encrypt): automatic SSL.
    acme:
      email: "webmaster@scrample.xyz"
      storage: "/acme.json"

      ## CA server to use.
      ## Par défaut :
      #caServer: "https://acme-v02.api.letsencrypt.org/directory"
      ## Environnement de qualification de LE
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"

      ## Use a HTTP-01 ACME challenge.
      #httpChallenge:
      #  entryPoint: "oueb"

      # Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
      dnsChallenge:
        provider: "ovh"
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"
  • J’ai décidé de mettre des noms stupides pour faciliter la compréhension : 2,4,25
  • La ligne 28 contient l’adresse e-mail transmise à Let’s Encrypt (LE)
  • A la ligne 35, vous remarquerez que j’ai spécifié l’adresse de “staging” de LE afin d’obtenir des certificats de tests.
    Vous trouverez plus d’information dessus sur la documentation officiel
  • Pour la configuration du défi DNS, je spécifie explicitement des DNS externes afin de ne pas entrer en conflit avec mon DNS interne

Cette configuration est à adapter selon vos spécificités.
Si vous préférez utiliser le défi HTTP-01, supprimez les lignes [40-46] et décommentez-les [38-39].

Fichier des variables d’environnement

Pour connaitre les informations de votre fournisseur DNS afin de faire votre fichier de variables, je vous invite à regarder la documentation à ce sujet.
Vous pouvez aussi vous rendre sur la page du module LEGO, module qu’utilise Traefik pour résoudre le défi.

Voici (en quelque sorte 🤣) le mien (config/.ovh-api.env) :

1
2
3
4
5
# OVH API credentials used by Certbot
OVH_ENDPOINT=ovh-eu
OVH_APPLICATION_KEY=bugsbunny
OVH_APPLICATION_SECRET=bipbip
OVH_CONSUMER_KEY=speedygonzales

Fichier des certificats

/img/2020-05-05-traefik-avec-support-HTTPS/acme.jpg
ACME Corporation

Le fichier acme.json doit être créé avec les droits RW seulement. Il est impératif que ce fichier existe avant le lancement de la stack. Dans le cas contraire, un dossier sera créé à sa place et génèrera donc une erreur.

1
2
touch config/acme.json
chmod 600 config/acme.json

Si vos essais mènent à des erreurs, je vous conseille d’effacer ce fichier et de le récréer à chaque fois.

Dashboard en HTTPS

Maintenant que nous avons notre résolveur de configuré, nous allons pouvoir appliquer cela à notre dashbord.

Le fichier config/dynamique/dashboard.yml prend donc la forme suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
http:
  routers:
    api:
      rule: "Host(`traefik.scrample.xyz`)"
      service: api@internal
      middlewares:
        - redirection
    api-secure:
      rule: "Host(`traefik.scrample.xyz`)"
      service: api@internal
      tls:
        certResolver: tomandjerry
      middlewares:
        - auth
  middlewares:
    auth:
      basicAuth:
        users:
          - "admin:$2y$10$sqgVdrmQxXQgYIpg3BycVuZ2J8sFSHheVnmd728KkE7fznFSlxA0u" 
          - "test:$2y$10$mPcvlMccO2mXjA2ye.uBWuhO36x46K/3b.g1AjJ1YllmKCSAuS.vG"
    redirection:
      redirectScheme:
        scheme: https
        permanent: true

On déclare donc un routeur api-secure (donnez lui n’importe quel nom) sur lequel on applique l’option tls: en spécifiant le résolveur définis dans le fichier de configuration de Traefik.

Avec ce routeur seul, le dashboard sera accessible seulement en tapant l’adresse en HTTPS. Pour avoir la redirection (HTTP vers HTTPS), on est obligé de créer un autre routeur comme le précise la section TLS de la documentation :

If you need to define the same route for both HTTP and HTTPS requests, you will need to define two different routers: one with the tls section, one without.

C’est donc ce que j’ai fait avec le routeur api où je demande l’application de la redirection définie en fin de fichier que j’ai sobrement appelé redirection.

J’ai fait suivre le lancement de la stack (docker-compose up -d) d’un docker-compose logs -f afin de voir les évènements.
En tapant traefik.scrample.xyz dans la barre d’adresse, je suis bien redirigé en HTTPS car j’ai l’avertissement d’un mauvais certificat car j’utilise l’environnement de qualification de LE. Une fois l’exception ajoutée, j’ai bien la demande d’authentification basique.

/img/2020-05-05-traefik-avec-support-HTTPS/Screenshot_1.png
Information du site Web

Service Web en HTTPS

Pour faire simple, j’ai crée un conteneur Nginx dans un nouveau Docker Compose. Je suis donc dans un autre dossier que celui de Traefik : /root/docker/web
J’y ai appliqué les mêmes règles que sur le dashbord mais en labels cette fois-ci.

Mon fichier prend donc la forme suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: '3.7'

services:
  monsiteweb:
    image: nginx:alpine
    labels:
      - "traefik.enable=true"
      - "traefik.port=80"
      - "traefik.http.routers.monsiteweb.rule=Host(`mywebsite.scrample.xyz`)"
      - "traefik.http.middlewares.redirection-monsiteweb.redirectscheme.scheme=https"
      - "traefik.http.middlewares.redirection-monsiteweb.redirectscheme.permanent=true"
      - "traefik.http.routers.monsiteweb.middlewares=redirection-monsiteweb"
      - "traefik.http.routers.monsiteweb-secure.rule=Host(`mywebsite.scrample.xyz`)"
      - "traefik.http.routers.monsiteweb-secure.tls=true"
      - "traefik.http.routers.monsiteweb-secure.tls.certresolver=tomandjerry"
    networks:
      - proxy-net

networks:
  proxy-net:
    external:
      name: traefik-net

Finalement, je me rends compte que c’est plutôt embêtant de faire la redirection pour chaque service… Ça double le nombre de routeur et d’intermédiaire ("middlewares"). J’ai donc cherché une façon d’appliquer la redirection de manière globale.

Redirection globale

Après avoir tout cassé (double docker-compose down), j’ai effectué les actions suivantes :

  • Suppression des “redirecteurs” (vous m’avez compris) :
    • Dashboard
    • Conteneur Nginx
  • Ajout d’une redirection globale

Ci-dessous, les fichiers que j’ai modifiés :

  • traefik/config/traefik.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
entryPoints:
  oueb:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: superoueb
          scheme: https
          permanent: true
  superoueb:
    address: ":443"
  dashboard:
    address: ':8080'
log:
  # Default: "ERROR"
  level: INFO
api:
  dashboard: true
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    # Expose containers by default in traefik
    exposedByDefault: true
  # Enable the file provider to define routers / middlewares / services in file
  file:
    directory: "/etc/dyn_config/"
    watch: true


certificatesResolvers:
  tomandjerry:
    # Enable ACME (Let's Encrypt): automatic SSL.
    acme:
      email: "anup.sungum@gmail.com"
      storage: "/acme.json"

      ## CA server to use.
      ## Par défaut :
      #caServer: "https://acme-v02.api.letsencrypt.org/directory"
      ## Environnement de qualification de LE
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"

      ## Use a HTTP-01 ACME challenge.
      #httpChallenge:
      #  entryPoint: "oueb"

      # Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
      dnsChallenge:
        provider: "ovh"
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"
  • traefik/config/dynamique/dashboard.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
http:
  routers:
    api:
      rule: "Host(`traefik.scrample.xyz`)"
      service: api@internal
      tls:
        certResolver: tomandjerry
      middlewares:
        - auth
  middlewares:
    auth:
      basicAuth:
        users:
          - "admin:$2y$10$sqgVdrmQxXQgYIpg3BycVuZ2J8sFSHheVnmd728KkE7fznFSlxA0u" 
          - "test:$2y$10$mPcvlMccO2mXjA2ye.uBWuhO36x46K/3b.g1AjJ1YllmKCSAuS.vG"
  • web/docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: '3.7'

services:
  monsiteweb:
    image: nginx:alpine
    labels:
      - "traefik.enable=true"
      - "traefik.port=80"
      - "traefik.http.routers.monsiteweb.rule=Host(`mywebsite.scrample.xyz`)"
      - "traefik.http.routers.monsiteweb.tls=true"
      - "traefik.http.routers.monsiteweb.tls.certresolver=tomandjerry"
    networks:
      - proxy-net

networks:
  proxy-net:
    external:
      name: traefik-net

C’est clairement plus simple !

J’ai remarqué qu’un routeur assez spécial est apparu sur le dashboard :

/img/2020-05-05-traefik-avec-support-HTTPS/Screenshot_2.png
Oueb to SuperOueb
La règle de “catching” est une expression régulière qui “match” tous les hôtes.

Pour avoir des certificats de production, il suffit de commenter la ligne précisant le endpoint de l’API de LE (ligne 41 de traefik.yml). Cela est suffisant car la valeur par défaut de cette variable est celle du serveur “normal” (soit “https://acme-v02.api.letsencrypt.org/directory").

Conclusion

Avec ce deuxième article sur Traefik, nous avons pu poser les bases d’une utilisation simple de ce “Edge Router”.

La série sur Traefik n’est pas finie, elle continuera quand j’aurai le courage de mettre cela en place en production. On verra surement s’il est possible d’aller plus loin et comment récupérer les métriques pour meublé un peu plus Grafana.