Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

keyholds
private_keythe private key, PEM encoded
certificatethe certificate
certificate_chainthe certificate, followed by the certificates of its issuers
fullpemthe 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 cn and alt_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)
  • secs lists the secs to renew. web and ./sec/web are secs of the namespace of the service. ns2/sec/web is a sec of another namespace, which must share it with this one.
  • webroot names a directory in a volume or a filesystem of the service, by the rid of its resource, as volume#1:/acme-challenges. The token is written under .well-known/acme-challenge/ there, and never outside the volume, whatever links it holds.
  • run_requires keeps 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