selfhostblocks/modules/blocks/ssl/docs/default.md
2025-12-08 11:39:35 +01:00

5.1 KiB

SSL Generator Block

This NixOS module is a block that implements the SSL certificate generator contract.

It is implemented by:

Self-Signed Certificates

Defined in /modules/blocks/ssl.nix.

To use self-signed certificates, we must first generate at least one Certificate Authority (CA):

shb.certs.cas.selfsigned.myca = {
  name = "My CA";
};

Every CA defined this way will be concatenated into the file /etc/ssl/certs/ca-certificates.cert which means those CAs and all certificates generated by those CAs will be automatically trusted.

We can then generate one or more certificates signed by that CA:

shb.certs.certs.selfsigned = {
  "example.com" = {
    ca = config.shb.certs.cas.selfsigned.myca;

    domain = "example.com";
    group = "nginx";
    reloadServices = [ "nginx.service" ];
  };
  "www.example.com" = {
    ca = config.shb.certs.cas.selfsigned.myca;

    domain = "www.example.com";
    group = "nginx";
  };
};

The group has been chosen to be nginx to be consistent with the examples further down in this document.

Let's Encrypt

Defined in /modules/blocks/ssl.nix.

We can ask Let's Encrypt to generate a certificate with:

shb.certs.certs.letsencrypt."example.com" = {
  domain = "example.com";
  group = "nginx";
  reloadServices = [ "nginx.service" ];
  dnsProvider = "linode";
  adminEmail = "admin@example.com";
  credentialsFile = /path/to/secret/file;
  additionalEnvironment = {
    LINODE_HTTP_TIMEOUT = "10";
    LINODE_POLLING_INTERVAL = "10";
    LINODE_PROPAGATION_TIMEOUT = "240";
  };
};

The credential file's content would be a key-value pair:

LINODE_TOKEN=XYZ...

If you use one subdomain per service, asking for certificates for a subdomain is done with:

shb.certs.certs.letsencrypt."example.com".extraDomains = [ "nextcloud.${domain}" ];

For other providers, see the official instruction.

Usage

To use either a self-signed certificates or a Let's Encrypt generated one, we can reference the path where the certificate and the private key are located:

config.shb.certs.certs.<implementation>.<name>.paths.cert
config.shb.certs.certs.<implementation>.<name>.paths.key
config.shb.certs.certs.<implementation>.<name>.systemdService

For example:

config.shb.certs.certs.selfsigned."example.com".paths.cert
config.shb.certs.certs.selfsigned."example.com".paths.key
config.shb.certs.certs.selfsigned."example.com".systemdService

The full CA bundle is generated by the following Systemd service, running after each individual generator finished:

config.shb.certs.systemdService

See also the SSL certificate generator usage for a more detailed usage example.

Monitoring

A dashboard for SSL certificates is provided. See SSL Certificates Dashboard and Alert section in the monitoring chapter.

Debug

Each CA and Cert is generated by a systemd service whose name can be seen in the systemdService option. You can then see the latest errors messages using journalctl.

Let's Encrypt debug

Since the SHB SSL block uses the security.acme module under the hood, knowing how that one works can become required if something goes wrong.

For each domain and subdomain, noted as fqdn hereunder, the following systemd timers and services are created:

  • acme-renew-${fqdn}.timer triggers the acme-order-renew-${fqdn}.service service every day.
  • acme-${fqdn}.service (re)generate the initial self-signed certificate, only if the following job never succeeded at least once yet.
  • acme-order-renew-${fqdn}.service asks for a new certificate only if the certificate will expire in the next 30 days. Has logic to only renew if the list of domains has not changed.

Also, a global service named acme-setup.service is created

Tests

The self-signed implementation is tested in /tests/vm/ssl.nix.

Options Reference

id-prefix: blocks-ssl-options-
list-id: selfhostblocks-options
source: @OPTIONS_JSON@