summaryrefslogtreecommitdiff
path: root/hosts/surtr
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/surtr')
-rw-r--r--hosts/surtr/default.nix3
-rw-r--r--hosts/surtr/dns/zones/email.nights.soa8
-rw-r--r--hosts/surtr/dns/zones/li.141.soa9
-rw-r--r--hosts/surtr/dns/zones/li.kleen.soa9
-rw-r--r--hosts/surtr/dns/zones/li.synapse.soa2
-rw-r--r--hosts/surtr/dns/zones/li.yggdrasil.soa4
-rw-r--r--hosts/surtr/dns/zones/org.praseodym.soa9
-rw-r--r--hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py17
-rw-r--r--hosts/surtr/email/default.nix145
-rw-r--r--hosts/surtr/http/default.nix2
-rw-r--r--hosts/surtr/kimai.nix2
-rw-r--r--hosts/surtr/postgresql/default.nix41
-rw-r--r--hosts/surtr/tls/default.nix2
13 files changed, 164 insertions, 89 deletions
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix
index 9d3101c0..1c66df2b 100644
--- a/hosts/surtr/default.nix
+++ b/hosts/surtr/default.nix
@@ -22,7 +22,6 @@ with lib;
22 device = "/dev/vda"; 22 device = "/dev/vda";
23 }; 23 };
24 24
25
26 tmp.useTmpfs = true; 25 tmp.useTmpfs = true;
27 26
28 zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id 27 zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id
@@ -31,7 +30,7 @@ with lib;
31 kernelPatches = [ 30 kernelPatches = [
32 { name = "zswap-default"; 31 { name = "zswap-default";
33 patch = null; 32 patch = null;
34 extraStructuredConfig = with lib.kernel; { 33 structuredExtraConfig = with lib.kernel; {
35 ZSWAP_DEFAULT_ON = yes; 34 ZSWAP_DEFAULT_ON = yes;
36 ZSWAP_SHRINKER_DEFAULT_ON = yes; 35 ZSWAP_SHRINKER_DEFAULT_ON = yes;
37 }; 36 };
diff --git a/hosts/surtr/dns/zones/email.nights.soa b/hosts/surtr/dns/zones/email.nights.soa
index 913a88d4..34209a99 100644
--- a/hosts/surtr/dns/zones/email.nights.soa
+++ b/hosts/surtr/dns/zones/email.nights.soa
@@ -1,7 +1,7 @@
1$ORIGIN nights.email. 1$ORIGIN nights.email.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2023013000 ; serial 4 2025060700 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -27,11 +27,7 @@ $TTL 3600
27 27
28_acme-challenge IN NS ns.yggdrasil.li. 28_acme-challenge IN NS ns.yggdrasil.li.
29 29
30ymir._domainkey IN TXT ( 30ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li.
31 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2"
32 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24"
33 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ=="
34)
35 31
36_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. 32_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li.
37_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. 33_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li.
diff --git a/hosts/surtr/dns/zones/li.141.soa b/hosts/surtr/dns/zones/li.141.soa
index ab117f09..78d137bb 100644
--- a/hosts/surtr/dns/zones/li.141.soa
+++ b/hosts/surtr/dns/zones/li.141.soa
@@ -1,7 +1,7 @@
1$ORIGIN 141.li. 1$ORIGIN 141.li.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2025020900 ; serial 4 2025060701 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -45,11 +45,8 @@ ymir IN AAAA 2a03:4000:6:d004::
45ymir IN MX 0 ymir.yggdrasil.li 45ymir IN MX 0 ymir.yggdrasil.li
46ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" 46ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li"
47 47
48ymir._domainkey IN TXT ( 48ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li.
49 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" 49surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li.
50 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24"
51 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ=="
52)
53 50
54_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. 51_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li.
55_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. 52_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li.
diff --git a/hosts/surtr/dns/zones/li.kleen.soa b/hosts/surtr/dns/zones/li.kleen.soa
index a1c7d35a..5dd3e697 100644
--- a/hosts/surtr/dns/zones/li.kleen.soa
+++ b/hosts/surtr/dns/zones/li.kleen.soa
@@ -1,7 +1,7 @@
1$ORIGIN kleen.li. 1$ORIGIN kleen.li.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2023013000 ; serial 4 2025060701 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -27,11 +27,8 @@ $TTL 3600
27 27
28_acme-challenge IN NS ns.yggdrasil.li. 28_acme-challenge IN NS ns.yggdrasil.li.
29 29
30ymir._domainkey IN TXT ( 30ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li.
31 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" 31surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li.
32 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24"
33 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ=="
34)
35 32
36_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. 33_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li.
37_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. 34_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li.
diff --git a/hosts/surtr/dns/zones/li.synapse.soa b/hosts/surtr/dns/zones/li.synapse.soa
index 086d4a85..247cf025 100644
--- a/hosts/surtr/dns/zones/li.synapse.soa
+++ b/hosts/surtr/dns/zones/li.synapse.soa
@@ -1,7 +1,7 @@
1$ORIGIN synapse.li. 1$ORIGIN synapse.li.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2023092100 ; serial 4 2025060701 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa
index ebb298b4..500194ae 100644
--- a/hosts/surtr/dns/zones/li.yggdrasil.soa
+++ b/hosts/surtr/dns/zones/li.yggdrasil.soa
@@ -1,7 +1,7 @@
1$ORIGIN yggdrasil.li. 1$ORIGIN yggdrasil.li.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2025052400 ; serial 4 2025060700 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -115,6 +115,8 @@ vidhar IN TXT "v=spf1 redirect=yggdrasil.li"
115 115
116mailout IN A 188.68.51.254 116mailout IN A 188.68.51.254
117mailout IN AAAA 2a03:4000:6:d004:: 117mailout IN AAAA 2a03:4000:6:d004::
118mailout IN A 202.61.241.61
119mailout IN AAAA 2a03:4000:52:ada::
118mailout IN MX 0 ymir.yggdrasil.li 120mailout IN MX 0 ymir.yggdrasil.li
119mailout IN TXT "v=spf1 redirect=yggdrasil.li" 121mailout IN TXT "v=spf1 redirect=yggdrasil.li"
120 122
diff --git a/hosts/surtr/dns/zones/org.praseodym.soa b/hosts/surtr/dns/zones/org.praseodym.soa
index df505b4c..2b97ca19 100644
--- a/hosts/surtr/dns/zones/org.praseodym.soa
+++ b/hosts/surtr/dns/zones/org.praseodym.soa
@@ -1,7 +1,7 @@
1$ORIGIN praseodym.org. 1$ORIGIN praseodym.org.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2023013000 ; serial 4 2025060701 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -32,11 +32,8 @@ surtr IN AAAA 2a03:4000:52:ada::
32surtr IN MX 0 ymir.yggdrasil.li 32surtr IN MX 0 ymir.yggdrasil.li
33surtr IN TXT "v=spf1 redirect=yggdrasil.li" 33surtr IN TXT "v=spf1 redirect=yggdrasil.li"
34 34
35ymir._domainkey IN TXT ( 35ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li.
36 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" 36surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li.
37 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24"
38 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ=="
39)
40 37
41_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. 38_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li.
42_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. 39_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li.
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 7117eb63..45619fb0 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
@@ -28,12 +28,14 @@ class PolicyHandler(StreamRequestHandler):
28 28
29 allowed = False 29 allowed = False
30 user = None 30 user = None
31 relay_eligible = False
31 if self.args['sasl_username']: 32 if self.args['sasl_username']:
32 user = self.args['sasl_username'] 33 user = self.args['sasl_username']
33 if self.args['ccert_subject']: 34 if self.args['ccert_subject']:
34 user = self.args['ccert_subject'] 35 user = self.args['ccert_subject']
36 relay_eligible = True
35 37
36 if user: 38 if user and '@' in self.args['sender']:
37 with self.server.db_pool.connection() as conn: 39 with self.server.db_pool.connection() as conn:
38 local, domain = self.args['sender'].split(sep='@', maxsplit=1) 40 local, domain = self.args['sender'].split(sep='@', maxsplit=1)
39 extension = None 41 extension = None
@@ -44,9 +46,16 @@ class PolicyHandler(StreamRequestHandler):
44 46
45 with conn.cursor() as cur: 47 with conn.cursor() as cur:
46 cur.row_factory = namedtuple_row 48 cur.row_factory = namedtuple_row
47 cur.execute('SELECT EXISTS(SELECT true 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) as "exists"', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) 49
48 if (row := cur.fetchone()) is not None: 50 if relay_eligible:
49 allowed = row.exists 51 cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "relay_access" ON "mailbox".id = "relay_access"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("domain" = %(domain)s OR %(domain)s ilike CONCAT(\'%%_.\', "domain"))) as "exists"', params = {'user': user, 'domain': domain})
52 if (row := cur.fetchone()) is not None:
53 allowed = row.exists
54
55 if not allowed:
56 cur.execute('SELECT EXISTS(SELECT true 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) as "exists"', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True)
57 if (row := cur.fetchone()) is not None:
58 allowed = row.exists
50 59
51 action = '550 5.7.0 Sender address not authorized for current user' 60 action = '550 5.7.0 Sender address not authorized for current user'
52 if allowed: 61 if allowed:
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix
index c6253e4c..b4b2b5c8 100644
--- a/hosts/surtr/email/default.nix
+++ b/hosts/surtr/email/default.nix
@@ -124,19 +124,20 @@ in {
124 services.postfix = { 124 services.postfix = {
125 enable = true; 125 enable = true;
126 enableSmtp = false; 126 enableSmtp = false;
127 hostname = "surtr.yggdrasil.li";
128 recipientDelimiter = "";
129 setSendmail = true; 127 setSendmail = true;
130 postmasterAlias = ""; rootAlias = ""; extraAliases = ""; 128 postmasterAlias = ""; rootAlias = ""; extraAliases = "";
131 destination = []; 129 settings.main = {
132 sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; 130 recpipient_delimiter = "";
133 sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; 131 mydestination = [];
134 networks = []; 132 mynetworks = [];
135 config = let 133 myhostname = "surtr.yggdrasil.li";
136 relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}"; 134
137 in {
138 smtpd_tls_security_level = "may"; 135 smtpd_tls_security_level = "may";
139 136
137 smtpd_tls_chain_files = [
138 "/run/credentials/postfix.service/surtr.yggdrasil.li.full.pem"
139 ];
140
140 #the dh params 141 #the dh params
141 smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; 142 smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path;
142 smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; 143 smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path;
@@ -171,21 +172,14 @@ in {
171 172
172 smtp_tls_connection_reuse = true; 173 smtp_tls_connection_reuse = true;
173 174
174 tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" ( 175 tls_server_sni_maps = "inline:{${concatMapStringsSep ", " (domain: "{ ${domain} = /run/credentials/postfix.service/${removePrefix "." domain}.full.pem }") (concatMap (domain: [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"]) emailDomains)}}";
175 concatMapStringsSep "\n\n" (domain:
176 concatMapStringsSep "\n" (subdomain: "${subdomain} /run/credentials/postfix.service/${removePrefix "." subdomain}.full.pem")
177 [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"]
178 ) emailDomains
179 )}'';
180 176
181 smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; 177 smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix";
182 178
183 local_recipient_maps = ""; 179 local_recipient_maps = "";
184 180
185 # 10 GiB 181 message_size_limit = 10 * 1024 * 1024 * 1024;
186 message_size_limit = "10737418240"; 182 mailbox_size_limit = 10 * 1024 * 1024 * 1024;
187 # 10 GiB
188 mailbox_size_limit = "10737418240";
189 183
190 smtpd_delay_reject = true; 184 smtpd_delay_reject = true;
191 smtpd_helo_required = true; 185 smtpd_helo_required = true;
@@ -200,7 +194,6 @@ in {
200 dbname = email 194 dbname = email
201 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' 195 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s'
202 ''}" 196 ''}"
203 "check_ccert_access ${relay_ccert}"
204 "reject_non_fqdn_helo_hostname" 197 "reject_non_fqdn_helo_hostname"
205 "reject_invalid_helo_hostname" 198 "reject_invalid_helo_hostname"
206 "reject_unauth_destination" 199 "reject_unauth_destination"
@@ -221,7 +214,6 @@ in {
221 address_verify_sender_ttl = "30045s"; 214 address_verify_sender_ttl = "30045s";
222 215
223 smtpd_relay_restrictions = [ 216 smtpd_relay_restrictions = [
224 "check_ccert_access ${relay_ccert}"
225 "reject_unauth_destination" 217 "reject_unauth_destination"
226 ]; 218 ];
227 219
@@ -244,6 +236,37 @@ in {
244 bounce_queue_lifetime = "20m"; 236 bounce_queue_lifetime = "20m";
245 delay_warning_time = "10m"; 237 delay_warning_time = "10m";
246 238
239 failure_template_file = toString (pkgs.writeText "failure.cf" ''
240 Charset: us-ascii
241 From: Mail Delivery System <MAILER-DAEMON>
242 Subject: Undelivered Mail Returned to Sender
243 Postmaster-Subject: Postmaster Copy: Undelivered Mail
244
245 This is the mail system at host $myhostname.
246
247 I'm sorry to have to inform you that your message could not
248 be delivered to one or more recipients. It's attached below.
249
250 The mail system
251 '');
252 delay_template_file = toString (pkgs.writeText "delay.cf" ''
253 Charset: us-ascii
254 From: Mail Delivery System <MAILER-DAEMON>
255 Subject: Delayed Mail (still being retried)
256 Postmaster-Subject: Postmaster Warning: Delayed Mail
257
258 This is the mail system at host $myhostname.
259
260 ####################################################################
261 # THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #
262 ####################################################################
263
264 Your message could not be delivered for more than $delay_warning_time_minutes minute(s).
265 It will be retried until it is $maximal_queue_lifetime_minutes minute(s) old.
266
267 The mail system
268 '');
269
247 smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" '' 270 smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" ''
248 # Allow DSN requests from local subnet only 271 # Allow DSN requests from local subnet only
249 192.168.0.0/16 silent-discard 272 192.168.0.0/16 silent-discard
@@ -268,13 +291,26 @@ in {
268 virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; 291 virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp";
269 smtputf8_enable = false; 292 smtputf8_enable = false;
270 293
271 authorized_submit_users = "inline:{ root= postfwd= dovecot2= }"; 294 authorized_submit_users = "inline:{ root= postfwd= ${config.services.dovecot2.user}= }";
295 authorized_flush_users = "inline:{ root= }";
296 authorized_mailq_users = "inline:{ root= }";
272 297
273 postscreen_access_list = ""; 298 postscreen_access_list = "";
274 postscreen_denylist_action = "drop"; 299 postscreen_denylist_action = "drop";
275 postscreen_greet_action = "enforce"; 300 postscreen_greet_action = "enforce";
301
302 sender_bcc_maps = ''pgsql:${pkgs.writeText "sender_bcc_maps.cf" ''
303 hosts = postgresql:///email
304 dbname = email
305 query = SELECT value FROM sender_bcc_maps WHERE key = '%s'
306 ''}'';
307 recipient_bcc_maps = ''pgsql:${pkgs.writeText "recipient_bcc_maps.cf" ''
308 hosts = postgresql:///email
309 dbname = email
310 query = SELECT value FROM recipient_bcc_maps WHERE key = '%s'
311 ''}'';
276 }; 312 };
277 masterConfig = { 313 settings.master = {
278 "465" = { 314 "465" = {
279 type = "inet"; 315 type = "inet";
280 private = false; 316 private = false;
@@ -342,7 +378,10 @@ in {
342 maxproc = 0; 378 maxproc = 0;
343 args = [ 379 args = [
344 "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' 380 "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" ''
381 if /^Received: /
382 !/by surtr\.yggdrasil\.li/ STRIP
345 /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 383 /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1
384 endif
346 ''}" 385 ''}"
347 ]; 386 ];
348 }; 387 };
@@ -390,7 +429,7 @@ in {
390 enable = true; 429 enable = true;
391 user = "postfix"; group = "postfix"; 430 user = "postfix"; group = "postfix";
392 socket = "local:/run/opendkim/opendkim.sock"; 431 socket = "local:/run/opendkim/opendkim.sock";
393 domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}''; 432 domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li" "yggdrasil.li" "141.li" "kleen.li" "synapse.li" "praseodym.org"] ++ emailDomains)}'';
394 selector = "surtr"; 433 selector = "surtr";
395 configFile = builtins.toFile "opendkim.conf" '' 434 configFile = builtins.toFile "opendkim.conf" ''
396 Syslog true 435 Syslog true
@@ -494,7 +533,7 @@ in {
494 }; 533 };
495 }; 534 };
496 535
497 users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user "dovecot2" ]; 536 users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user config.services.dovecot2.user ];
498 537
499 services.redis.servers.rspamd.enable = true; 538 services.redis.servers.rspamd.enable = true;
500 539
@@ -504,22 +543,22 @@ in {
504 services.dovecot2 = { 543 services.dovecot2 = {
505 enable = true; 544 enable = true;
506 enablePAM = false; 545 enablePAM = false;
507 sslServerCert = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.pem"; 546 sslServerCert = "/run/credentials/dovecot.service/surtr.yggdrasil.li.pem";
508 sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; 547 sslServerKey = "/run/credentials/dovecot.service/surtr.yggdrasil.li.key.pem";
509 sslCACert = toString ./ca/ca.crt; 548 sslCACert = toString ./ca/ca.crt;
510 mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; 549 mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u";
511 mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; 550 mailPlugins.globally.enable = [ "fts" "fts_xapian" ];
512 protocols = [ "lmtp" "sieve" ]; 551 protocols = [ "lmtp" "sieve" ];
513 sieve = { 552 sieve = {
514 extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; 553 extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"];
515 globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; 554 globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"];
516 }; 555 };
517 extraConfig = let 556 extraConfig = let
518 dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' 557 dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" ''
519 driver = pgsql 558 driver = pgsql
520 connect = dbname=email 559 connect = dbname=email
521 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' 560 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, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM imap_user WHERE "user" = '%n'
522 user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' 561 user_query = SELECT "user", quota_rule, '${config.services.dovecot2.user}' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n'
523 iterate_query = SELECT "user" FROM imap_user 562 iterate_query = SELECT "user" FROM imap_user
524 ''; 563 '';
525 in '' 564 in ''
@@ -527,16 +566,16 @@ in {
527 566
528 mail_plugins = $mail_plugins quota 567 mail_plugins = $mail_plugins quota
529 568
530 first_valid_uid = ${toString config.users.users.dovecot2.uid} 569 first_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid}
531 last_valid_uid = ${toString config.users.users.dovecot2.uid} 570 last_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid}
532 first_valid_gid = ${toString config.users.groups.dovecot2.gid} 571 first_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid}
533 last_valid_gid = ${toString config.users.groups.dovecot2.gid} 572 last_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid}
534 573
535 ${concatMapStringsSep "\n\n" (domain: 574 ${concatMapStringsSep "\n\n" (domain:
536 concatMapStringsSep "\n" (subdomain: '' 575 concatMapStringsSep "\n" (subdomain: ''
537 local_name ${subdomain} { 576 local_name ${subdomain} {
538 ssl_cert = </run/credentials/dovecot2.service/${subdomain}.pem 577 ssl_cert = </run/credentials/dovecot.service/${subdomain}.pem
539 ssl_key = </run/credentials/dovecot2.service/${subdomain}.key.pem 578 ssl_key = </run/credentials/dovecot.service/${subdomain}.key.pem
540 } 579 }
541 '') ["imap.${domain}" domain] 580 '') ["imap.${domain}" domain]
542 ) emailDomains} 581 ) emailDomains}
@@ -557,10 +596,10 @@ in {
557 auth_debug = yes 596 auth_debug = yes
558 597
559 service auth { 598 service auth {
560 user = dovecot2 599 user = ${config.services.dovecot2.user}
561 } 600 }
562 service auth-worker { 601 service auth-worker {
563 user = dovecot2 602 user = ${config.services.dovecot2.user}
564 } 603 }
565 604
566 userdb { 605 userdb {
@@ -581,7 +620,7 @@ in {
581 args = ${pkgs.writeText "dovecot-sql.conf" '' 620 args = ${pkgs.writeText "dovecot-sql.conf" ''
582 driver = pgsql 621 driver = pgsql
583 connect = dbname=email 622 connect = dbname=email
584 user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC 623 user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC
585 ''} 624 ''}
586 625
587 skip = never 626 skip = never
@@ -651,7 +690,7 @@ in {
651 quota_status_success = DUNNO 690 quota_status_success = DUNNO
652 quota_status_nouser = DUNNO 691 quota_status_nouser = DUNNO
653 quota_grace = 10%% 692 quota_grace = 10%%
654 quota_max_mail_size = ${config.services.postfix.config.message_size_limit} 693 quota_max_mail_size = ${toString config.services.postfix.settings.main.message_size_limit}
655 quota_vsizes = yes 694 quota_vsizes = yes
656 } 695 }
657 696
@@ -704,8 +743,8 @@ in {
704 743
705 systemd.services.dovecot-fts-xapian-optimize = { 744 systemd.services.dovecot-fts-xapian-optimize = {
706 description = "Optimize dovecot indices for fts_xapian"; 745 description = "Optimize dovecot indices for fts_xapian";
707 requisite = [ "dovecot2.service" ]; 746 requisite = [ "dovecot.service" ];
708 after = [ "dovecot2.service" ]; 747 after = [ "dovecot.service" ];
709 startAt = "*-*-* 22:00:00 Europe/Berlin"; 748 startAt = "*-*-* 22:00:00 Europe/Berlin";
710 serviceConfig = { 749 serviceConfig = {
711 Type = "oneshot"; 750 Type = "oneshot";
@@ -770,28 +809,26 @@ in {
770 809
771 security.acme.rfc2136Domains = { 810 security.acme.rfc2136Domains = {
772 "surtr.yggdrasil.li" = { 811 "surtr.yggdrasil.li" = {
773 restartUnits = [ "postfix.service" "dovecot2.service" ]; 812 restartUnits = [ "postfix.service" "dovecot.service" ];
774 }; 813 };
775 } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) 814 } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains)
776 // listToAttrs (concatMap (domain: [ 815 // listToAttrs (concatMap (domain: [
777 (nameValuePair domain { restartUnits = ["postfix.service" "dovecot2.service"]; }) 816 (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; })
778 (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) 817 (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; })
779 (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) 818 (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; })
780 (nameValuePair "imap.${domain}" { restartUnits = ["dovecot2.service"]; }) 819 (nameValuePair "imap.${domain}" { restartUnits = ["dovecot.service"]; })
781 (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) 820 (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; })
782 ]) emailDomains); 821 ]) emailDomains);
783 822
784 systemd.services.postfix = { 823 systemd.services.postfix = {
785 serviceConfig.LoadCredential = [ 824 serviceConfig.LoadCredential = let
786 "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" 825 tlsCredential = domain: "${domain}.full.pem:${config.security.acme.certs.${domain}.directory}/full.pem";
787 "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" 826 in [
788 ] ++ concatMap (domain: 827 (tlsCredential "surtr.yggdrasil.li")
789 map (subdomain: "${subdomain}.full.pem:${config.security.acme.certs.${subdomain}.directory}/full.pem") 828 ] ++ concatMap (domain: map tlsCredential [domain "mailin.${domain}" "mailsub.${domain}"]) emailDomains;
790 [domain "mailin.${domain}" "mailsub.${domain}"]
791 ) emailDomains;
792 }; 829 };
793 830
794 systemd.services.dovecot2 = { 831 systemd.services.dovecot = {
795 preStart = '' 832 preStart = ''
796 for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do 833 for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do
797 ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f 834 ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f
diff --git a/hosts/surtr/http/default.nix b/hosts/surtr/http/default.nix
index f3a7154e..ea527cb5 100644
--- a/hosts/surtr/http/default.nix
+++ b/hosts/surtr/http/default.nix
@@ -13,8 +13,6 @@
13 recommendedTlsSettings = true; 13 recommendedTlsSettings = true;
14 sslDhparam = config.security.dhparams.params.nginx.path; 14 sslDhparam = config.security.dhparams.params.nginx.path;
15 commonHttpConfig = '' 15 commonHttpConfig = ''
16 ssl_ecdh_curve X448:X25519:prime256v1:secp521r1:secp384r1;
17
18 log_format main 16 log_format main
19 '$remote_addr "$remote_user" ' 17 '$remote_addr "$remote_user" '
20 '"$host" "$request" $status $bytes_sent ' 18 '"$host" "$request" $status $bytes_sent '
diff --git a/hosts/surtr/kimai.nix b/hosts/surtr/kimai.nix
index a3712bb2..454b3d80 100644
--- a/hosts/surtr/kimai.nix
+++ b/hosts/surtr/kimai.nix
@@ -47,6 +47,8 @@
47 client_max_body_size 0; 47 client_max_body_size 0;
48 proxy_request_buffering off; 48 proxy_request_buffering off;
49 proxy_buffering off; 49 proxy_buffering off;
50
51 proxy_read_timeout 300;
50 ''; 52 '';
51 }; 53 };
52 }; 54 };
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix
index 0ae29058..3786ea7c 100644
--- a/hosts/surtr/postgresql/default.nix
+++ b/hosts/surtr/postgresql/default.nix
@@ -297,6 +297,47 @@ in {
297 297
298 COMMIT; 298 COMMIT;
299 299
300 BEGIN;
301 SELECT _v.register_patch('014-relay', ARRAY['000-base'], null);
302
303 CREATE TABLE relay_access (
304 id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
305 mailbox uuid REFERENCES mailbox(id),
306 domain citext NOT NULL CONSTRAINT domain_non_empty CHECK (domain <> ''')
307 );
308
309 COMMIT;
310
311 BEGIN;
312 SELECT _v.register_patch('015-relay-unique', ARRAY['000-base', '014-relay'], null);
313
314 CREATE UNIQUE INDEX relay_unique ON relay_access (mailbox, domain);
315
316 COMMIT;
317
318 BEGIN;
319 SELECT _v.register_patch('015-sender_bcc', null, null);
320
321 CREATE TABLE sender_bcc_maps (
322 id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
323 key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''),
324 value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''),
325 CONSTRAINT key_unique UNIQUE (key)
326 );
327
328 COMMIT;
329
330 BEGIN;
331 SELECT _v.register_patch('016-recipient_bcc', null, null);
332
333 CREATE TABLE recipient_bcc_maps (
334 id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
335 key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''),
336 value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''),
337 CONSTRAINT recipient_bcc_maps_key_unique UNIQUE (key)
338 );
339
340 COMMIT;
300 ''} 341 ''}
301 342
302 psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' 343 psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" ''
diff --git a/hosts/surtr/tls/default.nix b/hosts/surtr/tls/default.nix
index b1c05888..b25bd2ea 100644
--- a/hosts/surtr/tls/default.nix
+++ b/hosts/surtr/tls/default.nix
@@ -41,7 +41,7 @@ in {
41 41
42 acceptTerms = true; 42 acceptTerms = true;
43 # DNS challenge is slow 43 # DNS challenge is slow
44 preliminarySelfsigned = true; 44 # preliminarySelfsigned = true;
45 defaults = { 45 defaults = {
46 email = "phikeebaogobaegh@141.li"; 46 email = "phikeebaogobaegh@141.li";
47 # We don't like NIST curves and Let's Encrypt doesn't support 47 # We don't like NIST curves and Let's Encrypt doesn't support