at master 4.1 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 configureNginx = lib.mkOption { 24 type = lib.types.bool; 25 default = true; 26 description = '' 27 Whether to enable nginx reverse proxy for derper. 28 When enabled, nginx will proxy requests to the derper service. 29 ''; 30 }; 31 32 openFirewall = lib.mkOption { 33 type = lib.types.bool; 34 default = true; 35 description = '' 36 Whether to open the firewall for the specified port. 37 Derper requires the used ports to be opened, otherwise it doesn't work as expected. 38 ''; 39 }; 40 41 package = lib.mkPackageOption pkgs [ 42 "tailscale" 43 "derper" 44 ] { }; 45 46 stunPort = lib.mkOption { 47 type = lib.types.port; 48 default = 3478; 49 description = '' 50 STUN port to listen on. 51 See online docs <https://tailscale.com/kb/1118/custom-derp-servers#prerequisites> on how to configure a different external port. 52 ''; 53 }; 54 55 port = lib.mkOption { 56 type = lib.types.port; 57 default = 8010; 58 description = "The port the derper process will listen on. This is not the port tailscale will connect to."; 59 }; 60 61 verifyClients = lib.mkOption { 62 type = lib.types.bool; 63 default = false; 64 description = '' 65 Whether to verify clients against a locally running tailscale daemon if they are allowed to connect to this node or not. 66 ''; 67 }; 68 }; 69 }; 70 71 config = lib.mkIf cfg.enable { 72 networking.firewall = lib.mkIf cfg.openFirewall { 73 # port 80 and 443 are opened by nginx already when configureNginx is true 74 allowedUDPPorts = [ cfg.stunPort ]; 75 }; 76 77 services = { 78 nginx = lib.mkIf cfg.configureNginx { 79 enable = true; 80 virtualHosts."${cfg.domain}" = { 81 addSSL = true; # this cannot be forceSSL as derper sends some information over port 80, too. 82 locations."/" = { 83 proxyPass = "http://127.0.0.1:${toString cfg.port}"; 84 proxyWebsockets = true; 85 extraConfig = # nginx 86 '' 87 proxy_buffering off; 88 proxy_read_timeout 3600s; 89 ''; 90 }; 91 }; 92 }; 93 94 tailscale.enable = lib.mkIf cfg.verifyClients true; 95 }; 96 97 systemd.services.tailscale-derper = { 98 serviceConfig = { 99 ExecStart = 100 "${lib.getExe' cfg.package "derper"} -a :${toString cfg.port} -c /var/lib/derper/derper.key -hostname=${cfg.domain} -stun-port ${toString cfg.stunPort}" 101 + lib.optionalString cfg.verifyClients " -verify-clients"; 102 DynamicUser = true; 103 Restart = "always"; 104 RestartSec = "5sec"; # don't crash loop immediately 105 StateDirectory = "derper"; 106 Type = "simple"; 107 108 CapabilityBoundingSet = [ "" ]; 109 DeviceAllow = null; 110 LockPersonality = true; 111 NoNewPrivileges = true; 112 MemoryDenyWriteExecute = true; 113 PrivateDevices = true; 114 PrivateUsers = true; 115 ProcSubset = "pid"; 116 ProtectClock = true; 117 ProtectControlGroups = true; 118 ProtectHostname = true; 119 ProtectKernelLogs = true; 120 ProtectKernelModules = true; 121 ProtectKernelTunables = true; 122 ProtectProc = "invisible"; 123 RestrictAddressFamilies = [ 124 "AF_INET" 125 "AF_INET6" 126 "AF_UNIX" 127 ]; 128 RestrictNamespaces = true; 129 RestrictRealtime = true; 130 SystemCallArchitectures = "native"; 131 SystemCallFilter = [ "@system-service" ]; 132 }; 133 wantedBy = [ "multi-user.target" ]; 134 }; 135 }; 136}