{ config, pkgs, lib, ... }: with import ; let cfg = config.krebs.exim-smarthost; out = { options.krebs.exim-smarthost = api; config = lib.mkIf cfg.enable imp; }; api = { enable = mkEnableOption "krebs.exim-smarthost"; 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 { type = types.secret-file; default = { path = "/run/krebs.secret/${config.domain}.dkim_private_key"; owner.name = "exim"; source-path = toString + "/${config.domain}.dkim.priv"; }; }; selector = mkOption { type = types.str; default = "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 = unique (["localhost" cfg.primary_hostname] ++ 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" ]; }; 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 = []; }; ssl_cert = mkOption { type = types.nullOr types.str; default = null; }; ssl_key = mkOption { type = types.nullOr types.str; default = null; }; 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 = listToAttrs (flip map cfg.dkim (dkim: { name = "exim.dkim_private_key/${dkim.domain}"; value = dkim.private_key; })); systemd.services = mkIf (cfg.dkim != []) { exim = { after = [ "secret.service" ]; requires = [ "secret.service" ]; }; }; krebs.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 ${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) " *"} begin acl acl_check_rcpt: deny local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ message = restricted characters in address accept recipients = lsearch*@;${lsearch.internet-aliases} accept authenticated = * control = dkim_disable_verify control = submission accept control = dkim_disable_verify control = submission hosts = +relay_from_hosts accept domains = +local_domains : +relay_to_domains deny message = relay not permitted 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 != []) (indent '' dkim_canon = relaxed dkim_domain = $sender_address_domain dkim_private_key = ''${lookup{$sender_address_domain}lsearch{${lsearch.dkim_private_key}}} dkim_selector = ''${lookup{$sender_address_domain}lsearch{${lsearch.dkim_selector}}} '')} 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 ${concatStringsSep "\n" (mapAttrsToList (name: text: '' ${name}: ${indent text} '') cfg.authenticators)} ''; }; }; lsearch = mapAttrs (name: set: toFile name (to-lsearch set)) ({ inherit (cfg) internet-aliases; inherit (cfg) system-aliases; } // optionalAttrs (cfg.dkim != []) { dkim_private_key = flip map cfg.dkim (dkim: { from = dkim.domain; to = dkim.private_key.path; }); dkim_selector = flip map cfg.dkim (dkim: { from = dkim.domain; to = dkim.selector; }); }); to-lsearch = concatMapStrings ({ from, to, ... }: "${from}: ${to}\n"); in out