diff options
Diffstat (limited to 'hosts/surtr/email')
| -rw-r--r-- | hosts/surtr/email/ca/.gitignore | 3 | ||||
| -rw-r--r-- | hosts/surtr/email/ca/ca.crt | 11 | ||||
| -rw-r--r-- | hosts/surtr/email/default.nix | 230 |
3 files changed, 244 insertions, 0 deletions
diff --git a/hosts/surtr/email/ca/.gitignore b/hosts/surtr/email/ca/.gitignore new file mode 100644 index 00000000..7c894574 --- /dev/null +++ b/hosts/surtr/email/ca/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | ca.key | ||
| 2 | ca.cnf | ||
| 3 | *.old \ No newline at end of file | ||
diff --git a/hosts/surtr/email/ca/ca.crt b/hosts/surtr/email/ca/ca.crt new file mode 100644 index 00000000..a4a46000 --- /dev/null +++ b/hosts/surtr/email/ca/ca.crt | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | -----BEGIN CERTIFICATE----- | ||
| 2 | MIIBmjCCAUygAwIBAgIUb0fWK0YOiuanuqOsKemfDMb+LlUwBQYDK2VwMBcxFTAT | ||
| 3 | BgNVBAMMDHlnZ2RyYXNpbC5saTAgFw0yMjA1MDUxMTMxMzZaGA8yMDkwMDUyMzEx | ||
| 4 | MzEzNlowFzEVMBMGA1UEAwwMeWdnZHJhc2lsLmxpMCowBQYDK2VwAyEAuven1BCF | ||
| 5 | gNJtOa5Uga4opO6CD6anTdLHMYEgax6bFbejgacwgaQwHQYDVR0OBBYEFO+nGZ+J | ||
| 6 | ea3aQyWPNG53isOP91OVMFIGA1UdIwRLMEmAFO+nGZ+Jea3aQyWPNG53isOP91OV | ||
| 7 | oRukGTAXMRUwEwYDVQQDDAx5Z2dkcmFzaWwubGmCFG9H1itGDormp7qjrCnpnwzG | ||
| 8 | /i5VMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMBEGCWCGSAGG+EIBAQQE | ||
| 9 | AwICBDAFBgMrZXADQQD9C+L1EUIARdzeEvzGkBhcgggQQC4DKlLt0mpuUuGLxdfS | ||
| 10 | xwAHTGd6PLER3DMTMob4olsGkl09g6fqj9iJRrkM | ||
| 11 | -----END CERTIFICATE----- | ||
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix new file mode 100644 index 00000000..49f156eb --- /dev/null +++ b/hosts/surtr/email/default.nix | |||
| @@ -0,0 +1,230 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | with lib; | ||
| 4 | |||
| 5 | let | ||
| 6 | postfix_map = tableType: tableName: "${tableType}:/run/postfix/maps/${tableName}"; | ||
| 7 | postfix_hash = postfix_map "hash"; | ||
| 8 | in { | ||
| 9 | options = { | ||
| 10 | services.postfix.mapFilesRun = mkOption { | ||
| 11 | type = types.attrsOf (types.either types.path (types.submodule { | ||
| 12 | options = { | ||
| 13 | type = mkOption { | ||
| 14 | type = types.str; | ||
| 15 | default = "hash"; | ||
| 16 | }; | ||
| 17 | |||
| 18 | path = mkOption { | ||
| 19 | type = types.nullOr types.path; | ||
| 20 | default = null; | ||
| 21 | }; | ||
| 22 | |||
| 23 | text = mkOption { | ||
| 24 | type = types.nullOr types.lines; | ||
| 25 | default = null; | ||
| 26 | }; | ||
| 27 | }; | ||
| 28 | })); | ||
| 29 | default = {}; | ||
| 30 | }; | ||
| 31 | }; | ||
| 32 | |||
| 33 | config = { | ||
| 34 | services.postfix = { | ||
| 35 | enable = true; | ||
| 36 | hostname = "surtr.yggdrasil.li"; | ||
| 37 | recipientDelimiter = "+"; | ||
| 38 | setSendmail = true; | ||
| 39 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; | ||
| 40 | destination = []; | ||
| 41 | sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; | ||
| 42 | sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; | ||
| 43 | networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"]; | ||
| 44 | mapFilesRun = { | ||
| 45 | "relay_ccert" = { text = ""; }; | ||
| 46 | "sni" = { text = '' | ||
| 47 | bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem | ||
| 48 | mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.sni.pem | ||
| 49 | mailsub.bouncy.email /run/credentials/postfix.service/mailsub.bouncy.email.sni.pem | ||
| 50 | .bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem | ||
| 51 | '';}; | ||
| 52 | "esmtp_access" = { type = "cidr"; text = '' | ||
| 53 | # Allow DSN requests from local subnet only | ||
| 54 | 192.168.0.0/16 silent-discard | ||
| 55 | 172.16.0.0/12 silent-discard | ||
| 56 | 10.0.0.0/8 silent-discard | ||
| 57 | 0.0.0.0/0 silent-discard, dsn | ||
| 58 | fd00::/8 silent-discard | ||
| 59 | ::/0 silent-discard, dsn | ||
| 60 | '';}; | ||
| 61 | }; | ||
| 62 | config = { | ||
| 63 | #the dh params | ||
| 64 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; | ||
| 65 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; | ||
| 66 | #enable ECDH | ||
| 67 | smtpd_tls_eecdh_grade = "strong"; | ||
| 68 | #enabled SSL protocols, don't allow SSLv2 and SSLv3 | ||
| 69 | smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; | ||
| 70 | smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; | ||
| 71 | #allowed ciphers for smtpd_tls_security_level=encrypt | ||
| 72 | smtpd_tls_mandatory_ciphers = "high"; | ||
| 73 | #allowed ciphers for smtpd_tls_security_level=may | ||
| 74 | #smtpd_tls_ciphers = high | ||
| 75 | #enforce the server cipher preference | ||
| 76 | tls_preempt_cipherlist = true; | ||
| 77 | #disable following ciphers for smtpd_tls_security_level=encrypt | ||
| 78 | smtpd_tls_mandatory_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; | ||
| 79 | #disable following ciphers for smtpd_tls_security_level=may | ||
| 80 | smtpd_tls_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; | ||
| 81 | #enable TLS logging to see the ciphers for inbound connections | ||
| 82 | smtpd_tls_loglevel = "1"; | ||
| 83 | #enable TLS logging to see the ciphers for outbound connections | ||
| 84 | smtp_tls_loglevel = "1"; | ||
| 85 | |||
| 86 | smtpd_tls_ask_ccert = true; | ||
| 87 | smtpd_tls_CAfile = toString ./ca/ca.crt; | ||
| 88 | |||
| 89 | smtp_tls_security_level = "dane"; | ||
| 90 | smtp_dns_support_level = "dnssec"; | ||
| 91 | |||
| 92 | tls_server_sni_maps = postfix_hash "sni"; | ||
| 93 | |||
| 94 | local_recipient_maps = ""; | ||
| 95 | |||
| 96 | # 10 GiB | ||
| 97 | message_size_limit = "10737418240"; | ||
| 98 | # 10 GiB | ||
| 99 | mailbox_size_limit = "10737418240"; | ||
| 100 | |||
| 101 | smtpd_delay_reject = true; | ||
| 102 | smtpd_helo_required = true; | ||
| 103 | smtpd_helo_restrictions = "permit"; | ||
| 104 | |||
| 105 | smtpd_recipient_restrictions = [ | ||
| 106 | "reject_unauth_pipelining" | ||
| 107 | "reject_non_fqdn_recipient" | ||
| 108 | "reject_unknown_recipient_domain" | ||
| 109 | "permit_mynetworks" | ||
| 110 | "check_ccert_access ${postfix_hash "relay_ccert"}" | ||
| 111 | "reject_non_fqdn_helo_hostname" | ||
| 112 | "reject_invalid_helo_hostname" | ||
| 113 | "reject_unauth_destination" | ||
| 114 | "reject_unknown_recipient_domain" | ||
| 115 | "reject_unverified_recipient" | ||
| 116 | ]; | ||
| 117 | |||
| 118 | smtpd_relay_restrictions = [ | ||
| 119 | "permit_mynetworks" | ||
| 120 | "check_ccert_access ${postfix_hash "relay_ccert"}" | ||
| 121 | "reject_unauth_destination" | ||
| 122 | ]; | ||
| 123 | |||
| 124 | propagate_unmatched_extensions = ["canonical" "virtual" "alias"]; | ||
| 125 | smtpd_authorized_verp_clients = "$authorized_verp_clients"; | ||
| 126 | authorized_verp_clients = "$mynetworks"; | ||
| 127 | |||
| 128 | milter_default_action = "accept"; | ||
| 129 | smtpd_milters = [config.services.opendkim.socket]; | ||
| 130 | non_smtpd_milters = [config.services.opendkim.socket]; | ||
| 131 | |||
| 132 | alias_maps = ""; | ||
| 133 | |||
| 134 | queue_run_delay = "10s"; | ||
| 135 | minimal_backoff_time = "1m"; | ||
| 136 | maximal_backoff_time = "10m"; | ||
| 137 | maximal_queue_lifetime = "100m"; | ||
| 138 | bounce_queue_lifetime = "20m"; | ||
| 139 | |||
| 140 | smtpd_discard_ehlo_keyword_address_maps = postfix_map "cidr" "esmtp_access"; | ||
| 141 | |||
| 142 | sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; | ||
| 143 | sender_canonical_classes = "envelope_sender"; | ||
| 144 | recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; | ||
| 145 | recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; | ||
| 146 | }; | ||
| 147 | masterConfig = { | ||
| 148 | smtps = { | ||
| 149 | type = "inet"; | ||
| 150 | command = "smtpd"; | ||
| 151 | args = [ | ||
| 152 | "-o" "smtpd_tls_wrappermode=yes" | ||
| 153 | "-o" "smtpd_tls_req_ccert=yes" | ||
| 154 | "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject" | ||
| 155 | "-o" "smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_tls_all_clientcerts,reject" | ||
| 156 | ]; | ||
| 157 | }; | ||
| 158 | }; | ||
| 159 | }; | ||
| 160 | |||
| 161 | services.postsrsd = { | ||
| 162 | enable = true; | ||
| 163 | domain = "srs.surtr.yggdrasil.li"; | ||
| 164 | separator = "+"; | ||
| 165 | excludeDomains = [ "surtr.yggdrasil.li" | ||
| 166 | ".bouncy.email" "bouncy.email" | ||
| 167 | ]; | ||
| 168 | }; | ||
| 169 | |||
| 170 | services.opendkim = { | ||
| 171 | enable = true; | ||
| 172 | # user = "postfix"; group = "postfix"; | ||
| 173 | # socket = "local:/run/opendkim/opendkim.sock"; | ||
| 174 | domains = ''csl:${concatStringsSep "," ["surtr.yggdrasil.li" "bouncy.email"]}''; | ||
| 175 | selector = "surtr"; | ||
| 176 | configFile = builtins.toFile "opendkim.conf" '' | ||
| 177 | Syslog true | ||
| 178 | MTACommand ${config.security.wrapperDir}/sendmail | ||
| 179 | LogResults true | ||
| 180 | ''; | ||
| 181 | }; | ||
| 182 | |||
| 183 | security.dhparams = { | ||
| 184 | params = { | ||
| 185 | "postfix-512".bits = 512; | ||
| 186 | "postfix-1024".bits = 2048; | ||
| 187 | }; | ||
| 188 | }; | ||
| 189 | |||
| 190 | security.acme.domains = let | ||
| 191 | mkSNI = '' | ||
| 192 | cat key.pem full.pem > sni.pem | ||
| 193 | ''; | ||
| 194 | in { | ||
| 195 | "bouncy.email" = { | ||
| 196 | certCfg.postRun = mkSNI; | ||
| 197 | }; | ||
| 198 | "mailin.bouncy.email" = { | ||
| 199 | certCfg.postRun = mkSNI; | ||
| 200 | }; | ||
| 201 | "mailsub.bouncy.email" = { | ||
| 202 | certCfg.postRun = mkSNI; | ||
| 203 | }; | ||
| 204 | "surtr.yggdrasil.li" = {}; | ||
| 205 | }; | ||
| 206 | |||
| 207 | systemd.services.postfix = { | ||
| 208 | preStart = concatStringsSep "\n" (mapAttrsToList (to: from: let | ||
| 209 | cont = {type, path, text}: assert !(isNull path && isNull text); let | ||
| 210 | path' = if isNull path then pkgs.writeText to text else path; | ||
| 211 | in '' | ||
| 212 | ln -sf ${path'} /run/postfix/maps/${to} | ||
| 213 | postmap ${type}:/run/postfix/maps/${to} | ||
| 214 | ''; | ||
| 215 | in if builtins.isPath from then cont { path = from; } else cont from | ||
| 216 | ) config.services.postfix.mapFilesRun); | ||
| 217 | |||
| 218 | serviceConfig = { | ||
| 219 | RuntimeDirectory = ["postfix/maps"]; | ||
| 220 | LoadCredential = [ | ||
| 221 | "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" | ||
| 222 | "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" | ||
| 223 | "bouncy.email.sni.pem:${config.security.acme.certs."bouncy.email".directory}/sni.pem" | ||
| 224 | "mailin.bouncy.email.sni.pem:${config.security.acme.certs."mailin.bouncy.email".directory}/sni.pem" | ||
| 225 | "mailsub.bouncy.email.sni.pem:${config.security.acme.certs."mailsub.bouncy.email".directory}/sni.pem" | ||
| 226 | ]; | ||
| 227 | }; | ||
| 228 | }; | ||
| 229 | }; | ||
| 230 | } | ||
