Remove n0emis as direct maintainer (#365023)
[NixPkgs.git] / nixos / modules / services / networking / coturn.nix
blobb50aad89871b83a9921418dfa37228b2d362107e
2   config,
3   lib,
4   pkgs,
5   utils,
6   ...
7 }:
8 let
9   cfg = config.services.coturn;
10   pidfile = "/run/turnserver/turnserver.pid";
11   configFile = pkgs.writeText "turnserver.conf" ''
12     listening-port=${toString cfg.listening-port}
13     tls-listening-port=${toString cfg.tls-listening-port}
14     alt-listening-port=${toString cfg.alt-listening-port}
15     alt-tls-listening-port=${toString cfg.alt-tls-listening-port}
16     ${lib.concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)}
17     ${lib.concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)}
18     min-port=${toString cfg.min-port}
19     max-port=${toString cfg.max-port}
20     ${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"}
21     ${lib.optionalString cfg.no-auth "no-auth"}
22     ${lib.optionalString cfg.use-auth-secret "use-auth-secret"}
23     ${lib.optionalString (
24       cfg.static-auth-secret != null
25     ) "static-auth-secret=${cfg.static-auth-secret}"}
26     ${lib.optionalString (
27       cfg.static-auth-secret-file != null
28     ) "static-auth-secret=#static-auth-secret#"}
29     realm=${cfg.realm}
30     ${lib.optionalString cfg.no-udp "no-udp"}
31     ${lib.optionalString cfg.no-tcp "no-tcp"}
32     ${lib.optionalString cfg.no-tls "no-tls"}
33     ${lib.optionalString cfg.no-dtls "no-dtls"}
34     ${lib.optionalString cfg.no-udp-relay "no-udp-relay"}
35     ${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"}
36     ${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"}
37     ${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"}
38     ${lib.optionalString (cfg.dh-file != null) "dh-file=${cfg.dh-file}"}
39     no-stdout-log
40     syslog
41     pidfile=${pidfile}
42     ${lib.optionalString cfg.secure-stun "secure-stun"}
43     ${lib.optionalString cfg.no-cli "no-cli"}
44     cli-ip=${cfg.cli-ip}
45     cli-port=${toString cfg.cli-port}
46     ${lib.optionalString (cfg.cli-password != null) "cli-password=${cfg.cli-password}"}
47     ${cfg.extraConfig}
48   '';
51   options = {
52     services.coturn = {
53       enable = lib.mkEnableOption "coturn TURN server";
54       listening-port = lib.mkOption {
55         type = lib.types.int;
56         default = 3478;
57         description = ''
58           TURN listener port for UDP and TCP.
59           Note: actually, TLS and DTLS sessions can connect to the
60           "plain" TCP and UDP port(s), too - if allowed by configuration.
61         '';
62       };
63       tls-listening-port = lib.mkOption {
64         type = lib.types.int;
65         default = 5349;
66         description = ''
67           TURN listener port for TLS.
68           Note: actually, "plain" TCP and UDP sessions can connect to the TLS and
69           DTLS port(s), too - if allowed by configuration. The TURN server
70           "automatically" recognizes the type of traffic. Actually, two listening
71           endpoints (the "plain" one and the "tls" one) are equivalent in terms of
72           functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
73           For secure TCP connections, we currently support SSL version 3 and
74           TLS version 1.0, 1.1 and 1.2.
75           For secure UDP connections, we support DTLS version 1.
76         '';
77       };
78       alt-listening-port = lib.mkOption {
79         type = lib.types.int;
80         default = cfg.listening-port + 1;
81         defaultText = lib.literalExpression "listening-port + 1";
82         description = ''
83           Alternative listening port for UDP and TCP listeners;
84           default (or zero) value means "listening port plus one".
85           This is needed for RFC 5780 support
86           (STUN extension specs, NAT behavior discovery). The TURN Server
87           supports RFC 5780 only if it is started with more than one
88           listening IP address of the same family (IPv4 or IPv6).
89           RFC 5780 is supported only by UDP protocol, other protocols
90           are listening to that endpoint only for "symmetry".
91         '';
92       };
93       alt-tls-listening-port = lib.mkOption {
94         type = lib.types.int;
95         default = cfg.tls-listening-port + 1;
96         defaultText = lib.literalExpression "tls-listening-port + 1";
97         description = ''
98           Alternative listening port for TLS and DTLS protocols.
99         '';
100       };
101       listening-ips = lib.mkOption {
102         type = lib.types.listOf lib.types.str;
103         default = [ ];
104         example = [
105           "203.0.113.42"
106           "2001:DB8::42"
107         ];
108         description = ''
109           Listener IP addresses of relay server.
110           If no IP(s) specified in the config file or in the command line options,
111           then all IPv4 and IPv6 system IPs will be used for listening.
112         '';
113       };
114       relay-ips = lib.mkOption {
115         type = lib.types.listOf lib.types.str;
116         default = [ ];
117         example = [
118           "203.0.113.42"
119           "2001:DB8::42"
120         ];
121         description = ''
122           Relay address (the local IP address that will be used to relay the
123           packets to the peer).
124           Multiple relay addresses may be used.
125           The same IP(s) can be used as both listening IP(s) and relay IP(s).
127           If no relay IP(s) specified, then the turnserver will apply the default
128           policy: it will decide itself which relay addresses to be used, and it
129           will always be using the client socket IP address as the relay IP address
130           of the TURN session (if the requested relay address family is the same
131           as the family of the client socket).
132         '';
133       };
134       min-port = lib.mkOption {
135         type = lib.types.int;
136         default = 49152;
137         description = ''
138           Lower bound of UDP relay endpoints
139         '';
140       };
141       max-port = lib.mkOption {
142         type = lib.types.int;
143         default = 65535;
144         description = ''
145           Upper bound of UDP relay endpoints
146         '';
147       };
148       lt-cred-mech = lib.mkOption {
149         type = lib.types.bool;
150         default = false;
151         description = ''
152           Use long-term credential mechanism.
153         '';
154       };
155       no-auth = lib.mkOption {
156         type = lib.types.bool;
157         default = false;
158         description = ''
159           This option is opposite to lt-cred-mech.
160           (TURN Server with no-auth option allows anonymous access).
161           If neither option is defined, and no users are defined,
162           then no-auth is default. If at least one user is defined,
163           in this file or in command line or in usersdb file, then
164           lt-cred-mech is default.
165         '';
166       };
167       use-auth-secret = lib.mkOption {
168         type = lib.types.bool;
169         default = false;
170         description = ''
171           TURN REST API flag.
172           Flag that sets a special authorization option that is based upon authentication secret.
173           This feature can be used with the long-term authentication mechanism, only.
174           This feature purpose is to support "TURN Server REST API", see
175           "TURN REST API" link in the project's page
176           https://github.com/coturn/coturn/
178           This option is used with timestamp:
180           usercombo -> "timestamp:userid"
181           turn user -> usercombo
182           turn password -> base64(hmac(secret key, usercombo))
184           This allows TURN credentials to be accounted for a specific user id.
185           If you don't have a suitable id, the timestamp alone can be used.
186           This option is just turning on secret-based authentication.
187           The actual value of the secret is defined either by option static-auth-secret,
188           or can be found in the turn_secret table in the database.
189         '';
190       };
191       static-auth-secret = lib.mkOption {
192         type = lib.types.nullOr lib.types.str;
193         default = null;
194         description = ''
195           'Static' authentication secret value (a string) for TURN REST API only.
196           If not set, then the turn server
197           will try to use the 'dynamic' value in turn_secret table
198           in user database (if present). The database-stored  value can be changed on-the-fly
199           by a separate program, so this is why that other mode is 'dynamic'.
200         '';
201       };
202       static-auth-secret-file = lib.mkOption {
203         type = lib.types.nullOr lib.types.str;
204         default = null;
205         description = ''
206           Path to the file containing the static authentication secret.
207         '';
208       };
209       realm = lib.mkOption {
210         type = lib.types.str;
211         default = config.networking.hostName;
212         defaultText = lib.literalExpression "config.networking.hostName";
213         example = "example.com";
214         description = ''
215           The default realm to be used for the users when no explicit
216           origin/realm relationship was found in the database, or if the TURN
217           server is not using any database (just the commands-line settings
218           and the userdb file). Must be used with long-term credentials
219           mechanism or with TURN REST API.
220         '';
221       };
222       cert = lib.mkOption {
223         type = lib.types.nullOr lib.types.str;
224         default = null;
225         example = "/var/lib/acme/example.com/fullchain.pem";
226         description = ''
227           Certificate file in PEM format.
228         '';
229       };
230       pkey = lib.mkOption {
231         type = lib.types.nullOr lib.types.str;
232         default = null;
233         example = "/var/lib/acme/example.com/key.pem";
234         description = ''
235           Private key file in PEM format.
236         '';
237       };
238       dh-file = lib.mkOption {
239         type = lib.types.nullOr lib.types.str;
240         default = null;
241         description = ''
242           Use custom DH TLS key, stored in PEM format in the file.
243         '';
244       };
245       secure-stun = lib.mkOption {
246         type = lib.types.bool;
247         default = false;
248         description = ''
249           Require authentication of the STUN Binding request.
250           By default, the clients are allowed anonymous access to the STUN Binding functionality.
251         '';
252       };
253       no-cli = lib.mkOption {
254         type = lib.types.bool;
255         default = false;
256         description = ''
257           Turn OFF the CLI support.
258         '';
259       };
260       cli-ip = lib.mkOption {
261         type = lib.types.str;
262         default = "127.0.0.1";
263         description = ''
264           Local system IP address to be used for CLI server endpoint.
265         '';
266       };
267       cli-port = lib.mkOption {
268         type = lib.types.int;
269         default = 5766;
270         description = ''
271           CLI server port.
272         '';
273       };
274       cli-password = lib.mkOption {
275         type = lib.types.nullOr lib.types.str;
276         default = null;
277         description = ''
278           CLI access password.
279           For the security reasons, it is recommended to use the encrypted
280           for of the password (see the -P command in the turnadmin utility).
281         '';
282       };
283       no-udp = lib.mkOption {
284         type = lib.types.bool;
285         default = false;
286         description = "Disable UDP client listener";
287       };
288       no-tcp = lib.mkOption {
289         type = lib.types.bool;
290         default = false;
291         description = "Disable TCP client listener";
292       };
293       no-tls = lib.mkOption {
294         type = lib.types.bool;
295         default = false;
296         description = "Disable TLS client listener";
297       };
298       no-dtls = lib.mkOption {
299         type = lib.types.bool;
300         default = false;
301         description = "Disable DTLS client listener";
302       };
303       no-udp-relay = lib.mkOption {
304         type = lib.types.bool;
305         default = false;
306         description = "Disable UDP relay endpoints";
307       };
308       no-tcp-relay = lib.mkOption {
309         type = lib.types.bool;
310         default = false;
311         description = "Disable TCP relay endpoints";
312       };
313       extraConfig = lib.mkOption {
314         type = lib.types.lines;
315         default = "";
316         description = "Additional configuration options";
317       };
318     };
319   };
321   config = lib.mkIf cfg.enable (
322     lib.mkMerge [
323       {
324         assertions = [
325           {
326             assertion = cfg.static-auth-secret != null -> cfg.static-auth-secret-file == null;
327             message = "static-auth-secret and static-auth-secret-file cannot be set at the same time";
328           }
329         ];
330       }
332       {
333         users.users.turnserver = {
334           uid = config.ids.uids.turnserver;
335           group = "turnserver";
336           description = "coturn TURN server user";
337         };
338         users.groups.turnserver = {
339           gid = config.ids.gids.turnserver;
340           members = [ "turnserver" ];
341         };
343         systemd.services.coturn =
344           let
345             runConfig = "/run/coturn/turnserver.cfg";
346           in
347           {
348             description = "coturn TURN server";
349             after = [ "network-online.target" ];
350             wants = [ "network-online.target" ];
351             wantedBy = [ "multi-user.target" ];
353             unitConfig = {
354               Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)";
355             };
357             preStart = ''
358               cat ${configFile} > ${runConfig}
359               ${lib.optionalString (cfg.static-auth-secret-file != null) ''
360                 ${pkgs.replace-secret}/bin/replace-secret \
361                   "#static-auth-secret#" \
362                   ${cfg.static-auth-secret-file} \
363                   ${runConfig}
364               ''}
365               chmod 640 ${runConfig}
366             '';
367             serviceConfig = rec {
368               Type = "simple";
369               ExecStart = utils.escapeSystemdExecArgs [
370                 (lib.getExe' pkgs.coturn "turnserver")
371                 "-c"
372                 runConfig
373               ];
374               User = "turnserver";
375               Group = "turnserver";
376               RuntimeDirectory = [
377                 "coturn"
378                 "turnserver"
379               ];
380               RuntimeDirectoryMode = "0700";
381               Restart = "on-abort";
383               # Hardening
384               AmbientCapabilities =
385                 if
386                   cfg.listening-port < 1024
387                   || cfg.alt-listening-port < 1024
388                   || cfg.tls-listening-port < 1024
389                   || cfg.alt-tls-listening-port < 1024
390                   || cfg.min-port < 1024
391                 then
392                   [ "CAP_NET_BIND_SERVICE" ]
393                 else
394                   [ "" ];
395               CapabilityBoundingSet = AmbientCapabilities;
396               DevicePolicy = "closed";
397               LockPersonality = true;
398               MemoryDenyWriteExecute = true;
399               NoNewPrivileges = true;
400               PrivateDevices = true;
401               PrivateTmp = true;
402               PrivateUsers = true;
403               ProcSubset = "pid";
404               ProtectClock = true;
405               ProtectControlGroups = true;
406               ProtectHome = true;
407               ProtectHostname = true;
408               ProtectKernelLogs = true;
409               ProtectKernelModules = true;
410               ProtectKernelTunables = true;
411               ProtectProc = "invisible";
412               ProtectSystem = "strict";
413               RemoveIPC = true;
414               RestrictAddressFamilies =
415                 [
416                   "AF_INET"
417                   "AF_INET6"
418                 ]
419                 ++ lib.optionals (cfg.listening-ips == [ ]) [
420                   # only used for interface discovery when no listening ips are configured
421                   "AF_NETLINK"
422                 ];
423               RestrictNamespaces = true;
424               RestrictRealtime = true;
425               RestrictSUIDSGID = true;
426               SystemCallArchitectures = "native";
427               SystemCallFilter = [
428                 "@system-service"
429                 "~@privileged @resources"
430               ];
431               UMask = "0077";
432             };
433           };
434       }
435     ]
436   );