diff options
Diffstat (limited to 'hosts/surtr')
| -rw-r--r-- | hosts/surtr/changedetection-io.nix | 66 | ||||
| -rw-r--r-- | hosts/surtr/default.nix | 4 | ||||
| -rw-r--r-- | hosts/surtr/dns/default.nix | 2 | ||||
| -rw-r--r-- | hosts/surtr/dns/keys/changedetection.yggdrasil.li_acme | 18 | ||||
| -rw-r--r-- | hosts/surtr/dns/zones/email.nights.soa | 8 | ||||
| -rw-r--r-- | hosts/surtr/dns/zones/li.141.soa | 9 | ||||
| -rw-r--r-- | hosts/surtr/dns/zones/li.kleen.soa | 9 | ||||
| -rw-r--r-- | hosts/surtr/dns/zones/li.synapse.soa | 2 | ||||
| -rw-r--r-- | hosts/surtr/dns/zones/li.yggdrasil.soa | 12 | ||||
| -rw-r--r-- | hosts/surtr/dns/zones/org.praseodym.soa | 9 | ||||
| -rw-r--r-- | hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py | 17 | ||||
| -rw-r--r-- | hosts/surtr/email/default.nix | 184 | ||||
| -rw-r--r-- | hosts/surtr/http/default.nix | 3 | ||||
| -rw-r--r-- | hosts/surtr/postgresql/default.nix | 41 | ||||
| -rw-r--r-- | hosts/surtr/tls/default.nix | 8 | ||||
| -rw-r--r-- | hosts/surtr/tls/tsig_keys/changedetection.yggdrasil.li | 18 |
16 files changed, 286 insertions, 124 deletions
diff --git a/hosts/surtr/changedetection-io.nix b/hosts/surtr/changedetection-io.nix new file mode 100644 index 00000000..bfdedee1 --- /dev/null +++ b/hosts/surtr/changedetection-io.nix | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "changedetection.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."changedetection-io" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:4:1::]:5001" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "changedetection.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/changedetection.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/changedetection.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/changedetection.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://changedetection-io; | ||
| 35 | |||
| 36 | proxy_http_version 1.1; | ||
| 37 | proxy_set_header Upgrade $http_upgrade; | ||
| 38 | proxy_set_header Connection "upgrade"; | ||
| 39 | |||
| 40 | proxy_redirect off; | ||
| 41 | proxy_set_header Host $host; | ||
| 42 | proxy_set_header X-Real-IP $remote_addr; | ||
| 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| 44 | proxy_set_header X-Forwarded-Host $server_name; | ||
| 45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
| 46 | |||
| 47 | client_max_body_size 0; | ||
| 48 | proxy_request_buffering off; | ||
| 49 | proxy_buffering off; | ||
| 50 | ''; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.services.nginx = { | ||
| 57 | serviceConfig = { | ||
| 58 | LoadCredential = [ | ||
| 59 | "changedetection.yggdrasil.li.key.pem:${config.security.acme.certs."changedetection.yggdrasil.li".directory}/key.pem" | ||
| 60 | "changedetection.yggdrasil.li.pem:${config.security.acme.certs."changedetection.yggdrasil.li".directory}/fullchain.pem" | ||
| 61 | "changedetection.yggdrasil.li.chain.pem:${config.security.acme.certs."changedetection.yggdrasil.li".directory}/chain.pem" | ||
| 62 | ]; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | } | ||
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix index 9d3101c0..4a9bd6fe 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix | |||
| @@ -8,6 +8,7 @@ with lib; | |||
| 8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql | 8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql |
| 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix | 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix |
| 10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix ./kimai.nix | 10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix ./kimai.nix |
| 11 | ./changedetection-io.nix | ||
| 11 | ]; | 12 | ]; |
| 12 | 13 | ||
| 13 | config = { | 14 | config = { |
| @@ -22,7 +23,6 @@ with lib; | |||
| 22 | device = "/dev/vda"; | 23 | device = "/dev/vda"; |
| 23 | }; | 24 | }; |
| 24 | 25 | ||
| 25 | |||
| 26 | tmp.useTmpfs = true; | 26 | tmp.useTmpfs = true; |
| 27 | 27 | ||
| 28 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id | 28 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id |
| @@ -31,7 +31,7 @@ with lib; | |||
| 31 | kernelPatches = [ | 31 | kernelPatches = [ |
| 32 | { name = "zswap-default"; | 32 | { name = "zswap-default"; |
| 33 | patch = null; | 33 | patch = null; |
| 34 | extraStructuredConfig = with lib.kernel; { | 34 | structuredExtraConfig = with lib.kernel; { |
| 35 | ZSWAP_DEFAULT_ON = yes; | 35 | ZSWAP_DEFAULT_ON = yes; |
| 36 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | 36 | ZSWAP_SHRINKER_DEFAULT_ON = yes; |
| 37 | }; | 37 | }; |
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix index 8aca2b97..96599901 100644 --- a/hosts/surtr/dns/default.nix +++ b/hosts/surtr/dns/default.nix | |||
| @@ -157,7 +157,7 @@ in { | |||
| 157 | ${concatMapStringsSep "\n" mkZone [ | 157 | ${concatMapStringsSep "\n" mkZone [ |
| 158 | { domain = "yggdrasil.li"; | 158 | { domain = "yggdrasil.li"; |
| 159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; | 159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; |
| 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li" "kimai.yggdrasil.li"]; | 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li" "kimai.yggdrasil.li" "changedetection.yggdrasil.li"]; |
| 161 | } | 161 | } |
| 162 | { domain = "nights.email"; | 162 | { domain = "nights.email"; |
| 163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; | 163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; |
diff --git a/hosts/surtr/dns/keys/changedetection.yggdrasil.li_acme b/hosts/surtr/dns/keys/changedetection.yggdrasil.li_acme new file mode 100644 index 00000000..dcc7f85b --- /dev/null +++ b/hosts/surtr/dns/keys/changedetection.yggdrasil.li_acme | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:QWZAer8xKZvAGl3HxaIdsOT1n3os4EDyQoZOU3YzwDpPfweVRhhBfyAg7M6rMRj8K8ffkkRWatDmgyHV1R43GfNyb1sLjqdqPysYXxC8KlP22WlT+1xstQ2q1KYmeN6VEKF0q+QOMMPRvwQbSQ0eC4mXcE+WgQSTVywjab9hQuc8vin69RbFxbhepxYLXT1rzQpLlxFmUNZBcLpSqsHkSDa2B0d4j2kIvSl2BuUgb3QJwgyNS5pGbnfyVfmus7p5+/pVFCe5EwTVjwgpn/cpIB0mu1Bbt9r0EvCkYXI6wKcLDVbfdV7KsA==,iv:wjHpcClpybzCIi3JhxgXTd5nW9y223pJn2rBde/2cy8=,tag:etfnmj+HhIKeZMGjxE5jiw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlWkk2Z0FUNlNoaFVaSFEr\nSE54eUpHaTNzTTdjaytJemtQUW1mWjJNUGhzCkU1OGlNb3pidVFKalVCZUhBODNi\nMjcxR0xLUDRwYkZ5V2I1Q3Z6Y2pmRWMKLS0tIGNzaGNiTEMvdEhBMDRZY2pDQzNu\nWkpkcVNYVjJZQS9QRHEyOUx6RVpQVjAKKGjVrfeovCIml2hExydC9Cd7PyungtpJ\nCdXfrvzP/OtoBSiEDQGC2VafwKkZ98dQqVRnfVApDoxdVQ8vIrxmKQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTbUNBV1NYZEtKaDlzUVBF\nU2IrZ3dSWS9keVE1ak9qWkppc1hOY3JpMjEwCmNUbVJJMFNObmptVGQ0K1ovanVm\nc1dzN0VRVThyVWxPWTFLbldPQnE3Z2sKLS0tIEdWRENRRFROSm5SQ04yTG1wZWJ2\nNDMxK1ArYmdiQWJZV0d2TElZcFZLNVEKtmVrSIOcP4Ek1WW85f2/dNVYQMz9XqZ3\n0J04kqvkHZuM8PiBDg2l2rSh0xhHz3xb1iBhAddLXEjeEfy6o9HKyg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-12-08T12:46:12Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:waY6IDfabZ8B8069liXh7RXjgUTpOdr4U9VQK5xYRujAlI//Ea5lM2ODHJ7PrAkZsK0TGB9ezN8SA5QpxYZwOcpxg45jNbTALxZsZMEzrtCy4wSiBdiLvRoTXvwMZsnsaQEGk2ij2rEqNEOYYBFapBoIz2w5kbEZrrhVRHSkNME=,iv:tpU++qliONinepku/gdPJQ/h2NdyNw3GY+RV+6UM07U=,tag:yieMw3BOC134zAIqb1Fvjg==,type:str]", | ||
| 16 | "version": "3.11.0" | ||
| 17 | } | ||
| 18 | } | ||
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 | ||
| 30 | ymir._domainkey IN TXT ( | 30 | ymir._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:: | |||
| 45 | ymir IN MX 0 ymir.yggdrasil.li | 45 | ymir IN MX 0 ymir.yggdrasil.li |
| 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" | 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" |
| 47 | 47 | ||
| 48 | ymir._domainkey IN TXT ( | 48 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 49 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 49 | surtr._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 | ||
| 30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 31 | surtr._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..5234576f 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 | 2025120800 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -109,12 +109,22 @@ _acme-challenge.kimai IN NS ns.yggdrasil.li. | |||
| 109 | 109 | ||
| 110 | kimai IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | 110 | kimai IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" |
| 111 | 111 | ||
| 112 | changedetection IN A 202.61.241.61 | ||
| 113 | changedetection IN AAAA 2a03:4000:52:ada:: | ||
| 114 | changedetection IN MX 0 surtr.yggdrasil.li | ||
| 115 | changedetection IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 116 | _acme-challenge.changedetection IN NS ns.yggdrasil.li. | ||
| 117 | |||
| 118 | changedetection IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 119 | |||
| 112 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: | 120 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: |
| 113 | vidhar IN MX 0 ymir.yggdrasil.li | 121 | vidhar IN MX 0 ymir.yggdrasil.li |
| 114 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" | 122 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" |
| 115 | 123 | ||
| 116 | mailout IN A 188.68.51.254 | 124 | mailout IN A 188.68.51.254 |
| 117 | mailout IN AAAA 2a03:4000:6:d004:: | 125 | mailout IN AAAA 2a03:4000:6:d004:: |
| 126 | mailout IN A 202.61.241.61 | ||
| 127 | mailout IN AAAA 2a03:4000:52:ada:: | ||
| 118 | mailout IN MX 0 ymir.yggdrasil.li | 128 | mailout IN MX 0 ymir.yggdrasil.li |
| 119 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" | 129 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" |
| 120 | 130 | ||
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:: | |||
| 32 | surtr IN MX 0 ymir.yggdrasil.li | 32 | surtr IN MX 0 ymir.yggdrasil.li |
| 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" | 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" |
| 34 | 34 | ||
| 35 | ymir._domainkey IN TXT ( | 35 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 36 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 36 | surtr._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..b0e95a0e 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,49 +533,49 @@ 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 | ||
| 501 | users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; | 540 | users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; |
| 502 | 541 | ||
| 503 | environment.systemPackages = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ]; | 542 | environment.systemPackages = with pkgs; [ dovecot_pigeonhole dovecot-fts-flatcurve ]; |
| 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_flatcurve" ]; |
| 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 '' |
| 526 | mail_home = /var/lib/mail/%u | 565 | mail_home = /var/lib/mail/%u |
| 527 | 566 | ||
| 528 | mail_plugins = $mail_plugins quota | 567 | mail_plugins = $mail_plugins quota fts fts_flatcurve |
| 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 | ||
| @@ -687,13 +726,16 @@ in { | |||
| 687 | } | 726 | } |
| 688 | 727 | ||
| 689 | plugin { | 728 | plugin { |
| 690 | plugin = fts fts_xapian | 729 | fts = flatcurve |
| 691 | fts = xapian | ||
| 692 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 | ||
| 693 | 730 | ||
| 694 | fts_autoindex = yes | 731 | fts_languages = en de |
| 732 | fts_tokenizers = generic email-address | ||
| 695 | 733 | ||
| 696 | fts_enforced = no | 734 | fts_tokenizer_email_address = maxlen=100 |
| 735 | fts_tokenizer_generic = algorithm=simple maxlen=30 | ||
| 736 | |||
| 737 | fts_filters = normalizer-icu snowball stopwords | ||
| 738 | fts_filters_en = lowercase snowball stopwords | ||
| 697 | } | 739 | } |
| 698 | 740 | ||
| 699 | service indexer-worker { | 741 | service indexer-worker { |
| @@ -702,30 +744,6 @@ in { | |||
| 702 | ''; | 744 | ''; |
| 703 | }; | 745 | }; |
| 704 | 746 | ||
| 705 | systemd.services.dovecot-fts-xapian-optimize = { | ||
| 706 | description = "Optimize dovecot indices for fts_xapian"; | ||
| 707 | requisite = [ "dovecot2.service" ]; | ||
| 708 | after = [ "dovecot2.service" ]; | ||
| 709 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | ||
| 710 | serviceConfig = { | ||
| 711 | Type = "oneshot"; | ||
| 712 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; | ||
| 713 | PrivateDevices = true; | ||
| 714 | PrivateNetwork = true; | ||
| 715 | ProtectKernelTunables = true; | ||
| 716 | ProtectKernelModules = true; | ||
| 717 | ProtectControlGroups = true; | ||
| 718 | ProtectHome = true; | ||
| 719 | ProtectSystem = true; | ||
| 720 | PrivateTmp = true; | ||
| 721 | }; | ||
| 722 | }; | ||
| 723 | systemd.timers.dovecot-fts-xapian-optimize = { | ||
| 724 | timerConfig = { | ||
| 725 | RandomizedDelaySec = 4 * 3600; | ||
| 726 | }; | ||
| 727 | }; | ||
| 728 | |||
| 729 | environment.etc = { | 747 | environment.etc = { |
| 730 | "dovecot/sieve_before.d/tag-junk.sieve".text = '' | 748 | "dovecot/sieve_before.d/tag-junk.sieve".text = '' |
| 731 | require ["imap4flags"]; | 749 | require ["imap4flags"]; |
| @@ -770,28 +788,26 @@ in { | |||
| 770 | 788 | ||
| 771 | security.acme.rfc2136Domains = { | 789 | security.acme.rfc2136Domains = { |
| 772 | "surtr.yggdrasil.li" = { | 790 | "surtr.yggdrasil.li" = { |
| 773 | restartUnits = [ "postfix.service" "dovecot2.service" ]; | 791 | restartUnits = [ "postfix.service" "dovecot.service" ]; |
| 774 | }; | 792 | }; |
| 775 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) | 793 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) |
| 776 | // listToAttrs (concatMap (domain: [ | 794 | // listToAttrs (concatMap (domain: [ |
| 777 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot2.service"]; }) | 795 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) |
| 778 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) | 796 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) |
| 779 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) | 797 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) |
| 780 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot2.service"]; }) | 798 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot.service"]; }) |
| 781 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) | 799 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) |
| 782 | ]) emailDomains); | 800 | ]) emailDomains); |
| 783 | 801 | ||
| 784 | systemd.services.postfix = { | 802 | systemd.services.postfix = { |
| 785 | serviceConfig.LoadCredential = [ | 803 | serviceConfig.LoadCredential = let |
| 786 | "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" | 804 | 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" | 805 | in [ |
| 788 | ] ++ concatMap (domain: | 806 | (tlsCredential "surtr.yggdrasil.li") |
| 789 | map (subdomain: "${subdomain}.full.pem:${config.security.acme.certs.${subdomain}.directory}/full.pem") | 807 | ] ++ concatMap (domain: map tlsCredential [domain "mailin.${domain}" "mailsub.${domain}"]) emailDomains; |
| 790 | [domain "mailin.${domain}" "mailsub.${domain}"] | ||
| 791 | ) emailDomains; | ||
| 792 | }; | 808 | }; |
| 793 | 809 | ||
| 794 | systemd.services.dovecot2 = { | 810 | systemd.services.dovecot = { |
| 795 | preStart = '' | 811 | preStart = '' |
| 796 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do | 812 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do |
| 797 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f | 813 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
diff --git a/hosts/surtr/http/default.nix b/hosts/surtr/http/default.nix index f3a7154e..4cbd3eae 100644 --- a/hosts/surtr/http/default.nix +++ b/hosts/surtr/http/default.nix | |||
| @@ -7,14 +7,11 @@ | |||
| 7 | config = { | 7 | config = { |
| 8 | services.nginx = { | 8 | services.nginx = { |
| 9 | enable = true; | 9 | enable = true; |
| 10 | package = pkgs.nginxQuic; | ||
| 11 | recommendedGzipSettings = false; | 10 | recommendedGzipSettings = false; |
| 12 | recommendedProxySettings = true; | 11 | recommendedProxySettings = true; |
| 13 | recommendedTlsSettings = true; | 12 | recommendedTlsSettings = true; |
| 14 | sslDhparam = config.security.dhparams.params.nginx.path; | 13 | sslDhparam = config.security.dhparams.params.nginx.path; |
| 15 | commonHttpConfig = '' | 14 | commonHttpConfig = '' |
| 16 | ssl_ecdh_curve X448:X25519:prime256v1:secp521r1:secp384r1; | ||
| 17 | |||
| 18 | log_format main | 15 | log_format main |
| 19 | '$remote_addr "$remote_user" ' | 16 | '$remote_addr "$remote_user" ' |
| 20 | '"$host" "$request" $status $bytes_sent ' | 17 | '"$host" "$request" $status $bytes_sent ' |
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..2c346baa 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 |
| @@ -62,7 +62,7 @@ in { | |||
| 62 | RFC2136_NAMESERVER=127.0.0.1:53 | 62 | RFC2136_NAMESERVER=127.0.0.1:53 |
| 63 | RFC2136_TSIG_ALGORITHM=hmac-sha256. | 63 | RFC2136_TSIG_ALGORITHM=hmac-sha256. |
| 64 | RFC2136_TSIG_KEY=${domain}_acme_key | 64 | RFC2136_TSIG_KEY=${domain}_acme_key |
| 65 | RFC2136_TSIG_SECRET_FILE=/run/credentials/acme-${domain}.service/${tsigSecretName domain} | 65 | RFC2136_TSIG_SECRET_FILE=/run/credentials/acme-order-renew-${domain}.service/${tsigSecretName domain} |
| 66 | RFC2136_TTL=0 | 66 | RFC2136_TTL=0 |
| 67 | RFC2136_PROPAGATION_TIMEOUT=60 | 67 | RFC2136_PROPAGATION_TIMEOUT=60 |
| 68 | RFC2136_POLLING_INTERVAL=2 | 68 | RFC2136_POLLING_INTERVAL=2 |
| @@ -79,12 +79,12 @@ in { | |||
| 79 | sops.secrets = mapAttrs' (domain: domainCfg: nameValuePair (tsigSecretName domain) { | 79 | sops.secrets = mapAttrs' (domain: domainCfg: nameValuePair (tsigSecretName domain) { |
| 80 | format = "binary"; | 80 | format = "binary"; |
| 81 | sopsFile = tsigKey domain; | 81 | sopsFile = tsigKey domain; |
| 82 | restartUnits = [ "acme-${domain}.service" ]; | 82 | restartUnits = [ "acme-order-renew${domain}.service" ]; |
| 83 | }) cfg.rfc2136Domains; | 83 | }) cfg.rfc2136Domains; |
| 84 | 84 | ||
| 85 | # Provide appropriate `tsig_key/*` to systemd service performing | 85 | # Provide appropriate `tsig_key/*` to systemd service performing |
| 86 | # certificate provisioning | 86 | # certificate provisioning |
| 87 | systemd.services = mapAttrs' (domain: domainCfg: nameValuePair "acme-${domain}" { | 87 | systemd.services = mapAttrs' (domain: domainCfg: nameValuePair "acme-order-renew-${domain}" { |
| 88 | after = [ "knot.service" ]; | 88 | after = [ "knot.service" ]; |
| 89 | bindsTo = [ "knot.service" ]; | 89 | bindsTo = [ "knot.service" ]; |
| 90 | serviceConfig = { | 90 | serviceConfig = { |
diff --git a/hosts/surtr/tls/tsig_keys/changedetection.yggdrasil.li b/hosts/surtr/tls/tsig_keys/changedetection.yggdrasil.li new file mode 100644 index 00000000..ac332fe5 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/changedetection.yggdrasil.li | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:OD12OI11EpjWIGtCGzSIeFXIht1tM7YrEbo3XqcxD0XFaZ3CrELJgru9gtN/,iv:SXPNed6CUWCUDomJbx1kOjvxTBoHrgb6tKw9Jb/Qa0M=,tag:RiueVMBSdAF96d6190bwfg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwR3FGdks2TUtkSUJLcG1v\nbXZmWFZNVmc3a0x5d2tPNmpJVDJPQmIyNkFNClAwNzI0aHE5SFdaQ0RoZnE0ZEx5\nL3IzQTRpd0g4cHJSUHlrbGtRK283SEkKLS0tIDdLVzE0SFgvS0l1eDd5SHVQQ1By\nNmZ1M0cxSnNxTE5OdHBLZ2FFRWhXdUkKTykJ2kRJPrcPwuw3ufNaCJ6pOuvtDUcl\noHizOV+Yco7nhKtINE93mD4xIiER0i5h7lpKOTUGgjzhjJP2DR7ifw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxNVRmMVIvVVZ4SkVMUC9o\nOVMxekNiZTRJWTExMHU5Q3lOaVFkbnhGT3lrCmM3ZjNSV056WjlTeEZzdHpKL1Fl\nbFF4R1phSitzWlY2dFJuRDFvK09LWm8KLS0tIEZINU5KallPdnZsZEh6WUxJYW1K\ncEV0ZkJVK2JiZ1ZtSTZRQUtiaGFBTEkKC6DQLWqY4WrRCSRrWAqlvjw6lp0Y+XGo\nrwxWMwyEocizMR6i//a5P8RBPnvzAEHbXMobI4mSDyfIdezWUX/QNA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-12-08T12:46:13Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:FLdO6Bz74+aTd9ns8ysbcrNdwogJvnm/sRRTLntf5zAH16MyI+QbsBo2LORWr5O3t24+EfmZBhMsfj/AXvqkcMFjPwIhALQpPjjT2JfAsLFtSUqZRjBNKYkfoLlTUKb083RgDjEUIVGgsZzJLCyFtfZP0NXicTUsUz9mRZCYwYU=,iv:sSPuuoE3qgt+Qhh76rZtSCBnHYLK3AN7IljUDkr14AE=,tag:56rYSDonQwfKjNR5fBgQiA==,type:str]", | ||
| 16 | "version": "3.11.0" | ||
| 17 | } | ||
| 18 | } | ||
