Self-host your own digital island
1{ config, pkgs, lib, ... }: 2 3with lib; 4let 5 cfg = config.eilean; 6 domain = config.networking.domain; 7 subdomain = "turn.${domain}"; 8 staticAuthSecretFile = "/run/coturn/static-auth-secret"; 9in { 10 options.eilean.turn = { enable = mkEnableOption "TURN server"; }; 11 12 config = mkIf cfg.turn.enable { 13 security.acme-eon.certs."${subdomain}" = lib.mkIf cfg.acme-eon { 14 group = "turnserver"; 15 reloadServices = [ "coturn" ]; 16 }; 17 18 services.coturn = let 19 certDir = if cfg.acme-eon then 20 config.security.acme-eon.certs.${subdomain}.directory 21 else 22 config.security.acme.certs.${subdomain}.directory; 23 in { 24 enable = true; 25 no-cli = true; 26 no-tcp-relay = true; 27 secure-stun = true; 28 use-auth-secret = true; 29 static-auth-secret-file = staticAuthSecretFile; 30 realm = subdomain; 31 relay-ips = with config.eilean; [ serverIpv4 serverIpv6 ]; 32 cert = "${certDir}/fullchain.pem"; 33 pkey = "${certDir}/key.pem"; 34 }; 35 36 systemd.services = { 37 coturn-static-auth-secret-generator = { 38 description = "Generate coturn static auth secret file"; 39 script = '' 40 if [ ! -f '${staticAuthSecretFile}' ]; then 41 umask 077 42 tr -dc A-Za-z0-9 </dev/urandom | head -c 32 > '${staticAuthSecretFile}' 43 chown ${config.systemd.services.coturn.serviceConfig.User}:${config.systemd.services.coturn.serviceConfig.Group} '${staticAuthSecretFile}' 44 fi 45 ''; 46 serviceConfig.Type = "oneshot"; 47 serviceConfig.RemainAfterExit = true; 48 }; 49 "coturn" = { 50 after = [ "coturn-static-auth-secret-generator.service" ] 51 ++ lib.lists.optional cfg.acme-eon "acme-eon-${subdomain}.service"; 52 requires = [ "coturn-static-auth-secret-generator.service" ]; 53 wants = lib.lists.optional cfg.acme-eon "acme-eon-${subdomain}.service"; 54 }; 55 }; 56 57 networking.firewall = with config.services.coturn; 58 let 59 turn-range = { 60 from = min-port; 61 to = max-port; 62 }; 63 stun-ports = [ 64 listening-port 65 tls-listening-port 66 # these are only used if server has more than one IP address (of the same family 67 #alt-listening-port 68 #alt-tls-listening-port 69 ]; 70 in { 71 allowedTCPPorts = stun-ports; 72 allowedTCPPortRanges = [ turn-range ]; 73 allowedUDPPorts = stun-ports; 74 allowedUDPPortRanges = [ turn-range ]; 75 }; 76 77 security.acme.certs.${config.services.coturn.realm} = 78 lib.mkIf (!cfg.acme-eon) { 79 postRun = 80 "systemctl reload nginx.service; systemctl restart coturn.service"; 81 group = "turnserver"; 82 }; 83 services.nginx.enable = lib.mkIf (!cfg.acme-eon) true; 84 services.nginx.virtualHosts = lib.mkIf (!cfg.acme-eon) { 85 "${config.services.coturn.realm}" = { 86 forceSSL = true; 87 enableACME = true; 88 }; 89 }; 90 users.groups."turnserver".members = 91 lib.mkIf (!cfg.acme-eon) [ config.services.nginx.user ]; 92 93 eilean.dns.enable = true; 94 eilean.services.dns.zones.${config.networking.domain}.records = [{ 95 name = "turn"; 96 type = "CNAME"; 97 value = cfg.domainName; 98 }]; 99 }; 100}