04 - SSL avec Caddy : partager les certificats Let's Encrypt
Ce que tu vas apprendre
- Comment réutiliser les certificats Let's Encrypt de Caddy pour le mailserver
- La configuration Caddyfile et Docker Compose
- Pourquoi c'est mieux que de faire tourner certbot séparément
Prerequisites
- Caddy en reverse proxy sur ton VPS (voir la serie déploiement)
- 03 - docker-mailserver : le container est configure
Le problème des certificats en double
docker-mailserver a besoin de certificats SSL pour chiffrer les connexions IMAP (port 993) et SMTP (ports 465, 587). La méthode classique c'est d'installer certbot dans le container ou a cote, de configurer un cron pour le renouvellement, et de gerer tout ca en parallèle.
Sauf que si tu utilises deja Caddy comme reverse proxy, Caddy gere deja les certificats Let's Encrypt pour tous tes domaines. Automatiquement. Sans cron. Sans config.
Alors pourquoi faire le travail deux fois ?
L'astuce : un volume Docker partage
Le principe est simple. Caddy stocke ses certificats dans un volume Docker (caddy_data). On monte ce volume en lecture seule dans le container mailserver. docker-mailserver lit les certs directement. Fini.
Pas de certbot. Pas de cron de renouvellement. Pas de scripts de copie. Caddy renouvelle les certs, le mailserver les utilise. Un seul endroit, une seule source de vérité.
Le Caddyfile
Pour que Caddy généré un certificat pour mail.paltemps.fr, il suffit de déclarer le sous-domaine dans le Caddyfile :
textmail.paltemps.fr {
respond "Mail server" 200
}
C'est tout. Caddy voit qu'il doit servir mail.paltemps.fr, il provisionne automatiquement un certificat Let's Encrypt via le protocole ACME. Le certificat couvre exactement ce sous-domaine.
Le respond "Mail server" 200 c'est juste un handler minimal. On ne sert rien de vrai sur ce domaine en HTTP, mais Caddy a besoin d'un bloc pour déclencher la génération du cert. Si tu veux, tu peux aussi rediriger vers ton site principal, ca n'a pas d'importance.
Pour comprendre comment fonctionne ACME et Let's Encrypt en détail, j'en parle dans TLS, HTTPS et certificats.
Le Docker Compose
Cote Caddy, le volume est déclaré normalement :
yamlservices:
caddy:
image: caddy:latest
container_name: caddy
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
volumes:
caddy_data:
Cote mailserver, on monte le meme volume en lecture seule :
yamlservices:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
volumes:
- caddy_data:/caddy-data:ro
environment:
- SSL_TYPE=manual
- SSL_CERT_PATH=/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.paltemps.fr/mail.paltemps.fr.crt
- SSL_KEY_PATH=/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.paltemps.fr/mail.paltemps.fr.key
volumes:
caddy_data:
external: true
Le chemin est long, oui. C'est la structure interne de Caddy pour stocker ses certificats ACME. Le format c'est :
/data/caddy/certificates/<issuer>/<domain>/<domain>.crt et .key.
Si les deux services sont dans des fichiers docker-compose.yml différents, le volume caddy_data doit etre déclaré external: true dans le fichier du mailserver. Ca dit a Docker d'utiliser le volume existant au lieu d'en créer un nouveau.
Pourquoi SSL_TYPE=manual
docker-mailserver supporte plusieurs stratégies SSL :
letsencrypt: le container gere ses propres certs (pas ce qu'on veut)manual: tu fournis les chemins vers le cert et la cléself-signed: certificats auto-signes (a éviter en production)
On utilise manual parce qu'on pointe vers les fichiers générés par Caddy. Le mailserver ne sait meme pas que c'est Caddy qui les gere. Il voit juste un fichier .crt et un fichier .key au chemin qu'on lui donne.
Le renouvellement
Caddy renouvelle les certificats automatiquement avant leur expiration (Let's Encrypt certs durent 90 jours, Caddy renouvelle a 30 jours de l'echeance). Les fichiers sont mis à jour sur le volume.
Il y a un détail : docker-mailserver charge les certificats au démarrage. Si Caddy renouvelle un cert, le mailserver utilise toujours l'ancien en mémoire jusqu'au prochain redemarrage. En pratique c'est rarement un problème (les certs ont 90 jours de validite), mais si tu veux etre propre, un petit restart periodique fait l'affaire :
bash# Restart hebdomadaire dans un cron
0 4 * * 1 docker restart mailserver
Ou alors tu peux utiliser un healthcheck plus malin qui compare les dates de modification des fichiers cert. Mais franchement, le cron weekly c'est suffisant.
Tester la connexion SSL
Pour vérifier que le certificat est bien servi sur le port IMAP :
bashopenssl s_client -connect mail.paltemps.fr:993 -servername mail.paltemps.fr
Tu devrais voir le certificat Let's Encrypt avec le bon Common Name. Si tu vois "self-signed" ou une erreur de certificat, c'est que les chemins dans SSL_CERT_PATH / SSL_KEY_PATH ne sont pas bons. Verifie en entrant dans le container :
bashdocker exec mailserver ls -la /caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.paltemps.fr/
Tu dois voir le .crt et le .key. Si le répertoire n'existe pas, c'est que Caddy n'a pas encore généré le certificat pour mail.paltemps.fr. Verifie ton Caddyfile.
Pour tester SMTP avec STARTTLS :
bashopenssl s_client -connect mail.paltemps.fr:587 -starttls smtp
L'avantage concret
Avec cette approche, tu as un seul système de gestion de certificats pour tout ton serveur. Caddy gere les certs pour ton site web, ton API, et ton serveur mail. Pas de certbot a maintenir, pas de hooks post-renewal, pas de scripts de copie entre containers.
Quand j'ai mis ca en place la première fois, j'ai gagne une heure de debug par rapport a ma tentative précédente avec certbot standalone. Caddy rend le SSL presque invisible, et c'est exactement ce qu'on veut.
Prochain article : Delivrabilite : atteindre 9/10 sur mail-tester.com.
Navigation : Precedent : 03 - docker-mailserver | Suivant : 05 - Delivrabilite
Sources
- Caddy - Automatic HTTPS par Caddy
- Let's Encrypt - How It Works par Let's Encrypt
- docker-mailserver - SSL Configuration par docker-mailserver
Retrouve d'autres articles techniques sur paltemps.fr.