Zum Hauptinhalt springen
Gilt für:
DuoKey Cockpit v2ACME server (RFC 8555)ACME Renewal Information (RFC 9773)

The SC-081 timeline​

Ballot SC-081, adopted by the CA/Browser Forum in April 2025, shortens the maximum validity of publicly trusted TLS certificates and the period for which domain or IP validation may be reused:

Certificates issued on or afterMaximum validityDomain / IP validation reuse
Before 15 March 2026398 days398 days
15 March 2026200 days200 days
15 March 2027100 days100 days
15 March 202947 days10 days

Subject identity information (organization details in OV / EV certificates) may be reused for 398 days from 15 March 2026.

A private CA operated in DuoKey is not bound by these rules, but clients and tooling built for the public web increasingly assume short lifetimes. With 47-day certificates a certificate is renewed roughly every month: renewal has to be automated, and its timing is best decided by the CA.

ACME Renewal Information (ARI)​

ARI (RFC 9773) lets the CA tell each ACME client when to renew a given certificate, instead of every client applying a fixed rule such as "30 days before expiry", which does not fit 47-day or 7-day certificates. The DuoKey ACME server supports ARI on every CA directory.

Directory and renewal window​

The directory https://<cockpit>/api/pki/acme/<ca-id>/directory advertises a renewalInfo URL. A client asks for the window of a certificate with:

Renewal informationHTTP
GET /api/pki/acme/<ca-id>/renewal-info/<certID>

HTTP/1.1 200 OK
Retry-After: 3600
Content-Type: application/json

{"suggestedWindow": {"start": "2026-10-19T16:00:00Z", "end": "2026-10-20T20:00:00Z"}}

certID is the base64url (unpadded) Authority Key Identifier and serial number of the certificate, separated by a dot, as defined in RFC 9773. Certificates issued by the DuoKey ACME server carry an Authority Key Identifier for that purpose.

Certificate lifetimeSuggested renewal window
Any lifetime LFrom two thirds of L to five sixths of L (i.e. from notAfter - L/3 to notAfter - L/6)
90 daysDays 60 to 75
47 daysDays 31.3 to 39.2
7 daysDays 4.7 to 5.8

Retry-After is 1 hour for certificates of 10 days or less, 6 hours otherwise. An unknown certificate answers 404, a malformed certID 400.

Replacing a certificate​

A new order can name the certificate it replaces with "replaces": "<certID>". The server checks that the certificate was issued by the same ACME directory, to the same ACME account, shares at least one identifier with the new order, and has not already been replaced by another order (otherwise 409 alreadyReplaced, also when two orders race).

Forcing early renewal​

After an incident (CA key change, mis-issuance) an administrator can tell every ARI-aware client of a directory to renew now:

Force renewal (Operations.Pki.Acme.Update)HTTP
PUT /api/pki/acme/<ca-id>/ari-trigger
{"renew_issued_before": "2026-10-15T12:00:00Z"}

Every certificate of that directory issued before that instant gets a renewal window in the past, so clients renew at their next ARI check (within Retry-After). null clears the trigger; GET returns it (Operations.Pki.Acme.Read). Changes are recorded in the audit log.

ACME certificate validity​

ACME certificates are issued for the directory's configured maximum validity (1 to 3,650 days; 90 days by default), shortened to the order's notAfter when the client asks for an earlier date, and never beyond the CA's own expiry. For short-lived certificates, set the directory's maximum validity accordingly (for example 47 or 7 days).

Domain validation: http-01, dns-01, tls-alpn-01​

When an ACME client answers a challenge, the DuoKey ACME server checks domain control itself before the order can be finalized. An internal CA on a trusted network can instead set ACME_AUTO_VALIDATE=1, which accepts every challenge without a probe (unchanged behaviour; do not use it where applicants are not trusted).

Which challenges an order gets​

Each authorization offers the challenge types allowed on the ACME directory (allowed_challenge_types, in that order; http-01 when the CA has no ACME configuration), filtered per identifier:

IdentifierChallenge types offered
example.comEvery type allowed on the directory
*.example.com (wildcard)dns-01 only (RFC 8555 section 7.1.3). The authorization names example.com and carries "wildcard": true. If the directory does not allow dns-01, the new order is refused with rejectedIdentifier
IP addresshttp-01 only (tls-alpn-01 for IP addresses, RFC 8738, is not implemented)

Malformed DNS identifiers (a.*.example.com, *.*.example.com, empty labels) are refused with rejectedIdentifier. Challenge tokens are 256-bit random values.

