Cet article explicite la génération d'un certificat SSL de type 'wildcard' généré gratuitement par Let's Encrypt. Avoir un certificat par service ou sous-domaine c'est bien, mais allons plus loin et exploitons la fonctionnalité wildcard
permettant de générer un unique certificat pour tous les sous-domaines.
Importante mise Ă jour de l'article par ldez au 15/07/2020. Une nouvelle fois, un grand merci !!
Version | Date | Commentaires |
---|---|---|
1 | 07/2020 | Création de l'article |
1.1 | 05/2020 | Reformatage de l'article |
1.2 | 07/2022 | Mise Ă jour des informations |
2.0 | 08/2023 | Mise Ă jour des versions, ajout des blocs "docker labels" et "sans docker label", suppression des mauvaises informations |
Objectif : Configurer Traefik pour obtenir un certificat wildcard via Let's Encrypt pour tous vous services.
Environnement : Debian 12
, Docker 24.x
, docker compose (plugin) 2.20.x
, Traefik 2.10
.
Contexte d'exécution :
jho@vmi866042:/opt/docker/dc$ tree
.
âââ conf
â  âââ acme.json
â  âââ traefik.yml
â  âââ traefikdynamic
â  â  âââ dynamic.yml
âââ docker-compose.yml
âââ logs
âââ traefikAccess.log
âââ traefik.log
- dossier oĂč se trouvent tous les fichiers et dossiers pour cet exemple :
/opt/docker/dc
- emplacement du fichier de configuration principal de Traefik :
/opt/docker/dc/conf/traefik.yml
- dossier oĂč se trouvent les configurations de Traefik (fichiers dynamiques) :
/opt/docker/dc/conf/traefikdynamic
- emplacement du fichier comportant tous les certificats générés par let's encrypt (ou autre) :
/opt/docker/dc/conf/acme.json
- dossier oĂč se trouvent les journaux d'Ă©vĂ©nements (logs) :
/opt/docker/dc/logs/
Cet article implique une modification dans votre zone DNS. Assurez-vous d'avoir vos identifiants quant à l'accÚs au service ou serveur DNS, y compris les droits nécessaires pour ajouter des enregistrements et générer des clés API. Ici, j'utiliserai le fournisseur "Gandi" avec la fonctionnalité "LiveDNS". La procédure est similaire avec d'autres gestionnaires de service DNS, mais ne sera pas vu dans cet article.
Préparation du DNS
Plusieurs moyens de vérification sont possibles quant à la génération d'un certificat wildcard : TLS, HTTP ou encore DNS. Des API sont fournies par les hébergeurs, qui seront notamment utilisées par Traefik au travers de l'outil lego
.
Vous devez crĂ©er les enregistrements de type "A" nĂ©cessaires pour vos services. Ensuite, gĂ©nĂ©rez une clĂ© d'authentification API auprĂšs de votre hĂ©bergeur. Si comme moi, vous ĂȘtes chez Gandi, connectez-vous sur l'adresse "account.gandi.net", dirigez-vous dans l'onglet "Security" et gĂ©nĂ©rez une "Production API key". Gardez cette clĂ© prĂ©cieusement, si vous la perdez, vous devrez la rĂ©gĂ©nĂ©rer.
Configurations pour Docker et Traefik
J'utiliserai le dossier /opt/docker/dc
pour y stocker les fichiers YAML docker-compose et les fichiers de configuration de Traefik.
Ci-dessous le fichier docker-compose.yml
que j'utiliserai comme base pour cette documentation :
---
services:
traefik:
image: traefik:saintmarcelin
container_name: traefik
restart: unless-stopped
ports:
- target : 80
published : 80
protocol: tcp
mode : host
### BEGIN dashboad
- target : 8080
published : 8080
protocol: tcp
mode : host
### END dashboard
- target : 443
published : 443
protocol: tcp
mode : host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./conf/traefikdynamic:/dynamic
- ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
- ./conf/acme.json:/etc/traefik/acme.json
- ./logs/traefik.log:/etc/traefik/applog.log
environment:
TZ: Europe/Paris
GANDIV5_API_KEY: apikey
portainer:
container_name: portainer
image: portainer/portainer-ce:alpine
restart: unless-stopped
depends_on:
- traefik
command: -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- dataportainer:/data
labels:
traefik.enable: true
traefik.http.routers.portainer.entrypoints: websecure
traefik.http.routers.portainer.rule: Host(`portainer.domain.local`)
traefik.http.routers.portainer.middlewares: security@file
traefik.http.routers.portainer.tls: true
volumes:
dataportainer:
Fichier conf/traefik.yml
(attention, j'utilise Gandi, référez-vous à la page officielle pour adapter la configuration selon votre fournisseur DNS) :
---
global:
sendAnonymousUsage: false
checkNewVersion: false
api:
dashboard: true
log:
filePath: "/etc/traefik/applog.log"
format: json
level: "ERROR"
providers:
docker:
endpoint: unix:///var/run/docker.sock
exposedByDefault: false
watch: true
swarmMode: false
file:
directory: "/dynamic"
watch: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: contact@domain.local
# caServer: https://acme-staging-v02.api.letsencrypt.org/directory
caServer: https://acme-v02.api.letsencrypt.org/directory
storage: acme.json
keyType: EC256
dnsChallenge:
provider: gandiv5
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
Fichier conf/traefikdynamic/dynamic.yml
:
---
tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
alpnProtocols:
- h2
- http/1.1
mintls13:
minVersion: VersionTLS13
http:
middlewares:
security:
headers:
accessControlAllowMethods:
- GET
- OPTIONS
- PUT
accessControlMaxAge: 100
addVaryHeader: true
browserXssFilter: true
contentTypeNosniff: true
customFrameOptionsValue: SAMEORIGIN
customResponseHeaders:
Access-Control-Allow-Origin: "*"
Sec-Fetch-Site: cross-site
X-Forwarded-Proto: https
forceSTSHeader: true
frameDeny: true
hostsProxyHeaders:
- "X-Forwarded-Host"
referrerPolicy: "strict-origin-when-cross-origin"
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
J'attire votre attention Ă propos du fichier traefik.yml
⣠: il y a deux lignes caServer
, pointant vers des serveurs Let's Encrypt. Pendant vos tests, pour éviter un bannissement auprÚs de Let's Encrypt (nombre maximal de demandes par semaine), utilisez uniquement la ligne "caServer ... acme-staging". Lorsque vos tests seront validés (certificats correctement récupérés et générés), vous pourrez commenter la ligne "caServer ... acme-staging" et reprendre "caServer ... acme-v02". N'oubliez pas de redémarrer Traefik pour prendre en compte les modifications et régénérer les certificats "de production".
En résumé :
- Le fichier docker-compose contient la clé API précédemment générée, dans une variable d'environnement pour Traefik
- Le fichier
acme.json
est toujours présent avec les droits requis - Les labels TLS pour les services sont beaucoup plus courts et simples ! Tout se trouve dans la configuration de Traefik.
Enfin, utilisez soit l'une des deux méthodes : labels docker ou fichier de configuration dynamique.
Utilisation des labels docker
Chaque service que vous ajouterez dans votre docker-compose.yml devra comporter ces labels :
labels:
traefik.enable: true
traefik.http.routers.SERVICENAME.entrypoints: websecure
traefik.http.routers.SERVICENAME.rule: Host(`SERVICENAME.domain.local`)
traefik.http.routers.SERVICENAME.middlewares: security@file
traefik.http.routers.SERVICENAME.tls: true
traefik.http.routers.SERVICENAME.tls.certresolver: "letsencrypt"
Si on reprend le service traefik et qu'on ajoute (par exemple) le service portainer, voici ce Ă quoi doit ressembler votre fichier docker-compose.yml :
---
services:
traefik:
image: traefik:saintmarcelin
container_name: traefik
restart: unless-stopped
ports:
- target : 80
published : 80
protocol: tcp
mode : host
### BEGIN dashboad
- target : 8080
published : 8080
protocol: tcp
mode : host
### END dashboard
- target : 443
published : 443
protocol: tcp
mode : host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./conf/traefikdynamic:/dynamic
- ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
- ./conf/acme.json:/etc/traefik/acme.json
- ./logs/traefik.log:/etc/traefik/applog.log
environment:
TZ: Europe/Paris
GANDIV5_API_KEY: apikey
labels:
traefik.enable: true
traefik.http.routers.traefik-secure.entrypoints: websecure
traefik.http.routers.traefik-secure.rule: Host(`traefik.domain.local`)
traefik.http.routers.traefik-secure.tls: true
traefik.http.routers.traefik-secure.tls.certresolver: letsencrypt
traefik.http.routers.traefik-secure.tls.domains[0].main: domain.local
traefik.http.routers.traefik-secure.tls.domains[0].sans: *.domain.local
traefik.http.routers.traefik-secure.service: api@internal
portainer:
container_name: portainer
image: portainer/portainer-ce:alpine
restart: unless-stopped
depends_on:
- traefik
command: -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- dataportainer:/data
labels:
traefik.enable: true
traefik.http.routers.portainer.entrypoints: websecure
traefik.http.routers.portainer.rule: Host(`portainer.domain.local`)
traefik.http.routers.portainer.middlewares: security@file
traefik.http.routers.portainer.tls: true
volumes:
dataportainer:
Utilisation de la configuration dynamique plutĂŽt que les labels docker
Si vous n'utilisez pas les labels docker, vous devez ajouter les blocs nécessaires dans le fichier de configuration dynamique :
http:
...
services:
sc-SERVICENAME:
loadBalancer:
servers:
- url: "http://SERVICENAME:PORT"
routers:
rt-SERVICENAME:
entryPoints:
- websecure
middlewares:
- security
service: sc-SERVICENAME
rule: Host(`SERVICENAME.domain.local`)
tls:
certResolver: letsencrypt
Le fichier docker-compose (le mĂȘme que plus haut dans l'article) ne comporte aucun label (et n'en a pas besoin).
Le fichier acme.json
doit maintenant comporter quelques lignes pour votre certificat. Avec cette configuration, vos services seront à présent derriÚre Traefik, seront accessibles via SSL (et utiliseront le certificat SSL wildcard), et bénéficieront des options TLS.
Sources : Traefik et Let's Encrypt