[LXD] ACME support for server certificate

Project LXD
Status Implemented
Author(s) @monstermunchkin
Approver(s) @stgraber @tomp
Release LXD 5.7
Internal ID LX026

Abstract

Add support for LXD to automatically issue/renew its certificate through Let’s Encrypt or similar services (ACME).

Rationale

LXD doesn’t currently support ACME. If users wish to use certificates from Let’s Encrypt or other ACME services, this would involve creating or renewing the certificate, and manually replacing LXD server’s server.crt and server.key.

Adding ACME support to LXD will make this easier as the user will only need to set some config options, and LXD will take care of the rest.

Specification

Design

ACME support can be enabled by providing acme.domain and acme.email. If acme.ca_url is unset, it defaults to the URL used by Let’s Encrypt.

LXD will issue a new certificate if necessary. It will check the certificate’s expiration date daily, and on startup. Should the certificate only be valid for less than 30 days, it will be renewed. Should acme.domain change, LXD will issue a new certificate immediately.

For clusters, only the leader should create/renew certificates, and the new certificate should then be distributed using the API we put in place for cluster certificate update.

For the validation process, LXD will use the HTTP-01 challenge. This challenge requires the /.well-known/acme-challenge/<TOKEN> endpoint which will be provided by LXD. ACME also requires this endpoint to be available at port 80. However, LXD will not be listening on this port. Instead, the admin will be responsible for setting up a reverse proxy which listens on port 80 and passes the request on to LXD.

API changes

The following new endpoint will be added:

  • /.well-known/acme-challenge/<token>

CLI changes

No CLI changes.

Database changes

No database changes.

Upgrade handling

No upgrade handling.

Further information

  • Initially, only a single domain will be supported
  • Old certificates will be replaced
  • LXD will not store account information, i.e. it’ll basically register every time a certificate is issued/renewed.

How will we handle the validation process?

We’ll listen on two ports (one for HTTP and one for TLS) as described here. After we have obtained the new certificate, we’ll replace the existing server.crt and server.key, and restart the network endpoint.

Could you also consider validation process unsing dynamic DNS updates via rfc2136 This ways there would be a possibility for certificate renew without need of exposing additional ports
https://certbot-dns-rfc2136.readthedocs.io/en/stable/

Probably not initially but that’s certainly something we’d be happy with someone to contribute. We’d likely need a few additional keys to hold the target DNS server and TSIG key.

Sending the actual request shouldn’t be too bad as LXD already makes use of some Go DNS package including TSIG handling. What will be a bit trickier (speaking of someone who does all his letsencrypt through DNS validation) will be to know how long to wait before asking for issuance as the server you’re updating isn’t always the responding server and various delays can be added (dnssec, axfr, caches, …).