Order rule: every authorization must be valid​

An order becomes ready (and can be finalized) only when every one of its authorizations is valid. An order for example.com and *.example.com therefore needs both challenges to succeed.

A failed challenge makes the challenge, its authorization and the order invalid (RFC 8555 section 7.1.6), with an ACME problem document (dns, connection, tls, incorrectResponse, rejectedIdentifier or unauthorized). The client then starts a new order. A challenge that reached a final state is not validated again.

Stricter than before

Earlier releases marked a multi-identifier order ready as soon as one challenge was validated. A client that answered only some of the challenges must now answer all of them.

http-01​

The server fetches http://<domain>/.well-known/acme-challenge/<token> and compares the answer with the key authorization. This is the default type and the only one available for IP address identifiers.

dns-01 (RFC 8555 section 8.4)​

The server reads the TXT records at _acme-challenge.<domain> (for *.example.com: _acme-challenge.example.com) and accepts the challenge if one record equals base64url(SHA-256(keyAuthorization)).

  • Several TXT records may be present (for example the wildcard and the base domain of the same order, or a stale value).
  • CNAMEs are followed, so delegating _acme-challenge to a dedicated validation zone works.
  • NXDOMAIN, an empty answer or a timeout fail the challenge with a dns problem; records without the expected value fail with incorrectResponse.
  • No DNS answer is cached between attempts.

Use dns-01 for wildcard certificates and for hosts the cockpit cannot reach on port 80 or 443.

ACME clients publish the TXT record through their DNS provider plugin (cert-manager dns01 solver, certbot --preferred-challenges dns, lego --dns). The record must be visible from the resolver used by the cockpit before the client answers the challenge; with a slow-propagating DNS provider, configure the client's propagation wait.

Resolver settings and split-horizon DNS​

VariableDefaultMeaning
ACME_DNS_RESOLVERS(unset)Comma-separated nameservers, IP or IP:port ([IPv6]:port for IPv6). When set, dns-01 queries go only to these servers
ACME_DNS_TIMEOUT_SECS10Timeout per DNS query, 1 to 60 seconds

Without ACME_DNS_RESOLVERS, the nameservers of the pod or host (/etc/resolv.conf) are used. With split-horizon DNS, where the cockpit must see the internal view of a zone (or the public one), point ACME_DNS_RESOLVERS at the resolver that serves that view:

Cockpit deployment (environment)YAML
env:
- name: ACME_DNS_RESOLVERS
  value: "10.20.0.53,10.20.0.54"
- name: ACME_DNS_TIMEOUT_SECS
  value: "5"

tls-alpn-01 (RFC 8737)​

The server resolves the domain, connects to port 443 offering only the ALPN protocol acme-tls/1 (SNI set to the domain), and accepts the self-signed certificate presented if:

  • acme-tls/1 was negotiated;
  • its subjectAltName contains exactly one entry, a DNS name equal to the domain;
  • it carries the critical id-pe-acmeIdentifier extension (1.3.6.1.5.5.7.1.31) with the SHA-256 of the key authorization.

The certificate is self-signed by design, so no chain is validated, but the TLS handshake signature is checked: the peer holds the key of the certificate it presents. There are no redirects; each connection attempt times out after 10 seconds and at most four resolved addresses are tried. Not available for wildcard or IP identifiers.

Outbound connections​

http-01 and tls-alpn-01 connect to an address derived from the requested identifier. If any address the name resolves to is loopback, link-local (including the cloud metadata address 169.254.169.254) or unspecified, the challenge fails with rejectedIdentifier and nothing is dialled. Private LAN addresses (RFC 1918, IPv6 ULA) stay allowed, because an internal CA validates internal hosts. The connection goes to the address that was checked, so a second DNS answer cannot redirect it. dns-01 only queries the configured resolvers and never connects to an address taken from the identifier.

Renewal triggers in DuoKey​

CertificatesWhat triggers renewal
Issued by the DuoKey ACME server to ACME clients (cert-manager, certbot, lego, …)The client. ARI-aware clients follow suggestedWindow; others use their own rule (cert-manager: two thirds of the lifetime by default)
Issued by a DuoKey CA and deployed by Cockpit to a targetRenewal policies: threshold capped at half the lifetime, so a 47-day certificate with a 30-day threshold renews at about day 23 and a 7-day certificate does not loop
Any issued or discovered certificateExpiry alerts warn the tenant at its thresholds

47-day readiness​

