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

275 lines
7.7 KiB
Nix

{ config, pkgs, lib, ... }:
with config.krebs.lib;
let
cfg = config.krebs.exim-smarthost;
out = {
options.krebs.exim-smarthost = api;
config = lib.mkIf cfg.enable imp;
};
api = {
enable = mkEnableOption "krebs.exim-smarthost";
# TODO DKIM for multiple domains
dkim = mkOption {
default = null;
type = types.nullOr (types.submodule ({ config, ... }: {
options = {
canon = mkOption {
type = types.enum ["relaxed"];
default = "relaxed";
};
domain = mkOption {
type = types.str;
};
private_key = mkOption {
type = types.secret-file;
default = {
path = "/run/krebs.secret/${config.domain}.dkim_private_key";
owner.name = "exim";
source-path = toString <secrets> + "/${config.domain}.dkim.priv";
};
};
selector = mkOption {
type = types.str;
default = "default";
};
};
}));
};
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
};
};
}));
};
local_domains = mkOption {
type = with types; listOf hostname;
default = ["localhost"] ++ config.krebs.build.host.nets.retiolum.aliases;
};
relay_from_hosts = mkOption {
type = with types; listOf str;
default = [];
apply = xs: ["127.0.0.1" "::1"] ++ xs;
};
relay_to_domains = mkOption {
# TODO hostname with wildcards
type = with types; listOf str;
default = [
"*.r"
"*.retiolum"
];
};
primary_hostname = mkOption {
type = types.str;
default = let x = "${config.krebs.build.host.name}.r"; in
assert elem x config.krebs.build.host.nets.retiolum.aliases;
x;
};
sender_domains = mkOption {
type = with types; listOf str;
default = [];
};
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.secret.files = mkIf (cfg.dkim != null) {
exim-dkim_private_key = cfg.dkim.private_key;
};
systemd.services = mkIf (cfg.dkim != null) {
exim = {
after = [ "secret.service" ];
requires = [ "secret.service" ];
};
};
services.exim = {
enable = true;
config = ''
keep_environment =
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".
domainlist local_domains = ${concatStringsSep ":" cfg.local_domains}
domainlist relay_to_domains = ${concatStringsSep ":" cfg.relay_to_domains}
hostlist relay_from_hosts = <;${concatStringsSep ";" cfg.relay_from_hosts}
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
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
begin acl
acl_check_rcpt:
accept hosts = :
control = dkim_disable_verify
deny message = Restricted characters in address
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
deny message = Restricted characters in address
domains = !+local_domains
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
accept local_parts = postmaster
domains = +local_domains
accept hosts = +relay_from_hosts
control = submission
control = dkim_disable_verify
accept authenticated = *
control = submission
control = dkim_disable_verify
accept message = relay not permitted 2
recipients = lsearch;${lsearch.internet-aliases}
require message = relay not permitted
domains = +local_domains : +relay_to_domains
require
message = unknown user
verify = recipient/callout
accept
acl_check_data:
warn
sender_domains = ${concatStringsSep ":" cfg.sender_domains}
set acl_m_special_dom = $sender_address_domain
accept
begin routers
# feature RETIOLUM_MAIL
retiolum:
debug_print = "R: retiolum for $local_part@$domain"
driver = manualroute
domains = ! +local_domains : +relay_to_domains
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}}}
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
begin transports
retiolum_smtp:
driver = smtp
retry_include_ip_address = false
remote_smtp:
driver = smtp
${optionalString (cfg.dkim != null) ''
dkim_domain = ${cfg.dkim.domain}
dkim_selector = ${cfg.dkim.selector}
dkim_private_key = ${cfg.dkim.private_key.path}
dkim_canon = ${cfg.dkim.canon}
''}
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
${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
begin rewrite
begin authenticators
'';
};
};
lsearch = mapAttrs (name: set: toFile name (to-lsearch set)) {
inherit (cfg) internet-aliases;
inherit (cfg) system-aliases;
};
to-lsearch = concatMapStringsSep "\n" ({ from, to, ... }: "${from}: ${to}");
in out