Add support for DKIM key management

After bumping the generation of new DKIM keys to RSA 2048 in NixOS 25.11
key rotation for existing users could not be done safely.

To resolve this situation we now support multiple generations of
selectors per domain to enable proper DKIM key transitions as described
in RFC6376 3.1. The added documentation introduces and motivates DKIM
and guides the user through a DKIM key rotation.

Additionally, DKIM key material can now also be treated as a managed
secrets when autogenerated state on the mail server host is undesirable.

This change is fully backwards compatible in behavior and will continue
to use the previously generated DKIM key without any additional
configuration up until the point when DKIM selectors are configured
explicitly.
This commit is contained in:
Martin Weinelt
2026-03-06 02:36:42 +01:00
parent ea775773d9
commit 6ff4a50f02
11 changed files with 512 additions and 86 deletions
+164 -50
View File
@@ -917,63 +917,170 @@ in
'';
};
dkimSigning = mkOption {
type = types.bool;
default = true;
description = ''
Whether to activate dkim signing.
'';
};
dkim = {
enable = mkEnableOption "DKIM signing" // {
default = true;
};
dkimSelector = mkOption {
type = types.str;
default = "mail";
description = ''
The DKIM selector.
'';
};
keyDirectory = mkOption {
type = types.path;
default = "/var/dkim";
description = ''
The path where DKIM siging keys are stored.
'';
};
dkimKeyDirectory = mkOption {
type = types.path;
default = "/var/dkim";
description = ''
The DKIM directory.
'';
};
defaults = {
selector = mkOption {
type = types.str;
default = "mail";
description = ''
The default selector used to reference and lookup DKIM keys.
dkimKeyType = mkOption {
type = types.enum [
"rsa"
"ed25519"
];
default = "rsa";
description = ''
The key type used for generating DKIM keys. Ed25519 support was
introduced in RFC6376 (2018).
This value should most likely not be changed. Instead manage
{option}`mailserver.dkim.domains.<name>.selectors` to sign with one
or multiple DKIM key pairs and manage migrations.
'';
};
:::{warning}
Ed25519 DKIM keys are currently not recommended for primary use, as
various DKIM validators out there lack support and consider the keypair invalid.
:::
keyType = mkOption {
type = types.enum [
"rsa"
"ed25519"
];
default = "rsa";
description = ''
The key type used for generating DKIM keys. Ed25519 support was
introduced in RFC6376 (2018).
If you have already deployed a key with a different type than specified
here, then you should use a different selector ({option}`mailserver.dkimSelector`). In order to get
this package to generate a key with the new type, you will either have to
change the selector or delete the old key file.
'';
};
:::{warning}
Ed25519 DKIM keys are currently not recommended for sole use, as
various DKIM validators out there lack support and consider the
keypair invalid.
:::
dkimKeyBits = mkOption {
type = types.int;
default = 2048;
description = ''
How many bits in generated DKIM keys. RFC8301 suggests a minimum RSA key length of 2048 bit.
This value should most likely not be changed. Once DKIM keys for
domain and selector are generated changing this value will not
regenerate the keypair. Instead create a new selector and configure
{option}`mailserver.dkim.domains.<name>.selectors.<name>.keyType`.
'';
};
If you have already deployed a key with a different number of bits than specified
here, then you should use a different selector ({option}`mailserver.dkimSelector`). In order to get
this package to generate a key with the new number of bits, you will either have to
change the selector or delete the old key file.
'';
keyLength = mkOption {
type = types.int;
default = 2048;
description = ''
The default key length used for generating new DKIM keys.
Only applies for RSA keys, Ed25519 keys use a fixed key length.
Per [RFC8301 3.2] the minimum RSA key length should be at least
2048 bit.
This value should most likely not be changed. Once DKIM keys for
domain and selector are generated changing this value will not
regenerate the keypair. Instead create a new selector and configure
{option}`mailserver.dkim.domains.<name>.selectors.<name>.keyLength`.
[RFC8301 3.2]: https://datatracker.ietf.org/doc/html/rfc8301#section-3.2
'';
};
};
domains = mkOption {
description = "DKIM configuration per domain.";
type = types.attrsOf (
types.submodule ({
options = {
selectors = mkOption {
description = ''
DKIM selectors used for signing outgoing mail for this domain.
When no selector is configured a default selector will be
created with settings inherited from {option}`mailserver.defaults.dkim <mailserver.dkim.defaults.keyLength>`.
'';
type = types.attrsOf (
types.submodule ({
options = {
keyType = mkOption {
type =
with types;
nullOr (enum [
"rsa"
"ed25519"
]);
default = null;
example = "rsa";
description = ''
The key type used for generating this DKIM keypair.
:::{warning}
Ed25519 DKIM keys are currently not recommended for sole use, as
various DKIM validators out there lack support and consider the
keypair invalid.
:::
This option is mutually exclusive with `keyFile`.
'';
};
keyLength = mkOption {
type = with types; nullOr int;
default = null;
example = 2048;
description = ''
The key length used for generating this DKIM key.
Only applies for RSA keys, Ed25519 keys use a fixed key size.
This option is mutually exclusive with `keyFile`.
'';
};
keyFile = mkOption {
type =
with types;
nullOr (pathWith {
inStore = false;
});
default = null;
example = "/run/keys/example.com-dkim-rsa-2026-03.key";
description = ''
Path to an existing DKIM private key file.
DKIM keys can be generated using `rspamadm dkim_keygen`.
This option is mutually exclusive with `keyType` and `keyLength`.
'';
};
};
})
);
default = { };
example = lib.literalExpression ''
{
"mail" = {
# inherit defaults from mailserver.dkim.defaults
};
"rsa-2026-03".keyFile = "/run/keys/example.com-dkim-rsa-2026-03.key";
};
'';
};
};
})
);
default = { };
example = lib.literalExpression ''
{
"example.com".selectors = {
"mail" = {
# inherit defaults from mailserver.dkim.defaults
};
"rsa-2026-03".keyFile = "/run/keys/example.com-dkim-rsa-2026-03.key";
};
};
'';
};
};
dmarcReporting = {
@@ -1468,6 +1575,13 @@ in
(mkRemovedOptionModule [ "mailserver" "smtpdForbidBareNewline" ] ''
The workaround for the SMTP Smuggling attack is default enabled in Postfix >3.9. Use `services.postfix.config.smtpd_forbid_bare_newline` if you need to deviate from its default.
'')
(mkRenamedOptionModule [ "mailserver" "dkimSigning" ] [ "mailserver" "dkim" "enable" ])
(mkRenamedOptionModule [ "mailserver" "dkimKeyDirectory" ] [ "mailserver" "dkim" "keyDirectory" ])
(mkRenamedOptionModule
[ "mailserver" "dkimSelector" ]
[ "mailserver" "dkim" "defaults" "selector" ]
)
(mkRenamedOptionModule [ "mailserver" "dkimKeyType" ] [ "mailserver" "dkim" "defaults" "keyType" ])
(mkRenamedOptionModule [ "mailserver" "dmarcReporting" "domain" ] [ "mailserver" "systemDomain" ])
(mkRenamedOptionModule
[ "mailserver" "dmarcReporting" "organizationName" ]