fix mail on pennyworth
parent
173d32bae4
commit
4aa3e48640
|
@ -14,24 +14,8 @@ with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.services.mailz;
|
cfg = config.services.mailz;
|
||||||
|
|
||||||
# Convert:
|
alldomains = lib.concatLists (mapAttrsToList (n: usr: usr.domains) cfg.users);
|
||||||
#
|
|
||||||
# {
|
|
||||||
# a = { aliases = [ "x", "y" ]; };
|
|
||||||
# b = { aliases = [ "x" ]; };
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# To:
|
|
||||||
#
|
|
||||||
# {
|
|
||||||
# x = [ "a" "b" ];
|
|
||||||
# y = [ "a" ];
|
|
||||||
# }
|
|
||||||
aliases = foldAttrs (user: users: [user] ++ users) [ ]
|
|
||||||
(flatten (flip mapAttrsToList cfg.users
|
|
||||||
(user: options: flip map options.aliases
|
|
||||||
(alias: { ${alias} = user; }))));
|
|
||||||
|
|
||||||
files = {
|
files = {
|
||||||
credentials = pkgs.writeText "credentials"
|
credentials = pkgs.writeText "credentials"
|
||||||
|
@ -45,20 +29,8 @@ let
|
||||||
(flip mapAttrsToList cfg.users
|
(flip mapAttrsToList cfg.users
|
||||||
(user: options: "${user}:${options.password}:::::")));
|
(user: options: "${user}:${options.password}:::::")));
|
||||||
|
|
||||||
recipients = pkgs.writeText "recipients"
|
|
||||||
(concatStringsSep "\n"
|
|
||||||
(flip concatMap cfg.domains (domain:
|
|
||||||
(map (user: "${user}@${domain}")
|
|
||||||
(attrNames cfg.users ++ flatten ((flip mapAttrsToList) cfg.users
|
|
||||||
(user: options: options.aliases)))))));
|
|
||||||
|
|
||||||
aliases = pkgs.writeText "aliases"
|
|
||||||
(concatStringsSep "\n"
|
|
||||||
(flip mapAttrsToList aliases
|
|
||||||
(alias: users: "${alias} ${concatStringsSep "," users}")));
|
|
||||||
|
|
||||||
domains = pkgs.writeText "domains"
|
domains = pkgs.writeText "domains"
|
||||||
(concatStringsSep "\n" cfg.domains);
|
(concatStringsSep "\n" alldomains);
|
||||||
|
|
||||||
spamassassinSieve = pkgs.writeText "spamassassin.sieve" ''
|
spamassassinSieve = pkgs.writeText "spamassassin.sieve" ''
|
||||||
require "fileinto";
|
require "fileinto";
|
||||||
|
@ -67,15 +39,9 @@ let
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# From <https://github.com/OpenSMTPD/OpenSMTPD-extras/blob/master/extras/wip/filters/filter-regex/filter-regex.conf>
|
|
||||||
regex = pkgs.writeText "filter-regex.conf" ''
|
|
||||||
helo ! ^\[
|
|
||||||
helo ^\.
|
|
||||||
helo \.$
|
|
||||||
helo ^[^\.]*$
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -119,17 +85,15 @@ in
|
||||||
description = "Size of the generated DKIM key.";
|
description = "Size of the generated DKIM key.";
|
||||||
};
|
};
|
||||||
|
|
||||||
domains = mkOption {
|
mainUser = mkOption {
|
||||||
type = types.listOf types.str;
|
example = "root";
|
||||||
description = "The domains to look for";
|
type = types.str;
|
||||||
example = ["example.com"];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
keydir = mkOption {
|
keydir = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "The place to look for the ssl key";
|
description = "The place to look for the ssl key";
|
||||||
default = "${config.security.acme.directory}/${cfg.domain}";
|
default = "${config.security.acme.directory}/${cfg.domain}";
|
||||||
example = ["example.com"];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
users = mkOption {
|
users = mkOption {
|
||||||
|
@ -147,19 +111,16 @@ in
|
||||||
<literal>smtpctl encrypt</literal>.
|
<literal>smtpctl encrypt</literal>.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
domains = mkOption {
|
||||||
aliases = mkOption {
|
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [ ];
|
example = ["example.com"];
|
||||||
example = [ "postmaster" ];
|
|
||||||
description = "A list of aliases for this user.";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
example = {
|
example = {
|
||||||
"foo" = {
|
"foo" = {
|
||||||
password = "encrypted";
|
password = "encrypted";
|
||||||
aliases = [ "postmaster" ];
|
|
||||||
};
|
};
|
||||||
"bar" = {
|
"bar" = {
|
||||||
password = "encrypted";
|
password = "encrypted";
|
||||||
|
@ -170,62 +131,69 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf (cfg.users != { }) {
|
config = mkIf (cfg.users != { }) {
|
||||||
nixpkgs.config.packageOverrides = pkgs: {
|
|
||||||
# opensmtpd = overrideDerivation pkgs.opensmtpd (oldAttrs: {
|
|
||||||
# # Needed to listen on both IPv4 and IPv6
|
|
||||||
# patches = oldAttrs.patches ++ [ ./opensmtpd.diff ];
|
|
||||||
# });
|
|
||||||
opensmtpd-extras = pkgs.opensmtpd-extras.override {
|
|
||||||
# Needed to have PRNG working in chroot (for dkim-signer)
|
|
||||||
openssl = pkgs.libressl;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
system.activationScripts.mailz = ''
|
system.activationScripts.mailz = ''
|
||||||
# Make sure SpamAssassin database is present
|
# Make sure SpamAssassin database is present
|
||||||
if ! [ -d /etc/spamassassin ]; then
|
#if ! [ -d /etc/spamassassin ]; then
|
||||||
cp -r ${pkgs.spamassassin}/share/spamassassin /etc
|
# cp -r ${pkgs.spamassassin}/share/spamassassin /etc
|
||||||
fi
|
#fi
|
||||||
|
|
||||||
# Make sure a DKIM private key exist
|
# Make sure a DKIM private key exist
|
||||||
if ! [ -d ${cfg.dkimDirectory}/${head cfg.domains} ]; then
|
if ! [ -d ${cfg.dkimDirectory} ]; then
|
||||||
mkdir -p ${cfg.dkimDirectory}/${head cfg.domains}
|
mkdir -p ${cfg.dkimDirectory}
|
||||||
chmod 700 ${cfg.dkimDirectory}/${head cfg.domains}
|
chmod 700 ${cfg.dkimDirectory}
|
||||||
${pkgs.opendkim}/bin/opendkim-genkey --bits ${toString cfg.dkimBits} --domain ${head cfg.domains} --directory ${cfg.dkimDirectory}/${head cfg.domains}
|
chown ${config.services.rmilter.user} ${cfg.dkimDirectory}
|
||||||
fi
|
fi
|
||||||
'';
|
# Generate missing keys
|
||||||
|
'' +
|
||||||
services.spamassassin.enable = true;
|
(lib.concatMapStringsSep "\n" (domain: ''
|
||||||
# it turns out that the dkim header domain does not have to match the from address
|
if ! [ -e ${cfg.dkimDirectory}/${domain}.default.key ]; then
|
||||||
# but it would be a nice-to-have
|
${pkgs.opendkim}/bin/opendkim-genkey --bits ${toString cfg.dkimBits} --domain ${domain} --directory ${cfg.dkimDirectory} --selector default
|
||||||
services.opensmtpd = {
|
mv ${cfg.dkimDirectory}/default.private ${cfg.dkimDirectory}/${domain}.default.key
|
||||||
|
mv ${cfg.dkimDirectory}/default.txt ${cfg.dkimDirectory}/${domain}.default.txt
|
||||||
|
chown ${config.services.rmilter.user} ${cfg.dkimDirectory}/${domain}.default.*
|
||||||
|
fi
|
||||||
|
'') alldomains);
|
||||||
|
services.rspamd.enable = true;
|
||||||
|
services.rmilter = {
|
||||||
enable = true;
|
enable = true;
|
||||||
serverConfiguration = ''
|
socketActivation = false;
|
||||||
filter filter-pause pause
|
#debug = true;
|
||||||
filter filter-regex regex "${files.regex}"
|
rspamd.enable = true;
|
||||||
filter filter-spamassassin spamassassin "-saccept"
|
postfix.enable = true;
|
||||||
filter filter-dkim-signer dkim-signer "-d${head cfg.domains}" "-p${cfg.dkimDirectory}/${head cfg.domains}/default.private"
|
extraConfig = ''
|
||||||
filter in chain filter-pause filter-regex filter-spamassassin
|
dkim {
|
||||||
filter out chain filter-dkim-signer
|
domain {
|
||||||
|
key = ${cfg.dkimDirectory};
|
||||||
pki ${cfg.domain} certificate "${cfg.keydir}/fullchain.pem"
|
domain = "*";
|
||||||
pki ${cfg.domain} key "${cfg.keydir}/key.pem"
|
selector = "default";
|
||||||
|
};
|
||||||
table credentials file:${files.credentials}
|
header_canon = relaxed;
|
||||||
table recipients file:${files.recipients}
|
body_canon = relaxed;
|
||||||
table aliases file:${files.aliases}
|
sign_alg = sha256;
|
||||||
table domains file:${files.domains}
|
};
|
||||||
|
|
||||||
listen on 0.0.0.0 port 25 hostname ${cfg.domain} filter in tls pki ${cfg.domain}
|
|
||||||
#listen on :: port 25 hostname ${cfg.domain} filter in tls pki ${cfg.domain}
|
|
||||||
listen on 0.0.0.0 port 587 hostname ${cfg.domain} filter out tls-require pki ${cfg.domain} auth <credentials>
|
|
||||||
#listen on :: port 587 hostname ${cfg.domain} filter out tls-require pki ${cfg.domain} auth <credentials>
|
|
||||||
enqueuer filter out
|
|
||||||
|
|
||||||
accept from any for domain <domains> recipient <recipients> alias <aliases> deliver to lmtp localhost:24
|
|
||||||
accept from local for any relay
|
|
||||||
'';
|
'';
|
||||||
procPackages = [ pkgs.opensmtpd-extras ];
|
};
|
||||||
|
|
||||||
|
services.postfix = {
|
||||||
|
enable = true;
|
||||||
|
destination = alldomains ++ ["$myhostname" "localhost.$mydomain" "$mydomain" "localhost"];
|
||||||
|
sslCert = "${cfg.keydir}/fullchain.pem";
|
||||||
|
sslKey = "${cfg.keydir}/key.pem";
|
||||||
|
postmasterAlias = cfg.mainUser;
|
||||||
|
enableSubmission = true;
|
||||||
|
virtual = lib.concatStringsSep "\n" (lib.mapAttrsToList (name: usr:
|
||||||
|
lib.concatMapStringsSep "\n" (dom: "@${dom} ${name}") usr.domains) cfg.users);
|
||||||
|
extraConfig = ''
|
||||||
|
mailbox_transport = lmtp:unix:dovecot-lmtp
|
||||||
|
'';
|
||||||
|
submissionOptions = {
|
||||||
|
"smtpd_tls_security_level" = "encrypt";
|
||||||
|
"smtpd_sasl_auth_enable" = "yes";
|
||||||
|
"smtpd_sasl_type" = "dovecot";
|
||||||
|
"smtpd_sasl_path" = "/var/lib/postfix/auth";
|
||||||
|
"smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
|
||||||
|
#"milter_macro_daemon_name" = "ORIGINATING";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.dovecot2 = {
|
services.dovecot2 = {
|
||||||
|
@ -241,12 +209,21 @@ in
|
||||||
enablePAM = false;
|
enablePAM = false;
|
||||||
sieveScripts = { before = files.spamassassinSieve; };
|
sieveScripts = { before = files.spamassassinSieve; };
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
postmaster_address = postmaster@${head cfg.domains}
|
postmaster_address = postmaster@${head alldomains}
|
||||||
|
|
||||||
service lmtp {
|
service lmtp {
|
||||||
inet_listener lmtp {
|
unix_listener /var/lib/postfix/queue/dovecot-lmtp {
|
||||||
address = 127.0.0.1 ::1
|
mode = 0660
|
||||||
port = 24
|
user = postfix
|
||||||
|
group = postfix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service auth {
|
||||||
|
unix_listener /var/lib/postfix/auth {
|
||||||
|
mode = 0660
|
||||||
|
# Assuming the default Postfix user and group
|
||||||
|
user = postfix
|
||||||
|
group = postfix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,7 @@ in
|
||||||
mkdir -p /etc/nginx/
|
mkdir -p /etc/nginx/
|
||||||
${pkgs.openssl}/bin/openssl dhparam -out /etc/nginx/dhparam.pem 2048
|
${pkgs.openssl}/bin/openssl dhparam -out /etc/nginx/dhparam.pem 2048
|
||||||
fi
|
fi
|
||||||
|
# self-sign certs in case an invalid vhost is looked up
|
||||||
dir=${cfg.no_vhost_keydir}
|
dir=${cfg.no_vhost_keydir}
|
||||||
mkdir -m 0700 -p $dir
|
mkdir -m 0700 -p $dir
|
||||||
if ! [[ -e $dir/key.pem ]]; then
|
if ! [[ -e $dir/key.pem ]]; then
|
||||||
|
|
|
@ -23,6 +23,8 @@ in
|
||||||
|
|
||||||
networking.hostName = secrets.hostnames.pennyworth;
|
networking.hostName = secrets.hostnames.pennyworth;
|
||||||
|
|
||||||
|
services.nixosManual.enable = false;
|
||||||
|
|
||||||
environment.noXlibs = true;
|
environment.noXlibs = true;
|
||||||
|
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
|
@ -37,15 +39,14 @@ in
|
||||||
services.mailz = {
|
services.mailz = {
|
||||||
domain = config.networking.hostName;
|
domain = config.networking.hostName;
|
||||||
keydir = acmeKeyDir;
|
keydir = acmeKeyDir;
|
||||||
domains = secrets.email_domains;
|
mainUser = "yorick";
|
||||||
users = {
|
users = {
|
||||||
yorick = {
|
yorick = with secrets; {
|
||||||
password = secrets.yorick_mailPassword;
|
password = yorick_mailPassword;
|
||||||
aliases = ["postmaster" "me" "ik" "info" "~"];
|
domains = email_domains;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# website + lets encrypt challenge hosting
|
# website + lets encrypt challenge hosting
|
||||||
nginxssl = {
|
nginxssl = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -63,6 +64,7 @@ in
|
||||||
|
|
||||||
|
|
||||||
# Let's Encrypt configuration.
|
# Let's Encrypt configuration.
|
||||||
|
security.acme.preliminarySelfsigned = true;
|
||||||
security.acme.certs."yori.cc" =
|
security.acme.certs."yori.cc" =
|
||||||
{ email = secrets.email;
|
{ email = secrets.email;
|
||||||
extraDomains = {
|
extraDomains = {
|
||||||
|
@ -73,19 +75,6 @@ in
|
||||||
systemctl restart prosody.service
|
systemctl restart prosody.service
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
# Generate a dummy self-signed certificate until we get one from
|
|
||||||
# Let's Encrypt.
|
|
||||||
system.activationScripts.letsEncryptKeys =
|
|
||||||
''
|
|
||||||
dir=${acmeKeyDir}
|
|
||||||
mkdir -m 0700 -p $dir
|
|
||||||
if ! [[ -e $dir/key.pem ]]; then
|
|
||||||
${pkgs.openssl}/bin/openssl genrsa -passout pass:foo -des3 -out $dir/key-in.pem 1024
|
|
||||||
${pkgs.openssl}/bin/openssl req -passin pass:foo -new -key $dir/key-in.pem -out $dir/key.csr \
|
|
||||||
-subj "/C=NL/CN=www.example.com"
|
|
||||||
${pkgs.openssl}/bin/openssl rsa -passin pass:foo -in $dir/key-in.pem -out $dir/key.pem
|
|
||||||
${pkgs.openssl}/bin/openssl x509 -req -days 365 -in $dir/key.csr -signkey $dir/key.pem -out $dir/fullchain.pem
|
|
||||||
fi
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# hidden SSH service
|
# hidden SSH service
|
||||||
|
|
|
@ -21,9 +21,11 @@ in
|
||||||
path = "/old-root/boot";
|
path = "/old-root/boot";
|
||||||
devices = ["nodev"];
|
devices = ["nodev"];
|
||||||
}];
|
}];
|
||||||
|
splashImage = null;
|
||||||
};
|
};
|
||||||
initrd.availableKernelModules = [ "xen_blkfront" ];
|
initrd.availableKernelModules = [ "xen_blkfront" ];
|
||||||
};
|
};
|
||||||
|
sound.enable = false;
|
||||||
networking = {
|
networking = {
|
||||||
usePredictableInterfaceNames = false; # only eth0
|
usePredictableInterfaceNames = false; # only eth0
|
||||||
interfaces.eth0 = {
|
interfaces.eth0 = {
|
||||||
|
|
Loading…
Reference in New Issue