summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2024-05-25 20:37:25 +0200
committerGregor Kleen <gkleen@yggdrasil.li>2024-05-25 20:37:25 +0200
commit329de92b6e00f1af9925f56a4fc6da14087802e5 (patch)
treeefc06ae01168bff5db83907c96e51ed54bfdd32b
parent2f8b062363b293a72e4afa0e682f1c4371317515 (diff)
downloadnixos-329de92b6e00f1af9925f56a4fc6da14087802e5.tar
nixos-329de92b6e00f1af9925f56a4fc6da14087802e5.tar.gz
nixos-329de92b6e00f1af9925f56a4fc6da14087802e5.tar.bz2
nixos-329de92b6e00f1af9925f56a4fc6da14087802e5.tar.xz
nixos-329de92b6e00f1af9925f56a4fc6da14087802e5.zip
tkleen
-rw-r--r--hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py35
-rw-r--r--hosts/surtr/email/default.nix57
-rw-r--r--hosts/surtr/postgresql/default.nix14
-rw-r--r--hosts/surtr/ruleset.nft6
4 files changed, 85 insertions, 27 deletions
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):
27 logger.info('Connection parameters: %s', self.args) 27 logger.info('Connection parameters: %s', self.args)
28 28
29 allowed = False 29 allowed = False
30 with self.server.db_pool.connection() as conn: 30 user = None
31 local, domain = self.args['sender'].split(sep='@', maxsplit=1) 31 if self.args['sasl_username']:
32 extension = None 32 user = self.args['sasl_username']
33 if '+' in local: 33 if self.args['ccert_subject']:
34 local, extension = local.split(sep='+', maxsplit=1) 34 user = self.args['ccert_subject']
35 35
36 logger.debug('Parsed address: %s', {'local': local, 'extension': extension, 'domain': domain}) 36 if user:
37 37 with self.server.db_pool.connection() as conn:
38 with conn.cursor() as cur: 38 local, domain = self.args['sender'].split(sep='@', maxsplit=1)
39 cur.row_factory = namedtuple_row 39 extension = None
40 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) 40 if '+' in local:
41 for record in cur: 41 local, extension = local.split(sep='+', maxsplit=1)
42 logger.debug('Received result: %s', record) 42
43 allowed = True 43 logger.debug('Parsed address: %s', {'local': local, 'extension': extension, 'domain': domain})
44
45 with conn.cursor() as cur:
46 cur.row_factory = namedtuple_row
47 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)
48 for record in cur:
49 logger.debug('Received result: %s', record)
50 allowed = True
44 51
45 action = '550 5.7.0 Sender address not authorized for current user' 52 action = '550 5.7.0 Sender address not authorized for current user'
46 if allowed: 53 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 {
204 postscreen_greet_action = "enforce"; 204 postscreen_greet_action = "enforce";
205 }; 205 };
206 masterConfig = { 206 masterConfig = {
207 smtps = { 207 "465" = {
208 type = "inet"; 208 type = "inet";
209 private = false; 209 private = false;
210 command = "smtpd"; 210 command = "smtpd -v";
211 args = [ 211 args = [
212 "-o" "smtpd_tls_security_level=encrypt" 212 "-o" "smtpd_tls_security_level=encrypt"
213 "-o" "{smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}" 213 "-o" "{smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}"
214 "-o" "{smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}" 214 "-o" "{smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}"
215 "-o" "smtpd_tls_mandatory_ciphers=high" 215 "-o" "smtpd_tls_mandatory_ciphers=high"
216 "-o" "smtpd_tls_dh1024_param_file=${toString config.security.dhparams.params."postfix-smtps-1024".path}"
217 "-o" "smtpd_tls_dh512_param_file=${toString config.security.dhparams.params."postfix-smtps-512".path}"
218 "-o" "{tls_eecdh_auto_curves = X25519 X448}" 216 "-o" "{tls_eecdh_auto_curves = X25519 X448}"
219 217
220 "-o" "smtpd_tls_wrappermode=yes" 218 "-o" "smtpd_tls_wrappermode=yes"
@@ -223,16 +221,46 @@ in {
223 "-o" "smtpd_tls_received_header=no" 221 "-o" "smtpd_tls_received_header=no"
224 "-o" "cleanup_service_name=subcleanup" 222 "-o" "cleanup_service_name=subcleanup"
225 "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject" 223 "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject"
226 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}"
227 "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject"
228 "-o" "{smtpd_sender_restrictions = reject_unknown_sender_domain,reject_unverified_sender,check_policy_service unix:/run/postfix-ccert-sender-policy.sock}" 224 "-o" "{smtpd_sender_restrictions = reject_unknown_sender_domain,reject_unverified_sender,check_policy_service unix:/run/postfix-ccert-sender-policy.sock}"
225 "-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" ''
226 hosts = postgresql:///email
227 dbname = email
228 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'))
229 ''},permit_tls_all_clientcerts,reject}''
230 "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject"
231 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}"
229 "-o" "unverified_sender_reject_code=550" 232 "-o" "unverified_sender_reject_code=550"
230 "-o" "unverified_sender_reject_reason={Sender address rejected: undeliverable address}" 233 "-o" "unverified_sender_reject_reason={Sender address rejected: undeliverable address}"
234 "-o" "milter_macro_daemon_name=surtr.yggdrasil.li"
235 "-o" ''smtpd_milters=${config.services.opendkim.socket}''
236 ];
237 };
238 "466" = {
239 type = "inet";
240 private = false;
241 command = "smtpd -v";
242 args = [
243 "-o" "smtpd_tls_security_level=encrypt"
244
245 "-o" "smtpd_tls_wrappermode=yes"
246 "-o" "smtpd_tls_ask_ccert=no"
247 "-o" "smtpd_tls_req_ccert=no"
248 "-o" "smtpd_sasl_type=dovecot"
249 "-o" "smtpd_sasl_path=/run/dovecot-sasl"
250 "-o" "smtpd_sasl_auth_enable=yes"
251 "-o" "smtpd_tls_received_header=no"
252 "-o" "cleanup_service_name=subcleanup"
253 "-o" "smtpd_client_restrictions=permit_sasl_authenticated,reject"
254 "-o" "{smtpd_sender_restrictions = reject_unknown_sender_domain,reject_unverified_sender,check_policy_service unix:/run/postfix-ccert-sender-policy.sock}"
231 "-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" '' 255 "-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" ''
232 hosts = postgresql:///email 256 hosts = postgresql:///email
233 dbname = email 257 dbname = email
234 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')) 258 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'))
235 ''},permit_tls_all_clientcerts,reject}'' 259 ''},permit_sasl_authenticated,reject}''
260 "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject"
261 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}"
262 "-o" "unverified_sender_reject_code=550"
263 "-o" "unverified_sender_reject_reason={Sender address rejected: undeliverable address}"
236 "-o" "milter_macro_daemon_name=surtr.yggdrasil.li" 264 "-o" "milter_macro_daemon_name=surtr.yggdrasil.li"
237 "-o" ''smtpd_milters=${config.services.opendkim.socket}'' 265 "-o" ''smtpd_milters=${config.services.opendkim.socket}''
238 ]; 266 ];
@@ -256,7 +284,7 @@ in {
256 smtp_pass = { 284 smtp_pass = {
257 name = "smtpd"; 285 name = "smtpd";
258 type = "pass"; 286 type = "pass";
259 command = "smtpd"; 287 command = "smtpd -v";
260 }; 288 };
261 postscreen = { 289 postscreen = {
262 name = "smtp"; 290 name = "smtp";
@@ -413,7 +441,7 @@ in {
413 dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' 441 dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" ''
414 driver = pgsql 442 driver = pgsql
415 connect = dbname=email 443 connect = dbname=email
416 password_query = SELECT NULL as password, 'Y' as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' 444 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'
417 user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' 445 user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n'
418 iterate_query = SELECT "user" FROM imap_user 446 iterate_query = SELECT "user" FROM imap_user
419 ''; 447 '';
@@ -445,7 +473,7 @@ in {
445 473
446 auth_ssl_username_from_cert = yes 474 auth_ssl_username_from_cert = yes
447 ssl_cert_username_field = commonName 475 ssl_cert_username_field = commonName
448 auth_mechanisms = external 476 auth_mechanisms = plain login external
449 477
450 auth_verbose = yes 478 auth_verbose = yes
451 verbose_ssl = yes 479 verbose_ssl = yes
@@ -501,6 +529,15 @@ in {
501 group = postfix 529 group = postfix
502 } 530 }
503 } 531 }
532 service auth {
533 vsz_limit = 2G
534
535 unix_listener /run/dovecot-sasl {
536 mode = 0600
537 user = postfix
538 group = postfix
539 }
540 }
504 541
505 namespace inbox { 542 namespace inbox {
506 separator = / 543 separator = /
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix
index f0e42ee8..583e4443 100644
--- a/hosts/surtr/postgresql/default.nix
+++ b/hosts/surtr/postgresql/default.nix
@@ -262,6 +262,20 @@ in {
262 262
263 GRANT DELETE ON "mailbox_mapping" TO "spm"; 263 GRANT DELETE ON "mailbox_mapping" TO "spm";
264 COMMIT; 264 COMMIT;
265
266 BEGIN;
267 SELECT _v.register_patch('011-password', ARRAY['000-base'], null);
268
269 ALTER TABLE mailbox ADD COLUMN password text CONSTRAINT password_non_empty CHECK (password IS DISTINCT FROM ''');
270 COMMIT;
271
272 BEGIN;
273 SELECT _v.register_patch('012-imap-password', ARRAY['000-base', '002-citext'], null);
274
275 DROP VIEW imap_user;
276 CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox;
277
278 COMMIT;
265 ''} 279 ''}
266 280
267 psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' 281 psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" ''
diff --git a/hosts/surtr/ruleset.nft b/hosts/surtr/ruleset.nft
index ee72614f..14fc9b79 100644
--- a/hosts/surtr/ruleset.nft
+++ b/hosts/surtr/ruleset.nft
@@ -178,7 +178,7 @@ table inet filter {
178 udp dport 49000-50000 counter name turn-rx accept 178 udp dport 49000-50000 counter name turn-rx accept
179 179
180 tcp dport 25 counter name smtp-rx accept 180 tcp dport 25 counter name smtp-rx accept
181 tcp dport 465 counter name submissions-rx accept 181 tcp dport {465, 466} counter name submissions-rx accept
182 tcp dport 993 counter name imaps-rx accept 182 tcp dport 993 counter name imaps-rx accept
183 tcp dport 4190 counter name managesieve-rx accept 183 tcp dport 4190 counter name managesieve-rx accept
184 iifname yggdrasil tcp dport 8432 counter name pgbackrest-rx accept 184 iifname yggdrasil tcp dport 8432 counter name pgbackrest-rx accept
@@ -224,7 +224,7 @@ table inet filter {
224 udp sport 49000-50000 counter name turn-tx accept 224 udp sport 49000-50000 counter name turn-tx accept
225 225
226 tcp sport 25 counter name smtp-tx accept 226 tcp sport 25 counter name smtp-tx accept
227 tcp sport 465 counter name submissions-tx accept 227 tcp sport {465, 466} counter name submissions-tx accept
228 tcp sport 993 counter name imaps-tx accept 228 tcp sport 993 counter name imaps-tx accept
229 tcp sport 4190 counter name managesieve-tx accept 229 tcp sport 4190 counter name managesieve-tx accept
230 tcp sport 8432 counter name pgbackrest-tx accept 230 tcp sport 8432 counter name pgbackrest-tx accept
@@ -232,4 +232,4 @@ table inet filter {
232 232
233 counter name tx 233 counter name tx
234 } 234 }
235} \ No newline at end of file 235}