webmail dkim shenanigan backup
This commit is contained in:
parent
69df8d1a43
commit
56a3226347
|
@ -1,9 +1,11 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./mailserver/postfix.nix
|
||||
./mailserver/vmail.nix
|
||||
./mailserver/ssl.nix
|
||||
./mailserver/dovecot.nix
|
||||
./mailserver/firewall.nix
|
||||
];
|
||||
}
|
||||
{ ... }: {
|
||||
imports = [
|
||||
./mailserver/postfix.nix
|
||||
./mailserver/vmail.nix
|
||||
./mailserver/ssl.nix
|
||||
./mailserver/dovecot.nix
|
||||
./mailserver/firewall.nix
|
||||
./mailserver/webmail.nix
|
||||
./mailserver/opendkim.nix
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
fqdn = "mail.owo.monster";
|
||||
domains = [
|
||||
"owo.monster"
|
||||
#"kitteh.pw"
|
||||
"kitteh.pw"
|
||||
];
|
||||
|
||||
debug_mode = true;
|
||||
|
@ -22,11 +22,17 @@
|
|||
passwordFile = "/secrets/chaos-mail-password";
|
||||
aliases = [
|
||||
"all@owo.monster"
|
||||
"chaos@owo.monster"
|
||||
"kitteh@owo.monster"
|
||||
"kitteh@kitteh.pw"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
sieve_directory = "/var/sieve";
|
||||
dkim_directory = "/var/dkim";
|
||||
|
||||
policyd_config = "";
|
||||
|
||||
vmail_config = {
|
||||
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}]";
|
||||
unixSocket = sock: "unix:${sock}";
|
||||
|
||||
|
||||
# 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 value is a list of addresses
|
||||
|
@ -25,37 +24,39 @@ let
|
|||
valiases_postfix = mergeLookupTables (lib.flatten (lib.mapAttrsToList
|
||||
(name: value:
|
||||
let to = name;
|
||||
in map (from: {"${from}" = to;}) (value.aliases ++ lib.singleton name))
|
||||
in map (from: { "${from}" = to; }) (value.aliases ++ lib.singleton name))
|
||||
mail_config.accounts));
|
||||
|
||||
# all_valiases_postfix :: Map String [String]
|
||||
all_valiases_postfix = mergeLookupTables [valiases_postfix];
|
||||
all_valiases_postfix = mergeLookupTables [ valiases_postfix ];
|
||||
|
||||
# attrsToLookupTable :: Map String (Either String [ String ]) -> Map String [String]
|
||||
attrsToLookupTable = aliases: let
|
||||
lookupTables = lib.mapAttrsToList (from: to: {"${from}" = to;}) aliases;
|
||||
in mergeLookupTables lookupTables;
|
||||
attrsToLookupTable = aliases:
|
||||
let
|
||||
lookupTables = lib.mapAttrsToList (from: to: { "${from}" = to; }) aliases;
|
||||
in mergeLookupTables lookupTables;
|
||||
|
||||
# lookupTableToString :: Map String [String] -> String
|
||||
lookupTableToString = attrs: let
|
||||
valueToString = value: lib.concatStringsSep ", " value;
|
||||
in lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs);
|
||||
lookupTableToString = attrs:
|
||||
let valueToString = value: lib.concatStringsSep ", " value;
|
||||
in lib.concatStringsSep "\n"
|
||||
(lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs);
|
||||
|
||||
# valiases_file :: Path
|
||||
valiases_file = let
|
||||
content = lookupTableToString (mergeLookupTables [all_valiases_postfix]);
|
||||
content = lookupTableToString (mergeLookupTables [ all_valiases_postfix ]);
|
||||
in builtins.toFile "valias" content;
|
||||
|
||||
# vhosts_file :: Path
|
||||
vhosts_file = builtins.toFile "vhosts" (lib.concatStringsSep "\n" mail_config.domains);
|
||||
vaccounts_file = builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix);
|
||||
|
||||
smtpdMilters = [
|
||||
# "unix:/run/opendkim/opendkim.sock"
|
||||
];
|
||||
vhosts_file =
|
||||
builtins.toFile "vhosts" (lib.concatStringsSep "\n" mail_config.domains);
|
||||
vaccounts_file =
|
||||
builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix);
|
||||
|
||||
mappedFile = name: "hash:/var/lib/postfix/conf/${name}";
|
||||
|
||||
policyd-spf = pkgs.writeText "policyd-spf.conf" mail_config.policyd_config;
|
||||
|
||||
submissionOptions = {
|
||||
smtpd_tls_security_level = "encrypt";
|
||||
smtpd_sasl_auth_enable = "yes";
|
||||
|
@ -82,8 +83,7 @@ in {
|
|||
sslKey = mail_config.ssl_config.key;
|
||||
enableSubmission = true;
|
||||
enableSubmissions = true;
|
||||
#virtual = lookupTableToString
|
||||
# (mergeLookupTables [ all_valiases_postfix catchAllPostfix forwards ]);
|
||||
virtual = lookupTableToString (mergeLookupTables [ all_valiases_postfix ]);
|
||||
|
||||
config = {
|
||||
# Extra Config
|
||||
|
@ -112,18 +112,18 @@ in {
|
|||
"reject_unauth_destination"
|
||||
];
|
||||
|
||||
#policy-spf_time_limit = "3600s";
|
||||
policy-spf_time_limit = "3600s";
|
||||
|
||||
# reject selected senders
|
||||
#smtpd_sender_restrictions =
|
||||
# [ "check_sender_access ${mappedFile "reject_senders"}" ];
|
||||
|
||||
# quota and spf checking
|
||||
#smtpd_recipient_restrictions = [
|
||||
smtpd_recipient_restrictions = [
|
||||
#"check_recipient_access ${mappedFile "denied_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
|
||||
# Submission by mail clients is handled in submissionOptions
|
||||
|
@ -163,8 +163,9 @@ in {
|
|||
# Configure a non blocking source of randomness
|
||||
tls_random_source = "dev:/dev/urandom";
|
||||
|
||||
smtpd_milters = smtpdMilters;
|
||||
#non_smtpd_milters = [ "unix:/run/opendkim/opendkim.sock" ];
|
||||
smtpd_milters = [ "unix:/run/opendkim/opendkim.sock" ];
|
||||
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}";
|
||||
|
@ -180,6 +181,17 @@ in {
|
|||
# D => Delivered-To, O => X-Original-To, R => Return-Path
|
||||
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" = {
|
||||
type = "unix";
|
||||
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
|
||||
|
||||
mail_config = (import ./mailserver/config.nix { });
|
||||
|
||||
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 = {
|
||||
OnBootSec = "1m";
|
||||
OnCalendar = "daily";
|
||||
|
|
Loading…
Reference in a new issue