1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.murmur;
7 forking = cfg.logFile != null;
8 configFile = pkgs.writeText "murmurd.ini" ''
9 database=/var/lib/murmur/murmur.sqlite
10 dbDriver=QSQLITE
11
12 autobanAttempts=${toString cfg.autobanAttempts}
13 autobanTimeframe=${toString cfg.autobanTimeframe}
14 autobanTime=${toString cfg.autobanTime}
15
16 logfile=${optionalString (cfg.logFile != null) cfg.logFile}
17 ${optionalString forking "pidfile=/run/murmur/murmurd.pid"}
18
19 welcometext="${cfg.welcometext}"
20 port=${toString cfg.port}
21
22 ${if cfg.hostName == "" then "" else "host="+cfg.hostName}
23 ${if cfg.password == "" then "" else "serverpassword="+cfg.password}
24
25 bandwidth=${toString cfg.bandwidth}
26 users=${toString cfg.users}
27
28 textmessagelength=${toString cfg.textMsgLength}
29 imagemessagelength=${toString cfg.imgMsgLength}
30 allowhtml=${boolToString cfg.allowHtml}
31 logdays=${toString cfg.logDays}
32 bonjour=${boolToString cfg.bonjour}
33 sendversion=${boolToString cfg.sendVersion}
34
35 ${if cfg.registerName == "" then "" else "registerName="+cfg.registerName}
36 ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword}
37 ${if cfg.registerUrl == "" then "" else "registerUrl="+cfg.registerUrl}
38 ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname}
39
40 certrequired=${boolToString cfg.clientCertRequired}
41 ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert}
42 ${if cfg.sslKey == "" then "" else "sslKey="+cfg.sslKey}
43 ${if cfg.sslCa == "" then "" else "sslCA="+cfg.sslCa}
44
45 ${cfg.extraConfig}
46 '';
47in
48{
49 imports = [
50 (mkRenamedOptionModule [ "services" "murmur" "welcome" ] [ "services" "murmur" "welcometext" ])
51 (mkRemovedOptionModule [ "services" "murmur" "pidfile" ] "Hardcoded to /run/murmur/murmurd.pid now")
52 ];
53
54 options = {
55 services.murmur = {
56 enable = mkOption {
57 type = types.bool;
58 default = false;
59 description = "If enabled, start the Murmur Mumble server.";
60 };
61
62 autobanAttempts = mkOption {
63 type = types.int;
64 default = 10;
65 description = ''
66 Number of attempts a client is allowed to make in
67 <literal>autobanTimeframe</literal> seconds, before being
68 banned for <literal>autobanTime</literal>.
69 '';
70 };
71
72 autobanTimeframe = mkOption {
73 type = types.int;
74 default = 120;
75 description = ''
76 Timeframe in which a client can connect without being banned
77 for repeated attempts (in seconds).
78 '';
79 };
80
81 autobanTime = mkOption {
82 type = types.int;
83 default = 300;
84 description = "The amount of time an IP ban lasts (in seconds).";
85 };
86
87 logFile = mkOption {
88 type = types.nullOr types.path;
89 default = null;
90 example = "/var/log/murmur/murmurd.log";
91 description = "Path to the log file for Murmur daemon. Empty means log to journald.";
92 };
93
94 welcometext = mkOption {
95 type = types.str;
96 default = "";
97 description = "Welcome message for connected clients.";
98 };
99
100 port = mkOption {
101 type = types.int;
102 default = 64738;
103 description = "Ports to bind to (UDP and TCP).";
104 };
105
106 hostName = mkOption {
107 type = types.str;
108 default = "";
109 description = "Host to bind to. Defaults binding on all addresses.";
110 };
111
112 package = mkOption {
113 type = types.package;
114 default = pkgs.murmur;
115 defaultText = "pkgs.murmur";
116 description = "Overridable attribute of the murmur package to use.";
117 };
118
119 password = mkOption {
120 type = types.str;
121 default = "";
122 description = "Required password to join server, if specified.";
123 };
124
125 bandwidth = mkOption {
126 type = types.int;
127 default = 72000;
128 description = ''
129 Maximum bandwidth (in bits per second) that clients may send
130 speech at.
131 '';
132 };
133
134 users = mkOption {
135 type = types.int;
136 default = 100;
137 description = "Maximum number of concurrent clients allowed.";
138 };
139
140 textMsgLength = mkOption {
141 type = types.int;
142 default = 5000;
143 description = "Max length of text messages. Set 0 for no limit.";
144 };
145
146 imgMsgLength = mkOption {
147 type = types.int;
148 default = 131072;
149 description = "Max length of image messages. Set 0 for no limit.";
150 };
151
152 allowHtml = mkOption {
153 type = types.bool;
154 default = true;
155 description = ''
156 Allow HTML in client messages, comments, and channel
157 descriptions.
158 '';
159 };
160
161 logDays = mkOption {
162 type = types.int;
163 default = 31;
164 description = ''
165 How long to store RPC logs for in the database. Set 0 to
166 keep logs forever, or -1 to disable DB logging.
167 '';
168 };
169
170 bonjour = mkOption {
171 type = types.bool;
172 default = false;
173 description = ''
174 Enable Bonjour auto-discovery, which allows clients over
175 your LAN to automatically discover Murmur servers.
176 '';
177 };
178
179 sendVersion = mkOption {
180 type = types.bool;
181 default = true;
182 description = "Send Murmur version in UDP response.";
183 };
184
185 registerName = mkOption {
186 type = types.str;
187 default = "";
188 description = ''
189 Public server registration name, and also the name of the
190 Root channel. Even if you don't publicly register your
191 server, you probably still want to set this.
192 '';
193 };
194
195 registerPassword = mkOption {
196 type = types.str;
197 default = "";
198 description = ''
199 Public server registry password, used authenticate your
200 server to the registry to prevent impersonation; required for
201 subsequent registry updates.
202 '';
203 };
204
205 registerUrl = mkOption {
206 type = types.str;
207 default = "";
208 description = "URL website for your server.";
209 };
210
211 registerHostname = mkOption {
212 type = types.str;
213 default = "";
214 description = ''
215 DNS hostname where your server can be reached. This is only
216 needed if you want your server to be accessed by its
217 hostname and not IP - but the name *must* resolve on the
218 internet properly.
219 '';
220 };
221
222 clientCertRequired = mkOption {
223 type = types.bool;
224 default = false;
225 description = "Require clients to authenticate via certificates.";
226 };
227
228 sslCert = mkOption {
229 type = types.str;
230 default = "";
231 description = "Path to your SSL certificate.";
232 };
233
234 sslKey = mkOption {
235 type = types.str;
236 default = "";
237 description = "Path to your SSL key.";
238 };
239
240 sslCa = mkOption {
241 type = types.str;
242 default = "";
243 description = "Path to your SSL CA certificate.";
244 };
245
246 extraConfig = mkOption {
247 type = types.lines;
248 default = "";
249 description = "Extra configuration to put into murmur.ini.";
250 };
251
252 environmentFile = mkOption {
253 type = types.nullOr types.path;
254 default = null;
255 example = "/var/lib/murmur/murmurd.env";
256 description = ''
257 Environment file as defined in <citerefentry>
258 <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
259 </citerefentry>.
260
261 Secrets may be passed to the service without adding them to the world-readable
262 Nix store, by specifying placeholder variables as the option value in Nix and
263 setting these variables accordingly in the environment file.
264
265 <programlisting>
266 # snippet of murmur-related config
267 services.murmur.password = "$MURMURD_PASSWORD";
268 </programlisting>
269
270 <programlisting>
271 # content of the environment file
272 MURMURD_PASSWORD=verysecretpassword
273 </programlisting>
274
275 Note that this file needs to be available on the host on which
276 <literal>murmur</literal> is running.
277 '';
278 };
279 };
280 };
281
282 config = mkIf cfg.enable {
283 users.users.murmur = {
284 description = "Murmur Service user";
285 home = "/var/lib/murmur";
286 createHome = true;
287 uid = config.ids.uids.murmur;
288 group = "murmur";
289 };
290 users.groups.murmur = {
291 gid = config.ids.gids.murmur;
292 };
293
294 systemd.services.murmur = {
295 description = "Murmur Chat Service";
296 wantedBy = [ "multi-user.target" ];
297 after = [ "network-online.target "];
298 preStart = ''
299 ${pkgs.envsubst}/bin/envsubst \
300 -o /run/murmur/murmurd.ini \
301 -i ${configFile}
302 '';
303
304 serviceConfig = {
305 # murmurd doesn't fork when logging to the console.
306 Type = if forking then "forking" else "simple";
307 PIDFile = mkIf forking "/run/murmur/murmurd.pid";
308 EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
309 ExecStart = "${cfg.package}/bin/murmurd -ini /run/murmur/murmurd.ini";
310 Restart = "always";
311 RuntimeDirectory = "murmur";
312 RuntimeDirectoryMode = "0700";
313 User = "murmur";
314 Group = "murmur";
315 };
316 };
317 };
318}