From 329de92b6e00f1af9925f56a4fc6da14087802e5 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 25 May 2024 20:37:25 +0200 Subject: tkleen --- .../ccert_policy_server/__main__.py | 35 +++++++------ hosts/surtr/email/default.nix | 57 ++++++++++++++++++---- 2 files changed, 68 insertions(+), 24 deletions(-) (limited to 'hosts/surtr/email') diff --git a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py index f481090c..00182523 100644 --- a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py +++ b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py @@ -27,20 +27,27 @@ class PolicyHandler(StreamRequestHandler): logger.info('Connection parameters: %s', self.args) allowed = False - with self.server.db_pool.connection() as conn: - local, domain = self.args['sender'].split(sep='@', maxsplit=1) - extension = None - if '+' in local: - local, extension = local.split(sep='+', maxsplit=1) - - logger.debug('Parsed address: %s', {'local': local, 'extension': extension, 'domain': domain}) - - with conn.cursor() as cur: - cur.row_factory = namedtuple_row - cur.execute('SELECT "mailbox"."mailbox" as "user", "local", "extension", "domain" FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'user': self.args['ccert_subject'], 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) - for record in cur: - logger.debug('Received result: %s', record) - allowed = True + user = None + if self.args['sasl_username']: + user = self.args['sasl_username'] + if self.args['ccert_subject']: + user = self.args['ccert_subject'] + + if user: + with self.server.db_pool.connection() as conn: + local, domain = self.args['sender'].split(sep='@', maxsplit=1) + extension = None + if '+' in local: + local, extension = local.split(sep='+', maxsplit=1) + + logger.debug('Parsed address: %s', {'local': local, 'extension': extension, 'domain': domain}) + + with conn.cursor() as cur: + cur.row_factory = namedtuple_row + cur.execute('SELECT "mailbox"."mailbox" as "user", "local", "extension", "domain" FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) + for record in cur: + logger.debug('Received result: %s', record) + allowed = True action = '550 5.7.0 Sender address not authorized for current user' if allowed: diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index 9c3e8849..66c39e8f 100644 --- a/hosts/surtr/email/default.nix +++ b/hosts/surtr/email/default.nix @@ -204,17 +204,15 @@ in { postscreen_greet_action = "enforce"; }; masterConfig = { - smtps = { + "465" = { type = "inet"; private = false; - command = "smtpd"; + command = "smtpd -v"; args = [ "-o" "smtpd_tls_security_level=encrypt" "-o" "{smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}" "-o" "{smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}" "-o" "smtpd_tls_mandatory_ciphers=high" - "-o" "smtpd_tls_dh1024_param_file=${toString config.security.dhparams.params."postfix-smtps-1024".path}" - "-o" "smtpd_tls_dh512_param_file=${toString config.security.dhparams.params."postfix-smtps-512".path}" "-o" "{tls_eecdh_auto_curves = X25519 X448}" "-o" "smtpd_tls_wrappermode=yes" @@ -223,16 +221,46 @@ in { "-o" "smtpd_tls_received_header=no" "-o" "cleanup_service_name=subcleanup" "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject" - "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" - "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" "-o" "{smtpd_sender_restrictions = reject_unknown_sender_domain,reject_unverified_sender,check_policy_service unix:/run/postfix-ccert-sender-policy.sock}" + "-o" ''{smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,check_recipient_access pgsql:${pkgs.writeText "check_recipient_access.cf" '' + hosts = postgresql:///email + dbname = email + query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) + ''},permit_tls_all_clientcerts,reject}'' + "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" + "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" "-o" "unverified_sender_reject_code=550" "-o" "unverified_sender_reject_reason={Sender address rejected: undeliverable address}" + "-o" "milter_macro_daemon_name=surtr.yggdrasil.li" + "-o" ''smtpd_milters=${config.services.opendkim.socket}'' + ]; + }; + "466" = { + type = "inet"; + private = false; + command = "smtpd -v"; + args = [ + "-o" "smtpd_tls_security_level=encrypt" + + "-o" "smtpd_tls_wrappermode=yes" + "-o" "smtpd_tls_ask_ccert=no" + "-o" "smtpd_tls_req_ccert=no" + "-o" "smtpd_sasl_type=dovecot" + "-o" "smtpd_sasl_path=/run/dovecot-sasl" + "-o" "smtpd_sasl_auth_enable=yes" + "-o" "smtpd_tls_received_header=no" + "-o" "cleanup_service_name=subcleanup" + "-o" "smtpd_client_restrictions=permit_sasl_authenticated,reject" + "-o" "{smtpd_sender_restrictions = reject_unknown_sender_domain,reject_unverified_sender,check_policy_service unix:/run/postfix-ccert-sender-policy.sock}" "-o" ''{smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,check_recipient_access pgsql:${pkgs.writeText "check_recipient_access.cf" '' hosts = postgresql:///email dbname = email query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) - ''},permit_tls_all_clientcerts,reject}'' + ''},permit_sasl_authenticated,reject}'' + "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" + "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" + "-o" "unverified_sender_reject_code=550" + "-o" "unverified_sender_reject_reason={Sender address rejected: undeliverable address}" "-o" "milter_macro_daemon_name=surtr.yggdrasil.li" "-o" ''smtpd_milters=${config.services.opendkim.socket}'' ]; @@ -256,7 +284,7 @@ in { smtp_pass = { name = "smtpd"; type = "pass"; - command = "smtpd"; + command = "smtpd -v"; }; postscreen = { name = "smtp"; @@ -413,7 +441,7 @@ in { dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' driver = pgsql connect = dbname=email - password_query = SELECT NULL as password, 'Y' as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' + password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' iterate_query = SELECT "user" FROM imap_user ''; @@ -445,7 +473,7 @@ in { auth_ssl_username_from_cert = yes ssl_cert_username_field = commonName - auth_mechanisms = external + auth_mechanisms = plain login external auth_verbose = yes verbose_ssl = yes @@ -501,6 +529,15 @@ in { group = postfix } } + service auth { + vsz_limit = 2G + + unix_listener /run/dovecot-sasl { + mode = 0600 + user = postfix + group = postfix + } + } namespace inbox { separator = / -- cgit v1.2.3