Co pozostaje takie samo jak przy Let’s Encrypt

Jeśli kiedykolwiek pobrałeś certyfikat Let’s Encrypt, już umiesz używać wewnętrznego CA. Protokół jest identyczny — ACME RFC 8555. Klienci, których już znasz, działają bez modyfikacji.

To samo:

  • Challenges HTTP-01 i DNS-01.
  • Rejestracja konta i zarządzanie kluczami.
  • Domyślny okres ważności certyfikatu 90 dni.
  • Automatyczne odnawianie przy 60 dniach.
  • Certyfikaty wildcard przez DNS-01.

Co jest inne

Dwie drobne różnice, obie jednorazowe kroki setupowe:

  1. URL katalogu ACME wskazuje na wewnętrzne CA, nie na https://acme-v02.api.letsencrypt.org/directory.
  2. Certyfikat root wewnętrznego CA musi być akceptowany jako zaufany przez klienta wnioskującego. To jest z reguły konfigurowane przez configuration management (Ansible, Puppet, MDM) podczas provisioningu hosta.

To cała różnica. Wszystko inne to ten sam workflow Let’s Encrypt, który już znasz.

Skierować klienta na wewnętrzne CA

Wewnętrzny endpoint ACME jest opublikowany pod stabilnym wewnętrznym URL (typowo https://acme.example.internal/acme/acme/directory, ale dokładna ścieżka zależy od deploymentu). Poniżej przepisy dla popularnych klientów.

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

Przy odnawianiu flaga --server jest persystowana w renewal/*.conf, dzięki czemu periodyczny cron certbot renew przejmuje ją automatycznie.

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

W twoim Caddyfile:

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

service.example.internal {
  reverse_proxy localhost:8080
}

Caddy pobiera i odnawia certyfikat automatycznie.

Traefik

W twojej konfiguracji dynamicznej:

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

Odwoływać się przez certResolver: internal w definicjach routerów.

Zainstalować certyfikat root na klientach

Aby wystawione przez CA certyfikaty były ufane bez ostrzeżenia, root CA musi leżeć w systemowym truststore każdego hosta, który widzi te certyfikaty.

Linux (rodzina Debian/Devuan)

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

Linux (rodzina 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

Dla przeglądarek, które utrzymują własne truststores (Firefox), root musi być dodatkowo zaimportowany przez Ustawienia → Prywatność → Certyfikaty → Urzędy certyfikacji.

Instalacja ta jest z reguły przejmowana przez configuration management. Ręcznie jest OK na pojedynczy przypadek, ale nie skaluje się na flotę.

Certyfikaty wildcard

Wildcards są wystawiane przez challenge DNS-01 — klient wykazuje kontrolę nad domeną przez założenie rekordu TXT. Działa to dla czysto wewnętrznych domen, ponieważ wewnętrzne CA może walidować rekordy TXT na każdym serwerze DNS, który kontrolujesz.

Przykład z acme.sh i providerem BIND-nsupdate:

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

Provider nsupdate musi być skonfigurowany wobec twojego wewnętrznego DNS — patrz dokumentacja acme.sh po specyficzne zmienne środowiskowe.

Odnawianie

Odnawianie działa dokładnie tak samo jak przy Let’s Encrypt: klient sprawdza pozostały okres ważności i odnawia, gdy próg zostanie przekroczony (60 dni dla certyfikatu 90-dniowego).

Dla większości deploymentów istniejący cron odnawiania po prostu działa (certbot renew, acme.sh --cron, automatyczne odnawianie Caddy’ego).

Przy błędach odnawiania:

  1. Czy URL katalogu jest nadal dostępny? Zmiana sieci mogła złamać ścieżkę od klienta do CA.
  2. Czy certyfikat root nie wygasł lub został zrotowany? Roots są typowo ważne 10 lat; intermediates krócej. Rotacja, która nie została propagowana do wszystkich klientów, manifestuje się jako błąd odnawiania.
  3. Czy typ challenge nadal jest adekwatny? Serwis, który wcześniej był dostępny przez HTTP-01, może teraz siedzieć za load-balancerem, który łamie odpowiedź challenge.

Limity i polityki

Wewnętrzne CA wymusza polityki per-account — jakie domeny każde konto może wnioskować o certyfikaty, jaka maksymalna ważność jest dozwolona, jakie typy challenge są akceptowane.

Jeśli request, który wczoraj działał, nagle pada z błędem policy denied, zmieniła się polityka (lub twoje account-binding). Zwróć się do swojego operatora CA. Usługa Wsparcie jest ścieżką eskalacji, jeśli twoim operatorem CA jesteśmy my.