Use OpenDKIM instead of rmilter for DKIM
As part of #61 this moves DKIM handling from rmilter to OpenDKIM.
This commit is contained in:
parent
0c883d8bcd
commit
7036371f75
6 changed files with 122 additions and 54 deletions
|
@ -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
90
mail-server/opendkim.nix
Normal 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}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -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 =
|
||||||
|
|
|
@ -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 ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue