Switch to NixOS ACME module for certificate management

Drop most of the existing certificate handling, because we're effectively
duplicating functionality that NixOS offers for free with better
design, testing and maintainance than what we could provide downstream.

The remaining two options are to reference an
existing `security.acme.certs` configuration through
`mailserver.x509.useACMEHost` or to provide existing key material via
`mailserver.x509.certificateFile` and `mailserver.x509.privateKeyFile`.

Support for automatic creation of self-signed certificates has been
removed, because it is undesirable in public mail setups.

The updated setup guide now displays the recommended configuration that
relies on the NixOS ACME module, but requires further customization to
select a suitable challenge.

Co-Authored-By: Emily <git@emilylange.de>
This commit is contained in:
Martin Weinelt
2025-10-19 23:20:00 +02:00
parent 18ee2a44ed
commit 33ba1ff52b
19 changed files with 166 additions and 239 deletions
+38 -91
View File
@@ -32,7 +32,6 @@ let
mkRemovedOptionModule
mkRenamedOptionModule
types
warn
;
cfg = config.mailserver;
@@ -131,20 +130,6 @@ in
description = "The domains that this mail server serves.";
};
certificateDomains = mkOption {
type = types.listOf types.str;
example = [
"imap.example.com"
"pop3.example.com"
];
default = [ ];
description = ''
({option}`mailserver.certificateScheme` == `acme-nginx`)
Secondary domains and subdomains for which it is necessary to generate a certificate.
'';
};
messageSizeLimit = mkOption {
type = types.int;
example = 52428800;
@@ -788,91 +773,43 @@ in
};
};
certificateScheme =
let
schemes = [
"manual"
"selfsigned"
"acme-nginx"
"acme"
];
translate =
i:
warn
"Setting mailserver.certificateScheme by number is deprecated, please use names instead: 'mailserver.certificateScheme = ${builtins.toString i}' can be replaced by 'mailserver.certificateScheme = \"${
(builtins.elemAt schemes (i - 1))
}\"'."
(builtins.elemAt schemes (i - 1));
in
mkOption {
type =
with types;
coercedTo (enum [
1
2
3
]) translate (enum schemes);
default = "selfsigned";
x509 = {
useACMEHost = mkOption {
type = with types; nullOr str;
default = null;
example = literalExpression "config.mailserver.fqdn";
description = ''
The scheme to use for managing TLS certificates:
Common name used in the relevant `security.acme.certs` configuration.
1. `manual`: you specify locations via {option}`mailserver.certificateFile` and
{option}`mailserver.keyFile` and manually copy certificates there.
2. `selfsigned`: you let the server create new (self-signed) certificates on the fly.
3. `acme-nginx`: you let the server request certificates from [Let's Encrypt](https://letsencrypt.org)
via NixOS' ACME module. By default, this will set up a stripped-down Nginx server for
{option}`mailserver.fqdn` and open port 80. For this to work, the FQDN must be properly
configured to point to your server (see the [setup guide](setup-guide.rst) for more information).
4. `acme`: you already have an ACME certificate set up (for example, you're already running a TLS-enabled
Nginx server on the FQDN). This is better than `manual` because the appropriate services will be reloaded
when the certificate is renewed.
Mutually exclusive with {option}`mailserver.x509.certificateFile` and {option}`mailserver.x509.privateKeyFile`.
'';
};
certificateFile = mkOption {
type = types.path;
example = "/root/mail-server.crt";
description = ''
({option}`mailserver.certificateScheme` == `manual`)
certificateFile = mkOption {
type = with types; nullOr path;
default = null;
example = "/var/keys/certs/fullchain.pem";
description = ''
Path to the signed X509 certificate including intermediate certificates.
Location of the certificate.
'';
};
This is commonly referred to as {file}`fullchain.pem`.
keyFile = mkOption {
type = types.path;
example = "/root/mail-server.key";
description = ''
({option}`mailserver.certificateScheme` == `manual`)
Mutually exclusive with {option}`mailserver.x509.useACMEHost`.
'';
};
Location of the key file.
'';
};
privateKeyFile = mkOption {
type = with types; nullOr str;
default = null;
example = "/var/keys/certs/privkey.pem";
description = ''
Path to the X509 private key.
certificateDirectory = mkOption {
type = types.path;
default = "/var/certs";
description = ''
({option}`mailserver.certificateScheme` == `selfsigned`)
This is commonly referred to as {file}`privkey.pem`.
This is the folder where the self-signed certificate will be created. The name is
hardcoded to "cert-DOMAIN.pem" and "key-DOMAIN.pem" and the
certificate is valid for 10 years.
'';
};
acmeCertificateName = mkOption {
type = types.str;
default = cfg.fqdn;
defaultText = literalExpression "config.mailserver.fqdn";
example = "example.com";
description = ''
({option}`mailserver.certificateScheme` == `acme`)
When the `acme` `certificateScheme` is selected, you can use this option
to override the default certificate name. This is useful if you've
generated a wildcard certificate, for example.
'';
Mutually exclusive with {option}`mailserver.x509.useACMEHost`.
'';
};
};
enableImap = mkOption {
@@ -1502,7 +1439,6 @@ in
./mail-server/dovecot.nix
./mail-server/postfix.nix
./mail-server/rspamd.nix
./mail-server/nginx.nix
./mail-server/kresd.nix
(mkRemovedOptionModule [ "mailserver" "policydSPFExtraConfig" ] ''
SPF checking has been migrated to Rspamd, which makes this config redundant. Please look into the rspamd config to migrate your settings.
@@ -1531,5 +1467,16 @@ in
(mkRemovedOptionModule [ "mailserver" "dmarcReporting" "fromName" ] ''
The name in the `FROM` field for DMARC report now uses the `mailserver.systemName`.
'')
(mkRemovedOptionModule [ "mailserver" "certificateDomains" ] ''
Configure `security.acme.certs.''${config.mailserver.fqdn}.extraDomains` instead.
'')
(mkRemovedOptionModule [ "mailserver" "certificateScheme" ] "")
(mkRemovedOptionModule [ "mailserver" "certificateDirectory" ] ''
Automatic creation of self-signed certificates is no longer supported.
'')
(mkRenamedOptionModule [ "mailserver" "acmeCertificateName" ] [ "mailserver" "x509" "useACMEHost" ])
(mkRenamedOptionModule [ "mailserver" "certificateFile" ] [ "mailserver" "x509" "certificateFile" ])
(mkRenamedOptionModule [ "mailserver" "keyFile" ] [ "mailserver" "x509" "privateKeyFile" ])
];
}