# SSL Block {#ssl-block}

This NixOS module is a block that provides a contract to generate TLS certificates.

It is implemented by:
- [`shb.certs.cas.selfsigned`][10] and [`shb.certs.certs.selfsigned`][11]: Generates self-signed certificates,
  including self-signed CA thanks to the [certtool][1] package.
- [`shb.certs.certs.letsencrypt`][12]: Requests certificates from [Let's Encrypt][2].

[1]: https://search.nixos.org/packages?channel=23.11&show=gnutls&from=0&size=50&sort=relevance&type=packages&query=certtool
[2]: https://letsencrypt.org/

[10]: blocks-ssl.html#blocks-ssl-options-shb.certs.cas.selfsigned
[11]: blocks-ssl.html#blocks-ssl-options-shb.certs.certs.selfsigned
[12]: blocks-ssl.html#blocks-ssl-options-shb.certs.certs.letsencrypt

## Contract {#ssl-block-contract}

The contract for this block is defined in [`/modules/contracts/ssl.nix`](@REPO@/modules/contracts/ssl.nix).

Every module implementing this contract provides the following options:

- `domain`: Domain to generate the certificate for.
- `extraDomains`: Other domains the certificate should be generated for.
- `group`: The unix group owning this certificate.
- `reloadServices`: Systemd services to reload when the certificate gets renewed.
- `paths.cert`: Path to the cert file.
- `paths.key`: Path to the key file.
- `systemdService`: Systemd oneshot service used to generate the certificate.
  The Systemd service file name must include the `.service` suffix. Downstream users of the
  certificate can use this option to wait for the certificate to be generated.

## Implementations {#ssl-block-impl}

This sections explains how to generate certificates using the SSL block implementations.

### Self-Signed Certificates {#ssl-block-impl-self-signed}

Defined in [`/modules/blocks/ssl.nix`](@REPO@/modules/blocks/ssl.nix).

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

```nix
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:

```nix
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 {#ssl-block-impl-lets-encrypt}

Defined in [`/modules/blocks/ssl.nix`](@REPO@/modules/blocks/ssl.nix).

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

```nix
shb.certs.certs.letsencrypt."example.com" = {
  domain = "example.com";
  group = "nginx";
  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:

```yaml
LINODE_TOKEN=XYZ...
```

For other providers, see the [official instruction](https://go-acme.github.io/lego/dns/).

## Usage {#ssl-block-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:

```nix
config.shb.certs.certs.<implementation>.<name>.paths.cert
config.shb.certs.certs.<implementation>.<name>.paths.key
```

For example:

```nix
config.shb.certs.certs.selfsigned."example.com".paths.cert
config.shb.certs.certs.selfsigned."example.com".paths.key
```
We can then configure Nginx to use those certificates:

```nix
services.nginx.virtualHosts."example.com" =
  let
    cert = config.shb.certs.certs.selfsigned."example.com";
  in
  {
    onlySSL = true;
    sslCertificate = cert.paths.cert;
    sslCertificateKey = cert.paths.key;

    locations."/".extraConfig = ''
      add_header Content-Type text/plain;
      return 200 'It works!';
    '';
  };
```

To make sure the Nginx webserver can find the generated file, we will make it wait for the
certificate to the generated:

```nix
systemd.services.nginx = {
  after = [ config.shb.certs.certs.selfsigned."example.com".systemdService ];
  requires = [ config.shb.certs.certs.selfsigned."example.com".systemdService ];
};
```

If needed, we can also wait on the CA bundle to be generated by waiting for the Systemd service:

```nix
config.shb.certs.systemdService
```

## Debug {#ssl-block-debug}

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

## Tests {#ssl-block-tests}

This block is tested in [`/tests/vm/ssl.nix`](@REPO@/tests/vm/ssl.nix).

## Options Reference {#ssl-block-options}

```{=include=} options
id-prefix: blocks-ssl-options-
list-id: selfhostblocks-options
source: @OPTIONS_JSON@
```
