1 { config, lib, pkgs, ... }:
4 cfg = config.services.coturn;
5 pidfile = "/run/turnserver/turnserver.pid";
6 configFile = pkgs.writeText "turnserver.conf" ''
7 listening-port=${toString cfg.listening-port}
8 tls-listening-port=${toString cfg.tls-listening-port}
9 alt-listening-port=${toString cfg.alt-listening-port}
10 alt-tls-listening-port=${toString cfg.alt-tls-listening-port}
11 ${concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)}
12 ${concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)}
13 min-port=${toString cfg.min-port}
14 max-port=${toString cfg.max-port}
15 ${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"}
16 ${lib.optionalString cfg.no-auth "no-auth"}
17 ${lib.optionalString cfg.use-auth-secret "use-auth-secret"}
18 ${lib.optionalString (cfg.static-auth-secret != null) ("static-auth-secret=${cfg.static-auth-secret}")}
19 ${lib.optionalString (cfg.static-auth-secret-file != null) ("static-auth-secret=#static-auth-secret#")}
21 ${lib.optionalString cfg.no-udp "no-udp"}
22 ${lib.optionalString cfg.no-tcp "no-tcp"}
23 ${lib.optionalString cfg.no-tls "no-tls"}
24 ${lib.optionalString cfg.no-dtls "no-dtls"}
25 ${lib.optionalString cfg.no-udp-relay "no-udp-relay"}
26 ${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"}
27 ${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"}
28 ${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"}
29 ${lib.optionalString (cfg.dh-file != null) ("dh-file=${cfg.dh-file}")}
33 ${lib.optionalString cfg.secure-stun "secure-stun"}
34 ${lib.optionalString cfg.no-cli "no-cli"}
36 cli-port=${toString cfg.cli-port}
37 ${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")}
43 enable = mkEnableOption (lib.mdDoc "coturn TURN server");
44 listening-port = mkOption {
47 description = lib.mdDoc ''
48 TURN listener port for UDP and TCP.
49 Note: actually, TLS and DTLS sessions can connect to the
50 "plain" TCP and UDP port(s), too - if allowed by configuration.
53 tls-listening-port = mkOption {
56 description = lib.mdDoc ''
57 TURN listener port for TLS.
58 Note: actually, "plain" TCP and UDP sessions can connect to the TLS and
59 DTLS port(s), too - if allowed by configuration. The TURN server
60 "automatically" recognizes the type of traffic. Actually, two listening
61 endpoints (the "plain" one and the "tls" one) are equivalent in terms of
62 functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
63 For secure TCP connections, we currently support SSL version 3 and
64 TLS version 1.0, 1.1 and 1.2.
65 For secure UDP connections, we support DTLS version 1.
68 alt-listening-port = mkOption {
70 default = cfg.listening-port + 1;
71 defaultText = literalExpression "listening-port + 1";
72 description = lib.mdDoc ''
73 Alternative listening port for UDP and TCP listeners;
74 default (or zero) value means "listening port plus one".
75 This is needed for RFC 5780 support
76 (STUN extension specs, NAT behavior discovery). The TURN Server
77 supports RFC 5780 only if it is started with more than one
78 listening IP address of the same family (IPv4 or IPv6).
79 RFC 5780 is supported only by UDP protocol, other protocols
80 are listening to that endpoint only for "symmetry".
83 alt-tls-listening-port = mkOption {
85 default = cfg.tls-listening-port + 1;
86 defaultText = literalExpression "tls-listening-port + 1";
87 description = lib.mdDoc ''
88 Alternative listening port for TLS and DTLS protocols.
91 listening-ips = mkOption {
92 type = types.listOf types.str;
94 example = [ "203.0.113.42" "2001:DB8::42" ];
95 description = lib.mdDoc ''
96 Listener IP addresses of relay server.
97 If no IP(s) specified in the config file or in the command line options,
98 then all IPv4 and IPv6 system IPs will be used for listening.
101 relay-ips = mkOption {
102 type = types.listOf types.str;
104 example = [ "203.0.113.42" "2001:DB8::42" ];
105 description = lib.mdDoc ''
106 Relay address (the local IP address that will be used to relay the
107 packets to the peer).
108 Multiple relay addresses may be used.
109 The same IP(s) can be used as both listening IP(s) and relay IP(s).
111 If no relay IP(s) specified, then the turnserver will apply the default
112 policy: it will decide itself which relay addresses to be used, and it
113 will always be using the client socket IP address as the relay IP address
114 of the TURN session (if the requested relay address family is the same
115 as the family of the client socket).
118 min-port = mkOption {
121 description = lib.mdDoc ''
122 Lower bound of UDP relay endpoints
125 max-port = mkOption {
128 description = lib.mdDoc ''
129 Upper bound of UDP relay endpoints
132 lt-cred-mech = mkOption {
135 description = lib.mdDoc ''
136 Use long-term credential mechanism.
142 description = lib.mdDoc ''
143 This option is opposite to lt-cred-mech.
144 (TURN Server with no-auth option allows anonymous access).
145 If neither option is defined, and no users are defined,
146 then no-auth is default. If at least one user is defined,
147 in this file or in command line or in usersdb file, then
148 lt-cred-mech is default.
151 use-auth-secret = mkOption {
154 description = lib.mdDoc ''
156 Flag that sets a special authorization option that is based upon authentication secret.
157 This feature can be used with the long-term authentication mechanism, only.
158 This feature purpose is to support "TURN Server REST API", see
159 "TURN REST API" link in the project's page
160 https://github.com/coturn/coturn/
162 This option is used with timestamp:
164 usercombo -> "timestamp:userid"
165 turn user -> usercombo
166 turn password -> base64(hmac(secret key, usercombo))
168 This allows TURN credentials to be accounted for a specific user id.
169 If you don't have a suitable id, the timestamp alone can be used.
170 This option is just turning on secret-based authentication.
171 The actual value of the secret is defined either by option static-auth-secret,
172 or can be found in the turn_secret table in the database.
175 static-auth-secret = mkOption {
176 type = types.nullOr types.str;
178 description = lib.mdDoc ''
179 'Static' authentication secret value (a string) for TURN REST API only.
180 If not set, then the turn server
181 will try to use the 'dynamic' value in turn_secret table
182 in user database (if present). The database-stored value can be changed on-the-fly
183 by a separate program, so this is why that other mode is 'dynamic'.
186 static-auth-secret-file = mkOption {
187 type = types.nullOr types.str;
189 description = lib.mdDoc ''
190 Path to the file containing the static authentication secret.
195 default = config.networking.hostName;
196 defaultText = literalExpression "config.networking.hostName";
197 example = "example.com";
198 description = lib.mdDoc ''
199 The default realm to be used for the users when no explicit
200 origin/realm relationship was found in the database, or if the TURN
201 server is not using any database (just the commands-line settings
202 and the userdb file). Must be used with long-term credentials
203 mechanism or with TURN REST API.
207 type = types.nullOr types.str;
209 example = "/var/lib/acme/example.com/fullchain.pem";
210 description = lib.mdDoc ''
211 Certificate file in PEM format.
215 type = types.nullOr types.str;
217 example = "/var/lib/acme/example.com/key.pem";
218 description = lib.mdDoc ''
219 Private key file in PEM format.
223 type = types.nullOr types.str;
225 description = lib.mdDoc ''
226 Use custom DH TLS key, stored in PEM format in the file.
229 secure-stun = mkOption {
232 description = lib.mdDoc ''
233 Require authentication of the STUN Binding request.
234 By default, the clients are allowed anonymous access to the STUN Binding functionality.
240 description = lib.mdDoc ''
241 Turn OFF the CLI support.
246 default = "127.0.0.1";
247 description = lib.mdDoc ''
248 Local system IP address to be used for CLI server endpoint.
251 cli-port = mkOption {
254 description = lib.mdDoc ''
258 cli-password = mkOption {
259 type = types.nullOr types.str;
261 description = lib.mdDoc ''
263 For the security reasons, it is recommended to use the encrypted
264 for of the password (see the -P command in the turnadmin utility).
270 description = lib.mdDoc "Disable UDP client listener";
275 description = lib.mdDoc "Disable TCP client listener";
280 description = lib.mdDoc "Disable TLS client listener";
285 description = lib.mdDoc "Disable DTLS client listener";
287 no-udp-relay = mkOption {
290 description = lib.mdDoc "Disable UDP relay endpoints";
292 no-tcp-relay = mkOption {
295 description = lib.mdDoc "Disable TCP relay endpoints";
297 extraConfig = mkOption {
300 description = lib.mdDoc "Additional configuration options";
305 config = mkIf cfg.enable (mkMerge ([
307 { assertion = cfg.static-auth-secret != null -> cfg.static-auth-secret-file == null ;
308 message = "static-auth-secret and static-auth-secret-file cannot be set at the same time";
313 users.users.turnserver =
314 { uid = config.ids.uids.turnserver;
315 group = "turnserver";
316 description = "coturn TURN server user";
318 users.groups.turnserver =
319 { gid = config.ids.gids.turnserver;
320 members = [ "turnserver" ];
323 systemd.services.coturn = let
324 runConfig = "/run/coturn/turnserver.cfg";
326 description = "coturn TURN server";
327 after = [ "network-online.target" ];
328 wants = [ "network-online.target" ];
329 wantedBy = [ "multi-user.target" ];
332 Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)";
336 cat ${configFile} > ${runConfig}
337 ${optionalString (cfg.static-auth-secret-file != null) ''
338 ${pkgs.replace-secret}/bin/replace-secret \
339 "#static-auth-secret#" \
340 ${cfg.static-auth-secret-file} \
343 chmod 640 ${runConfig}
347 ExecStart = "${pkgs.coturn}/bin/turnserver -c ${runConfig}";
348 RuntimeDirectory = "turnserver";
350 Group = "turnserver";
351 AmbientCapabilities =
353 cfg.listening-port < 1024 ||
354 cfg.alt-listening-port < 1024 ||
355 cfg.tls-listening-port < 1024 ||
356 cfg.alt-tls-listening-port < 1024 ||
358 ) "cap_net_bind_service";
359 Restart = "on-abort";
362 systemd.tmpfiles.rules = [
363 "d /run/coturn 0700 turnserver turnserver - -"