Certificates
A sec holds a TLS certificate as it holds any secret: as keys, encrypted at
rest, installed into the storage of the services
that serve it. The agent also makes the certificate, and renews it before it
expires, from an ACME directory like Let’s Encrypt or generated by the sec
itself.
The keys of a certificate
The certificate is described by keywords of the sec: cn is its subject, and
the first of its names, alt_names its other names. bits, validity,
email, and the c, st, l, o and ou fields complete what a
generated certificate says.
[DEFAULT]
cn = www.example.com
alt_names = example.com
Wherever it comes from, the certificate is kept as four keys:
| key | holds |
|---|---|
private_key | the private key, PEM encoded |
certificate | the certificate |
certificate_chain | the certificate, followed by the certificates of its issuers |
fullpem | the private key followed by the chain, the one file haproxy reads |
An application installs the ones it reads, as it would any key:
[volume#1]
install = /certs/www.pem from ./sec/web key fullpem signal HUP:container#1
Generate a certificate
certificate create generates a certificate, signed by the sec itself:
om ns1/sec/web create --kw cn=www.example.com
om ns1/sec/web certificate create
With ca naming another sec of the namespace that holds a certificate, the
certificate is signed by that one instead. A self-signed certificate is what
an internal endpoint or a first test needs. A browser warns about it.
Obtain a certificate from an ACME directory
A sec naming a directory in its acme section obtains its certificate there:
[DEFAULT]
cn = www.example.com
alt_names = example.com
email = admin@example.com
[acme]
directory = letsencrypt
letsencrypt and letsencrypt-staging name the two directories of Let’s
Encrypt. Any other value is the url of a directory. The staging directory is
the one to try a setup out with: its rate limits are far higher, and its
certificates are not trusted, so a mistake shows before it costs anything.
Changing the directory afterwards renews the certificate from the new one.
The directory proves that the requester controls the domains, cn and
alt_names, with the http-01 challenge. It asks for a token, then fetches
it from each domain at http://<domain>/.well-known/acme-challenge/<token>.
So the http server answering for the domains on port 80 must serve that path
from the directory the token is written in, the webroot. The other
challenges, through the dns or tls-alpn, are not supported.
The account at the directory is registered on the first renewal, with the
email of the sec as its contact when it has one, and kept in the sec, as the
certificate is. Any node renewing the certificate next renews it with the
same account.
Renew
A renewal does something only when it is due:
- there is no certificate yet
- the certificate names other domains than
cnandalt_names - it expires within
acme.renew_before, 30 days by default - an ACME directory is asked, and the certificate is self-signed or was issued by another directory
Otherwise it says why it did nothing. So it is safe, and meant, to run on a schedule:
no certificate issued: the certificate is valid until 2026-12-31T19:19:00+01:00
A sec naming no directory renews by generating its certificate again, as
certificate create does. Its renewal is due on the same conditions, a
self-signed certificate excepted.
A renewed certificate does not wait for the next start of the services using
it. Every volume installing a key of the sec, on every node running it, gets
the new key, and the signal of its install line is sent, so the server
reloads its certificates:
INF web: volume#1: reinstall key fullpem from ns1/sec/web to /srv/web.ns1.vol.cluster1/certs/www.pem with owner 99:99 perm -rw-------
INF web: container#1: send SIGHUP signal to container ns1..web.container.1 (pid 3375461)
From the service publishing the domains: task.acme
The natural place to renew from is the service whose http server answers for
the domains, because the challenge token must be written where that server
reads it. A task.acme resource of that service renews the secs it lists, and
writes the token in one of its volumes:
[task#acme]
type = acme
secs = web
webroot = volume#1:/acme-challenges
schedule = @1d
run_requires = container#1(up)
secslists the secs to renew.weband./sec/webare secs of the namespace of the service.ns2/sec/webis a sec of another namespace, which must share it with this one.webrootnames a directory in a volume or a filesystem of the service, by the rid of its resource, asvolume#1:/acme-challenges. The token is written under.well-known/acme-challenge/there, and never outside the volume, whatever links it holds.run_requireskeeps the task from proving the domains while the server is not there to answer the directory.
The task runs where the service runs, so the server it writes the token for is the one the domains reach. Run it now rather than wait for its schedule:
om ns1/svc/web instance run acme --node <node> --follow
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"https://<node>:1215/api/node/name/<node>/instance/path/ns1/svc/web/action/run?rid=task%23acme"
INF ns1/svc/web: task#acme: ns1/sec/web: certificate issued for www.example.com example.com: no certificate
A task.acme is one a namespace administrator may configure. Its action is
the agent’s own rather than a command its writer chooses, it renews only the
secs its namespace may use, and the only thing it writes on the node is the
token, inside a volume of its own service. The Ingress Gateway
page deploys a haproxy renewing its certificates this way.
From the command line
certificate renew renews one sec where it runs, the token written in the
host directory acme.webroot names, or --webroot:
om ns1/sec/web certificate renew --webroot /var/www/html
It suits a web server running on the node itself. acme.webroot being a path
of the node, setting it requires the root grant. --force renews a
certificate that is not due. The command has no api counterpart, a renewal
from a service being the task.acme above.
Through the agent listener
A sec whose certificate the agent listener presents, listed in the cluster
listener.tls_secs, needs no webroot: the listener of every node answers its
challenges on listener.acme_port, from the token the renewal stores in the
sec and the daemon replicates. The public name of the cluster API is
certified this way, see
Cluster API:
om system/sec/public certificate renew
➡️ See Also
- Configs and Secrets for the install lines and the sharing of a sec.
- Ingress Gateway for a haproxy renewing its certificates.