mirror of
https://gitlab.com/simple-nixos-mailserver/nixos-mailserver.git
synced 2025-05-05 02:00:50 +05:00
Merge branch 'rspamd-dkim-signing' into 'master'
Use rspamd for DKIM signing, drop OpennDKIM Closes #210 See merge request simple-nixos-mailserver/nixos-mailserver!374
This commit is contained in:
commit
2c69257bdc
@ -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" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -402,7 +402,7 @@ pkgs.nixosTest {
|
||||
client.succeed("fetchmail --nosslcertck -v")
|
||||
client.succeed("cat ~/mail/* >&2")
|
||||
# make sure it is dkim signed
|
||||
client.succeed("grep DKIM ~/mail/*")
|
||||
client.succeed("grep DKIM-Signature: ~/mail/*")
|
||||
|
||||
with subtest("aliases"):
|
||||
client.execute("rm ~/mail/*")
|
||||
|
Loading…
x
Reference in New Issue
Block a user