Unverified Commit 689b9903 authored by Franz Pletz's avatar Franz Pletz Committed by GitHub
Browse files

nixos/tailscale-derper: init (#306533)

parents ae4102c9 d82d6897
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1231,6 +1231,7 @@
  ./services/networking/syncthing.nix
  ./services/networking/tailscale.nix
  ./services/networking/tailscale-auth.nix
  ./services/networking/tailscale-derper.nix
  ./services/networking/tayga.nix
  ./services/networking/tcpcrypt.nix
  ./services/networking/teamspeak3.nix
+132 −0
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  ...
}:

let
  cfg = config.services.tailscale.derper;
in
{
  meta.maintainers = with lib.maintainers; [ SuperSandro2000 ];

  options = {
    services.tailscale.derper = {
      enable = lib.mkEnableOption "Tailscale Derper. See upstream doc <https://tailscale.com/kb/1118/custom-derp-servers> how to configure it on clients";

      domain = lib.mkOption {
        type = lib.types.str;
        description = "Domain name under which the derper server is reachable.";
      };

      openFirewall = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = ''
          Whether to open the firewall for the specified port.
          Derper requires the used ports to be opened, otherwise it doesn't work as expected.
        '';
      };

      package = lib.mkPackageOption pkgs [
        "tailscale"
        "derper"
      ] { };

      stunPort = lib.mkOption {
        type = lib.types.port;
        default = 3478;
        description = ''
          STUN port to listen on.
          See online docs <https://tailscale.com/kb/1118/custom-derp-servers#prerequisites> on how to configure a different external port.
        '';
      };

      port = lib.mkOption {
        type = lib.types.port;
        default = 8010;
        description = "The port the derper process will listen on. This is not the port tailscale will connect to.";
      };

      verifyClients = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = ''
          Whether to verify clients against a locally running tailscale daemon if they are allowed to connect to this node or not.
        '';
      };
    };
  };

  config = lib.mkIf cfg.enable {
    networking.firewall = lib.mkIf cfg.openFirewall {
      # port 80 and 443 are opened by nginx already
      allowedUDPPorts = [ cfg.stunPort ];
    };

    services = {
      nginx = {
        enable = true;
        upstreams.tailscale-derper = {
          servers."127.0.0.1:${toString cfg.port}" = { };
          extraConfig = ''
            keepalive 64;
          '';
        };
        virtualHosts."${cfg.domain}" = {
          addSSL = true; # this cannot be forceSSL as derper sends some information over port 80, too.
          locations."/" = {
            proxyPass = "http://tailscale-derper";
            proxyWebsockets = true;
            extraConfig = ''
              keepalive_timeout 0;
              proxy_buffering off;
            '';
          };
        };
      };

      tailscale.enable = lib.mkIf cfg.verifyClients true;
    };

    systemd.services.tailscale-derper = {
      serviceConfig = {
        ExecStart =
          "${lib.getExe' cfg.package "derper"} -a :${toString cfg.port} -c /var/lib/derper/derper.key -hostname=${cfg.domain} -stun-port ${toString cfg.stunPort}"
          + lib.optionalString cfg.verifyClients " -verify-clients";
        DynamicUser = true;
        Restart = "always";
        RestartSec = "5sec"; # don't crash loop immediately
        StateDirectory = "derper";
        Type = "simple";

        CapabilityBoundingSet = [ "" ];
        DeviceAllow = null;
        LockPersonality = true;
        NoNewPrivileges = true;
        MemoryDenyWriteExecute = true;
        PrivateDevices = true;
        PrivateUsers = true;
        ProcSubset = "pid";
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
          "AF_UNIX"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [ "@system-service" ];
      };
      wantedBy = [ "multi-user.target" ];
    };
  };
}
+4 −1
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ buildGoModule {
  pname = "tailscale";
  inherit version;

  outputs = [ "out" "derper" ];

  src = fetchFromGitHub {
    owner = "tailscale";
    repo = "tailscale";
@@ -43,7 +45,7 @@ buildGoModule {

  CGO_ENABLED = 0;

  subPackages = [ "cmd/tailscaled" ];
  subPackages = [ "cmd/derper" "cmd/tailscaled" ];

  ldflags = [
    "-w"
@@ -60,6 +62,7 @@ buildGoModule {

  postInstall = ''
    ln -s $out/bin/tailscaled $out/bin/tailscale
    moveToOutput "bin/derper" "$derper"
  '' + lib.optionalString stdenv.hostPlatform.isLinux ''
    wrapProgram $out/bin/tailscaled \
      --prefix PATH : ${lib.makeBinPath [ iproute2 iptables getent shadow ]} \