1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8
9 cfg = config.services.postsrsd;
10 runtimeDirectoryName = "postsrsd";
11 runtimeDirectory = "/run/${runtimeDirectoryName}";
12 # TODO: follow RFC 42, but we need a libconfuse format first:
13 # https://github.com/NixOS/nixpkgs/issues/401565
14 # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"}
15 # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html.
16 #
17 # Note: We're using `builtins.toJSON` to escape strings, but JSON strings
18 # don't have exactly the same semantics as libconfuse strings. For example,
19 # "${F}" gets treated as an env var reference, see above issue for details.
20 libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }";
21 configFile = pkgs.writeText "postsrsd.conf" ''
22 secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file"
23 domains = ${libconfuseDomains}
24 separator = "${cfg.separator}"
25 socketmap = "unix:${cfg.socketPath}"
26
27 # Disable postsrsd's jailing in favor of confinement with systemd.
28 unprivileged-user = ""
29 chroot-dir = ""
30 '';
31
32in
33{
34 imports =
35 map
36 (
37 name:
38 lib.mkRemovedOptionModule [ "services" "postsrsd" name ] ''
39 `postsrsd` was upgraded to `>= 2.0.0`, with some different behaviors and configuration settings:
40 - NixOS Release Notes: https://nixos.org/manual/nixos/unstable/release-notes#sec-nixpkgs-release-25.05-incompatibilities
41 - NixOS Options Reference: https://nixos.org/manual/nixos/unstable/options#opt-services.postsrsd.enable
42 - Migration instructions: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x
43 - Postfix Setup: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#postfix-setup
44 ''
45 )
46 [
47 "domain"
48 "forwardPort"
49 "reversePort"
50 "timeout"
51 "excludeDomains"
52 ];
53
54 options = {
55 services.postsrsd = {
56 enable = lib.mkOption {
57 type = lib.types.bool;
58 default = false;
59 description = "Whether to enable the postsrsd SRS server for Postfix.";
60 };
61
62 secretsFile = lib.mkOption {
63 type = lib.types.path;
64 default = "/var/lib/postsrsd/postsrsd.secret";
65 description = "Secret keys used for signing and verification";
66 };
67
68 domains = lib.mkOption {
69 type = lib.types.listOf lib.types.str;
70 description = "Domain names for rewrite";
71 default = [ config.networking.hostName ];
72 defaultText = lib.literalExpression "[ config.networking.hostName ]";
73 };
74
75 separator = lib.mkOption {
76 type = lib.types.enum [
77 "-"
78 "="
79 "+"
80 ];
81 default = "=";
82 description = "First separator character in generated addresses";
83 };
84
85 user = lib.mkOption {
86 type = lib.types.str;
87 default = "postsrsd";
88 description = "User for the daemon";
89 };
90
91 group = lib.mkOption {
92 type = lib.types.str;
93 default = "postsrsd";
94 description = "Group for the daemon";
95 };
96
97 socketPath = lib.mkOption {
98 type = lib.types.path;
99 default = "${runtimeDirectory}/socket";
100 readOnly = true;
101 description = ''
102 Path to the Unix socket for connecting to postsrsd.
103 Read-only, intended for usage when integrating postsrsd into other NixOS config.'';
104 };
105 };
106 };
107
108 config = lib.mkIf cfg.enable {
109 users.users = lib.optionalAttrs (cfg.user == "postsrsd") {
110 postsrsd = {
111 group = cfg.group;
112 uid = config.ids.uids.postsrsd;
113 };
114 };
115
116 users.groups = lib.optionalAttrs (cfg.group == "postsrsd") {
117 postsrsd.gid = config.ids.gids.postsrsd;
118 };
119
120 systemd.services.postsrsd-generate-secrets = {
121 path = [ pkgs.coreutils ];
122 script = ''
123 if [ -e "${cfg.secretsFile}" ]; then
124 echo "Secrets file exists. Nothing to do!"
125 else
126 echo "WARNING: secrets file not found, autogenerating!"
127 DIR="$(dirname "${cfg.secretsFile}")"
128 install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR"
129 install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}"
130 fi
131 '';
132 serviceConfig = {
133 Type = "oneshot";
134 };
135 };
136
137 systemd.services.postsrsd = {
138 description = "PostSRSd SRS rewriting server";
139 after = [
140 "network.target"
141 "postsrsd-generate-secrets.service"
142 ];
143 before = [ "postfix.service" ];
144 wantedBy = [ "multi-user.target" ];
145 requires = [ "postsrsd-generate-secrets.service" ];
146 confinement.enable = true;
147
148 serviceConfig = {
149 ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}";
150 User = cfg.user;
151 Group = cfg.group;
152 PermissionsStartOnly = true;
153 RuntimeDirectory = runtimeDirectoryName;
154 LoadCredential = "secrets-file:${cfg.secretsFile}";
155 };
156 };
157 };
158}