Le TLS mutuel — où le serveur valide le certificat du client, pas seulement l’inverse — est la façon dont les API internes, les flottes IoT et les passerelles zero-trust prouvent qui se connecte avant qu’aucune charge utile ne soit envoyée. Caddy supporte mTLS via le bloc client_auth sous tls. Cet article est la configuration de production qu’on déploie chez les clients : câblage du pool de confiance, modes de validation, transmission d’en-têtes pour consommation amont, et le motif de rotation qui garde la flotte exploitable.
Comment vérifier
Un endpoint mTLS fonctionnel se comporte comme ceci :
curl -I https://api.example.com/ # rejeté, aucun certificat client
curl --cert /path/to/client.crt --key /path/to/client.key \
-I https://api.example.com/ # accepte
openssl s_client -connect api.example.com:443 -servername api.example.com \
-cert /path/to/client.crt -key /path/to/client.key < /dev/null
sudo journalctl -u caddy --since '5m ago' | grep -i 'client.cert\|verif'
Sans certificat client, Caddy retourne une alerte TLS. Avec un certificat valide, le handshake complète et la requête atteint l’application. L’upstream voit les détails du certificat dans les en-têtes transmis — X-Client-Subject, X-Client-Issuer, etc.
Ce qui se passe
mTLS étend le handshake TLS standard en ajoutant un côté client. Après que le serveur a présenté son certificat, le serveur envoie un CertificateRequest au client. Le client répond avec sa propre chaîne de certificats et prouve la possession de la clé privée en signant la transcription du handshake. Le serveur valide la chaîne client contre un pool de confiance configuré (un ensemble de CA racine de confiance) et accepte ou rejette la connexion.
Le bloc client_auth de Caddy contrôle ça :
mode— un parmirequest,require,verify_if_given,require_and_verify. Le mode détermine si un certificat client manquant ou invalide est fatal.trusted_ca_cert_fileoutrusted_ca_cert— la ou les CA depuis lesquelles Caddy acceptera les certificats client. C’est l’ancre de confiance.trusted_leaf_cert_file— certificats client individuels épinglés, contournant la validation basée CA. Utile quand vous avez un petit ensemble fixe de clients.
Après validation, Caddy expose les détails du certificat au gestionnaire de requête via des espaces réservés : {tls_client_subject}, {tls_client_issuer}, {tls_client_serial}, {tls_client_fingerprint}. Vous les transmettez à l’upstream comme en-têtes pour que l’application puisse autoriser basé sur l’identité, pas seulement sur la connexion.
La procédure
-
Préparer le pool de confiance. Utilisez le certificat racine de votre CA interne, ou assemblez un bundle de CA de confiance :
# Racine CA interne sudo cp /etc/pki/ca-trust/source/anchors/internal-ca.crt /etc/caddy/clients/ca.crt sudo chown caddy:caddy /etc/caddy/clients/ca.crt sudo chmod 644 /etc/caddy/clients/ca.crtLe fichier est encodé PEM. Concaténez plusieurs blocs PEM si vous avez plusieurs CA.
-
Activer mTLS sur le site. La config minimale :
api.example.com { tls { client_auth { mode require_and_verify trusted_ca_cert_file /etc/caddy/clients/ca.crt } } reverse_proxy localhost:3000 }require_and_verifyest le mode strict : le client DOIT présenter un certificat ET le certificat DOIT valider contre le pool de confiance. Tout le reste est rejeté au handshake — l’application ne voit jamais la requête. -
Transmettre les détails du certificat à l’upstream. L’application a besoin de savoir quel client est connecté :
api.example.com { tls { client_auth { mode require_and_verify trusted_ca_cert_file /etc/caddy/clients/ca.crt } } reverse_proxy localhost:3000 { header_up X-Client-Subject {tls_client_subject} header_up X-Client-Issuer {tls_client_issuer} header_up X-Client-Serial {tls_client_serial} header_up X-Client-Fingerprint {tls_client_fingerprint} } }Le code de l’application lit
X-Client-Subjectpour identifier l’appelant. L’en-tête d’empreinte est l’identifiant le plus stable pour les recherches de révocation. -
Site en mode mixte — certains chemins exigent mTLS, d’autres non. Courant pour une API qui a un endpoint de santé public et des routes authentifiées :
api.example.com { tls { client_auth { mode verify_if_given trusted_ca_cert_file /etc/caddy/clients/ca.crt } } @authenticated { not path /health /version /docs/* } handle @authenticated { @no_cert vars {tls_client_subject} "" respond @no_cert "Client certificate required" 401 reverse_proxy localhost:3000 { header_up X-Client-Subject {tls_client_subject} } } handle { reverse_proxy localhost:3000 } }verify_if_givenaccepte la connexion sans certificat client, valide si l’un est présenté. Le gestionnaire applique alors la présence du certificat par chemin. -
Certificats feuille épinglés. Quand vous avez un petit ensemble connu de clients et que vous voulez contourner la confiance basée CA :
internal-api.example.com { tls { client_auth { mode require_and_verify trusted_leaf_cert_file /etc/caddy/clients/leaf-bundle.pem } } reverse_proxy localhost:3000 }leaf-bundle.pemest une concaténation de chaque certificat client individuel qui devrait être accepté. Ajoutez ou retirez un certificat, rechargez Caddy. Pas de CA, pas de chaîne — juste la feuille. -
Révocation. Caddy ne vérifie pas nativement les CRL ou OCSP pour les certificats client. Les deux motifs d’exploitation :
- Reconstruire le pool de confiance. Émettez une nouvelle CA interne, réémettez chaque certificat client, échangez le fichier de confiance. Lourd mais étanche.
- Épingler les certificats feuille. Maintenez le
trusted_leaf_cert_filecomme source de vérité et retirez les certificats révoqués du bundle. - Autoriser dans l’application. Caddy valide la chaîne ; l’application vérifie le numéro de série du certificat contre une liste de révocation tirée de la base de données de votre CA.
Pièges courants
mode requestn’est pas la même chose quemode verify_if_given.requestdemande un certificat et permet la connexion peu importe ;verify_if_givenvalide si présenté.requestn’est presque jamais ce que vous voulez — utilisezverify_if_givenourequire_and_verify.- Les certificats client auto-signés ne valident pas contre une CA. Si votre fichier CA est juste le certificat auto-signé lui-même, il marche uniquement pour ce certificat — c’est de l’épinglage de feuille déguisé.
- L’espace réservé
{tls_client_subject}est le nom distingué du certificat sous forme de chaîne commeCN=client01,O=Example,C=US. Votre code d’application devrait le parser ou utiliser l’empreinte à la place pour des identifiants stables. - Les navigateurs mettent en cache les sélections de certificat client par session. Un utilisateur testant différents certificats voit un comportement périmé — il doit fermer et rouvrir le navigateur, ou frapper un nom d’hôte différent.
- Un mauvais fichier CA (mauvais format, retours à la ligne manquants entre blocs PEM, racine expirée) fait échouer le démarrage de Caddy. Validez avec
openssl x509 -in /etc/caddy/clients/ca.crt -noout -textavant rechargement.
Stack Harbor opère des endpoints mTLS pour les clients dont les services internes doivent prouver l’identité au-delà de la liste blanche d’IP — infrastructure de CA interne sous gestion, cycle de vie des certificats client intégré au fournisseur d’identité du client, et pools de confiance Caddy tournés à l’horaire. Si vous avez une API interne ou une passerelle zero-trust qui a besoin du plan certificats opéré pour vous, c’est le genre de travail couvert par notre pratique Exploitation infogérée.