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