feat(acme): Tier 2 shortlived cadence ramp + cert renewal failure counter - #62
Draft
ovexro wants to merge 1 commit into
Draft
feat(acme): Tier 2 shortlived cadence ramp + cert renewal failure counter#62ovexro wants to merge 1 commit into
ovexro wants to merge 1 commit into
Conversation
…re counter
Locked behind LE's 2026-05-13 shortlived profile flip (pre-flight blocked
by sandbox — see PR body). Carryover from v2.8.2.
(1) Tier 2 ACME renewal cadence: 6-day shortlived cert, renewal triggered at
≤ 3 days remaining (50% lifetime). Backend auto_healer calls the new
POST /ssl/agent/renew agent route; agent provisions via instant_acme with
profile="shortlived" and returns the new SHA-256 fingerprint so cert_fingerprint
is updated atomically. New DB columns agent_acme_cert_{domain,expiry} on
servers table track per-server state (opt-in; self-signed fallback unchanged).
fallback_renewal_margin("shortlived") corrected 2 → 3 days.
(2) Prometheus counter dockpanel_cert_renewal_failures_total{cert_kind,reason}
registered in the fleet exporter. Persisted in new cert_renewal_failures DB
table; all 15 label combos pre-seeded at migration time. Unit test
acme_renewal_failure_metric_increments asserts rendering and increment logic.
https://claude.ai/code/session_01BWS1NViJkJDwcWLJUK3Gnd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Closes the v2.8.2 carryover for Tier 2 ACME. Locked behind LE's 2026-05-13 shortlived profile flip — pre-flight was attempted on 2026-05-14 but blocked by network sandbox (no outbound HTTP from the CI environment). Contextual confidence: today's date is 2026-05-14 (one day after the scheduled flip), the existing
ProvisionOptsinagent/src/services/ssl.rsalready references"shortlived"as a valid profile, and the code was previously wired to accept it. If the actual LE flip has not yet propagated, the firstauto_renew_agent_certscycle will surface an ACME error and incrementdockpanel_cert_renewal_failures_total{cert_kind="agent_pinned",reason="acme_error"}, which will alert operators.What
(1) Tier 2 ACME cert renewal — shortlived cadence
The Tier 2 path issues certs for inter-server agent TLS (server-to-agent pinning via
RemoteAgentClient::PinnedFingerprintVerifier).Agent side (
panel/agent/src/services/ssl.rs,panel/agent/src/routes/ssl.rs):provision_agent_acme_cert(email, domain)— issues/renews the agent's own TLS cert viainstant_acmewithprofile = "shortlived"(6-day validity). Writes to/etc/dockpanel/ssl/agent-acme.{crt,key}. Passes the existing cert as the RFC 9773 ARIreplaceshint for continuity.fingerprint_from_pem(pem)— SHA-256 of the cert DER (reusesfirst_cert_deralready in the service).POST /ssl/agent/renew— called by the backend auto-healer. Returns{ok, expiry, fingerprint, …}so the panel can updateservers.cert_fingerprintin one round-trip (no extra checkin needed).GET /ssl/agent/status— returns{has_acme_cert, fingerprint}for monitoring.Backend side (
panel/backend/src/services/auto_healer.rs):auto_renew_agent_certs(pool, agent)— queriesservers WHERE is_local = TRUE AND agent_acme_cert_domain IS NOT NULL. Triggers renewal whenagent_acme_cert_expiry - now ≤ 3 days(50% of 6-day lifetime). 3-hour cooldown per domain. On success: updatesagent_acme_cert_expiryandcert_fingerprint. On failure: increments the new Prom counter.run()loop afterauto_renew_ssl.DB (
migrations/20260514000000_cert_renewal_failures.sql):servers.agent_acme_cert_domain TEXT— opt-in domain for Tier 2 ACME.NULL= server uses the self-signed rcgen fallback (no behaviour change).servers.agent_acme_cert_expiry TIMESTAMPTZ— expiry of the current ACME cert;NULLtriggers immediate first-time provisioning.Renewal cadence correction —
fallback_renewal_margin("shortlived")corrected fromDuration::days(2)toDuration::days(3)(50% of 6-day lifetime, up from ≈ 33%). Applies to site certs withssl_profile = 'shortlived'when ARI is unavailable.(2)
dockpanel_cert_renewal_failures_total{cert_kind,reason}GET /api/metrics(no new scrape endpoint).cert_renewal_failuresDB table (PRIMARY KEY (cert_kind, reason)); survives backend restarts unlike in-memory atomics.record_renewal_failure(pool, cert_kind, reason)— upsert helper called from bothauto_renew_ssl(site certs →cert_kind="site") andauto_renew_agent_certs(Tier 2 →cert_kind="agent_pinned").classify_renewal_error(err)— pure fn mapping error strings to reason labels; unit tested.Test
cargo build --releaseclean on bothpanel/backendandpanel/agent✓acme_renewal_failure_metric_incrementsinprometheus_exporter.rs— asserts TYPE line, non-zero and zero-value rows render correctly (no DB required).classify_rate_limit,classify_dns_check,classify_network,classify_acme_error,classify_otherinauto_healer.rs— assert error string → reason label mapping.cargo test> 2 min compile time on cold cache — not blocking per task instructions. Existingtests/tier2-pin-e2e.shcovers the TOFU/fingerprint-pin flow unmodified.Questions for review
Live TLS reload. The renewed ACME cert is written to disk but the axum-server instance is not hot-reloaded. The new cert takes effect on the next agent restart (systemd
Restart=on-failureor manualsystemctl restart dockpanel-agent). Options: (a)RustlsConfigdynamic cert resolver viaaxum-server'sreload()API — adds complexity but enables zero-downtime rotation; (b) graceful restart signal after write — simpler, brief connection gap. Which approach do you prefer before this merges?Remote agent cert renewal.
auto_renew_agent_certscurrently only renews the LOCAL agent (it receivesAgentClient, notAgentRegistry). Remote agents (multi-server fleet) would needAgentRegistry::for_servercalls. Shouldauto_healer::runbe extended to acceptAgentRegistry, or should remote agent renewal be a separate background service?HTTP-01 challenge for agent cert. The agent must serve the HTTP-01 challenge at
/.well-known/acme-challenge/on port 80. For agents behind a firewall or without a public domain, this will fail withdns_checkoracme_error(the counter will increment, no cert will be provisioned, the self-signed fallback continues to work). Should we add a pre-flight DNS check before attempting the ACME order, and/or surface a panel warning whenagent_acme_cert_domainis set but HTTP-01 is unreachable?panel_publiccert_kind. The counter schema reservescert_kind="panel_public"for the panel's own public-facing cert (if/when it uses ACME). There is no existing renewal path for this —panel_publicrows will always be 0. Should we wire it up in this PR or defer to a follow-on?Files changed
panel/backend/migrations/20260514000000_cert_renewal_failures.sqlpanel/backend/src/services/prometheus_exporter.rspanel/backend/src/services/auto_healer.rspanel/agent/src/services/ssl.rspanel/agent/src/routes/ssl.rsCHANGELOG.mdGenerated by Claude Code