nixcloud.TLS
unifies TLS certificate management in NixOS.
The motivation for creating nixcloud.TLS
was:
-
Easy to use, no longer manual certificate management
-
Easily switch between 'ACME', 'selfsigned' or 'usersupplied' scenarious:
This makes it easy for testing (using selfsigned TLS certificates) and production (using "ACME" or you own certificates) configurations
-
security.acme
was a major inspiraten for this implementation but we needed a more modular approach to certificate management -
Meaningful defaults:
We encourage let's encrypt based ACME and
security.acme
-
Because of the ACME rate limits we try to minimize the amount of requests
Note: We might fully replace security.acme
from nixpkgs but we want to be backward compatible so one can use nixcloud-webservices along with services which use security.acme
. The primary conflict is that nixcloud.reverse-proxy
must interact with simp_le
and the .well-known
directory is a hardcoded requirement in the let's encrypt ACME challenge/response pattern.
Note: nixcloud.TLS
was meant to be used with nixcloud.reverse-proxy
running on port 80. if you are running services.nginx
on port 80 a lot of assumptions won't work and this isn't tested on the other hand. See issue 23 on that matter.
The simplest configuration would be this:
nixcloud.TLS.certs = {
"nixcloud.io" = {};
};
Here the identifier is the left hand string, "nixcloud.io" and the attribute set on the right is basically empty. As a result the configuration will be using default values where domain
will be set to the identifier and mode
will be "ACME".
A more complex example configuration for nixcloud.TLS
would be:
nixcloud.TLS.certs = {
"example.com-ACME" = {
domain = "example.com";
mode = "ACME";
email = "[email protected]";
reload = [ "postifx.service" "myservice.service" ];
};
"example.com-selfsigned" = {
domain = "example.com";
mode = "selfsigned";
email = "[email protected]";
};
"example.com-usersupplied" = {
domain = "example.com";
mode = {
tls_certificate="/root/.tls/fullchain.pem";
tls_certificate_key="/root/.tls/key.pem";
};
email = "[email protected]";
};
};
As said, the default value for domain
is the identifier
. It would not make sense in any of the above examples as "example.com-ACME" is not a correct domain therefore the domain
is set explicitly to "example.com" in each example. In `nixcloud.TLS.certs."nixcloud.io" the domain is set to "nixcloud.io" which is a correct domain and an intended default.
The reload
example for "example.com-ACME" adds two services, "postfix.service" and "myservice.service" to the postrun hook. If you would use nixcloud.email
and nixcloud-webservices
it would contain [ "postfix.service" "dovecot2.service" "nixcloud.reverse-proxy" "myservice.service" ] as it accumulates all defined services and applies lib.unique
to the list.
The example above creates three certificates for the same domain. The certificates can be found in:
/var/lib/acme/example.com-ACME/
/var/lib/nixcloud/TLS/example.com-selfsigned/selfsigned
/var/lib/nixcloud/TLS/example.com-usersupplied/usersupplied
But they should be referenced using:
config.nixcloud.TLS.certs."identifier".tls_certificate
Since the nixcloud.TLS
abstraction will return the correct location according to the mode
of operation.
If you are using nixcloud-webservices
or nixcloud.email
you will be using nixcloud.TLS
without knowing it as we use it as a default from now on.
nixcloud.TLS.certs = {
"example.com" = {
mode = "ACME";
email = "[email protected]";
};
};
nixcloud.TLS.certs = {
"example.com" = {
mode = {
tls_certificate="/root/.tls/fullchain.pem";
tls_certificate_key="/root/.tls/key.pem";
};
email = "[email protected]";
};
};
nixcloud.TLS.certs = {
"example.com" = {
mode = "selfsigned";
};
};
Note: security.acme
also creates a self-signed certificate but if your testing environment can't successfully use ACME to replace it with a valid
certificate it will always report simp_le
errors on nixos-rebuild switch
updates and this is the reason we created a self-signed implementation.
This section is for users who have a service as murmur (mumble backend) which is in nixpkgs and won't be tunneled trough nixcloud.reverse-proxy
. Generally a servic e like https://nixos.org/nixos/options.html#ssl+cert, so basically a custom TLS configuration.
This brief guide shows how to manage your certificates using nixcloud.TLS
in that case.
In a nutshell, you need to do three things:
- Create a nixcloud.TLS.certs."identifier" record and pick your
mode
of operation - In the service, reference the
tls_certificate
andtls_certificate_key
from the globalconfig
- Inject systemd service dependencies
nixcloud.TLS.certs = {
"example.org" = {
mode = "ACME";
reload = [ "myservice1.service" ];
restart = [ "myservice2.service" ];
};
};
Note: It is important that you list your systemd services in reload
or restart
so they get reloaded or restarted once a new certificate arrives.
If you want to reference a tls_certificate
or a tls_certificate_key
you can use the nixcloud.TLS
identifier (string) with the config
variable:
sslServerCert = config.nixcloud.TLS.certs."example.org".tls_certificate;
sslServerKey = config.nixcloud.TLS.certs."example.org".tls_certificate_key;
Note: Most often the identifier "example.org" is the same as the the domain you want to have a certificate for. However, using such identifier you can easily issue several different certificates for the same domain. You must use the quotes, so that "example.org" is a single attribute in the Nix attribute path!
You also need to inject the systemd dependencies so that both nixcloud.TLS
or security.acme
has enough time to allocate the
certificates before they are used from a daemon.
Here is an example how one would extend postfix
:
systemd.services.postfix.after = [ "nixcloud.TLS-certificates.target" ];
systemd.services.postfix.wants = [ "nixcloud.TLS-certificates.target" ];
Note: This code was copied from nixcloud.email
.
The "nixcloud.TLS-certificates.target" waits for these 4 targets to finish:
-
security.acme:
acme-selfsigned-certificates.target
acme-certificates.target
-
nixcloud.TLS:
nixcloud.TLS-selfsigned.target
nixcloud.TLS-usersupplied.target
Note: Most services in nixpkgs using security.acme
lack the former two target dependencies and if you deploy a server for the first time the services might and probably will fail to start since there won't be any certificates in place for the configured cert/key pair yet. This is a race condition we'd like to fix in the future.
These commands might come in handy:
systemctl list-units --type=target
systemctl status nixcloud.TLS-certificates.target
systemctl status nixcloud.TLS-usersupplied-certificates.target
systemctl status nixcloud.TLS-selfsigned-certificates.target
systemctl status nixcloud.TLS
Thanks to the security.acme
authors for their inspiration and example code & uwap!