diff --git a/default.nix b/default.nix
index 36d0848..ebeb782 100644
--- a/default.nix
+++ b/default.nix
@@ -733,6 +733,7 @@ in
./mail-server/networking.nix
./mail-server/systemd.nix
./mail-server/dovecot.nix
+ ./mail-server/opendkim.nix
./mail-server/postfix.nix
./mail-server/rmilter.nix
./mail-server/nginx.nix
diff --git a/mail-server/opendkim.nix b/mail-server/opendkim.nix
new file mode 100644
index 0000000..d060323
--- /dev/null
+++ b/mail-server/opendkim.nix
@@ -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
+{ 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}";
+ };
+ };
+}
\ No newline at end of file
diff --git a/mail-server/postfix.nix b/mail-server/postfix.nix
index c35c10e..aad87bb 100644
--- a/mail-server/postfix.nix
+++ b/mail-server/postfix.nix
@@ -90,6 +90,17 @@ let
/^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
{
config = with cfg; lib.mkIf enable {
@@ -151,6 +162,11 @@ in
# Configure a non blocking source of randomness
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 =
diff --git a/mail-server/rmilter.nix b/mail-server/rmilter.nix
index ed2d019..aed5009 100644
--- a/mail-server/rmilter.nix
+++ b/mail-server/rmilter.nix
@@ -27,23 +27,8 @@ let
};
''
else "";
- dkim = if cfg.dkimSigning
- # Note: domain = "*"; causes Rmilter to try to search key in the key path
- # 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 "";
+ postfixCfg = config.services.postfix;
+ rmilter = config.services.rmilter;
in
{
config = with cfg; lib.mkIf enable {
@@ -54,7 +39,6 @@ in
services.rmilter = {
inherit debug;
enable = true;
- postfix.enable = true;
rspamd = {
enable = true;
extraConfig = "extended_spam_headers = yes;";
@@ -65,10 +49,9 @@ in
max_size = 20M;
${clamav}
-
- ${dkim}
'';
};
+ users.extraUsers.${postfixCfg.user}.extraGroups = [ rmilter.group ];
};
}
diff --git a/mail-server/systemd.nix b/mail-server/systemd.nix
index e083d85..9066eab 100644
--- a/mail-server/systemd.nix
+++ b/mail-server/systemd.nix
@@ -19,33 +19,6 @@
let
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
dovecotVersion = builtins.fromJSON
(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
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" ];
- requires = [ "rmilter.socket" "dovecot2.service" ];
+ requires = [ "rmilter.socket" "dovecot2.service" ]
+ ++ (lib.optional cfg.dkimSigning "opendkim.service");
};
- # Create dkim certificates
systemd.services.rmilter = {
requires = [ "rmilter.socket" ];
after = [ "rmilter.socket" ];
- preStart =
- ''
- ${create_dkim_cert}
- '';
};
};
}
diff --git a/tests/extern.nix b/tests/extern.nix
index 30a871e..4ad2ff7 100644
--- a/tests/extern.nix
+++ b/tests/extern.nix
@@ -23,6 +23,14 @@ import {
../default.nix
];
+ services.rsyslogd = {
+ enable = true;
+ defaultConfig = ''
+ *.* /dev/console
+ '';
+ };
+
+
mailserver = {
enable = true;
debug = true;