Ce rămâne la fel ca la Let’s Encrypt

Dacă ai obținut vreodată un certificat Let’s Encrypt, deja știi să folosești CA-ul intern. Protocolul este identic — ACME RFC 8555. Clienții pe care îi cunoști deja funcționează fără modificare.

La fel:

  • Challenges HTTP-01 și DNS-01.
  • Înregistrarea contului și administrarea cheilor.
  • Durata implicită de 90 de zile pentru certificat.
  • Reînnoirea automată la 60 de zile.
  • Certificate wildcard prin DNS-01.

Ce este diferit

Două mici diferențe, ambele pași de setup unici:

  1. URL-ul de directory ACME indică spre CA-ul intern, nu spre https://acme-v02.api.letsencrypt.org/directory.
  2. Certificatul root al CA-ului intern trebuie să fie acceptat ca de încredere de către clientul solicitant. Asta se configurează de obicei prin configuration management (Ansible, Puppet, MDM) la provisioningul hostului.

Asta e toată diferența. Restul este același workflow Let’s Encrypt pe care îl cunoști.

Îndreaptă clientul către CA-ul intern

Endpointul ACME intern este publicat la un URL intern stabil (de regulă https://acme.example.internal/acme/acme/directory, dar calea exactă depinde de deployment). Urmează rețete pentru clienții uzuali.

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

La reînnoire, flag-ul --server este persistat în renewal/*.conf, astfel încât cronul periodic de certbot renew îl preia automat.

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

În Caddyfile-ul tău:

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

service.example.internal {
  reverse_proxy localhost:8080
}

Caddy obține și reînnoiește certificatul automat.

Traefik

În configurația ta dinamică:

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

Referenția certResolver: internal în definițiile de router.

Instalează certificatul root pe clienți

Ca certificatele emise de CA să fie de încredere fără avertizare, root-ul CA trebuie să stea în trust store-ul de sistem al fiecărui host care vede certificatele.

Linux (familie Debian/Devuan)

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

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

Pentru browserele care își țin propriile trust stores (Firefox), root-ul trebuie importat în plus prin Setări → Confidențialitate → Certificate → Autorități de certificare.

Această instalare se face de obicei prin configuration management. Manual e OK pentru un caz singular, dar nu scalează pentru o flotă.

Certificate wildcard

Wildcard-urile se emit prin challenge DNS-01 — clientul demonstrează controlul asupra unui domeniu prin crearea unui record TXT. Asta funcționează pentru domenii pur interne, pentru că CA-ul intern poate valida record-uri TXT pe orice server DNS pe care îl controlezi.

Exemplu cu acme.sh și providerul BIND nsupdate:

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

Providerul nsupdate trebuie configurat împotriva DNS-ului tău intern — vezi documentația acme.sh pentru variabilele de mediu specifice.

Reînnoire

Reînnoirea funcționează la fel ca la Let’s Encrypt: clientul verifică valabilitatea rămasă și reînnoiește când e sub prag (60 de zile pentru un certificat de 90).

Pentru cele mai multe deployments, cronul de reînnoire existent pur și simplu funcționează (certbot renew, acme.sh --cron, reînnoirea automată Caddy).

La erori de reînnoire:

  1. URL-ul de directory mai e accesibil? O modificare de rețea poate fi rupt calea de la client la CA.
  2. Certificatul root a expirat sau a fost rotat? Root-urile sunt de regulă valabile 10 ani; intermediate-urile mai scurt. O rotație care nu a fost propagată la toți clienții se manifestă ca eroare de reînnoire.
  3. Tipul de challenge mai este adecvat? Un serviciu care era accesibil pentru HTTP-01 poate sta acum în spatele unui load balancer care strică răspunsul la challenge.

Limite și policies

CA-ul intern aplică policies per-account — ce domenii poate solicita fiecare cont, ce valabilitate maximă este permisă, ce tipuri de challenge sunt acceptate.

Dacă o cerere care funcționa ieri eșuează brusc cu eroare policy denied, policy-ul s-a schimbat (sau binding-ul tău de account). Adresează-te operatorului tău de CA. Serviciul de Operare este calea de escaladare, dacă operatorul tău de CA suntem noi.