feat(ra): pluggable server-cert issuance via certificate-order lifecycle#45
Draft
csnitker-godaddy wants to merge 1 commit into
Draft
feat(ra): pluggable server-cert issuance via certificate-order lifecycle#45csnitker-godaddy wants to merge 1 commit into
csnitker-godaddy wants to merge 1 commit into
Conversation
Wire an RFC 8555 ACME provider (Let's Encrypt-shaped) alongside the local self-signed CA behind a single port.ServerCertificateIssuer, so a deployment can swap in a public/cloud CA without touching the service layer. Issuance runs through a certificate-order lifecycle: - CreateOrder (at registration/renewal submission) returns the provider's domain-control challenges, relayed verbatim to the domain owner. ANS never writes DNS or serves challenge files on their behalf. - FinalizeOrder (at verify-acme, gated on a verified challenge) returns the cert, or ErrOrderPending for asynchronous providers such as ACME CAs; re-POSTing verify-acme re-drives the order (202 / PENDING_CERTS). Identity certificates remain privately issued behind port.IdentityCertificateAuthority (self-CA default, swappable for AWS Private CA / GCP CAS), signed only after the server order completes. Both CA ports gain an idempotent RevokeCertificate. Close several holes so the flows behave correctly regardless of the underlying adapter: - Always gate FinalizeOrder on a verified DNS-01 or HTTP-01 artifact; gate/order rejections return 422, pending-conflict 409. - SSRF-harden the HTTP-01 verifier: guarded dialer rejecting non-public dial targets (loopback, link-local incl. the cloud metadata endpoint, RFC 1918, ULA, RFC 6598 CGNAT 100.64/10, 6to4, NAT64), refuse redirects, bounded body read. - Distinguish "no server cert on file" from a transient store failure, so a recoverable fault can never activate an agent and sign a wrong, immutable transparency-log leaf. - Persist order/challenge state and certificate serial/ref in SQLite (migrations 006/007); guarded set-based expiry of lapsed pending validations; in-process agent auto-expiry sweeper. spec/api-spec-v2.yaml and the docsui copy stay byte-identical. internal/domain remains at 100% statement coverage; overall >=90%; race-clean. Signed-off-by: Connor Snitker <csnitker@godaddy.com>
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.
Summary
Wires an RFC 8555 ACME provider (Let's Encrypt–shaped) alongside the local
self-signed CA behind a single
port.ServerCertificateIssuer, so a deploymentcan swap in a public or cloud CA (AWS Private CA, GCP CAS, etc.) without
touching the service layer. Issuance now runs through an explicit
certificate-order lifecycle rather than assuming synchronous, local signing.
Order lifecycle
CreateOrder(at registration / renewal submission) returns theprovider's domain-control challenges, relayed verbatim to the domain owner.
ANS never writes DNS or serves challenge files on the owner's behalf — the
pending response carries the records/paths for the owner to publish.
FinalizeOrder(atverify-acme, gated on a verified challenge) returnsthe certificate, or
ErrOrderPendingfor asynchronous providers such as ACMECAs. Re-POSTing
verify-acmere-drives the pending order (202/PENDING_CERTS), so the async path is plumbed end-to-end instead of beingskipped.
Identity certificates remain privately issued behind
port.IdentityCertificateAuthority(self-CA default; swappable for AWS PrivateCA / GCP CAS) and are signed only after the server order completes. Both CA
ports gain an idempotent
RevokeCertificate.Correctness / hardening
Closes holes so the flows behave correctly regardless of the underlying adapter:
FinalizeOrderon a verified DNS-01 or HTTP-01 artifact;gate/order rejections return
422, pending-conflict409.targets (loopback, link-local incl. the cloud metadata endpoint, RFC 1918,
ULA, RFC 6598 CGNAT
100.64.0.0/10, 6to4, NAT64), refuse redirects, boundedbody read — fails closed.
recoverable fault can never activate an agent and sign a wrong, immutable
transparency-log leaf.
(migrations
006/007); guarded set-based expiry of lapsed pendingvalidations; in-process agent auto-expiry sweeper.
Wire contract
spec/api-spec-v2.yamland the docsui copy stay byte-identical.PENDING_CERTSis restored to
RegistrationPending.status;CANCELadded to the next-stepaction enum.
Testing
make checkpasses (fmt,vet,golangci-lint, coverage gate).internal/domainat 100% statement coverage; overall ≥ 90%;make test-raceclean.
in-process RFC 8555 fake), the SSRF guard, and the SQLite persistence
round-trips.
scripts/demo/start.shbehind--with-acmefor live testing against a real domain + Let's Encrypt staging.
Notes
Marked draft for review of the port surface before merge.