webmail dkim shenanigan backup
This commit is contained in:
parent
69df8d1a43
commit
56a3226347
|
@ -5,5 +5,7 @@
|
||||||
./mailserver/ssl.nix
|
./mailserver/ssl.nix
|
||||||
./mailserver/dovecot.nix
|
./mailserver/dovecot.nix
|
||||||
./mailserver/firewall.nix
|
./mailserver/firewall.nix
|
||||||
|
./mailserver/webmail.nix
|
||||||
|
./mailserver/opendkim.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
fqdn = "mail.owo.monster";
|
fqdn = "mail.owo.monster";
|
||||||
domains = [
|
domains = [
|
||||||
"owo.monster"
|
"owo.monster"
|
||||||
#"kitteh.pw"
|
"kitteh.pw"
|
||||||
];
|
];
|
||||||
|
|
||||||
debug_mode = true;
|
debug_mode = true;
|
||||||
|
@ -22,11 +22,17 @@
|
||||||
passwordFile = "/secrets/chaos-mail-password";
|
passwordFile = "/secrets/chaos-mail-password";
|
||||||
aliases = [
|
aliases = [
|
||||||
"all@owo.monster"
|
"all@owo.monster"
|
||||||
|
"chaos@owo.monster"
|
||||||
|
"kitteh@owo.monster"
|
||||||
|
"kitteh@kitteh.pw"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
sieve_directory = "/var/sieve";
|
sieve_directory = "/var/sieve";
|
||||||
|
dkim_directory = "/var/dkim";
|
||||||
|
|
||||||
|
policyd_config = "";
|
||||||
|
|
||||||
vmail_config = {
|
vmail_config = {
|
||||||
user_group_name = "vmail";
|
user_group_name = "vmail";
|
||||||
|
|
76
hosts/hetzner-vm/services/mailserver/opendkim.nix
Normal file
76
hosts/hetzner-vm/services/mailserver/opendkim.nix
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
mail_config = (import ./config.nix { });
|
||||||
|
|
||||||
|
dkimUser = config.services.opendkim.user;
|
||||||
|
dkimGroup = config.services.opendkim.group;
|
||||||
|
|
||||||
|
keyDir = mail_config.dkim_directory;
|
||||||
|
selector = "mail";
|
||||||
|
|
||||||
|
domains = mail_config.domains;
|
||||||
|
|
||||||
|
createDomainDkimCert = dom:
|
||||||
|
let
|
||||||
|
dkim_key = "${keyDir}/${dom}.${selector}.key";
|
||||||
|
dkim_txt = "${keyDir}/${dom}.${selector}.txt";
|
||||||
|
in ''
|
||||||
|
if [ ! -f "${dkim_key}" ]
|
||||||
|
then
|
||||||
|
${pkgs.opendkim}/bin/opendkim-genkey -s "${selector}" \
|
||||||
|
-d "${dom}" \
|
||||||
|
--bits="1024" \
|
||||||
|
--directory="${keyDir}"
|
||||||
|
mv "${keyDir}/${selector}.private" "${dkim_key}"
|
||||||
|
mv "${keyDir}/${selector}.txt" "${dkim_txt}"
|
||||||
|
echo "Generated key for domain ${dom} selector ${selector}"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
createAllCerts =
|
||||||
|
lib.concatStringsSep "\n" (map createDomainDkimCert mail_config.domains);
|
||||||
|
|
||||||
|
keyTable = pkgs.writeText "opendkim-KeyTable" (lib.concatStringsSep "\n"
|
||||||
|
(lib.flip map domains
|
||||||
|
(dom: "${dom} ${dom}:${selector}:${keyDir}/${dom}.${selector}.key")));
|
||||||
|
|
||||||
|
signingTable = pkgs.writeText "opendkim-SigningTable"
|
||||||
|
(lib.concatStringsSep "\n" (lib.flip map domains (dom: "${dom} ${dom}")));
|
||||||
|
|
||||||
|
dkim = config.services.opendkim;
|
||||||
|
args = [ "-f" "-l" ]
|
||||||
|
++ lib.optionals (dkim.configFile != null) [ "-x" dkim.configFile ];
|
||||||
|
in {
|
||||||
|
services.opendkim = {
|
||||||
|
enable = true;
|
||||||
|
selector = selector;
|
||||||
|
keyPath = keyDir;
|
||||||
|
domains = "csl:${builtins.concatStringsSep "," domains}";
|
||||||
|
configFile = pkgs.writeText "opendkim.conf" (''
|
||||||
|
Canonicalization relaxed/relaxed
|
||||||
|
UMask 0002
|
||||||
|
Socket ${dkim.socket}
|
||||||
|
KeyTable file:${keyTable}
|
||||||
|
SigningTable file:${signingTable}
|
||||||
|
'' + (lib.optionalString mail_config.debug_mode ''
|
||||||
|
Syslog yes
|
||||||
|
SyslogSuccess yes
|
||||||
|
LogWhy yes
|
||||||
|
''));
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users = lib.optionalAttrs (config.services.postfix.user == "postfix") {
|
||||||
|
postfix.extraGroups = [ "${dkimGroup}" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.opendkim = {
|
||||||
|
preStart = lib.mkForce createAllCerts;
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart =
|
||||||
|
lib.mkForce "${pkgs.opendkim}/bin/opendkim ${lib.escapeShellArgs args}";
|
||||||
|
PermissionsStartOnly = lib.mkForce false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.tmpfiles.rules =
|
||||||
|
[ "d '${keyDir}' - ${dkimUser} ${dkimGroup} - -" ];
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ let
|
||||||
inetSocket = addr: port: "inet:[${toString port}@${addr}]";
|
inetSocket = addr: port: "inet:[${toString port}@${addr}]";
|
||||||
unixSocket = sock: "unix:${sock}";
|
unixSocket = sock: "unix:${sock}";
|
||||||
|
|
||||||
|
|
||||||
# Merge several lookup tables. A lookup table is a attribute set where
|
# Merge several lookup tables. A lookup table is a attribute set where
|
||||||
# - the key is an address (user@example.com) or a domain (@example.com)
|
# - the key is an address (user@example.com) or a domain (@example.com)
|
||||||
# - the value is a list of addresses
|
# - the value is a list of addresses
|
||||||
|
@ -32,14 +31,16 @@ let
|
||||||
all_valiases_postfix = mergeLookupTables [ valiases_postfix ];
|
all_valiases_postfix = mergeLookupTables [ valiases_postfix ];
|
||||||
|
|
||||||
# attrsToLookupTable :: Map String (Either String [ String ]) -> Map String [String]
|
# attrsToLookupTable :: Map String (Either String [ String ]) -> Map String [String]
|
||||||
attrsToLookupTable = aliases: let
|
attrsToLookupTable = aliases:
|
||||||
|
let
|
||||||
lookupTables = lib.mapAttrsToList (from: to: { "${from}" = to; }) aliases;
|
lookupTables = lib.mapAttrsToList (from: to: { "${from}" = to; }) aliases;
|
||||||
in mergeLookupTables lookupTables;
|
in mergeLookupTables lookupTables;
|
||||||
|
|
||||||
# lookupTableToString :: Map String [String] -> String
|
# lookupTableToString :: Map String [String] -> String
|
||||||
lookupTableToString = attrs: let
|
lookupTableToString = attrs:
|
||||||
valueToString = value: lib.concatStringsSep ", " value;
|
let valueToString = value: lib.concatStringsSep ", " value;
|
||||||
in lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs);
|
in lib.concatStringsSep "\n"
|
||||||
|
(lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs);
|
||||||
|
|
||||||
# valiases_file :: Path
|
# valiases_file :: Path
|
||||||
valiases_file = let
|
valiases_file = let
|
||||||
|
@ -47,15 +48,15 @@ let
|
||||||
in builtins.toFile "valias" content;
|
in builtins.toFile "valias" content;
|
||||||
|
|
||||||
# vhosts_file :: Path
|
# vhosts_file :: Path
|
||||||
vhosts_file = builtins.toFile "vhosts" (lib.concatStringsSep "\n" mail_config.domains);
|
vhosts_file =
|
||||||
vaccounts_file = builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix);
|
builtins.toFile "vhosts" (lib.concatStringsSep "\n" mail_config.domains);
|
||||||
|
vaccounts_file =
|
||||||
smtpdMilters = [
|
builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix);
|
||||||
# "unix:/run/opendkim/opendkim.sock"
|
|
||||||
];
|
|
||||||
|
|
||||||
mappedFile = name: "hash:/var/lib/postfix/conf/${name}";
|
mappedFile = name: "hash:/var/lib/postfix/conf/${name}";
|
||||||
|
|
||||||
|
policyd-spf = pkgs.writeText "policyd-spf.conf" mail_config.policyd_config;
|
||||||
|
|
||||||
submissionOptions = {
|
submissionOptions = {
|
||||||
smtpd_tls_security_level = "encrypt";
|
smtpd_tls_security_level = "encrypt";
|
||||||
smtpd_sasl_auth_enable = "yes";
|
smtpd_sasl_auth_enable = "yes";
|
||||||
|
@ -82,8 +83,7 @@ in {
|
||||||
sslKey = mail_config.ssl_config.key;
|
sslKey = mail_config.ssl_config.key;
|
||||||
enableSubmission = true;
|
enableSubmission = true;
|
||||||
enableSubmissions = true;
|
enableSubmissions = true;
|
||||||
#virtual = lookupTableToString
|
virtual = lookupTableToString (mergeLookupTables [ all_valiases_postfix ]);
|
||||||
# (mergeLookupTables [ all_valiases_postfix catchAllPostfix forwards ]);
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
# Extra Config
|
# Extra Config
|
||||||
|
@ -112,18 +112,18 @@ in {
|
||||||
"reject_unauth_destination"
|
"reject_unauth_destination"
|
||||||
];
|
];
|
||||||
|
|
||||||
#policy-spf_time_limit = "3600s";
|
policy-spf_time_limit = "3600s";
|
||||||
|
|
||||||
# reject selected senders
|
# reject selected senders
|
||||||
#smtpd_sender_restrictions =
|
#smtpd_sender_restrictions =
|
||||||
# [ "check_sender_access ${mappedFile "reject_senders"}" ];
|
# [ "check_sender_access ${mappedFile "reject_senders"}" ];
|
||||||
|
|
||||||
# quota and spf checking
|
# quota and spf checking
|
||||||
#smtpd_recipient_restrictions = [
|
smtpd_recipient_restrictions = [
|
||||||
#"check_recipient_access ${mappedFile "denied_recipients"}"
|
#"check_recipient_access ${mappedFile "denied_recipients"}"
|
||||||
#"check_recipient_access ${mappedFile "reject_recipients"}"
|
#"check_recipient_access ${mappedFile "reject_recipients"}"
|
||||||
#"check_policy_service unix:private/policy-spf"
|
"check_policy_service unix:private/policy-spf"
|
||||||
#];
|
];
|
||||||
|
|
||||||
# TLS settings, inspired by https://github.com/jeaye/nix-files
|
# TLS settings, inspired by https://github.com/jeaye/nix-files
|
||||||
# Submission by mail clients is handled in submissionOptions
|
# Submission by mail clients is handled in submissionOptions
|
||||||
|
@ -163,8 +163,9 @@ 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 = smtpdMilters;
|
smtpd_milters = [ "unix:/run/opendkim/opendkim.sock" ];
|
||||||
#non_smtpd_milters = [ "unix:/run/opendkim/opendkim.sock" ];
|
non_smtpd_milters = [ "unix:/run/opendkim/opendkim.sock" ];
|
||||||
|
|
||||||
milter_protocol = "6";
|
milter_protocol = "6";
|
||||||
milter_mail_macros =
|
milter_mail_macros =
|
||||||
"i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}";
|
"i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}";
|
||||||
|
@ -180,6 +181,17 @@ in {
|
||||||
# D => Delivered-To, O => X-Original-To, R => Return-Path
|
# D => Delivered-To, O => X-Original-To, R => Return-Path
|
||||||
args = [ "flags=O" ];
|
args = [ "flags=O" ];
|
||||||
};
|
};
|
||||||
|
"policy-spf" = {
|
||||||
|
type = "unix";
|
||||||
|
privileged = true;
|
||||||
|
chroot = false;
|
||||||
|
command = "spawn";
|
||||||
|
args = [
|
||||||
|
"user=nobody"
|
||||||
|
"argv=${pkgs.pypolicyd-spf}/bin/policyd-spf"
|
||||||
|
"${policyd-spf}"
|
||||||
|
];
|
||||||
|
};
|
||||||
"submission-header-cleanup" = {
|
"submission-header-cleanup" = {
|
||||||
type = "unix";
|
type = "unix";
|
||||||
private = false;
|
private = false;
|
||||||
|
|
13
hosts/hetzner-vm/services/mailserver/webmail.nix
Normal file
13
hosts/hetzner-vm/services/mailserver/webmail.nix
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{ ... }:
|
||||||
|
let mail_config = (import ./config.nix { });
|
||||||
|
in {
|
||||||
|
services.roundcube = {
|
||||||
|
enable = true;
|
||||||
|
hostName = "mail.owo.monster";
|
||||||
|
extraConfig = ''
|
||||||
|
$config['smtp_server'] = "tls://${mail_config.fqdn}";
|
||||||
|
$config['smtp_user'] = "%u";
|
||||||
|
$config['smtp_pass'] = "%p";
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,7 +1,18 @@
|
||||||
_:
|
_:
|
||||||
let
|
let
|
||||||
|
|
||||||
|
mail_config = (import ./mailserver/config.nix { });
|
||||||
|
|
||||||
backupUser = "root";
|
backupUser = "root";
|
||||||
backupPaths = [ "/var/lib/postgresql" "/var/lib/vault" "/var/lib/acme" ];
|
backupPaths = [
|
||||||
|
"/var/lib/postgresql"
|
||||||
|
"/var/lib/vault"
|
||||||
|
"/var/lib/acme"
|
||||||
|
"/secrets"
|
||||||
|
mail_config.vmail_config.directory
|
||||||
|
mail_config.sieve_directory
|
||||||
|
mail_config.dkim_directory
|
||||||
|
];
|
||||||
timerConfig = {
|
timerConfig = {
|
||||||
OnBootSec = "1m";
|
OnBootSec = "1m";
|
||||||
OnCalendar = "daily";
|
OnCalendar = "daily";
|
||||||
|
|
Loading…
Reference in a new issue