Publié le : 09/08/2025
De l’Émission ACME au SMTP Chiffré : Guide Complet : acme.sh + HestiaCP + Exim4 (TLS)

Marre des certificats qui se renouvellent d’un côté mais que votre SMTP refuse de l’autre ?
Ce guide pas-à-pas montre comment émettre avec acme.sh, déployer automatiquement dans HestiaCP et activer le TLS/SNI dynamique d’Exim4 pour des soumissions 587 et SMTPS 465 fiables et cohérentes.
Hooks prêts à l’emploi, commandes reproductibles et testsopenssl
: tout pour passer de l’ACME au SMTP chiffré sans surprise.
1. Introduction
1.1Pourquoi externaliser les certificats avec acme.sh ?
Le module Let’s Encrypt intégré de HestiaCP repose surtout sur le challenge HTTP‑01 (et parfois sur l’interface) ; il devient contraignant si :
- Votre serveur SMTP / mail est derrière des proxys, tunnels, ou ne sert pas directement le port 80.
- Vous souhaitez des wildcards (
*.domaine.tld
) ou un multi‑SAN en DNS‑01. - Vous voulez un contrôle fin (ECC vs RSA, hooks personnalisés, script de déploiement).
acme.sh
est léger (bash pur), indépendant, offre DNS‑01 sur de nombreux fournisseurs (Cloudflare, Route53, etc.) et une API simple pour déployer / régénérer.
1.2 Objectifs du guide
- Obtenir un certificat Let’s Encrypt (wildcard ou multi‑SAN) avec
acme.sh
via DNS‑01 (Cloudflare). - L’installer proprement dans l’écosystème Hestia (répertoires attendus par Exim4).
- Activer / renforcer TLS sur Exim4 (ports 25, 587, 465 facultatif) et masquer AUTH avant chiffrement.
- Automatiser le renouvellement avec un hook unique.
- Sécuriser (protocoles, permissions) et diagnostiquer les erreurs.
1.3 Architecture Flux Certificat

