at 25.11-pre 3.8 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 cfg = config.services.tailscale.derper; 10in 11{ 12 meta.maintainers = with lib.maintainers; [ SuperSandro2000 ]; 13 14 options = { 15 services.tailscale.derper = { 16 enable = lib.mkEnableOption "Tailscale Derper. See upstream doc <https://tailscale.com/kb/1118/custom-derp-servers> how to configure it on clients"; 17 18 domain = lib.mkOption { 19 type = lib.types.str; 20 description = "Domain name under which the derper server is reachable."; 21 }; 22 23 openFirewall = lib.mkOption { 24 type = lib.types.bool; 25 default = true; 26 description = '' 27 Whether to open the firewall for the specified port. 28 Derper requires the used ports to be opened, otherwise it doesn't work as expected. 29 ''; 30 }; 31 32 package = lib.mkPackageOption pkgs [ 33 "tailscale" 34 "derper" 35 ] { }; 36 37 stunPort = lib.mkOption { 38 type = lib.types.port; 39 default = 3478; 40 description = '' 41 STUN port to listen on. 42 See online docs <https://tailscale.com/kb/1118/custom-derp-servers#prerequisites> on how to configure a different external port. 43 ''; 44 }; 45 46 port = lib.mkOption { 47 type = lib.types.port; 48 default = 8010; 49 description = "The port the derper process will listen on. This is not the port tailscale will connect to."; 50 }; 51 52 verifyClients = lib.mkOption { 53 type = lib.types.bool; 54 default = false; 55 description = '' 56 Whether to verify clients against a locally running tailscale daemon if they are allowed to connect to this node or not. 57 ''; 58 }; 59 }; 60 }; 61 62 config = lib.mkIf cfg.enable { 63 networking.firewall = lib.mkIf cfg.openFirewall { 64 # port 80 and 443 are opened by nginx already 65 allowedUDPPorts = [ cfg.stunPort ]; 66 }; 67 68 services = { 69 nginx = { 70 enable = true; 71 virtualHosts."${cfg.domain}" = { 72 addSSL = true; # this cannot be forceSSL as derper sends some information over port 80, too. 73 locations."/" = { 74 proxyPass = "http://127.0.0.1:${toString cfg.port}"; 75 proxyWebsockets = true; 76 extraConfig = '' 77 keepalive_timeout 0; 78 proxy_buffering off; 79 ''; 80 }; 81 }; 82 }; 83 84 tailscale.enable = lib.mkIf cfg.verifyClients true; 85 }; 86 87 systemd.services.tailscale-derper = { 88 serviceConfig = { 89 ExecStart = 90 "${lib.getExe' cfg.package "derper"} -a :${toString cfg.port} -c /var/lib/derper/derper.key -hostname=${cfg.domain} -stun-port ${toString cfg.stunPort}" 91 + lib.optionalString cfg.verifyClients " -verify-clients"; 92 DynamicUser = true; 93 Restart = "always"; 94 RestartSec = "5sec"; # don't crash loop immediately 95 StateDirectory = "derper"; 96 Type = "simple"; 97 98 CapabilityBoundingSet = [ "" ]; 99 DeviceAllow = null; 100 LockPersonality = true; 101 NoNewPrivileges = true; 102 MemoryDenyWriteExecute = true; 103 PrivateDevices = true; 104 PrivateUsers = true; 105 ProcSubset = "pid"; 106 ProtectClock = true; 107 ProtectControlGroups = true; 108 ProtectHostname = true; 109 ProtectKernelLogs = true; 110 ProtectKernelModules = true; 111 ProtectKernelTunables = true; 112 ProtectProc = "invisible"; 113 RestrictAddressFamilies = [ 114 "AF_INET" 115 "AF_INET6" 116 "AF_UNIX" 117 ]; 118 RestrictNamespaces = true; 119 RestrictRealtime = true; 120 SystemCallArchitectures = "native"; 121 SystemCallFilter = [ "@system-service" ]; 122 }; 123 wantedBy = [ "multi-user.target" ]; 124 }; 125 }; 126}