Ce qui reste identique à Let’s Encrypt

Si vous avez déjà obtenu un certificat Let’s Encrypt, vous pouvez déjà utiliser l’AC interne. Le protocole est identique — ACME RFC 8555. Les clients que vous connaissez déjà fonctionnent sans modification.

Identique :

  • Challenges HTTP-01 et DNS-01.
  • Enregistrement de compte et gestion de clé.
  • Durée par défaut de 90 jours.
  • Renouvellement automatique à 60 jours.
  • Certificats wildcard via DNS-01.

Ce qui diffère

Deux petites différences, toutes deux des étapes de setup ponctuelles :

  1. L’URL du répertoire ACME pointe sur l’AC interne, pas sur https://acme-v02.api.letsencrypt.org/directory.
  2. Le certificat racine de l’AC interne doit être approuvé par le client qui fait la demande. Cela se met en place via gestion de configuration (Ansible, Puppet, MDM) lors du provisioning des hôtes.

C’est toute la différence. Tout le reste est le même workflow Let’s Encrypt que vous connaissez.

Pointer votre client sur l’AC interne

Le endpoint ACME interne est publié à une URL interne stable (typiquement https://acme.example.internal/acme/acme/directory, mais le chemin exact dépend de votre déploiement). Voici des recettes pour les clients courants.

certbot

certbot --server https://acme.example.internal/acme/acme/directory \
        --email ops@example.internal \
        --no-eff-email \
        --agree-tos \
        certonly --standalone -d service.example.internal

Au renouvellement, le flag --server est persisté dans renewal/*.conf, donc le cron périodique certbot renew le reprend automatiquement.

acme.sh

acme.sh --register-account \
        --server https://acme.example.internal/acme/acme/directory \
        -m ops@example.internal

acme.sh --issue \
        --server https://acme.example.internal/acme/acme/directory \
        --standalone \
        -d service.example.internal

lego

lego --server=https://acme.example.internal/acme/acme/directory \
     --email=ops@example.internal \
     --domains=service.example.internal \
     --http run

Caddy

Dans votre Caddyfile :

{
  acme_ca https://acme.example.internal/acme/acme/directory
  email ops@example.internal
}

service.example.internal {
  reverse_proxy localhost:8080
}

Caddy obtient et renouvelle le certificat automatiquement.

Traefik

Dans votre configuration dynamique :

certificatesResolvers:
  internal:
    acme:
      caServer: "https://acme.example.internal/acme/acme/directory"
      email: ops@example.internal
      storage: /etc/traefik/acme.json
      httpChallenge:
        entryPoint: web

Référencer certResolver: internal depuis vos définitions de routeur.

Installer le certificat racine sur les clients

Pour que les certificats émis par l’AC soient approuvés sans avertissement, la racine de l’AC doit être dans le store de confiance système de chaque hôte qui verra les certificats.

Linux (famille Debian/Devuan)

sudo cp internal-ca-root.crt /usr/local/share/ca-certificates/internal-ca-root.crt
sudo update-ca-certificates

Linux (famille RHEL)

sudo cp internal-ca-root.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract

OpenBSD / FreeBSD

# OpenBSD
sudo install -m 0644 internal-ca-root.crt /etc/ssl/internal-ca-root.crt
sudo cat /etc/ssl/cert.pem internal-ca-root.crt > /etc/ssl/cert.pem.new
sudo mv /etc/ssl/cert.pem.new /etc/ssl/cert.pem

macOS

sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain internal-ca-root.crt

Windows

certutil.exe -addstore -f "ROOT" internal-ca-root.crt

Pour les navigateurs qui maintiennent leurs propres stores de confiance (Firefox), la racine doit aussi être importée via Paramètres → Vie privée → Certificats → Autorités.

Cette installation est normalement gérée par la gestion de configuration. Le faire à la main est ok pour un cas unique mais ne passe pas à l’échelle pour une flotte.

Certificats wildcard

Les wildcards sont émis via le challenge DNS-01 — le client prouve le contrôle d’un domaine en créant un enregistrement TXT. Cela fonctionne pour des domaines purement internes parce que l’AC interne peut valider des enregistrements TXT sur n’importe quel serveur DNS que vous contrôlez.

Exemple avec acme.sh utilisant le provider BIND nsupdate :

acme.sh --issue \
        --server https://acme.example.internal/acme/acme/directory \
        --dns dns_nsupdate \
        -d '*.example.internal' \
        -d 'example.internal'

Vous aurez besoin que le provider nsupdate soit configuré contre votre DNS interne — voir la documentation acme.sh pour les variables d’environnement spécifiques.

Renouvellement

Le renouvellement fonctionne comme avec Let’s Encrypt : le client vérifie la validité restante et renouvelle quand sous le seuil (60 jours pour un cert de 90 jours).

Pour la plupart des déploiements, le cron de renouvellement existant (certbot renew, acme.sh --cron, le renouvellement auto de Caddy) marche tout seul.

En cas d’échecs de renouvellement :

  1. L’URL du répertoire est-elle toujours joignable ? Un changement réseau peut avoir cassé le chemin du client à l’AC.
  2. Le certificat racine a-t-il expiré ou été tourné ? Les racines sont typiquement valides 10 ans ; les intermédiaires pour des périodes plus courtes. Une rotation qui n’a pas propagé à tous les clients se manifestera par des échecs de renouvellement.
  3. Le type de challenge est-il toujours approprié ? Un service qui était joignable pour HTTP-01 peut maintenant se trouver derrière un load balancer qui casse la réponse du challenge.

Limites et politiques

L’AC interne applique des politiques par compte — quels domaines chaque compte peut demander des certificats, quelle durée maximum est autorisée, quels types de challenge sont acceptés.

Si une requête qui marchait hier échoue soudainement avec une erreur policy denied, la politique a changé (ou votre binding de compte). Contactez votre opérateur d’AC. Le service Exploitation est le chemin d’escalade si votre opérateur d’AC c’est nous.