1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.prosody;
8
9 sslOpts = { ... }: {
10
11 options = {
12
13 key = mkOption {
14 type = types.path;
15 description = "Path to the key file.";
16 };
17
18 cert = mkOption {
19 type = types.path;
20 description = "Path to the certificate file.";
21 };
22
23 extraOptions = mkOption {
24 type = types.attrs;
25 default = {};
26 description = "Extra SSL configuration options.";
27 };
28
29 };
30 };
31
32 moduleOpts = {
33
34 roster = mkOption {
35 type = types.bool;
36 default = true;
37 description = "Allow users to have a roster";
38 };
39
40 saslauth = mkOption {
41 type = types.bool;
42 default = true;
43 description = "Authentication for clients and servers. Recommended if you want to log in.";
44 };
45
46 tls = mkOption {
47 type = types.bool;
48 default = true;
49 description = "Add support for secure TLS on c2s/s2s connections";
50 };
51
52 dialback = mkOption {
53 type = types.bool;
54 default = true;
55 description = "s2s dialback support";
56 };
57
58 disco = mkOption {
59 type = types.bool;
60 default = true;
61 description = "Service discovery";
62 };
63
64 legacyauth = mkOption {
65 type = types.bool;
66 default = true;
67 description = "Legacy authentication. Only used by some old clients and bots";
68 };
69
70 version = mkOption {
71 type = types.bool;
72 default = true;
73 description = "Replies to server version requests";
74 };
75
76 uptime = mkOption {
77 type = types.bool;
78 default = true;
79 description = "Report how long server has been running";
80 };
81
82 time = mkOption {
83 type = types.bool;
84 default = true;
85 description = "Let others know the time here on this server";
86 };
87
88 ping = mkOption {
89 type = types.bool;
90 default = true;
91 description = "Replies to XMPP pings with pongs";
92 };
93
94 console = mkOption {
95 type = types.bool;
96 default = false;
97 description = "telnet to port 5582";
98 };
99
100 bosh = mkOption {
101 type = types.bool;
102 default = false;
103 description = "Enable BOSH clients, aka 'Jabber over HTTP'";
104 };
105
106 httpserver = mkOption {
107 type = types.bool;
108 default = false;
109 description = "Serve static files from a directory over HTTP";
110 };
111
112 websocket = mkOption {
113 type = types.bool;
114 default = false;
115 description = "Enable WebSocket support";
116 };
117
118 };
119
120 toLua = x:
121 if builtins.isString x then ''"${x}"''
122 else if builtins.isBool x then toString x
123 else if builtins.isInt x then toString x
124 else throw "Invalid Lua value";
125
126 createSSLOptsStr = o: ''
127 ssl = {
128 key = "${o.key}";
129 certificate = "${o.cert}";
130 ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)}
131 };
132 '';
133
134 vHostOpts = { ... }: {
135
136 options = {
137
138 # TODO: require attribute
139 domain = mkOption {
140 type = types.str;
141 description = "Domain name";
142 };
143
144 enabled = mkOption {
145 type = types.bool;
146 default = false;
147 description = "Whether to enable the virtual host";
148 };
149
150 ssl = mkOption {
151 type = types.nullOr (types.submodule sslOpts);
152 default = null;
153 description = "Paths to SSL files";
154 };
155
156 extraConfig = mkOption {
157 type = types.lines;
158 default = "";
159 description = "Additional virtual host specific configuration";
160 };
161
162 };
163
164 };
165
166in
167
168{
169
170 ###### interface
171
172 options = {
173
174 services.prosody = {
175
176 enable = mkOption {
177 type = types.bool;
178 default = false;
179 description = "Whether to enable the prosody server";
180 };
181
182 package = mkOption {
183 type = types.package;
184 description = "Prosody package to use";
185 default = pkgs.prosody;
186 defaultText = "pkgs.prosody";
187 example = literalExample ''
188 pkgs.prosody.override {
189 withExtraLibs = [ pkgs.luaPackages.lpty ];
190 withCommunityModules = [ "auth_external" ];
191 };
192 '';
193 };
194
195 allowRegistration = mkOption {
196 type = types.bool;
197 default = false;
198 description = "Allow account creation";
199 };
200
201 modules = moduleOpts;
202
203 extraModules = mkOption {
204 type = types.listOf types.str;
205 default = [];
206 description = "Enable custom modules";
207 };
208
209 virtualHosts = mkOption {
210
211 description = "Define the virtual hosts";
212
213 type = with types; loaOf (submodule vHostOpts);
214
215 example = {
216 myhost = {
217 domain = "my-xmpp-example-host.org";
218 enabled = true;
219 };
220 };
221
222 default = {
223 localhost = {
224 domain = "localhost";
225 enabled = true;
226 };
227 };
228
229 };
230
231 ssl = mkOption {
232 type = types.nullOr (types.submodule sslOpts);
233 default = null;
234 description = "Paths to SSL files";
235 };
236
237 admins = mkOption {
238 type = types.listOf types.str;
239 default = [];
240 example = [ "admin1@example.com" "admin2@example.com" ];
241 description = "List of administrators of the current host";
242 };
243
244 extraConfig = mkOption {
245 type = types.lines;
246 default = "";
247 description = "Additional prosody configuration";
248 };
249
250 };
251 };
252
253
254 ###### implementation
255
256 config = mkIf cfg.enable {
257
258 environment.systemPackages = [ pkgs.prosody ];
259
260 environment.etc."prosody/prosody.cfg.lua".text = ''
261
262 pidfile = "/var/lib/prosody/prosody.pid"
263
264
265 log = "*syslog"
266
267 data_path = "/var/lib/prosody"
268
269 allow_registration = ${boolToString cfg.allowRegistration};
270
271 ${ optionalString cfg.modules.console "console_enabled = true;" }
272
273 ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) }
274
275 admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } };
276
277 modules_enabled = {
278
279 ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList
280 (name: val: optionalString val ''"${name}";'')
281 cfg.modules) }
282
283 ${ optionalString cfg.allowRegistration "\"register\"\;" }
284
285 ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)}
286
287 "posix";
288 };
289
290 ${ cfg.extraConfig }
291
292 ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
293 VirtualHost "${v.domain}"
294 enabled = ${boolToString v.enabled};
295 ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) }
296 ${ v.extraConfig }
297 '') cfg.virtualHosts) }
298 '';
299
300 users.extraUsers.prosody = {
301 uid = config.ids.uids.prosody;
302 description = "Prosody user";
303 createHome = true;
304 group = "prosody";
305 home = "/var/lib/prosody";
306 };
307
308 users.extraGroups.prosody = {
309 gid = config.ids.gids.prosody;
310 };
311
312 systemd.services.prosody = {
313 description = "Prosody XMPP server";
314 after = [ "network-online.target" ];
315 wants = [ "network-online.target" ];
316 wantedBy = [ "multi-user.target" ];
317 restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
318 serviceConfig = {
319 User = "prosody";
320 Type = "forking";
321 PIDFile = "/var/lib/prosody/prosody.pid";
322 ExecStart = "${cfg.package}/bin/prosodyctl start";
323 };
324 };
325
326 };
327
328}