stockholm/krebs/3modules/exim-smarthost.nix

324 lines
9.6 KiB
Nix
Raw Normal View History

2015-08-14 13:48:17 +00:00
{ config, pkgs, lib, ... }:
2016-10-20 18:54:38 +00:00
with import <stockholm/lib>;
2015-08-14 13:48:17 +00:00
let
cfg = config.krebs.exim-smarthost;
out = {
options.krebs.exim-smarthost = api;
2016-02-14 15:43:44 +00:00
config = lib.mkIf cfg.enable imp;
2015-08-14 13:48:17 +00:00
};
api = {
enable = mkEnableOption "krebs.exim-smarthost";
enableSPFVerification = mkEnableOption "SPF verification";
authenticators = mkOption {
type = types.attrsOf types.str;
default = {};
};
dkim = mkOption {
type = types.listOf (types.submodule ({ config, ... }: {
options = {
domain = mkOption {
type = types.str;
};
private_key = mkOption {
2021-12-24 08:22:41 +00:00
type = types.absolute-pathname;
default = toString <secrets> + "/${config.domain}.dkim.priv";
2021-11-08 02:53:14 +00:00
defaultText = "secrets/domain.dkim.priv";
};
selector = mkOption {
type = types.str;
default = "default";
};
};
}));
default = [];
};
2023-03-09 08:09:37 +00:00
extraRouters = mkOption {
type = types.nullOr types.str;
default = null;
};
2015-08-14 13:48:17 +00:00
internet-aliases = mkOption {
type = types.listOf (types.submodule ({
options = {
from = mkOption {
type = types.str; # TODO e-mail address
};
to = mkOption {
type = types.str; # TODO e-mail address / TODO listOf
};
};
}));
};
2016-02-21 20:51:11 +00:00
local_domains = mkOption {
type = with types; listOf hostname;
default = unique (["localhost" cfg.primary_hostname] ++ config.krebs.build.host.nets.retiolum.aliases);
2016-02-21 20:51:11 +00:00
};
2015-08-14 13:48:17 +00:00
relay_from_hosts = mkOption {
type = with types; listOf str;
default = [];
2016-02-21 20:51:11 +00:00
apply = xs: ["127.0.0.1" "::1"] ++ xs;
};
relay_to_domains = mkOption {
# TODO hostname with wildcards
type = with types; listOf str;
default = [
"*.r"
];
2015-08-14 13:48:17 +00:00
};
primary_hostname = mkOption {
type = types.str;
2016-02-21 20:51:11 +00:00
default = let x = "${config.krebs.build.host.name}.r"; in
assert elem x config.krebs.build.host.nets.retiolum.aliases;
x;
2015-08-14 13:48:17 +00:00
};
sender_domains = mkOption {
type = with types; listOf str;
default = [];
};
2016-07-23 17:16:41 +00:00
ssl_cert = mkOption {
type = types.nullOr types.str;
default = null;
};
ssl_key = mkOption {
type = types.nullOr types.str;
default = null;
};
2015-08-14 13:48:17 +00:00
system-aliases = mkOption {
type = types.listOf (types.submodule ({
options = {
from = mkOption {
type = types.str; # TODO e-mail address
};
to = mkOption {
type = types.str; # TODO e-mail address / TODO listOf
};
};
}));
};
};
imp = {
krebs.systemd.services.exim.restartIfCredentialsChange = true;
2021-12-24 08:22:41 +00:00
systemd.services.exim.serviceConfig.LoadCredential =
map (dkim: "${dkim.domain}.dkim_private_key:${dkim.private_key}") cfg.dkim;
krebs.exim = {
2015-08-14 13:48:17 +00:00
enable = true;
2019-06-22 10:55:16 +00:00
config = /* exim */ ''
2021-12-24 08:22:41 +00:00
keep_environment = CREDENTIALS_DIRECTORY
2015-08-14 13:48:17 +00:00
primary_hostname = ${cfg.primary_hostname}
# HOST_REDIR contains the real destinations for "local_domains".
#HOST_REDIR = /etc/exim4/host_redirect
# Domains not listed in local_domains need to be deliverable remotely.
# XXX We abuse local_domains to mean "domains, we're the gateway for".
2016-02-21 20:51:11 +00:00
domainlist local_domains = ${concatStringsSep ":" cfg.local_domains}
domainlist relay_to_domains = ${concatStringsSep ":" cfg.relay_to_domains}
domainlist sender_domains = ${concatStringsSep ":" cfg.sender_domains}
2016-02-21 20:51:11 +00:00
hostlist relay_from_hosts = <;${concatStringsSep ";" cfg.relay_from_hosts}
2015-08-14 13:48:17 +00:00
acl_smtp_data = acl_check_data
acl_smtp_mail = acl_check_mail
acl_smtp_rcpt = acl_check_rcpt
2015-08-14 13:48:17 +00:00
never_users = root
host_lookup = *
rfc1413_hosts = *
rfc1413_query_timeout = 5s
log_selector = -queue_run +address_rewrite +all_parents +queue_time
log_file_path = syslog
syslog_timestamp = false
syslog_duplication = false
2016-07-23 17:16:41 +00:00
${optionalString (cfg.ssl_cert != null) "tls_certificate = ${cfg.ssl_cert}"}
${optionalString (cfg.ssl_key != null) "tls_privatekey = ${cfg.ssl_key}"}
tls_advertise_hosts =${optionalString (cfg.ssl_cert != null) " *"}
2015-08-14 13:48:17 +00:00
begin acl
acl_check_rcpt:
deny
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
message = restricted characters in address
2015-08-14 13:48:17 +00:00
accept
recipients = lsearch*@;${lsearch.internet-aliases}
2015-08-14 13:48:17 +00:00
accept
authenticated = *
control = dkim_disable_verify
control = submission
2015-08-14 13:48:17 +00:00
accept
control = dkim_disable_verify
control = submission
hosts = +relay_from_hosts
2015-08-14 13:48:17 +00:00
accept
domains = +local_domains : +relay_to_domains
deny
message = relay not permitted
2015-08-14 13:48:17 +00:00
acl_check_data:
warn
sender_domains = +sender_domains
2015-08-14 13:48:17 +00:00
set acl_m_special_dom = $sender_address_domain
accept
acl_check_mail:
${if cfg.enableSPFVerification then indent /* exim */ ''
accept
authenticated = *
accept
hosts = +relay_from_hosts
deny
spf = fail : softfail
log_message = spf=$spf_result
message = SPF validation failed: \
$sender_host_address is not allowed to send mail from \
''${if def:sender_address_domain\
{$sender_address_domain}\
{$sender_helo_name}}
deny
spf = permerror
log_message = spf=$spf_result
message = SPF validation failed: \
syntax error in SPF record(s) for \
''${if def:sender_address_domain\
{$sender_address_domain}\
{$sender_helo_name}}
defer
spf = temperror
log_message = spf=$spf_result; deferred
message = temporary error during SPF validation; \
please try again later
warn
spf = none : neutral
log_message = spf=$spf_result
accept
add_header = $spf_received
'' else indent /* exim */ ''
accept
''}
2015-08-14 13:48:17 +00:00
begin routers
# feature RETIOLUM_MAIL
retiolum:
debug_print = "R: retiolum for $local_part@$domain"
driver = manualroute
2016-02-21 20:51:11 +00:00
domains = ! +local_domains : +relay_to_domains
2015-08-14 13:48:17 +00:00
transport = retiolum_smtp
route_list = ^.* $0 byname
no_more
internet_aliases:
debug_print = "R: internet_aliases for $local_part@$domain"
driver = redirect
data = ''${lookup{$local_part@$domain}lsearch*@{${lsearch.internet-aliases}}}
2015-08-14 13:48:17 +00:00
dnslookup:
debug_print = "R: dnslookup for $local_part@$domain"
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
no_more
system_aliases:
debug_print = "R: system_aliases for $local_part@$domain"
driver = redirect
data = ''${lookup{$local_part}lsearch{${lsearch.system-aliases}}}
local_user:
debug_print = "R: local_user for $local_part@$domain"
driver = accept
check_local_user
transport = home_maildir
cannot_route_message = Unknown user
2023-03-09 08:09:37 +00:00
${lib.optionalString (cfg.extraRouters != null) cfg.extraRouters}
2015-08-14 13:48:17 +00:00
begin transports
retiolum_smtp:
driver = smtp
retry_include_ip_address = false
remote_smtp:
driver = smtp
2019-06-22 10:55:16 +00:00
${optionalString (cfg.dkim != []) (indent /* exim */ ''
dkim_canon = relaxed
dkim_domain = $sender_address_domain
2021-12-24 08:22:41 +00:00
dkim_private_key = ''${lookup{$sender_address_domain.dkim_private_key}dsearch,ret=full{''${env{CREDENTIALS_DIRECTORY}{$value}fail}}}
dkim_selector = ''${lookup{$sender_address_domain}lsearch{${lsearch.dkim_selector}}}
2021-12-24 09:19:13 +00:00
dkim_strict = true
2016-10-27 11:31:12 +00:00
'')}
2015-08-14 13:48:17 +00:00
helo_data = ''${if eq{$acl_m_special_dom}{} \
{$primary_hostname} \
{$acl_m_special_dom} }
home_maildir:
driver = appendfile
maildir_format
maildir_use_size_file
directory = $home/Mail
directory_mode = 0700
delivery_date_add
envelope_to_add
return_path_add
begin retry
2016-02-21 20:51:11 +00:00
${concatMapStringsSep "\n" (k: "${k} * F,42d,1m") cfg.relay_to_domains}
${concatMapStringsSep "\n" (k: "${k} * F,42d,1m")
# TODO don't include relay_to_domains
(map (getAttr "from") cfg.internet-aliases)}
* * F,2h,15m; G,16h,1h,1.5; F,4d,6h
2015-08-14 13:48:17 +00:00
begin rewrite
begin authenticators
2019-06-22 10:55:16 +00:00
${concatStringsSep "\n" (mapAttrsToList (name: text: /* exim */ ''
${name}:
${indent text}
'') cfg.authenticators)}
2015-08-14 13:48:17 +00:00
'';
};
};
lsearch = mapAttrs (name: set: toFile name (to-lsearch set)) ({
2015-08-14 13:48:17 +00:00
inherit (cfg) internet-aliases;
inherit (cfg) system-aliases;
} // optionalAttrs (cfg.dkim != []) {
dkim_selector = flip map cfg.dkim (dkim: {
from = dkim.domain;
to = dkim.selector;
});
});
2015-08-14 13:48:17 +00:00
to-lsearch = concatMapStrings ({ from, to, ... }: "${from}: ${to}\n");
2015-08-14 13:48:17 +00:00
in out