mirror of
https://gitlab.com/simple-nixos-mailserver/nixos-mailserver.git
synced 2025-05-04 17:50:51 +05:00
Use rspamd for DKIM signing, drop OpenDKIM
OpenDKIM has not been updated in the last 7 years and failed to adopt RFC8463, which introduces Ed25519-SHA256 signatures. It has thereby held back the DKIM ecosystem, which relies on the DNS system to publish its public keys. The DNS system in turn does not handle large record sizes well (see RFC8301), which is why Ed25519 public keys would be preferable, but I'm not sure the ecosystem has caught up, so we stay on the conservative side with RSA for now. Closes: #210
This commit is contained in:
parent
95e2de368f
commit
15306cba72
@ -49,7 +49,7 @@ can stay up to date with bug fixes and updates.
|
||||
* Virus Scanning
|
||||
- [x] via clamav
|
||||
* DKIM Signing
|
||||
- [x] via opendkim
|
||||
- [x] via rspamd
|
||||
* User Management
|
||||
- [x] declarative user management
|
||||
- [x] declarative password management
|
||||
|
40
default.nix
40
default.nix
@ -802,6 +802,19 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
dkimKeyType = mkOption {
|
||||
type = types.enum [ "rsa" "ed25519" ];
|
||||
default = "rsa";
|
||||
description = ''
|
||||
The key type used for generating DKIM keys. ED25519 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.
|
||||
'';
|
||||
};
|
||||
|
||||
dkimKeyBits = mkOption {
|
||||
type = types.int;
|
||||
default = 1024;
|
||||
@ -815,26 +828,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
dkimHeaderCanonicalization = mkOption {
|
||||
type = types.enum ["relaxed" "simple"];
|
||||
default = "relaxed";
|
||||
description = ''
|
||||
DKIM canonicalization algorithm for message headers.
|
||||
|
||||
See https://datatracker.ietf.org/doc/html/rfc6376/#section-3.4 for details.
|
||||
'';
|
||||
};
|
||||
|
||||
dkimBodyCanonicalization = mkOption {
|
||||
type = types.enum ["relaxed" "simple"];
|
||||
default = "relaxed";
|
||||
description = ''
|
||||
DKIM canonicalization algorithm for message bodies.
|
||||
|
||||
See https://datatracker.ietf.org/doc/html/rfc6376/#section-3.4 for details.
|
||||
'';
|
||||
};
|
||||
|
||||
dmarcReporting = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
@ -1299,7 +1292,6 @@ in
|
||||
./mail-server/networking.nix
|
||||
./mail-server/systemd.nix
|
||||
./mail-server/dovecot.nix
|
||||
./mail-server/opendkim.nix
|
||||
./mail-server/postfix.nix
|
||||
./mail-server/rspamd.nix
|
||||
./mail-server/nginx.nix
|
||||
@ -1308,5 +1300,11 @@ in
|
||||
SPF checking has been migrated to Rspamd, which makes this config redundant. Please look into the rspamd config to migrate your settings.
|
||||
It may be that they are redundant and are already configured in rspamd like for skip_addresses.
|
||||
'')
|
||||
(lib.mkRemovedOptionModule [ "mailserver" "dkimHeaderCanonicalization" ] ''
|
||||
DKIM signing has been migrated to Rspamd, which always uses relaxed canonicalization.
|
||||
'')
|
||||
(lib.mkRemovedOptionModule [ "mailserver" "dkimBodyCanonicalization" ] ''
|
||||
DKIM signing has been migrated to Rspamd, which always uses relaxed canonicalization.
|
||||
'')
|
||||
];
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ Release Notes
|
||||
NixOS 25.05
|
||||
-----------
|
||||
|
||||
- OpenDKIM has been removed and DKIM signing is now handled by Rspamd, which only supports ``relaxed``` canoncalizaliaton.
|
||||
(`merge request <https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/merge_requests/374` __)
|
||||
- Rspamd now connects to Redis over its Unix Domain Socket by default
|
||||
(`merge request <https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/merge_requests/375>`__)
|
||||
|
||||
|
@ -173,7 +173,7 @@ Note that it can take a while until a DNS entry is propagated.
|
||||
Set ``DKIM`` signature
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
On your server, the ``opendkim`` systemd service generated a file
|
||||
On your server, the ``rspamd`` systemd service generated a file
|
||||
containing your DKIM public key in the file
|
||||
``/var/dkim/example.com.mail.txt``. The content of this file looks
|
||||
like
|
||||
|
@ -22,7 +22,7 @@ in
|
||||
{
|
||||
config = with cfg; lib.mkIf enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
dovecot opendkim openssh postfix rspamd
|
||||
dovecot openssh postfix rspamd
|
||||
] ++ (if certificateScheme == "selfsigned" then [ openssl ] else []);
|
||||
};
|
||||
}
|
||||
|
@ -1,89 +0,0 @@
|
||||
# nixos-mailserver: a simple mail server
|
||||
# Copyright (C) 2017 Brian Olsen
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.mailserver;
|
||||
|
||||
dkimUser = config.services.opendkim.user;
|
||||
dkimGroup = config.services.opendkim.group;
|
||||
|
||||
createDomainDkimCert = dom:
|
||||
let
|
||||
dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key";
|
||||
dkim_txt = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.txt";
|
||||
in
|
||||
''
|
||||
if [ ! -f "${dkim_key}" ]
|
||||
then
|
||||
${pkgs.opendkim}/bin/opendkim-genkey -s "${cfg.dkimSelector}" \
|
||||
-d "${dom}" \
|
||||
--bits="${toString cfg.dkimKeyBits}" \
|
||||
--directory="${cfg.dkimKeyDirectory}"
|
||||
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.private" "${dkim_key}"
|
||||
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.txt" "${dkim_txt}"
|
||||
chmod 644 "${dkim_txt}"
|
||||
echo "Generated key for domain ${dom} selector ${cfg.dkimSelector}"
|
||||
fi
|
||||
'';
|
||||
createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.domains);
|
||||
|
||||
keyTable = pkgs.writeText "opendkim-KeyTable"
|
||||
(lib.concatStringsSep "\n" (lib.flip map cfg.domains
|
||||
(dom: "${dom} ${dom}:${cfg.dkimSelector}:${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key")));
|
||||
signingTable = pkgs.writeText "opendkim-SigningTable"
|
||||
(lib.concatStringsSep "\n" (lib.flip map cfg.domains (dom: "${dom} ${dom}")));
|
||||
|
||||
dkim = config.services.opendkim;
|
||||
args = [ "-f" "-l" ] ++ lib.optionals (dkim.configFile != null) [ "-x" dkim.configFile ];
|
||||
in
|
||||
{
|
||||
config = mkIf (cfg.dkimSigning && cfg.enable) {
|
||||
services.opendkim = {
|
||||
enable = true;
|
||||
selector = cfg.dkimSelector;
|
||||
keyPath = cfg.dkimKeyDirectory;
|
||||
domains = "csl:${builtins.concatStringsSep "," cfg.domains}";
|
||||
configFile = pkgs.writeText "opendkim.conf" (''
|
||||
Canonicalization ${cfg.dkimHeaderCanonicalization}/${cfg.dkimBodyCanonicalization}
|
||||
UMask 0002
|
||||
Socket ${dkim.socket}
|
||||
KeyTable file:${keyTable}
|
||||
SigningTable file:${signingTable}
|
||||
'' + (lib.optionalString cfg.debug ''
|
||||
Syslog yes
|
||||
SyslogSuccess yes
|
||||
LogWhy yes
|
||||
''));
|
||||
};
|
||||
|
||||
users.users = optionalAttrs (config.services.postfix.user == "postfix") {
|
||||
postfix.extraGroups = [ "${dkimGroup}" ];
|
||||
};
|
||||
systemd.services.opendkim = {
|
||||
preStart = lib.mkForce createAllCerts;
|
||||
serviceConfig = {
|
||||
ExecStart = lib.mkForce "${pkgs.opendkim}/bin/opendkim ${escapeShellArgs args}";
|
||||
PermissionsStartOnly = lib.mkForce false;
|
||||
};
|
||||
};
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${cfg.dkimKeyDirectory}' - ${dkimUser} ${dkimGroup} - -"
|
||||
];
|
||||
};
|
||||
}
|
@ -126,9 +126,7 @@ let
|
||||
inetSocket = addr: port: "inet:[${toString port}@${addr}]";
|
||||
unixSocket = sock: "unix:${sock}";
|
||||
|
||||
smtpdMilters =
|
||||
(lib.optional cfg.dkimSigning "unix:/run/opendkim/opendkim.sock")
|
||||
++ [ "unix:/run/rspamd/rspamd-milter.sock" ];
|
||||
smtpdMilters = [ "unix:/run/rspamd/rspamd-milter.sock" ];
|
||||
|
||||
policyd-spf = pkgs.writeText "policyd-spf.conf" cfg.policydSPFExtraConfig;
|
||||
|
||||
@ -300,9 +298,9 @@ in
|
||||
tls_random_source = "dev:/dev/urandom";
|
||||
|
||||
smtpd_milters = smtpdMilters;
|
||||
non_smtpd_milters = lib.mkIf cfg.dkimSigning ["unix:/run/opendkim/opendkim.sock"];
|
||||
non_smtpd_milters = lib.mkIf cfg.dkimSigning [ "unix:/run/rspamd/rspamd-milter.sock" ];
|
||||
milter_protocol = "6";
|
||||
milter_mail_macros = "i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}";
|
||||
milter_mail_macros = "i {mail_addr} {client_addr} {client_name} {auth_authen}";
|
||||
|
||||
# Fix for https://www.postfix.org/smtp-smuggling.html
|
||||
smtpd_forbid_bare_newline = cfg.smtpdForbidBareNewline;
|
||||
|
@ -22,6 +22,26 @@ let
|
||||
postfixCfg = config.services.postfix;
|
||||
rspamdCfg = config.services.rspamd;
|
||||
rspamdSocket = "rspamd.service";
|
||||
|
||||
rspamdUser = config.services.rspamd.user;
|
||||
rspamdGroup = config.services.rspamd.group;
|
||||
|
||||
createDkimKeypair = domain: let
|
||||
privateKey = "${cfg.dkimKeyDirectory}/${domain}.${cfg.dkimSelector}.key";
|
||||
publicKey = "${cfg.dkimKeyDirectory}/${domain}.${cfg.dkimSelector}.txt";
|
||||
in pkgs.writeShellScript "dkim-keygen-${domain}" ''
|
||||
if [ ! -f "${privateKey}" ]
|
||||
then
|
||||
${lib.getExe' pkgs.rspamd "rspamadm"} dkim_keygen \
|
||||
--domain "${domain}" \
|
||||
--selector "${cfg.dkimSelector}" \
|
||||
--type "${cfg.dkimKeyType}" \
|
||||
--bits ${toString cfg.dkimKeyBits} \
|
||||
--privkey "${privateKey}" > "${publicKey}"
|
||||
chmod 0644 "${publicKey}"
|
||||
echo "Generated key for domain ${domain} and selector ${cfg.dkimSelector}"
|
||||
fi
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = with cfg; lib.mkIf enable {
|
||||
@ -66,8 +86,11 @@ in
|
||||
}
|
||||
''; };
|
||||
"dkim_signing.conf" = { text = ''
|
||||
# Disable outbound email signing, we use opendkim for this
|
||||
enabled = false;
|
||||
enabled = ${lib.boolToString cfg.dkimSigning};
|
||||
path = "${cfg.dkimKeyDirectory}/$domain.$selector.key";
|
||||
selector = "${cfg.dkimSelector}";
|
||||
# Allow for usernames w/o domain part
|
||||
allow_username_mismatch = true
|
||||
''; };
|
||||
"dmarc.conf" = { text = ''
|
||||
${lib.optionalString cfg.dmarcReporting.enable ''
|
||||
@ -119,10 +142,29 @@ in
|
||||
|
||||
services.redis.servers.rspamd.enable = lib.mkDefault true;
|
||||
|
||||
systemd.tmpfiles.settings."10-rspamd.conf" = {
|
||||
"${cfg.dkimKeyDirectory}" = {
|
||||
d = {
|
||||
# Create /var/dkim owned by rspamd user/group
|
||||
user = rspamdUser;
|
||||
group = rspamdGroup;
|
||||
};
|
||||
Z = {
|
||||
# Recursively adjust permissions in /var/dkim
|
||||
user = rspamdUser;
|
||||
group = rspamdGroup;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.rspamd = {
|
||||
requires = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
||||
after = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
||||
serviceConfig.SupplementaryGroups = [ config.services.redis.servers.rspamd.group ];
|
||||
serviceConfig = lib.optionalAttrs cfg.dkimSigning {
|
||||
ExecStartPre = map createDkimKeypair cfg.domains;
|
||||
ReadWritePaths = [ cfg.dkimKeyDirectory ];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.rspamd-dmarc-reporter = lib.optionalAttrs (cfg.dmarcReporting.enable) {
|
||||
|
@ -75,11 +75,8 @@ in
|
||||
# Postfix requires dovecot lmtp socket, dovecot auth socket and certificate to work
|
||||
systemd.services.postfix = {
|
||||
wants = certificatesDeps;
|
||||
after = [ "dovecot2.service" ]
|
||||
++ lib.optional cfg.dkimSigning "opendkim.service"
|
||||
++ certificatesDeps;
|
||||
requires = [ "dovecot2.service" ]
|
||||
++ lib.optional cfg.dkimSigning "opendkim.service";
|
||||
after = [ "dovecot2.service" ] ++ certificatesDeps;
|
||||
requires = [ "dovecot2.service" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user