Skip to content

ADR 0059 — ACME-Everywhere + SC-081v3 Compliance (Short-Lived Certs + ARI)

  • Status: Accepted
  • Date: 2026-05-15
  • Deciders: André Luiz Gallon (Architect/Operator)
  • Supersedes: —
  • Related: ADR 0001 (TLS 1.3 baseline), ADR 0007 (Public-Internet Realism), ADR 0017 (Backup/DR), ADR 0054 (OCTOPUS PQC-Everywhere), ADR 0055 (Admin Console + Marketplace), ADR 0056 (Customer Auto-Provisioning)
  • References:
  • CA/Browser Forum Ballot SC-081v3 ("Introduce Schedule of Reducing Validity and Data Reuse Periods"), approved 2025-04-11 (25-0-5)
  • RFC 8555 — Automatic Certificate Management Environment (ACME), Mar 2019
  • RFC 9773 — ACME Renewal Information (ARI) Extension, Jun 2025

Context

A indústria de certificados TLS públicos está caminhando rapidamente para lifetimes muito curtos. A ballot SC-081v3 do CA/Browser Forum, aprovada em 11 de abril de 2025 por 25 votos a favor, 0 contra e 5 abstenções, institui um cronograma agressivo de redução do validity máximo:

Fase Início Max validity TLS Max validity SAN data reuse
0 (status quo) hoje 398 dias 398 dias
1 2026-03-15 ⚠️ já em vigor 200 dias 200 dias
2 2027-03-15 100 dias 100 dias
3 2029-03-15 47 dias 10 dias

Implicações de negócio + operacionais:

  1. Renovação manual é operacionalmente inviável. Em fase 3 (47 dias) um humano operando manualmente fica preso em loop de renovação semanal sem capacidade para fazer outra coisa.
  2. Outage por cert expirado custa caro. Em CONNECT.Art ou STUN-coord, um cert expirado derruba o serviço de federation para todo customer conectado ao cell — afeta TODOS os clientes daquele cell ao mesmo tempo.
  3. CA mass-revocation já aconteceu (Entrust 2024, GoDaddy 2025). Quando uma CA precisa revogar em massa, clientes ARI-aware renovam ANTES da revogação efetivar — clientes ARI-blind tomam outage.
  4. Compliance audits cada vez mais exigentes. SOC 2 CC6.1 (logical access) + ISO 27001 A.10.1 (cryptographic controls) já pedem evidence de automation. Auditor de 2027+ vai exigir ARI.
  5. PQC migration (ADR 0054) só viabiliza com cert rotation frequente. Re-emitir cert com algoritmo novo (ML-KEM/ML-DSA) precisa de pipeline ACME automatizado.

Estado atual do projeto (auditoria 2026-05-15):

Componente Cert source atual Internet-facing? SC-081v3 ready?
webserver/Caddyfile (DUT lab) Caddy tls internal self-signed ❌ Lab-only ✅ N/A — não é Internet
platform/pki/persona-pki.yaml Multi-CA mock (intencional) ❌ Lab fabric ✅ Design — não é Internet
platform/pki/ca-art-bto.yaml (CA.Art) cert-manager + CA interna ❌ Lab fabric ✅ Manter
GATEWAY.Art mTLS file-mount, BYO ou CA.Art interno Cloud uplink + operator-facing GAP
RELAY.Art mTLS interno, file-mount ⚠️ Pode WAN/MPLS GAP
CONNECT.Art (OCTOPUS cloud) OCTOPUS_CONNECT_CERT_FILE static Cloud Internet GAP
STUN-coord (OCTOPUS cloud) OCTOPUS_CERT_FILE static Cloud Internet GAP
Admin Console (admin.tlsstress.art) TBD (Wave-1 PR-OCTOPUS-4) ✅ Cloud GAP
Customer App (app.tlsstress.art) TBD ✅ Cloud GAP
Marketing/institucional (tlsstress.art apex + www.tlsstress.art) Hosted (verificar) ✅ Public ⚠️ Verificar

