webmail dkim shenanigan backup

This commit is contained in:
ChaotiCryptidz 2022-06-22 16:59:41 +01:00
parent 69df8d1a43
commit 56a3226347
No known key found for this signature in database
6 changed files with 155 additions and 35 deletions

View file

@ -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
];
}

View file

@ -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";

View 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} - -" ];
}

View file

@ -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;

View 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";
'';
};
}

View file

@ -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";