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#"}
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}"}
42 ${lib.optionalString cfg.secure-stun "secure-stun"}
43 ${lib.optionalString cfg.no-cli "no-cli"}
45 cli-port=${toString cfg.cli-port}
46 ${lib.optionalString (cfg.cli-password != null) "cli-password=${cfg.cli-password}"}
53 enable = lib.mkEnableOption "coturn TURN server";
54 listening-port = lib.mkOption {
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.
63 tls-listening-port = lib.mkOption {
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.
78 alt-listening-port = lib.mkOption {
80 default = cfg.listening-port + 1;
81 defaultText = lib.literalExpression "listening-port + 1";
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".
93 alt-tls-listening-port = lib.mkOption {
95 default = cfg.tls-listening-port + 1;
96 defaultText = lib.literalExpression "tls-listening-port + 1";
98 Alternative listening port for TLS and DTLS protocols.
101 listening-ips = lib.mkOption {
102 type = lib.types.listOf lib.types.str;
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.
114 relay-ips = lib.mkOption {
115 type = lib.types.listOf lib.types.str;
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).
134 min-port = lib.mkOption {
135 type = lib.types.int;
138 Lower bound of UDP relay endpoints
141 max-port = lib.mkOption {
142 type = lib.types.int;
145 Upper bound of UDP relay endpoints
148 lt-cred-mech = lib.mkOption {
149 type = lib.types.bool;
152 Use long-term credential mechanism.
155 no-auth = lib.mkOption {
156 type = lib.types.bool;
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.
167 use-auth-secret = lib.mkOption {
168 type = lib.types.bool;
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.
191 static-auth-secret = lib.mkOption {
192 type = lib.types.nullOr lib.types.str;
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'.
202 static-auth-secret-file = lib.mkOption {
203 type = lib.types.nullOr lib.types.str;
206 Path to the file containing the static authentication secret.
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";
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.
222 cert = lib.mkOption {
223 type = lib.types.nullOr lib.types.str;
225 example = "/var/lib/acme/example.com/fullchain.pem";
227 Certificate file in PEM format.
230 pkey = lib.mkOption {
231 type = lib.types.nullOr lib.types.str;
233 example = "/var/lib/acme/example.com/key.pem";
235 Private key file in PEM format.
238 dh-file = lib.mkOption {
239 type = lib.types.nullOr lib.types.str;
242 Use custom DH TLS key, stored in PEM format in the file.
245 secure-stun = lib.mkOption {
246 type = lib.types.bool;
249 Require authentication of the STUN Binding request.
250 By default, the clients are allowed anonymous access to the STUN Binding functionality.
253 no-cli = lib.mkOption {
254 type = lib.types.bool;
257 Turn OFF the CLI support.
260 cli-ip = lib.mkOption {
261 type = lib.types.str;
262 default = "127.0.0.1";
264 Local system IP address to be used for CLI server endpoint.
267 cli-port = lib.mkOption {
268 type = lib.types.int;
274 cli-password = lib.mkOption {
275 type = lib.types.nullOr lib.types.str;
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).
283 no-udp = lib.mkOption {
284 type = lib.types.bool;
286 description = "Disable UDP client listener";
288 no-tcp = lib.mkOption {
289 type = lib.types.bool;
291 description = "Disable TCP client listener";
293 no-tls = lib.mkOption {
294 type = lib.types.bool;
296 description = "Disable TLS client listener";
298 no-dtls = lib.mkOption {
299 type = lib.types.bool;
301 description = "Disable DTLS client listener";
303 no-udp-relay = lib.mkOption {
304 type = lib.types.bool;
306 description = "Disable UDP relay endpoints";
308 no-tcp-relay = lib.mkOption {
309 type = lib.types.bool;
311 description = "Disable TCP relay endpoints";
313 extraConfig = lib.mkOption {
314 type = lib.types.lines;
316 description = "Additional configuration options";
321 config = lib.mkIf cfg.enable (
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";
333 users.users.turnserver = {
334 uid = config.ids.uids.turnserver;
335 group = "turnserver";
336 description = "coturn TURN server user";
338 users.groups.turnserver = {
339 gid = config.ids.gids.turnserver;
340 members = [ "turnserver" ];
343 systemd.services.coturn =
345 runConfig = "/run/coturn/turnserver.cfg";
348 description = "coturn TURN server";
349 after = [ "network-online.target" ];
350 wants = [ "network-online.target" ];
351 wantedBy = [ "multi-user.target" ];
354 Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)";
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} \
365 chmod 640 ${runConfig}
367 serviceConfig = rec {
369 ExecStart = utils.escapeSystemdExecArgs [
370 (lib.getExe' pkgs.coturn "turnserver")
375 Group = "turnserver";
380 RuntimeDirectoryMode = "0700";
381 Restart = "on-abort";
384 AmbientCapabilities =
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
392 [ "CAP_NET_BIND_SERVICE" ]
395 CapabilityBoundingSet = AmbientCapabilities;
396 DevicePolicy = "closed";
397 LockPersonality = true;
398 MemoryDenyWriteExecute = true;
399 NoNewPrivileges = true;
400 PrivateDevices = true;
405 ProtectControlGroups = true;
407 ProtectHostname = true;
408 ProtectKernelLogs = true;
409 ProtectKernelModules = true;
410 ProtectKernelTunables = true;
411 ProtectProc = "invisible";
412 ProtectSystem = "strict";
414 RestrictAddressFamilies =
419 ++ lib.optionals (cfg.listening-ips == [ ]) [
420 # only used for interface discovery when no listening ips are configured
423 RestrictNamespaces = true;
424 RestrictRealtime = true;
425 RestrictSUIDSGID = true;
426 SystemCallArchitectures = "native";
429 "~@privileged @resources"