at 23.11-pre 14 kB view raw
1{ config, lib, pkgs, ... }: 2with lib; 3let 4 cfg = config.services.coturn; 5 pidfile = "/run/turnserver/turnserver.pid"; 6 configFile = pkgs.writeText "turnserver.conf" '' 7listening-port=${toString cfg.listening-port} 8tls-listening-port=${toString cfg.tls-listening-port} 9alt-listening-port=${toString cfg.alt-listening-port} 10alt-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)} 13min-port=${toString cfg.min-port} 14max-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#")} 20realm=${cfg.realm} 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}")} 30no-stdout-log 31syslog 32pidfile=${pidfile} 33${lib.optionalString cfg.secure-stun "secure-stun"} 34${lib.optionalString cfg.no-cli "no-cli"} 35cli-ip=${cfg.cli-ip} 36cli-port=${toString cfg.cli-port} 37${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")} 38${cfg.extraConfig} 39''; 40in { 41 options = { 42 services.coturn = { 43 enable = mkEnableOption (lib.mdDoc "coturn TURN server"); 44 listening-port = mkOption { 45 type = types.int; 46 default = 3478; 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. 51 ''; 52 }; 53 tls-listening-port = mkOption { 54 type = types.int; 55 default = 5349; 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. 66 ''; 67 }; 68 alt-listening-port = mkOption { 69 type = types.int; 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". 81 ''; 82 }; 83 alt-tls-listening-port = mkOption { 84 type = types.int; 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. 89 ''; 90 }; 91 listening-ips = mkOption { 92 type = types.listOf types.str; 93 default = []; 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. 99 ''; 100 }; 101 relay-ips = mkOption { 102 type = types.listOf types.str; 103 default = []; 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). 110 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). 116 ''; 117 }; 118 min-port = mkOption { 119 type = types.int; 120 default = 49152; 121 description = lib.mdDoc '' 122 Lower bound of UDP relay endpoints 123 ''; 124 }; 125 max-port = mkOption { 126 type = types.int; 127 default = 65535; 128 description = lib.mdDoc '' 129 Upper bound of UDP relay endpoints 130 ''; 131 }; 132 lt-cred-mech = mkOption { 133 type = types.bool; 134 default = false; 135 description = lib.mdDoc '' 136 Use long-term credential mechanism. 137 ''; 138 }; 139 no-auth = mkOption { 140 type = types.bool; 141 default = false; 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. 149 ''; 150 }; 151 use-auth-secret = mkOption { 152 type = types.bool; 153 default = false; 154 description = lib.mdDoc '' 155 TURN REST API flag. 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/ 161 162 This option is used with timestamp: 163 164 usercombo -> "timestamp:userid" 165 turn user -> usercombo 166 turn password -> base64(hmac(secret key, usercombo)) 167 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. 173 ''; 174 }; 175 static-auth-secret = mkOption { 176 type = types.nullOr types.str; 177 default = null; 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'. 184 ''; 185 }; 186 static-auth-secret-file = mkOption { 187 type = types.nullOr types.str; 188 default = null; 189 description = lib.mdDoc '' 190 Path to the file containing the static authentication secret. 191 ''; 192 }; 193 realm = mkOption { 194 type = types.str; 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. 204 ''; 205 }; 206 cert = mkOption { 207 type = types.nullOr types.str; 208 default = null; 209 example = "/var/lib/acme/example.com/fullchain.pem"; 210 description = lib.mdDoc '' 211 Certificate file in PEM format. 212 ''; 213 }; 214 pkey = mkOption { 215 type = types.nullOr types.str; 216 default = null; 217 example = "/var/lib/acme/example.com/key.pem"; 218 description = lib.mdDoc '' 219 Private key file in PEM format. 220 ''; 221 }; 222 dh-file = mkOption { 223 type = types.nullOr types.str; 224 default = null; 225 description = lib.mdDoc '' 226 Use custom DH TLS key, stored in PEM format in the file. 227 ''; 228 }; 229 secure-stun = mkOption { 230 type = types.bool; 231 default = false; 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. 235 ''; 236 }; 237 no-cli = mkOption { 238 type = types.bool; 239 default = false; 240 description = lib.mdDoc '' 241 Turn OFF the CLI support. 242 ''; 243 }; 244 cli-ip = mkOption { 245 type = types.str; 246 default = "127.0.0.1"; 247 description = lib.mdDoc '' 248 Local system IP address to be used for CLI server endpoint. 249 ''; 250 }; 251 cli-port = mkOption { 252 type = types.int; 253 default = 5766; 254 description = lib.mdDoc '' 255 CLI server port. 256 ''; 257 }; 258 cli-password = mkOption { 259 type = types.nullOr types.str; 260 default = null; 261 description = lib.mdDoc '' 262 CLI access password. 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). 265 ''; 266 }; 267 no-udp = mkOption { 268 type = types.bool; 269 default = false; 270 description = lib.mdDoc "Disable UDP client listener"; 271 }; 272 no-tcp = mkOption { 273 type = types.bool; 274 default = false; 275 description = lib.mdDoc "Disable TCP client listener"; 276 }; 277 no-tls = mkOption { 278 type = types.bool; 279 default = false; 280 description = lib.mdDoc "Disable TLS client listener"; 281 }; 282 no-dtls = mkOption { 283 type = types.bool; 284 default = false; 285 description = lib.mdDoc "Disable DTLS client listener"; 286 }; 287 no-udp-relay = mkOption { 288 type = types.bool; 289 default = false; 290 description = lib.mdDoc "Disable UDP relay endpoints"; 291 }; 292 no-tcp-relay = mkOption { 293 type = types.bool; 294 default = false; 295 description = lib.mdDoc "Disable TCP relay endpoints"; 296 }; 297 extraConfig = mkOption { 298 type = types.lines; 299 default = ""; 300 description = lib.mdDoc "Additional configuration options"; 301 }; 302 }; 303 }; 304 305 config = mkIf cfg.enable (mkMerge ([ 306 { assertions = [ 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"; 309 } 310 ];} 311 312 { 313 users.users.turnserver = 314 { uid = config.ids.uids.turnserver; 315 group = "turnserver"; 316 description = "coturn TURN server user"; 317 }; 318 users.groups.turnserver = 319 { gid = config.ids.gids.turnserver; 320 members = [ "turnserver" ]; 321 }; 322 323 systemd.services.coturn = let 324 runConfig = "/run/coturn/turnserver.cfg"; 325 in { 326 description = "coturn TURN server"; 327 after = [ "network-online.target" ]; 328 wants = [ "network-online.target" ]; 329 wantedBy = [ "multi-user.target" ]; 330 331 unitConfig = { 332 Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)"; 333 }; 334 335 preStart = '' 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} \ 341 ${runConfig} 342 '' } 343 chmod 640 ${runConfig} 344 ''; 345 serviceConfig = { 346 Type = "simple"; 347 ExecStart = "${pkgs.coturn}/bin/turnserver -c ${runConfig}"; 348 RuntimeDirectory = "turnserver"; 349 User = "turnserver"; 350 Group = "turnserver"; 351 AmbientCapabilities = 352 mkIf ( 353 cfg.listening-port < 1024 || 354 cfg.alt-listening-port < 1024 || 355 cfg.tls-listening-port < 1024 || 356 cfg.alt-tls-listening-port < 1024 || 357 cfg.min-port < 1024 358 ) "cap_net_bind_service"; 359 Restart = "on-abort"; 360 }; 361 }; 362 systemd.tmpfiles.rules = [ 363 "d /run/coturn 0700 turnserver turnserver - -" 364 ]; 365 }])); 366}