1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7let
8 cfg = config.services.schleuder;
9 settingsFormat = pkgs.formats.yaml { };
10 postfixMap =
11 entries: lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${value}") entries);
12 writePostfixMap = name: entries: pkgs.writeText name (postfixMap entries);
13 configScript = pkgs.writeScript "schleuder-cfg" ''
14 #!${pkgs.runtimeShell}
15 set -exuo pipefail
16 umask 0077
17 ${pkgs.yq}/bin/yq \
18 --slurpfile overrides <(${pkgs.yq}/bin/yq . <${lib.escapeShellArg cfg.extraSettingsFile}) \
19 < ${settingsFormat.generate "schleuder.yml" cfg.settings} \
20 '. * $overrides[0]' \
21 > /etc/schleuder/schleuder.yml
22 chown schleuder: /etc/schleuder/schleuder.yml
23 '';
24in
25{
26 options.services.schleuder = {
27 enable = lib.mkEnableOption "Schleuder secure remailer";
28 enablePostfix = lib.mkEnableOption "automatic postfix integration" // {
29 default = true;
30 };
31 lists = lib.mkOption {
32 description = ''
33 List of list addresses that should be handled by Schleuder.
34
35 Note that this is only handled by the postfix integration, and
36 the setup of the lists, their members and their keys has to be
37 performed separately via schleuder's API, using a tool such as
38 schleuder-cli.
39 '';
40 type = lib.types.listOf lib.types.str;
41 default = [ ];
42 example = [
43 "widget-team@example.com"
44 "security@example.com"
45 ];
46 };
47 /*
48 maybe one day....
49 domains = lib.mkOption {
50 description = "Domains for which all mail should be handled by Schleuder.";
51 type = lib.types.listOf lib.types.str;
52 default = [];
53 example = ["securelists.example.com"];
54 };
55 */
56 settings = lib.mkOption {
57 description = ''
58 Settings for schleuder.yml.
59
60 Check the [example configuration](https://0xacab.org/schleuder/schleuder/blob/master/etc/schleuder.yml) for possible values.
61 '';
62 type = lib.types.submodule {
63 freeformType = settingsFormat.type;
64 options.keyserver = lib.mkOption {
65 type = lib.types.str;
66 description = ''
67 Key server from which to fetch and update keys.
68
69 Note that NixOS uses a different default from upstream, since the upstream default sks-keyservers.net is deprecated.
70 '';
71 default = "keys.openpgp.org";
72 };
73 };
74 default = { };
75 };
76 extraSettingsFile = lib.mkOption {
77 description = "YAML file to merge into the schleuder config at runtime. This can be used for secrets such as API keys.";
78 type = lib.types.nullOr lib.types.path;
79 default = null;
80 };
81 listDefaults = lib.mkOption {
82 description = ''
83 Default settings for lists (list-defaults.yml).
84
85 Check the [example configuration](https://0xacab.org/schleuder/schleuder/-/blob/master/etc/list-defaults.yml) for possible values.
86 '';
87 type = settingsFormat.type;
88 default = { };
89 };
90 };
91 config = lib.mkIf cfg.enable {
92 assertions = [
93 {
94 assertion = !(cfg.settings.api ? valid_api_keys);
95 message = ''
96 services.schleuder.settings.api.valid_api_keys is set. Defining API keys via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store API keys in a non-public location.
97 '';
98 }
99 {
100 assertion = !(lib.any (db: db ? password) (lib.attrValues cfg.settings.database or { }));
101 message = ''
102 A password is defined for at least one database in services.schleuder.settings.database. Defining passwords via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store database passwords in a non-public location.
103 '';
104 }
105 ];
106 users.users.schleuder.isSystemUser = true;
107 users.users.schleuder.group = "schleuder";
108 users.groups.schleuder = { };
109 environment.systemPackages = [
110 pkgs.schleuder-cli
111 ];
112 services.postfix = lib.mkIf cfg.enablePostfix {
113 extraMasterConf = ''
114 schleuder unix - n n - - pipe
115 flags=DRhu user=schleuder argv=/${pkgs.schleuder}/bin/schleuder work ''${recipient}
116 '';
117 transport = lib.mkIf (cfg.lists != [ ]) (postfixMap (lib.genAttrs cfg.lists (_: "schleuder:")));
118 settings.main.schleuder_destination_recipient_limit = 1;
119 # review: does this make sense?
120 localRecipients = lib.mkIf (cfg.lists != [ ]) cfg.lists;
121 };
122 systemd.services =
123 let
124 commonServiceConfig = {
125 # We would have liked to use DynamicUser, but since the default
126 # database is SQLite and lives in StateDirectory, and that same
127 # database needs to be readable from the postfix service, this
128 # isn't trivial to do.
129 User = "schleuder";
130 StateDirectory = "schleuder";
131 StateDirectoryMode = "0700";
132 };
133 in
134 {
135 schleuder-init = {
136 serviceConfig = commonServiceConfig // {
137 ExecStartPre = lib.mkIf (cfg.extraSettingsFile != null) [
138 "+${configScript}"
139 ];
140 ExecStart = [ "${pkgs.schleuder}/bin/schleuder install" ];
141 Type = "oneshot";
142 };
143 };
144 schleuder-api-daemon = {
145 after = [
146 "local-fs.target"
147 "network.target"
148 "schleuder-init.service"
149 ];
150 wantedBy = [ "multi-user.target" ];
151 requires = [ "schleuder-init.service" ];
152 serviceConfig = commonServiceConfig // {
153 ExecStart = [ "${pkgs.schleuder}/bin/schleuder-api-daemon" ];
154 };
155 };
156 schleuder-weekly-key-maintenance = {
157 after = [
158 "local-fs.target"
159 "network.target"
160 ];
161 startAt = "weekly";
162 serviceConfig = commonServiceConfig // {
163 ExecStart = [
164 "${pkgs.schleuder}/bin/schleuder refresh_keys"
165 "${pkgs.schleuder}/bin/schleuder check_keys"
166 ];
167 };
168 };
169 };
170
171 environment.etc."schleuder/schleuder.yml" = lib.mkIf (cfg.extraSettingsFile == null) {
172 source = settingsFormat.generate "schleuder.yml" cfg.settings;
173 };
174 environment.etc."schleuder/list-defaults.yml".source =
175 settingsFormat.generate "list-defaults.yml" cfg.listDefaults;
176
177 services.schleuder = {
178 #lists_dir = "/var/lib/schleuder.lists";
179 settings.filters_dir = lib.mkDefault "/var/lib/schleuder/filters";
180 settings.keyword_handlers_dir = lib.mkDefault "/var/lib/schleuder/keyword_handlers";
181 };
182 };
183}