Feature Specification: Resource ssl
Created: 2026-10-07
Status: Implemented
Input: Reconstructed from the code of core/resources/ssl/ and the TLS setup wiring at 093e68b6f, pull requests #1184, #1205, #1236, #1238, #1241, #1246, #1266, #1361, #1383, #1405, #1433 and #1690, and issue #527.
User Scenarios & Testing (mandatory)
The ssl resource gives each served domain a certificate and makes each node trust the certificates that other nodes serve. It wraps the ssl contract, with three providers: self-signed, acme and openbao-pki. The shared resource rules are in the resource family spec.
User Story 1 - Serve a domain over TLS (Priority: P1)
A component serves one or more domains and requests them. The policy routes each domain to a provider of the ssl contract. The consumer node gets a web server site that forces HTTPS, with the certificate and the key from the provider. A service that terminates TLS itself, such as PostgreSQL or maddy, gets the files but no site.
Why this priority: each public and internal endpoint needs a certificate. Without one, clients refuse to connect.
Independent Test: a contract test of the self-signed provider; a VM test of the openbao-pki provider; a unit test of the key owners; a unit test of the consumer side: the site and the open port for each domain, and no site for a domain whose key another service owns.
Acceptance Scenarios:
- Given a request for a domain, When the consumer side is evaluated, Then the web server gets a site for that domain with the certificate and the key from the contract result, and the firewall opens the HTTPS port.
- Given a domain whose key belongs to a service other than the web server, When the consumer side is evaluated, Then the web server gets no site for that domain.
- Given a domain without an explicit key group, When the consumer side is evaluated, Then the key belongs to the web server group, and a new certificate restarts the web server.
- Given a request with no domains, When the policy is evaluated, Then it gives no consumer side.
User Story 2 - Pick a certificate authority per domain (Priority: P1)
A domain that browsers open needs a certificate from a public CA. A domain that only the platform opens can use the private CA of OpenBao, or a self-signed certificate. The setups sort each domain into one of these routes from the certificate audience of its component, and turn the result into a provider per domain.
Why this priority: a wrong route gives either a browser warning or a failed ACME order for a name that the internet cannot reach.
Independent Test: unit tests of the TLS routing, the wildcard certificates and the served domains.
Acceptance Scenarios:
- Given a domain of a component whose audience is the browser, When the routing runs, Then the domain goes to
acme. - Given a domain of a component whose audience is the bootstrap, and that is not a UI domain, When the routing runs, Then the domain goes to the private CA.
- Given the group has a PKI, When the routing runs, Then the
valkey,postgresqland forward-auth UI domains go toopenbao-pki. Without a PKI, they go toself-signed. The routing reads, per resource name, whether the group has a provider of that resource. - Given a wildcard domain routed to
acme, When the node is evaluated, Then the certificate uses a DNS-01 challenge. A domain that is not a wildcard uses the HTTP-01 challenge of the web server.
User Story 3 - Issue and renew private certificates from OpenBao (Priority: P2)
A node logs in to OpenBao with its SPIFFE identity (see spiffe). It issues each certificate from the PKI mount. At a short interval, it renews each certificate that is in the last third of its lifetime, and restarts the services that use that certificate.
Why this priority: private certificates expire after three days. Without renewal, the internal endpoints break.
Independent Test: a VM test of the openbao-pki provider, and a unit test of the order of the login.
Acceptance Scenarios:
- Given a domain routed to
openbao-pki, When the issue step runs, Then it issues the certificate from the PKI role, and writes the certificate readable by all and the key readable only by its owner and group. - Given a new certificate, When the step writes it, Then it restarts each service that uses the certificate, without a wait on them.
- Given a node without its own SPIRE login, When the login step runs, Then it fetches an SVID, logs in to OpenBao with it, and keeps the token for the issue steps.
- Given the SPIRE agent is not healthy before the deadline, When the login step times out, Then it fails, and systemd does not restart it.
- Given a node with the login step of this provider, When the node is evaluated, Then the login comes before the issue step of each domain routed to
openbao-pki, and before no other step. Each issue step also wants the login.
User Story 4 - Trust certificates that other nodes serve (Priority: P2)
A node connects to services on other nodes over TLS. It must trust their certificates. For a self-signed domain, the node builds the certificate into its system trust. For a private CA, the node fetches the CA and builds a trust bundle.
Why this priority: without trust, every cross-node TLS connection fails the certificate check.
Independent Test: a unit test of the PKI trust.
Acceptance Scenarios:
- Given a trusted domain routed to
self-signed, When the node is evaluated, Then the certificate of that domain is in the system trust of the node. - Given a CA file and a fetch address, When the fetch step runs, Then it fetches the CA over plain HTTP, and a timer fetches it again at a fixed interval.
- Given a CA file, When the trust step runs, Then it writes a bundle of the system trust plus the CA, and all processes on the node use that bundle as their default trust.
Edge Cases
- A wildcard domain routes to
acmewithout a DNS provider: the evaluation fails. - A self-signed certificate is built at evaluation time. Its build name carries a part of the certificate digest, so a new certificate changes the name.
- The CA fetch fails: the step fails soft, and the timer tries again.
- OpenBao is not reachable at issue time: the issue step starts again after a short delay, without a start limit.
- The OpenBao address uses HTTPS and a CA file is set: the issue step verifies OpenBao against that CA.
Requirements (mandatory)
Functional Requirements
- FR-001: The policy MUST route each domain through a want per consumer and domain on the
sslcontract. - FR-002: The policy MUST make web server sites only for the domains that the web server terminates.
- FR-003: The policy MUST use the HTTP-01 challenge of the web server only for a domain routed to
acmethat is not a wildcard. A wildcard domain MUST use its own DNS-01 certificate. - FR-004: The policy MUST expose, for the other nodes, the results, the domains routed to
self-signed, and the CA files of the private PKI. - FR-005: The
self-signedprovider MUST make an RSA 4096 certificate for the domain and its extra names, with a lifetime that does not end in practice. - FR-006: The
acmeprovider MUST use Let's Encrypt when no other ACME server is set, and MUST use HTTP-01 only when no DNS provider is set. - FR-007: The
openbao-pkiprovider MUST issue certificates with a short lifetime, three days by default. At a short interval, it MUST renew a certificate that is missing, that is in the last third of its lifetime, or that does not chain to the CA file, and only then restart the services that use it. - FR-008: The login step MUST refresh the OpenBao token at a fixed interval, well before the token expires.
- FR-009: When the OpenBao credential provider of the node is on, the login step MUST reload or restart it after a login.
- FR-010: The trust bundle MUST be the default trust of the shells and of the services of the node.
Key Entities
- Request: the domains that the node serves, and the domains served elsewhere that the node trusts.
- Policy: the routing, and the read-only data for the other nodes.
- Providers:
self-signed,acmeandopenbao-pki. - Provider request: the domain, the key owner, the key group, the key mode and the services to restart.
- Routing plan: the domains per route (private CA, forward-auth UI on the private CA,
acme, self-signed), and the nodes that answer the ACME challenges.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: the contract test of
self-signedand the VM test ofopenbao-pkipass. - SC-002: the unit tests of the TLS routing, the PKI trust, the wildcard certificates, the key owners, the served domains and the consumer side pass.
- SC-003: A browser opens each public domain without a certificate warning.
Assumptions
- The wiring and the setup flow are as in 005. The request and policy model is as in 001.
- The private CA is the PKI mount of the
openbaocomponent. The node identity comes from thespirecomponent. - The DNS-01 challenge needs a zone that the platform publishes, see the
bindcomponent. - The
smtp,oidcandpostgresqlresources declare their own TLS domains. - No check runs the
acmeprovider against an ACME server.
Known Deviations
None.