Conclusão: zero módulo Internet-facing está SC-081v3-ready hoje. Precisamos de uma wave dedicada de compliance ACME antes que fase 2 (100d) entre em vigor em 2027-03-15 — idealmente concluída em 2026-Q3 para dar folga operacional.

Decisão

TLSStress.Art e OCTOPUS adotam ACME-Everywhere como padrão arquitetural para todo certificado TLS público. As diretivas LOCKED desta ADR:

D1 — Toda conexão Internet-facing usa cert público confiável

Qualquer endpoint TLS que aceita conexão proveniente da Internet pública (direta ou via WAN/MPLS/Metro-Ethernet sem mTLS L7 prévia) DEVE apresentar um cert emitido por uma CA pública confiável incluída nos root stores dos browsers padrão (Chrome / Firefox / Safari / Edge).

Lista de módulos abrangidos por esta diretiva: - CONNECT.Artconnect-${cell}.tlsstress.art - STUN-coordstun-${cell}.tlsstress.art - GATEWAY.Art — operator-facing FQDN, pattern gw-${slot}.${customer}.gw.tlsstress.art - RELAY.Art — quando exposto WAN: relay.${customer}.tlsstress.art - Admin Console — admin.tlsstress.art - Customer App — app.tlsstress.art - Provisioning webhook — provisioning.tlsstress.art - Marketing/institucional — tlsstress.art (apex) + www.tlsstress.art

Módulos lab-internal explicitamente FORA do escopo desta ADR (mantêm PKI interna intencional): - webserver/Caddyfile (DUT) — Caddy internal CA - Personas (*.persona.tlsstress.art) — multi-CA mock por design (ADR 0007) - CA.Art bench-internal — PKI interna ZTP-prem (ADR 0048) - OOBI Bearer / Hello mesh — PSK + mTLS interno

D2 — Issuer: Let's Encrypt como CA primária

Let's Encrypt (ISRG) é a CA pública primária. Critérios: - ACME RFC 8555 nativo - ARI RFC 9773 em produção (primeira CA a shippar) - Free → zero blocker financeiro para 47-day cadence - Rate limit 50 certs/account/week com escape (per-account scaling) - Profile tlsserver short-lived (90 days hoje, 47 days plano) - Suporte universal em cert-manager / lego / acmez