Ready today

  • ARI on the built-in ACME server: renewal information, replaces, alreadyReplaced, administrator-forced renewal.
  • ACME certificate validity follows the directory configuration, down to one day.
  • Server-side renewal of deployed certificates that does not loop on short-lived certificates, with retries, backoff, alerting and single renewal across replicas.
  • Expiry alerts per tenant (e-mail, Teams, Slack, webhooks), once per threshold.
  • Kubernetes cert-manager through the ACME server.
  • Domain validation by http-01, dns-01 (including wildcard certificates and split-horizon resolvers) and tls-alpn-01; an order is ready only when every authorization is valid.

Limits​

  • Validation is never reused: every order re-validates, which meets a 10-day reuse limit by construction (but saves no work either).
  • Validation runs from a single vantage point, synchronously in the challenge request. There is no multi-perspective validation (required of public CAs by CA/Browser Forum ballot SC-067) and no CAA check. This suits a private CA; it is not the validation profile of a publicly trusted CA.
  • tls-alpn-01 for IP address identifiers (RFC 8738) is not implemented.
  • Certificates issued over ACME are kept on their ACME order: they are not yet listed in the issued-certificate inventory and are not covered by expiry alerts or renewal policies.
  • ACME revoke-cert is accepted but does not yet add the certificate to the CA's CRL or OCSP responses. Prefer short validity for ACME certificates so that compromise exposure stays bounded.
  • Cockpit's own ACME client (towards public CAs) does not read ARI from those CAs.
  • Concurrent load is measured on a single node only, with auto-validated challenges (see Measured load). Throughput across several replicas and with real http-01 / dns-01 validation is not measured.
  • The public protocol routes, ACME included, are limited to 1000 requests per minute per source address by default, which caps one address at about 100 ACME certificates per minute. Raise DKE_PROTOCOL_RATE_LIMIT_PER_MIN (up to 100000) when a fleet reaches Cockpit from one egress address, for example cert-manager in a cluster behind NAT.

Measured load​

A load test drives the ACME protocol against one Cockpit instance: N ACME clients, each with its own account, run in parallel; each client issues 7-day certificates and renews each one right after issuance (renewal information, then a new order with replaces, through finalize and download). Throughput counts issued plus renewed certificates over the wall time of the parallel phase.

Runs of 24 October 2026, release build, ECDSA P-256 CA, 7-day certificates, 40 database connections, one run per row:

Clients x certificates per clientIssuedRenewedErrorsWall timeCertificates/s (issued + renewed)Issue p50 / p95 / p99Renew p50 / p95 / p99
1 x 200200200045.1 s8.9104 / 128 / 141 ms115 / 139 / 155 ms
8 x 25200200010.0 s39.9188 / 212 / 264 ms205 / 236 / 244 ms
8 x 100800800041.0 s39.0192 / 230 / 293 ms210 / 248 / 275 ms
16 x 13208208010.8 s38.5355 / 732 / 802 ms410 / 563 / 588 ms
32 x 722422408.4 s53.3558 / 748 / 757 ms621 / 686 / 697 ms
32 x 25800800030.8 s52.0570 / 695 / 727 ms640 / 727 / 736 ms
64 x 425625609.9 s51.71.16 / 1.39 / 1.39 s1.27 / 1.29 / 1.30 s
64 x 1610241024044.2 s46.31.32 / 1.56 / 1.58 s1.44 / 1.59 / 1.61 s

All runs completed without errors. Throughput levels off at about 40 to 53 certificates per second from 8 clients upward; more clients only add waiting time. The 10-second rows are shorter and noisier than the 30 to 45-second rows.

Test conditions, which you should keep in mind when reading the figures:

  • One Cockpit instance, requests sent in-process (no network hop, no TLS), PostgreSQL 16 in Docker on the same machine: a developer laptop (AMD Ryzen AI 9 HX PRO 370, 12 cores / 24 threads, 55.6 GB RAM, Windows 11) also running an IDE and a browser.
  • Challenges were accepted without a probe (ACME_AUTO_VALIDATE=1). In production, http-01 / dns-01 validation (including DNS propagation) usually takes longer than issuance itself.
  • The per-address rate limit was raised to 100000 requests per minute for the test. With the default (1000), the single-client run was stopped after about 10 seconds.
  • The test client fetches a fresh nonce before every signed request (10 requests per issuance, 11 per renewal); real ACME clients reuse the nonce of each response.

Read these figures as an order of magnitude, not a capacity guarantee.