fix(mailserver): enable Sender Rewriting Scheme (SRS) on umbriel
We had deliverability issues when forwarding mail to other domains (if the domain of the email we were forwarding had a strict SPF policy, the receiving mailserver would drop it due to SPF fail). See https://github.com/NixOS/infra/issues/485#issuecomment-2787490679 for details.
Jeremy Fleischman committed
Apr 9, 2025 at 03:21 UTC
8de80a51c6b9bff9b632916d32b4b54b75035f75
3 files changed
+69
non-critical-infra/modules/mailserver/default.nix
+1
@@ -3,6 +3,7 @@
3
{
4
imports = [
5
./mailing-lists.nix
6
+ ./postsrsd.nix
7
];
8
9
mailserver = {
non-critical-infra/modules/mailserver/postsrsd.nix
new
+40
@@ -0,0 +1,40 @@
1
+{ config, ... }:
2
+# We use `postsrsd` to enable Sender Rewriting Scheme (SRS) so mail we forward
3
+# to another domain does not fail SPF. See
4
+# https://github.com/NixOS/infra/issues/485#issuecomment-2787490679 for
5
+# details.
6
+{
7
+ services.postsrsd = {
8
+ enable = true;
9
+ domain = "nixos.org";
10
+ secretsFile = config.sops.secrets.postsrsd-secret.path;
11
+ };
12
+
13
+ # Configure postfix as per
14
+ # https://github.com/roehling/postsrsd?tab=readme-ov-file#postfix-setup
15
+ services.postfix.config = {
16
+ # TODO: switch to "socketmap:unix:/run/postsrsd/socket:forward" once
17
+ # postsrsd 2 is available: https://github.com/NixOS/nixpkgs/pull/397316
18
+ sender_canonical_maps = "tcp:127.0.0.1:${builtins.toString config.services.postsrsd.forwardPort}";
19
+ sender_canonical_classes = "envelope_sender";
20
+
21
+ # TODO: switch to "socketmap:unix:/run/postsrsd/socket:forward" once
22
+ # postsrsd 2 is available: https://github.com/NixOS/nixpkgs/pull/397316
23
+ recipient_canonical_maps = "tcp:127.0.0.1:${builtins.toString config.services.postsrsd.reversePort}";
24
+ recipient_canonical_classes = "envelope_recipient, header_recipient";
25
+ };
26
+
27
+ # ```
28
+ # How to generate:
29
+ #
30
+ # ```console
31
+ # cd non-critical-infra
32
+ # SECRET_PATH=secrets/postsrsd-secret.umbriel
33
+ # dd if=/dev/random bs=18 count=1 status=none | base64 > "$SECRET_PATH"
34
+ # sops encrypt --in-place "$SECRET_PATH"
35
+ # ```
36
+ sops.secrets.postsrsd-secret = {
37
+ format = "binary";
38
+ sopsFile = ../../secrets/postsrsd-secret.umbriel;
39
+ };
40
+}
non-critical-infra/secrets/postsrsd-secret.umbriel
new
+28
@@ -0,0 +1,28 @@
1
+{
2
+ "data": "ENC[AES256_GCM,data:Oy/Lqq1DTXVX0SK0pJOa6fJhf9H3Qi2G3w==,iv:JG2o4C9EjBbt4PqrE3kHPNabFbk2Ar3IHseQyrQxnP0=,tag:IRG+EYLebBDf99svFmcHWw==,type:str]",
3
+ "sops": {
4
+ "kms": null,
5
+ "gcp_kms": null,
6
+ "azure_kv": null,
7
+ "hc_vault": null,
8
+ "age": [
9
+ {
10
+ "recipient": "age15vcp7875xwtf64j4yshyld0a3hpgzv6n2kxky493s3q0swr9hdaqxugpv6",
11
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArTmVPZGo0aXlsbklBZlEy\naXZ3SmcxNlN1WkowQUFuSkdSVGo0Q3BneGhZClliMnIvbUVnb1FyMWJEcXFxOTh5\neDNHMFN3NUw5bGMxcm5EUWpncnkxNnMKLS0tIDNVaXFJOGxQa2FUVUhYbVkrQ0Rp\nS3RmbElCUlUxdkNXblY1S2VSQnkrc2sKusUip4Vnr56lfiEAHRPqQiZjb91rovLA\nQhPCz/LwTyxDDMTsq1dNbasniqeErmUfUfK792HqigBo2qXbsk4bjw==\n-----END AGE ENCRYPTED FILE-----\n"
12
+ },
13
+ {
14
+ "recipient": "age1j3mkgedmeru63vwww6m44zfw09tg8yw6xdzstaq7ejfkvgcau40qwakm8x",
15
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBY3gzUU1WSk92QTBFNlFU\nbVhLamEybkZUYWs4U3FPYWJFSTQybXlkK2pRCjhBdUlOVEVsMkhlbUJFbEVnbS9u\nN1VuV0xpTzdQVk1zMURyb0VQTHVMWVUKLS0tIFkzdmNtMmgxajVRQlNiTTFzTnY4\nMGdZcVpGRkRLQVVSMHJ1QlZjUm9halkKnTU4dXWpYOj4GLuyoNvz90uarPbn5CBR\nfiIXd7QPQl2+3SstC8KrxXSvRx2DmxR+imtPRvqGEf7EaspXpfy1UQ==\n-----END AGE ENCRYPTED FILE-----\n"
16
+ },
17
+ {
18
+ "recipient": "age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h",
19
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSRUY3M0haTkV1RTlUbnpi\nNk9WNU5OTkR6dVozUTJKbzhmVXJRSlJxMG1FCmUwWTJmbm12UDNmSG0rdThVekRw\nWk1sOUFLUFlzMW0vKy9IWW5HNmtDd2cKLS0tIFc3cjVLTEF3QVdaeG1Kck9KMm13\nS2pWZEpkUU5OaTJIYmNpZzIrRHdReGsKN15CghVCZmL5irAOuhewFIR1hL8YkT27\nwahQun20ahRiIkQRZxGWi1C9HJ/v4IVjwsTRYbM9BwIuopDQkofosw==\n-----END AGE ENCRYPTED FILE-----\n"
20
+ }
21
+ ],
22
+ "lastmodified": "2025-04-09T10:20:42Z",
23
+ "mac": "ENC[AES256_GCM,data:QhBJD/BaFKX16orijmxl/Oi/50d8iqBKR90QPOHgWSp+MYXfzArS/OLu8ZtZ/xwucW7pNh1oC+CGmMHTW1xrBpuMg6MQ5qyhedKjlINptYG/IIcqIkakulppOxw9LMuUWznXcDApd3w5SkMux63tO7vti0kWQ3Zv4A9XpmOXRCM=,iv:85o1/ucnYoKBc/2cCCr+TgrXCQinzX3vGYv5xzEaJmQ=,tag:dokOTNpd0aQMsvVX4MOxFg==,type:str]",
24
+ "pgp": null,
25
+ "unencrypted_suffix": "_unencrypted",
26
+ "version": "3.9.4"
27
+ }
28
+}
\ No newline at end of file