Backup CA (failover quando Let's Encrypt rate-limit / outage): plano ZeroSSL como standby (também ACME-compliant). Implementado via cert-manager multi-issuer fallback. CA secundária é runbook-only no MVP — feature gate em ADR 0059-A futura.

D3 — Challenge: DNS-01 obrigatório, HTTP-01 vedado em produção

DNS-01 é o único challenge type permitido para certs públicos TLSStress.Art em produção. Razões:

  1. Suporta wildcard (necessário para per-cell *.connect.tlsstress.art)
  2. Não exige expor :80 HTTP — superfície de ataque menor
  3. Funciona dentro de VPN — GATEWAY.Art on-prem em rede privada ainda valida via DNS público
  4. Trabalha com CDN/anycast — Cloudflare Magic Transit não precisa bypass para validação

Provedores DNS suportados no MVP: - AWS Route53 — para zona tlsstress.art primária - Cloudflare — para zona delegada *.gw.tlsstress.art (customer fleets)

Solver implementation: cert-manager Webhook (oficial Route53 + Cloudflare). On-prem standalone: lego v5 DNS provider plugin.

HTTP-01 fica permitido APENAS em dev/staging via Pebble. Tentativa de configurar HTTP-01 issuer em produção falha CI gate.

D4 — ARI (RFC 9773) feature gate ON desde dia 1

ARI é GA no cert-manager v1.18 — não é mais um feature gate. As gates UseCertificateRequestApprovedCondition e ACMEAuthorizationLifetime não existem na v1.18 e fazem o controller crashar (k8s component-base rejeita gate desconhecida com erro FATAL → CrashLoopBackOff); NUNCA configure essas gates. A única gate válida é ServerSideApply=true (ver platform/pki/cert-manager-helm-values.yaml).

Todo client ACME embedded (binário standalone) DEVE poll renewalInfo endpoint ao iniciar e a cada Retry-After (RFC 9773 §4.3.4).

Benefícios: - Mass-revocation safety — se Let's Encrypt revogar cert por CT log misissuance, ARI nos avisa em horas, não em semanas - Renewal smoothing — evita thundering herd quando fleet inteiro renova na mesma janela

D5 — Cert-manager para K8s, mholt/acmez para binários standalone

Ambiente Tooling Por quê
K8s (OCTOPUS cloud, customer fleet K8s) cert-manager v1.17+ Padrão de mercado, CRD-based, ARI
Binários Go standalone mholt/acmez Pure Go, leve, ARI, mesma família do Caddy certmagic
Caddy embedded certmagic (já incluso) Caddy autogestão
CLI debugging lego v5 DNS provider catalog amplo

Não usamos vancluever/acme (community fork pessoal, menos battle-tested).

D6 — Hot-reload sem restart

Todo módulo Go que serve TLS público DEVE implementar hot-reload de cert via fsnotify no path de mount do cert + atomic swap de tls.Config:

// pkg/cert-hotreload — high-level API
import "github.com/nollagluiz/AI_forSE/pkg/cert-hotreload"

reloader, err := certreload.New(certreload.Config{
    CertFile:    "/etc/tls/tls.crt",
    KeyFile:     "/etc/tls/tls.key",
    ClientCAFile: "/etc/tls/ca.crt", // optional for mTLS
    OnReload: func(newCert *tls.Certificate) {
        slog.Info("cert reloaded", "not_after", newCert.Leaf.NotAfter)
    },
})

// Server usa reloader.GetCertificate em tls.Config:
tlsConfig := &tls.Config{
    GetCertificate: reloader.GetCertificate,
    // ...
}

Garantias: - Zero downtime — conexões existentes não dropam; novas pegam novo cert - Atomic — mutex-guarded swap; nunca lê cert meio-rotacionado - Crash-safe — se novo cert falha parse, mantém o antigo + alerta - Throttled — máximo 1 reload / 5s para evitar storm

D7 — Monitoramento + alertas Prometheus

Cada módulo TLS público expõe métricas Prometheus de cert lifetime:

Métrica Tipo Descrição
tlsstress_tls_cert_not_after_seconds{module, sni} Gauge Unix timestamp do NotAfter do leaf cert
tlsstress_tls_cert_not_before_seconds{module, sni} Gauge Unix timestamp do NotBefore
tlsstress_tls_cert_serial_hash{module, sni} Gauge Hash truncado do serial (cardinality-safe)
tlsstress_tls_cert_reload_total{module, result} Counter Total de hot-reloads (result=success/parse_fail/file_missing)
tlsstress_tls_cert_reload_age_seconds{module} Gauge Seconds since last successful reload
tlsstress_acme_renewal_total{issuer, result} Counter Renewals executados por issuer (result=success/auth_fail/finalize_fail/rate_limit)
tlsstress_acme_ari_suggested_window_start{cert} Gauge ARI suggested renewal window start (epoch)
tlsstress_acme_ari_suggested_window_end{cert} Gauge ARI suggested renewal window end (epoch)

Alerting rules (Prometheus):

# 14d warning — give ops a week of slack
- alert: TLSCertExpiringWarning
  expr: tlsstress_tls_cert_not_after_seconds - time() < 14 * 86400
  for: 30m
  labels: { severity: warning }
  annotations:
    summary: "TLS cert {{ $labels.sni }} expires in less than 14 days"

# 7d critical — page on-call business hours
- alert: TLSCertExpiringCritical
  expr: tlsstress_tls_cert_not_after_seconds - time() < 7 * 86400
  for: 10m
  labels: { severity: critical }

# 2d page — PagerDuty 24/7
- alert: TLSCertExpiringPage
  expr: tlsstress_tls_cert_not_after_seconds - time() < 2 * 86400
  for: 5m
  labels: { severity: page, pagerduty: "true" }

# Renewal failure cascade (3+ failures in 1h)
- alert: ACMERenewalFailing
  expr: increase(tlsstress_acme_renewal_total{result=~"auth_fail|finalize_fail"}[1h]) >= 3
  for: 15m
  labels: { severity: critical }

# Reload health (cert file present but reload not happening)
- alert: TLSCertReloadStale
  expr: tlsstress_tls_cert_reload_age_seconds > 90 * 86400
  for: 1h
  labels: { severity: warning }
  annotations:
    description: "Module {{ $labels.module }} has not reloaded cert in 90+ days"

Adicionalmente, ssl_exporter + blackbox_exporter deployados como canary external-vantage: probam endpoints públicos do exterior (não da mesma K8s) para detectar problemas que cert-manager interno não vê (DNS misconfig, edge LB cert mismatch, etc.).

D8 — Per-service certs (no wildcard)

Cada serviço cloud OCTOPUS recebe SEU próprio cert por cell. Pattern:

connect-us-east-1-a.tlsstress.art    → CONNECT.Art cell us-east-1-a
connect-eu-west-1-a.tlsstress.art    → CONNECT.Art cell eu-west-1-a
stun-us-east-1-a.tlsstress.art       → STUN-coord cell us-east-1-a
admin.tlsstress.art                  → Admin Console (single global)
app.tlsstress.art                    → Customer App (single global)
provisioning.tlsstress.art           → Stripe webhook receiver

Razões para per-service: 1. Blast radius isolado — compromise de 1 chave não derruba todos 2. Per-cell rotation — failover regional não precisa global cert rotate 3. Audit clarity — cada cert vinculado a serviço específico no audit chain

GATEWAY.Art on-prem usa subzona delegada por customer:

gw-1.acme-corp.gw.tlsstress.art    → GATEWAY.Art slot 250
gw-2.acme-corp.gw.tlsstress.art    → GATEWAY.Art slot 251 (HA standby)
gw-1.bigco.gw.tlsstress.art        → GATEWAY.Art slot 250 outro customer

A zona gw.tlsstress.art é delegada via NS records para um ACME-DNS provider customer-side. Customer cert-manager / lego edita apenas sua subzona ${customer-id}.gw.tlsstress.art.

D9 — BYO cert opcional para GATEWAY.Art (regulated escape hatch)

Clientes regulados (defesa, telecom, governo) podem exigir PKI própria ou Cisco Satellite Root CA específica. GATEWAY.Art SUPORTA modo BYO via Dashboard:

Dashboard → /admin/gateways/${slot}/cert-source
   ○ ACME-managed (Let's Encrypt)        ← default
   ● Operator-supplied (BYO)
       └─ Upload PEM (cert + key + chain)
       └─ Validação: serial check, NotAfter > 30d, SAN match FQDN
       └─ Renewal: operator responsável; alertas idem D7

BYO mode aceita warning explícito ("operator owns renewal"); audit chain registra opt-out + responsável.

D10 — Sigstore-signed manifests para cert-manager Issuer YAMLs

ClusterIssuer YAMLs que provisionam ACME accounts são consideradas infrastructure-as-code crítica. Devem ser: - Versionadas em git - Assinadas com Sigstore cosign na pipeline CI (já existe — extend) - Revisadas via PR (não kubectl apply ad-hoc em produção)

Wave de implementação

PR-ACME-1 (este PR) — ADR + docs: - docs/ADR/0059-acme-everywhere-sc081v3-compliance.md (este arquivo) - pkg/octopus/docs/08-security/acme-ari-compliance.md (reference deep) - pkg/octopus/docs/08-security/cert-management.md (operational) - pkg/octopus/docs/05-operations/runbooks/cert-renewal-failure.md (on-call) - README index update

PR-ACME-2 — cert-manager ClusterIssuer + DNS-01: - platform/pki/acme-letsencrypt-prod.yaml - platform/pki/acme-letsencrypt-staging.yaml - platform/pki/dns-01-route53.yaml - platform/pki/dns-01-cloudflare.yaml - ARI feature gate enabled - Sigstore signing pipeline

PR-ACME-3 — Certificate CRs para cloud OCTOPUS: - CONNECT.Art per-cell - STUN-coord per-cell - Admin Console + Customer App - Provisioning webhook - Existing tests + smoke

PR-ACME-4pkg/cert-hotreload library Go: - fsnotify-based, atomic swap, throttled - 100% unit test coverage - Benchmark < 1ms swap latency

PR-ACME-5 — Wire hot-reload em todos os módulos relevantes

PR-ACME-6 — Prometheus metrics em todos os módulos

PR-ACME-7 — ssl_exporter + blackbox_exporter + Grafana dashboard + Alertmanager rules

PR-ACME-8pkg/acme-client (lego wrapper) para standalone

PR-ACME-9 — BYO cert source operator escape hatch (BTO-1 D3 ext)

PR-ACME-10 — DNS subzone delegation + runbook ops

PR-ACME-11 — 47-day forward-compat e2e test (Pebble + ARI sim)

PR-ACME-12 — PR reviewer checklist + Dashboard /admin/security/certs

Estimativa total: 12 PRs, ~6.300 LoC + 1.300 docs, 6-8 semanas sequencial OR 4-5 com paralelismo. Compliance SC-081v3 fase 2 (100d, 2027-03-15) requer wave completa antes de 2027-Q1.

Alternativas consideradas

Alternativa Por que rejeitada
Manter cert-manager interno + ignorar SC-081v3 Conexões Internet-facing browser-untrusted → quebra customer journey, marketing site → não viável
Comprar certs DigiCert/Sectigo/GlobalSign 1-year manuais Em fase 3 (47d) significa $X * fleet_size * 8 renewals/year → custos altos + manual = impossível
HTTP-01 challenge Não suporta wildcard; exige :80 público em todos módulos; surface attack maior; falha atrás de WAF/CDN
TLS-ALPN-01 challenge Exige conexão TLS direta ao módulo — incompatível com terminação em LB / CDN
vancluever/acme em vez de lego/acmez Community fork pessoal, menos battle-tested, smaller DNS provider catalog
Cert público apenas em cloud, BYO em on-prem GATEWAY.Art operator-facing precisa ser browser-trusted para Dashboard funcionar — não viável BYO universal
Self-hosted ACME CA (step-ca) Não é publicamente confiável → browser warning → não resolve o problema do operador
Wildcard global *.tlsstress.art Blast radius gigante; compromise de 1 chave → todos serviços; rejeitado por D8

Consequências

Positivas

  • SC-081v3 compliance alcançado antes da fase 2 (100d, 2027-03)
  • Mass-revocation safety via ARI — robusto contra próximo Entrust/GoDaddy event
  • Zero manual cert work para operadores customer-side — Dashboard "just works"
  • Audit-ready — SOC 2 CC6.1 + ISO 27001 A.10.1 evidence pronta
  • PQC migration enablement — ADR 0054 viabiliza com rotation cadence semanal
  • Cost zero — Let's Encrypt grátis, escala sem custos por cert
  • Browser-trusted universalmente — sem warnings para customers
  • Reuse de infra K8s existente — cert-manager já está em platform/pki/

Negativas

  • Wave de 12 PRs ~6 semanas — capacity-impactante; mas necessário
  • Operator BYO mode adiciona complexidade ao GATEWAY.Art Dashboard
  • DNS-01 dependência de Route53/Cloudflare credentials — secret management precisa hardening adicional
  • Rate limit Let's Encrypt (50 certs/account/week) — mitigado por multi-account (acme-account-pool) ou backup ZeroSSL para spikes
  • 47-day cadence requer disciplina — incidents que tomem cert offline por > 5 dias = cert expira; runbook D7 mitiga
  • External vantage probing custa $/mês — ssl_exporter + blackbox_exporter
  • monitoring infrastructure ~$50-100/mês

Neutras

  • ADR 0007 (Public-Internet Realism) continua válida — persona PKI multi-CA mock é design distinto, não afetado por SC-081v3
  • ADR 0048 (ZTP-prem) continua válida — internal CA.Art mantém-se como camada bench-internal; ACME-Everywhere é a camada Internet-facing
  • ADR 0054 (PQC-Everywhere) se beneficia — rotation frequente viabiliza algoritmo upgrade rolling

Compliance mapping

Standard Requirement How this ADR satisfies
CA/B Forum SC-081v3 Phased validity reduction 200/100/47d D1+D2+D6+D7 garantem fleet renova dentro do prazo
RFC 8555 §6 JWS-signed ACME requests cert-manager + lego/acmez implementam corretamente
RFC 8555 §7.4 Order finalization with CSR Default em ambas tools
RFC 8555 §8.3 DNS-01 challenge D3 mandatório
RFC 8555 §9 Rate limiting Retry-After cert-manager honra; ours pool accounts
RFC 9773 §3 ARI renewalInfo endpoint poll D4 mandatório
RFC 9773 §4.3.4 Retry-After on renewalInfo D4 implementado em pkg/acme-client
SOC 2 CC6.1 Logical access automation Cert-manager + ARI = evidence ready
ISO 27001 A.10.1 Cryptographic controls Documented in cert-management.md
PCI DSS 4.0 4.2.1.1 Inventory of trusted keys Prometheus + audit chain provide
NIST SP 800-57 §8.4 Key lifetimes documented Per-service in cert-management.md
GDPR Art. 32 State-of-the-art crypto TLS 1.3 + ACME-automated + ARI

Riscos + Mitigações

Risco Probabilidade Impacto Mitigação
Let's Encrypt outage > 24h durante renewal window Baixa Alto Backup ZeroSSL issuer em standby (Wave-ACME-2.5)
ARI poll storm sobrecarrega CA Muito baixa Médio Jitter ARI poll por cell; cap acme-account-pool size
DNS provider credential leak Baixa Crítico Sealed Secrets + rotation 90d; audit log de access
Cert-manager bug em ARI feature gate Baixa Alto Staging cell pinned a cert-manager N-1; only roll forward after staging valida 30d
Customer DNS subzone delegation falha Média Médio Setup wizard + runbook D10; pre-flight check no Dashboard onboarding
Operator opta-out BYO sem entender lifecycle Média Alto Warning + double-confirm modal; PagerDuty notification when BYO cert < 14d

Open items / future work

  • ADR 0059-A (Q3 2026) — Backup ACME CA failover policy (ZeroSSL, Google Trust Services) — agora documented como runbook only
  • ADR 0059-B (Q4 2026) — PQC ACME profiles (ml-dsa-65 leaf signatures) quando CAs públicas suportarem (provavelmente 2027+)
  • ADR 0059-C (Q1 2027) — Multi-account account pool para Let's Encrypt rate limit escape (fleet > 1000 customers)
  • Patent angle: arquitetura DNS subzone delegation + ACME per-customer via GATEWAY.Art pode integrar Patent Family F (mobile + edge ops) como claim adicional. Avaliar com IP counsel.

Memory + cross-references

Adicionar entrada em MEMORY.md (auto-memory) sob seção FOUNDATIONAL RULES:

⭐⭐⭐ ACME-Everywhere + SC-081v3 Compliance LOCKED — Toda conexão TLS Internet-facing usa cert público Let's Encrypt via cert-manager + DNS-01 + ARI feature gate. Per-service certs (no wildcard). Hot-reload obrigatório. Monitoring Prometheus 14d/7d/2d. GATEWAY.Art BYO escape hatch. ADR 0059. Wave de 12 PRs até 2026-Q3.