Use OpenDKIM instead of rmilter for DKIM

As part of #61 this moves DKIM handling from rmilter to OpenDKIM.
This commit is contained in:
Brian Olsen 2018-05-04 18:17:51 +02:00 committed by Ruben Maher
parent 0c883d8bcd
commit 7036371f75
6 changed files with 122 additions and 54 deletions

View file

@ -733,6 +733,7 @@ in
./mail-server/networking.nix ./mail-server/networking.nix
./mail-server/systemd.nix ./mail-server/systemd.nix
./mail-server/dovecot.nix ./mail-server/dovecot.nix
./mail-server/opendkim.nix
./mail-server/postfix.nix ./mail-server/postfix.nix
./mail-server/rmilter.nix ./mail-server/rmilter.nix
./mail-server/nginx.nix ./mail-server/nginx.nix

90
mail-server/opendkim.nix Normal file
View file

@ -0,0 +1,90 @@
# 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}" ] || [ ! -f "${dkim_txt}" ]
then
${pkgs.opendkim}/bin/opendkim-genkey -s "${cfg.dkimSelector}" \
-d "${dom}" \
--directory="${cfg.dkimKeyDirectory}"
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.private" "${dkim_key}"
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.txt" "${dkim_txt}"
echo "Generated key for domain ${dom} selector ${cfg.dkimSelector}"
fi
'';
createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.domains);
create_dkim_cert =
''
# Create dkim dir
mkdir -p "${cfg.dkimKeyDirectory}"
chown ${dkimUser}:${dkimGroup} "${cfg.dkimKeyDirectory}"
${createAllCerts}
chown -R ${dkimUser}:${dkimGroup} "${cfg.dkimKeyDirectory}"
'';
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;
domains = "csl:${builtins.concatStringsSep "," cfg.domains}";
configFile = pkgs.writeText "opendkim.conf" (''
Canonicalization relaxed/simple
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 = [ "${config.services.opendkim.group}" ];
};
systemd.services.opendkim = {
preStart = create_dkim_cert;
serviceConfig.ExecStart = lib.mkForce "${pkgs.opendkim}/bin/opendkim ${escapeShellArgs args}";
};
};
}

View file

@ -90,6 +90,17 @@ let
/^Message-ID:\s+<(.*?)@.*?>/ REPLACE Message-ID: <$1@${cfg.fqdn}> /^Message-ID:\s+<(.*?)@.*?>/ REPLACE Message-ID: <$1@${cfg.fqdn}>
''); '');
inetSocket = addr: port: "inet:[${toString port}@${addr}]";
unixSocket = sock: "unix:${sock}";
rmilter = config.services.rmilter;
rmilterSocket = if rmilter.bindSocket.type == "unix" then unixSocket rmilter.bindSocket.path
else inetSocket rmilter.bindSocket.address rmilter.bindSocket.port;
smtpdMilters =
(lib.optional cfg.dkimSigning "unix:/run/opendkim/opendkim.sock")
++ [ rmilterSocket ];
in in
{ {
config = with cfg; lib.mkIf enable { config = with cfg; lib.mkIf enable {
@ -151,6 +162,11 @@ in
# Configure a non blocking source of randomness # Configure a non blocking source of randomness
tls_random_source = dev:/dev/urandom tls_random_source = dev:/dev/urandom
smtpd_milters = ${lib.concatStringsSep "," smtpdMilters}
${lib.optionalString cfg.dkimSigning "non_smtpd_milters = unix:/run/opendkim/opendkim.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}
''; '';
submissionOptions = submissionOptions =

View file

@ -27,23 +27,8 @@ let
}; };
'' ''
else ""; else "";
dkim = if cfg.dkimSigning postfixCfg = config.services.postfix;
# Note: domain = "*"; causes Rmilter to try to search key in the key path rmilter = config.services.rmilter;
# as keypath/domain.selector.key for any domain.
then
''
dkim {
domain {
key = "${cfg.dkimKeyDirectory}";
domain = "*";
selector = "${cfg.dkimSelector}";
};
sign_alg = sha256;
auth_only = yes;
header_canon = relaxed;
}
''
else "";
in in
{ {
config = with cfg; lib.mkIf enable { config = with cfg; lib.mkIf enable {
@ -54,7 +39,6 @@ in
services.rmilter = { services.rmilter = {
inherit debug; inherit debug;
enable = true; enable = true;
postfix.enable = true;
rspamd = { rspamd = {
enable = true; enable = true;
extraConfig = "extended_spam_headers = yes;"; extraConfig = "extended_spam_headers = yes;";
@ -65,10 +49,9 @@ in
max_size = 20M; max_size = 20M;
${clamav} ${clamav}
${dkim}
''; '';
}; };
users.extraUsers.${postfixCfg.user}.extraGroups = [ rmilter.group ];
}; };
} }

View file

@ -19,33 +19,6 @@
let let
cfg = config.mailserver; cfg = config.mailserver;
createDomainDkimCert = dom:
let
dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key";
dkim_txt = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.txt";
in
''
if [ ! -f "${dkim_key}" ] || [ ! -f "${dkim_txt}" ]
then
${pkgs.opendkim}/bin/opendkim-genkey -s "${cfg.dkimSelector}" \
-d "${dom}" \
--directory="${cfg.dkimKeyDirectory}"
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.private" "${dkim_key}"
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.txt" "${dkim_txt}"
fi
'';
createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.domains);
create_dkim_cert =
''
# Create dkim dir
mkdir -p "${cfg.dkimKeyDirectory}"
chown rmilter:rmilter "${cfg.dkimKeyDirectory}"
${createAllCerts}
chown -R rmilter:rmilter "${cfg.dkimKeyDirectory}"
'';
createDhParameterFile = let createDhParameterFile = let
dovecotVersion = builtins.fromJSON dovecotVersion = builtins.fromJSON
(builtins.readFile (pkgs.callPackage ./dovecot-version.nix {})); (builtins.readFile (pkgs.callPackage ./dovecot-version.nix {}));
@ -121,19 +94,16 @@ in
# Postfix requires rmilter socket, dovecot lmtp socket, dovecot auth socket and certificate to work # Postfix requires rmilter socket, dovecot lmtp socket, dovecot auth socket and certificate to work
systemd.services.postfix = { systemd.services.postfix = {
after = [ "rmilter.socket" "dovecot2.service" "mailserver-certificates.target" ]; after = [ "rmilter.socket" "dovecot2.service" "mailserver-certificates.target" ]
++ (lib.optional cfg.dkimSigning "opendkim.service");
wants = [ "mailserver-certificates.target" ]; wants = [ "mailserver-certificates.target" ];
requires = [ "rmilter.socket" "dovecot2.service" ]; requires = [ "rmilter.socket" "dovecot2.service" ]
++ (lib.optional cfg.dkimSigning "opendkim.service");
}; };
# Create dkim certificates
systemd.services.rmilter = { systemd.services.rmilter = {
requires = [ "rmilter.socket" ]; requires = [ "rmilter.socket" ];
after = [ "rmilter.socket" ]; after = [ "rmilter.socket" ];
preStart =
''
${create_dkim_cert}
'';
}; };
}; };
} }

View file

@ -23,6 +23,14 @@ import <nixpkgs/nixos/tests/make-test.nix> {
../default.nix ../default.nix
]; ];
services.rsyslogd = {
enable = true;
defaultConfig = ''
*.* /dev/console
'';
};
mailserver = { mailserver = {
enable = true; enable = true;
debug = true; debug = true;