From 84c79ad5a262728f4cbae83f51b7764b5fe850d3 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Thu, 5 May 2022 14:12:31 +0200 Subject: surtr: email --- hosts/surtr/email/ca/.gitignore | 3 + hosts/surtr/email/ca/ca.crt | 11 ++ hosts/surtr/email/default.nix | 230 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 hosts/surtr/email/ca/.gitignore create mode 100644 hosts/surtr/email/ca/ca.crt create mode 100644 hosts/surtr/email/default.nix (limited to 'hosts/surtr/email') 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 @@ +ca.key +ca.cnf +*.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 @@ +-----BEGIN CERTIFICATE----- +MIIBmjCCAUygAwIBAgIUb0fWK0YOiuanuqOsKemfDMb+LlUwBQYDK2VwMBcxFTAT +BgNVBAMMDHlnZ2RyYXNpbC5saTAgFw0yMjA1MDUxMTMxMzZaGA8yMDkwMDUyMzEx +MzEzNlowFzEVMBMGA1UEAwwMeWdnZHJhc2lsLmxpMCowBQYDK2VwAyEAuven1BCF +gNJtOa5Uga4opO6CD6anTdLHMYEgax6bFbejgacwgaQwHQYDVR0OBBYEFO+nGZ+J +ea3aQyWPNG53isOP91OVMFIGA1UdIwRLMEmAFO+nGZ+Jea3aQyWPNG53isOP91OV +oRukGTAXMRUwEwYDVQQDDAx5Z2dkcmFzaWwubGmCFG9H1itGDormp7qjrCnpnwzG +/i5VMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMBEGCWCGSAGG+EIBAQQE +AwICBDAFBgMrZXADQQD9C+L1EUIARdzeEvzGkBhcgggQQC4DKlLt0mpuUuGLxdfS +xwAHTGd6PLER3DMTMob4olsGkl09g6fqj9iJRrkM +-----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 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + postfix_map = tableType: tableName: "${tableType}:/run/postfix/maps/${tableName}"; + postfix_hash = postfix_map "hash"; +in { + options = { + services.postfix.mapFilesRun = mkOption { + type = types.attrsOf (types.either types.path (types.submodule { + options = { + type = mkOption { + type = types.str; + default = "hash"; + }; + + path = mkOption { + type = types.nullOr types.path; + default = null; + }; + + text = mkOption { + type = types.nullOr types.lines; + default = null; + }; + }; + })); + default = {}; + }; + }; + + config = { + services.postfix = { + enable = true; + hostname = "surtr.yggdrasil.li"; + recipientDelimiter = "+"; + setSendmail = true; + postmasterAlias = ""; rootAlias = ""; extraAliases = ""; + destination = []; + sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; + sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; + networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"]; + mapFilesRun = { + "relay_ccert" = { text = ""; }; + "sni" = { text = '' + bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem + mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.sni.pem + mailsub.bouncy.email /run/credentials/postfix.service/mailsub.bouncy.email.sni.pem + .bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem + '';}; + "esmtp_access" = { type = "cidr"; text = '' + # Allow DSN requests from local subnet only + 192.168.0.0/16 silent-discard + 172.16.0.0/12 silent-discard + 10.0.0.0/8 silent-discard + 0.0.0.0/0 silent-discard, dsn + fd00::/8 silent-discard + ::/0 silent-discard, dsn + '';}; + }; + config = { + #the dh params + smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; + smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; + #enable ECDH + smtpd_tls_eecdh_grade = "strong"; + #enabled SSL protocols, don't allow SSLv2 and SSLv3 + smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; + smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; + #allowed ciphers for smtpd_tls_security_level=encrypt + smtpd_tls_mandatory_ciphers = "high"; + #allowed ciphers for smtpd_tls_security_level=may + #smtpd_tls_ciphers = high + #enforce the server cipher preference + tls_preempt_cipherlist = true; + #disable following ciphers for smtpd_tls_security_level=encrypt + smtpd_tls_mandatory_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; + #disable following ciphers for smtpd_tls_security_level=may + smtpd_tls_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; + #enable TLS logging to see the ciphers for inbound connections + smtpd_tls_loglevel = "1"; + #enable TLS logging to see the ciphers for outbound connections + smtp_tls_loglevel = "1"; + + smtpd_tls_ask_ccert = true; + smtpd_tls_CAfile = toString ./ca/ca.crt; + + smtp_tls_security_level = "dane"; + smtp_dns_support_level = "dnssec"; + + tls_server_sni_maps = postfix_hash "sni"; + + local_recipient_maps = ""; + + # 10 GiB + message_size_limit = "10737418240"; + # 10 GiB + mailbox_size_limit = "10737418240"; + + smtpd_delay_reject = true; + smtpd_helo_required = true; + smtpd_helo_restrictions = "permit"; + + smtpd_recipient_restrictions = [ + "reject_unauth_pipelining" + "reject_non_fqdn_recipient" + "reject_unknown_recipient_domain" + "permit_mynetworks" + "check_ccert_access ${postfix_hash "relay_ccert"}" + "reject_non_fqdn_helo_hostname" + "reject_invalid_helo_hostname" + "reject_unauth_destination" + "reject_unknown_recipient_domain" + "reject_unverified_recipient" + ]; + + smtpd_relay_restrictions = [ + "permit_mynetworks" + "check_ccert_access ${postfix_hash "relay_ccert"}" + "reject_unauth_destination" + ]; + + propagate_unmatched_extensions = ["canonical" "virtual" "alias"]; + smtpd_authorized_verp_clients = "$authorized_verp_clients"; + authorized_verp_clients = "$mynetworks"; + + milter_default_action = "accept"; + smtpd_milters = [config.services.opendkim.socket]; + non_smtpd_milters = [config.services.opendkim.socket]; + + alias_maps = ""; + + queue_run_delay = "10s"; + minimal_backoff_time = "1m"; + maximal_backoff_time = "10m"; + maximal_queue_lifetime = "100m"; + bounce_queue_lifetime = "20m"; + + smtpd_discard_ehlo_keyword_address_maps = postfix_map "cidr" "esmtp_access"; + + sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; + sender_canonical_classes = "envelope_sender"; + recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; + recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; + }; + masterConfig = { + smtps = { + type = "inet"; + command = "smtpd"; + args = [ + "-o" "smtpd_tls_wrappermode=yes" + "-o" "smtpd_tls_req_ccert=yes" + "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject" + "-o" "smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_tls_all_clientcerts,reject" + ]; + }; + }; + }; + + services.postsrsd = { + enable = true; + domain = "srs.surtr.yggdrasil.li"; + separator = "+"; + excludeDomains = [ "surtr.yggdrasil.li" + ".bouncy.email" "bouncy.email" + ]; + }; + + services.opendkim = { + enable = true; + # user = "postfix"; group = "postfix"; + # socket = "local:/run/opendkim/opendkim.sock"; + domains = ''csl:${concatStringsSep "," ["surtr.yggdrasil.li" "bouncy.email"]}''; + selector = "surtr"; + configFile = builtins.toFile "opendkim.conf" '' + Syslog true + MTACommand ${config.security.wrapperDir}/sendmail + LogResults true + ''; + }; + + security.dhparams = { + params = { + "postfix-512".bits = 512; + "postfix-1024".bits = 2048; + }; + }; + + security.acme.domains = let + mkSNI = '' + cat key.pem full.pem > sni.pem + ''; + in { + "bouncy.email" = { + certCfg.postRun = mkSNI; + }; + "mailin.bouncy.email" = { + certCfg.postRun = mkSNI; + }; + "mailsub.bouncy.email" = { + certCfg.postRun = mkSNI; + }; + "surtr.yggdrasil.li" = {}; + }; + + systemd.services.postfix = { + preStart = concatStringsSep "\n" (mapAttrsToList (to: from: let + cont = {type, path, text}: assert !(isNull path && isNull text); let + path' = if isNull path then pkgs.writeText to text else path; + in '' + ln -sf ${path'} /run/postfix/maps/${to} + postmap ${type}:/run/postfix/maps/${to} + ''; + in if builtins.isPath from then cont { path = from; } else cont from + ) config.services.postfix.mapFilesRun); + + serviceConfig = { + RuntimeDirectory = ["postfix/maps"]; + LoadCredential = [ + "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" + "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" + "bouncy.email.sni.pem:${config.security.acme.certs."bouncy.email".directory}/sni.pem" + "mailin.bouncy.email.sni.pem:${config.security.acme.certs."mailin.bouncy.email".directory}/sni.pem" + "mailsub.bouncy.email.sni.pem:${config.security.acme.certs."mailsub.bouncy.email".directory}/sni.pem" + ]; + }; + }; + }; +} -- cgit v1.2.3