2. Concepts & Terminologie
Terme | Description | Impact Exim4 |
---|---|---|
ACME | Protocole d’automatisation d’émission de certificats (RFC 8555) | Utilisé par Let’s Encrypt via acme.sh |
DNS‑01 | Challenge prouvant le contrôle du domaine par enregistrement TXT | Permet wildcard et sans ouverture HTTP/80 |
HTTP‑01 | Challenge via fichier HTTP sur port 80 | Peu pratique si reverse proxy complexe |
Wildcard | Cert couvrant *.domaine.tld + (optionnel) le domaine apex | Couvre mail. , smtp. , etc. |
SAN | Subject Alternative Name – liste des FQDN inclus | Permet un seul cert multi‑hôtes |
Fullchain | Cert feuille + intermédiaire(s) | Éviter chain incomplete chez clients |
STARTTLS (25/587) | Négociation TLS après EHLO | Mode opportuniste ou forcé sur submission |
SMTPS (465) | TLS implicite dès la connexion | Héritage, encore utilisé par certains clients |
SNI | Indication du nom serveur dans ClientHello | Exim4 peut choisir un cert par host |
3. Pré‑requis
- Accès root au serveur HestiaCP (Debian/Ubuntu).
- HestiaCP installé avec Exim4 actif.
- DNS contrôlé (exemple Cloudflare) et possibilité de créer un token API à permissions limitées (Zone:DNS:Edit + Zone:Zone:Read).
- Ports ouverts :
25
(SMTP serveur),587
(submission STARTTLS),465
(optionnel SMTPS). - Résolution DNS :
mail.domaine.tld
(ousmtp.
) pointe vers l’IP publique du serveur. - Version Exim4 ≥ 4.94 recommandée (TLS moderne & SNI corrects).
4. Installation & Mise à Jour d’acme.sh
4.1 Installation
curl https://get.acme.sh | sh
source ~/.bashrc # ou relogin pour charger alias
~/.acme.sh/acme.sh --version
4.2 Structure
~/.acme.sh/
├── acme.sh (script)
├── domaine.tld_ecc/ (cert ECC)
└── domaine.tld/ (cert RSA si émis)
4.3 Forcer Let’s Encrypt comme CA
acme.sh --set-default-ca --server letsencrypt
4.4 Mise à jour
acme.sh --upgrade
# rollback (rare) : acme.sh --upgrade --rollback
5. Émission d’un Certificat (Wildcard / Multi‑SAN)
5.1 Créer un Token Cloudflare minimal
Permissions : Zone.Zone:Read
, Zone.DNS:Edit
, restreint à la zone concernée.
5.2 Exporter les variables d’environnement
export CF_Token="CLF_T0K3N_LONG"
# (Ancienne méthode si token indisponible)
# export CF_Key="GLOBAL_API_KEY"
# export CF_Email="email@cloudflare"
Pour vérifier que le token est actif et valide, nous pouvons exécuter la commande :
curl -s -H "Authorization: Bearer $CF_Token" https://api.cloudflare.com/client/v4/user/tokens/verify | jq .
5.3 Commande d’émission (ECC recommandé)
acme.sh --issue \
-d domaine.tld \
-d '*.domaine.tld' \
--dns dns_cf \
--keylength ec-256
5.4 Vérifier SAN & Dates
openssl x509 -in ~/.acme.sh/domaine.tld_ecc/domaine.tld.cer -noout -ext subjectAltName -dates -subject
Attendu : DNS:domaine.tld, DNS:*.domaine.tld
.
5.5 Variante RSA (si compat héritage nécessaire)
acme.sh --issue -d domaine.tld -d '*.domaine.tld' --dns dns_cf --keylength 2048
5.6 Erreurs Fréquentes
Erreur | Cause | Solution |
---|---|---|
« No CF token specified » | Variable non exportée | Exporter CF_Token avant commande |
TXT not found | Propagation DNS lente | Attendre + utiliser --dnssleep si besoin |
Rate limit | Trop d’émissions | Regrouper SAN, éviter --force répétitif |
SAN manquant (pas wildcard) | Oubli -d '*.domaine.tld' | Ré‑émettre avec wildcard + --force |
6. Stratégies de Gestion de Certificats Exim4
Stratégie | Description | Quand | Avantages | Inconvénients |
---|---|---|---|---|
Wildcard unique | domaine.tld + *.domaine.tld | Multi sous‑domaines simples | Un seul renouvellement | Ré‑émission pour autre domaine racine |
Multi‑SAN | mail.domaineA , mail.domaineB , etc. | Peu de domaines divers | Un cert pour tout | Ajout = ré‑émettre |
Certs multiples (SNI) | 1 cert par mail.domaineX | Hébergement multi‑clients | Isolation, révocation ciblée | Gestion plus complexe |
ECC only | Courant moderne | Clients récents | Handshake plus léger | Vieux MTA/clients rares incompatibles |
ECC + RSA | Deux jeux de certs | Environnement hétérogène | Compat maximale | Maintenance double |
Conseil : commencer avec un wildcard ECC unique. Ajouter RSA uniquement si un client concret échoue.
7. Comprendre la Config TLS Générée par Hestia
Hestia injecte dans la config Exim4 (fichier auto‑généré) une expression SNI ressemblant à :
tls_certificate = \
${if and { { eq {${domain:foo@$tls_in_sni}} {$tls_in_sni}} \
{ exists{/usr/local/hestia/ssl/mail/$tls_in_sni.crt} } } \
{/usr/local/hestia/ssl/mail/$tls_in_sni.crt} \
{/usr/local/hestia/ssl/certificate.crt} }
Même logique pour tls_privatekey
.
Logique :
- Si un fichier spécifique
/usr/local/hestia/ssl/mail/<FQDN>.crt
existe (ex:mail.domaine.tld.crt
), il est utilisé. - Sinon Exim4 retombe sur le fallback global
/usr/local/hestia/ssl/certificate.crt
(souvent auto‑signé par défaut).
Conséquence : Vous devez déployer le certificat wildcard dans ces chemins pour qu’Exim l’utilise sans modifier la macro.
8. Méthodes de Déploiement du Certificat
8.1 Déploiement direct (recommandé)
acme.sh --install-cert
écrit directement dans les chemins qu’Exim attend.
acme.sh --install-cert -d domaine.tld --ecc \
--fullchain-file /usr/local/hestia/ssl/mail/mail.domaine.tld.crt \
--key-file /usr/local/hestia/ssl/mail/mail.domaine.tld.key \
--reloadcmd "chown root:Debian-exim /usr/local/hestia/ssl/mail/mail.domaine.tld.key; \
chmod 640 /usr/local/hestia/ssl/mail/mail.domaine.tld.key; \
cp /usr/local/hestia/ssl/mail/mail.domaine.tld.crt /usr/local/hestia/ssl/certificate.crt; \
cp /usr/local/hestia/ssl/mail/mail.domaine.tld.key /usr/local/hestia/ssl/certificate.key; \
chown root:Debian-exim /usr/local/hestia/ssl/certificate.key; \
chmod 640 /usr/local/hestia/ssl/certificate.key; \
chmod 644 /usr/local/hestia/ssl/mail/mail.domaine.tld.crt /usr/local/hestia/ssl/certificate.crt; \
systemctl reload exim4"
8.2 Variante Script (multi‑domaines)
Créer /usr/local/bin/deploy-mail-cert.sh
:
#!/usr/bin/env bash
set -euo pipefail
FQDN="$1" # ex: mail.domaine.tld
CRT_SRC="$2" # fullchain source
KEY_SRC="$3"
MAIL_DIR="/usr/local/hestia/ssl/mail"
mkdir -p "$MAIL_DIR"
cp "$CRT_SRC" "$MAIL_DIR/$FQDN.crt"
cp "$KEY_SRC" "$MAIL_DIR/$FQDN.key"
chown root:Debian-exim "$MAIL_DIR/$FQDN.key"
chmod 640 "$MAIL_DIR/$FQDN.key"; chmod 644 "$MAIL_DIR/$FQDN.crt"
# Met à jour fallback (optionnel)
cp "$CRT_SRC" /usr/local/hestia/ssl/certificate.crt
cp "$KEY_SRC" /usr/local/hestia/ssl/certificate.key
chown root:Debian-exim /usr/local/hestia/ssl/certificate.key
chmod 640 /usr/local/hestia/ssl/certificate.key; chmod 644 /usr/local/hestia/ssl/certificate.crt
systemctl reload exim4
chmod +x /usr/local/bin/deploy-mail-cert.sh
Installer :
acme.sh --install-cert -d domaine.tld --ecc \
--fullchain-file /tmp/fullchain.pem \
--key-file /tmp/priv.key \
--reloadcmd "/usr/local/bin/deploy-mail-cert.sh mail.domaine.tld /tmp/fullchain.pem /tmp/priv.key"
8.3 Comparatif
Méthode | Avantage | Inconvénient |
---|---|---|
Directe | Simplicité, aucun fichier intermédiaire | Moins flexible multi‑cibles |
Script | Extensible (multi FQDN, logs) | Maintenance script |
Symlink | Un seul master | Fragile lors d’écrasement Hestia |
9. Configuration TLS Exim4 : Paramètres Clés
Créer un fichier macros précoce : /etc/exim4/conf.d/main/01_local_tls_macros
:
MAIN_TLS_ENABLE = yes
MAIN_TLS_ADVERTISE_HOSTS = *
# Ports SMTP (server, submission, SMTPS)
daemon_smtp_ports = 25 : 587 : 465
# SMTPS implicite
tls_on_connect_ports = 465
(Vous n’indiquez pas ici tls_certificate
car l’expression SNI générée par Hestia le gère.)
Masquer AUTH avant TLS (si non déjà présent) dans un override tardif :/etc/exim4/conf.d/main/40_local_auth_tls.conf
:
auth_advertise_hosts = ${if eq{$tls_in_cipher}{}{}{*}}
Regénérer & recharger :
update-exim4.conf
systemctl reload exim4
9.1 Vérifier paramètres effectifs
exim -bP daemon_smtp_ports
exim -bP tls_on_connect_ports
exim -bP tls_certificate
exim -bP tls_privatekey
10. Port 465 (SMTPS) vs 587 (Submission)
Aspect | 465 (SMTPS implicite) | 587 (Submission STARTTLS) |
---|---|---|
Standardisation actuelle | Héritage (réactivé officiel RFC 8314) | Recommandé moderne |
Négociation TLS | Directe (moins d’aller‑retour) | Upgrade après EHLO |
Compat clients anciens | Certains insistent sur 465 | Tous supportent 587 |
Durcissement AUTH | Déjà chiffré dès connexion | Nécessite cacher AUTH avant TLS |
Conseil : Garder 587 obligatoire + 465 pour compat (ou monitoring séparé). Si simplification : retirer 465 (enlever de daemon_smtp_ports
).
11. Sécurité & Bonnes Pratiques TLS SMTP
11.1 Protocoles & Ciphers
Dans un override (ex. 50_local_tls_policies.conf
) :
openssl_options = +no_sslv2 +no_sslv3 +no_tlsv1 +no_tlsv1_1
# (GnuTLS selon build ; adapter si nécessaire)
Pour ciphers, Exim peut utiliser tls_require_ciphers
(OpenSSL) :
tls_require_ciphers = HIGH:!aNULL:!MD5:!RC4:!3DES:!CAMELLIA:!DES:!EXP:!PSK:!SRP:!DSS
11.2 Chaîne Complète
Toujours déployer le fullchain (cert + intermédiaire). Sans cela : Verify return code: 21
côté clients.
11.3 Permissions Clé
chown root:Debian-exim /usr/local/hestia/ssl/mail/mail.domaine.tld.key
chmod 640 /usr/local/hestia/ssl/mail/mail.domaine.tld.key
11.4 STARTTLS Opportuniste vs Strict
- Port 25 (serveur à serveur) : opportuniste (permettre plain fallback pour compat, sauf politique MTA‑STS / DANE avancée).
- Port 587 : exiger TLS avant AUTH (déjà couvert par
auth_advertise_hosts
).
11.5 MTA-STS / DANE (aperçu)
Non traité en détail ici ; préparez en veillant à une politique TLS stable & publication DNS (TLSA) si adoption DANE.
12. Automatisation du Renouvellement
acme.sh crée un cron quotidien (~/.acme.sh/acme.sh --cron
). Le renouvellement se lance à ~60 jours pour un cert LE (validité 90 jours).
12.1 Test de renouvellement forcé
acme.sh --renew -d domaine.tld --ecc --force
Vérifier que le hook :
- Met à jour les fichiers.
- Recharge Exim4 (
systemctl reload exim4
).
12.2 Vérification Post‑Renouvellement
openssl s_client -connect mail.domaine.tld:587 -starttls smtp -servername mail.domaine.tld </dev/null 2>/dev/null | openssl x509 -noout -dates
Comparer la nouvelle date notAfter
.
12.3 Monitoring Expiration (cron simple)
#!/usr/bin/env bash
CRT="/usr/local/hestia/ssl/mail/mail.domaine.tld.crt"
EXP=$(date -d "$(openssl x509 -in $CRT -noout -enddate | cut -d= -f2)" +%s)
NOW=$(date +%s)
DAYS=$(( (EXP-NOW)/86400 ))
if [ $DAYS -lt 20 ]; then
echo "ALERTE: Certificat SMTP expire dans $DAYS jours" | mail -s "Certificat SMTP" [email protected]
fi
13. Tests & Vérifications Post‑Déploiement
13.1 STARTTLS Submission (587)
openssl s_client -connect mail.domaine.tld:587 -starttls smtp -servername mail.domaine.tld </dev/null 2>/dev/null | grep -E 'subject=|issuer=|Verify'
13.2 SMTPS (465)
openssl s_client -connect mail.domaine.tld:465 -servername mail.domaine.tld </dev/null 2>/dev/null | grep -E 'subject=|issuer=|Verify'
13.3 Vérifier annonce AUTH après TLS
- Lancer handshake STARTTLS.
- Après chiffrement, envoyer :
EHLO test
Attendu : ligne 250-AUTH PLAIN LOGIN
(si auth active), absente en clair avant STARTTLS.
13.4 Paramètres Exim
exim -bP tls_certificate
tls_privatekey=$(exim -bP tls_privatekey)
13.5 Outils externes
- CheckTLS ou Hardenize : audit public.
- SSL Labs (SMTP) via projets tiers (affiche protocole, ciphers, chaîne).
14. Dépannage (Troubleshooting)
Symptôme | Analyse | Correctif |
---|---|---|
tls_certificate = \ (vide) | Expression SNI retombe sur fallback inexistant | Déployer mail.<domaine>.crt ou fallback certificate.crt |
Self‑signed toujours présenté | Fichiers personnalisés absents / non lisibles | Copier cert/clé dans /usr/local/hestia/ssl/mail/ , permissions 640 clé |
Cannot load private key | Permissions ou format clé | chown root:Debian-exim , chmod 640 , vérifier BEGIN PRIVATE KEY |
Pas de STARTTLS sur 587 | MAIN_TLS_ENABLE absent / macro non chargée | Ajouter macros + update-exim4.conf |
AUTH visible avant TLS | auth_advertise_hosts non configuré | Ajouter override (voir §9) |
Erreur verify code 21 | Intermédiaire manquant | Utiliser fullchain en déploiement |
ECC rejeté par client | Client obsolète | Fournir aussi un RSA (multi-cert + SNI ou multi-SAN) |
Nouveau domaine non chiffré | Cert non déployé sous nom exact | Créer mail.nvdom.tld.crt/.key + reload |
15. Scénarios d’Évolution
Scénario | Action rapide |
---|---|
Ajouter mail.client2.tld | Émettre cert dédié ou ré‑émettre multi‑SAN ; déployer mail.client2.tld.crt |
Passer d’un wildcard à multi‑cert | Générer certs individuels + script boucle copie |
Migrer HTTP‑01 → DNS‑01 | Émettre nouveau cert DNS‑01 (sans downtime), recharger Exim |
Ajouter RSA parallèle | Émettre RSA, adapter expression SNI (préfixer nom), prioriser ECC |
16. Monitoring & Alerting
16.1 Indicateurs Clés
- Jours avant expiration
- Pourcentage connexions TLS vs total (analyser logs
- Échecs handshake / erreurs clé
16.2 Script Stats (exemple simple)
grep -i STARTTLS /var/log/exim4/mainlog | wc -l
(Pour ratio, comparer aux lignes <=
/ =>
trafic total.)
16.3 Intégration Basique
- Cron journalier : test openssl + si échec → mail admin.
- Webhook (curl) vers outil externe (UptimeRobot, Healthchecks.io).
17. FAQ Spécifique Exim4
Wildcard ou multi‑cert ? Wildcard unique suffit dans 80% des cas. Multi‑cert si clients isolés / résiliation granulaire.
Faut‑il garder le fallback Hestia auto‑signé ? Non, remplacez-le par un public pour éviter présentation accidentelle self‑signed.
Comment forcer AUTH seulement après TLS ? auth_advertise_hosts = ${if eq{$tls_in_cipher}{}{}{*}}
.
Dois‑je maintenir le port 465 ? Optionnel. Conservez-le si des clients historiques l’utilisent ; sinon 587 suffit.
Puis-je partager un cert entre domaines non inclus ? Non. Chaque FQDN demandé par le client doit figurer en SAN ou être couvert par un wildcard approprié.
Pourquoi le cert ne change pas après renouvellement ? Hook absent ou pas de reload Exim (systemctl reload exim4
).
18. Checklist Finale Mise en Production
Item | OK |
---|---|
Cert wildcard émis (SAN correct) | ☐ |
Fichiers déployés /usr/local/hestia/ssl/mail/mail.domaine.tld.* | ☐ |
Permissions clé 640 root:Debian-exim | ☐ |
Fallback remplacé (/usr/local/hestia/ssl/certificate.crt ) | ☐ |
daemon_smtp_ports inclut 587 (et 465 si voulu) | ☐ |
tls_on_connect_ports contient 465 (si utilisé) | ☐ |
AUTH masqué avant TLS | ☐ |
Tests openssl 25/587/465 OK (verify code 0) | ☐ |
Cron acme.sh présent & test renew forcé OK | ☐ |
Monitoring expiration (<20j) actif | ☐ |
Logs Exim sans erreurs TLS | ☐ |
19. Annexes
19.1 Commandes acme.sh Essentielles
# Wildcard ECC
a cme.sh --issue -d domaine.tld -d '*.domaine.tld' --dns dns_cf --keylength ec-256
# Installation + hook
acme.sh --install-cert -d domaine.tld --ecc --fullchain-file /usr/local/hestia/ssl/mail/mail.domaine.tld.crt --key-file /usr/local/hestia/ssl/mail/mail.domaine.tld.key --reloadcmd "systemctl reload exim4"
# Forcer renouvellement (test)
acme.sh --renew -d domaine.tld --ecc --force
19.2 Exemple 01_local_tls_macros
MAIN_TLS_ENABLE = yes
MAIN_TLS_ADVERTISE_HOSTS = *
daemon_smtp_ports = 25 : 587 : 465
tls_on_connect_ports = 465
19.3 Séquence de Tests OpenSSL (copier-coller)
# Submission STARTTLS
openssl s_client -connect mail.domaine.tld:587 -starttls smtp -servername mail.domaine.tld </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
# SMTPS
openssl s_client -connect mail.domaine.tld:465 -servername mail.domaine.tld </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
# Port 25 (opportuniste)
openssl s_client -connect mail.domaine.tld:25 -starttls smtp -servername mail.domaine.tld </dev/null 2>/dev/null | grep -i "Verify return code"
19.4 Exemple Hook Multi‑Actions Minimal
#!/usr/bin/env bash
# /usr/local/bin/deploy-mail-cert.sh
set -e
CRT="/usr/local/hestia/ssl/mail/mail.domaine.tld.crt"
KEY="/usr/local/hestia/ssl/mail/mail.domaine.tld.key"
chown root:Debian-exim "$KEY"; chmod 640 "$KEY"; chmod 644 "$CRT"
cp "$CRT" /usr/local/hestia/ssl/certificate.crt
cp "$KEY" /usr/local/hestia/ssl/certificate.key
chown root:Debian-exim /usr/local/hestia/ssl/certificate.key
chmod 640 /usr/local/hestia/ssl/certificate.key; chmod 644 /usr/local/hestia/ssl/certificate.crt
systemctl reload exim4
19.5 Profil Ciphers (intermédiaire) Exemple
# OpenSSL style
# (Adapter selon exigences PCI/legacy)
tls_require_ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL:!MD5:!RC4:!3DES:!DES:!EXP:!DSS"

20. TL;DR / Résumé Express
- Émettre wildcard via
acme.sh --issue -d domaine -d '*.domaine' --dns dns_cf
. - Installer cert directement dans
/usr/local/hestia/ssl/mail/mail.domaine.crt|.key
avec--install-cert
+ hook reload Exim. - Vérifier macros Exim :
MAIN_TLS_ENABLE
,daemon_smtp_ports
,tls_on_connect_ports
. - Masquer AUTH avant TLS (
auth_advertise_hosts
). - Tester
openssl
sur 587 (STARTTLS) & 465 (SMTPS) → verify code 0. - Remplacer fallback auto‑signé Hestia par le public.
- Cron acme.sh gère renouvellement ; script hook recharge Exim.
- Monitorer expiration & erreurs handshake.
- Ajouter nouveaux domaines en déposant leurs
.crt/.key
SNI. - Garder config simple (ECC unique) jusqu’à besoin réel de RSA / multi‑cert.