NixOS-WSL icon indicating copy to clipboard operation
NixOS-WSL copied to clipboard

Inclusion of wsl-vpnkit

Open terlar opened this issue 1 year ago • 11 comments

I have already implemented this and is wondering if it makes sense to include in this project.

Problem

Some VPN solutions causes problem when used together with WSL, there is wsl-vpnkit to address this issue.

Solution

wsl-vpnkit can be run within NixOS and requires the following:

Packing wsl-vpnkit:

{
  lib,
  stdenv,
  fetchFromGitHub,
  makeWrapper,
  # Runtime
  gvproxy,
  dnsutils,
  gawk,
  iproute2,
  iptables,
  iputils,
  wget,
}:

let
  gvproxyCross = gvproxy.overrideAttrs (_: {
    buildPhase = "make cross qemu-wrapper vm";
  });
in stdenv.mkDerivation rec {
  pname = "wsl-vpnkit";
  version = "0.4.1";

  src = fetchFromGitHub {
    owner = "sakai135";
    repo = "wsl-vpnkit";
    rev = "v${version}";
    sha256 = "sha256-Igbr3L2W32s4uBepllSz07bkbI3qwAKMZkBrXLqGrGA=";
  };

  nativeBuildInputs = [makeWrapper];

  postPatch = ''
    substituteInPlace wsl-vpnkit \
      --replace "/app/wsl-vm" "${gvproxyCross}/bin/vm" \
      --replace "/app/wsl-gvproxy.exe" "${gvproxyCross}/bin/gvproxy-windows.exe"
  '';

  installPhase = ''
    mkdir -p $out/bin
    cp wsl-vpnkit $out/bin
  '';

  postFixup = ''
    wrapProgram "$out/bin/wsl-vpnkit" \
      --prefix PATH : "${lib.makeBinPath [ dnsutils gawk iproute2 iptables iputils wget ]}"
  '';
}

Creating a systemd service:

{
  systemd.services.wsl-vpnkit = {
    enable = true;
    description = "wsl-vpnkit";
    after = [ "network.target" ];

    serviceConfig = {
      ExecStart = "${pkgs.wsl-vpnkit}/bin/wsl-vpnkit";
      Restart = "always";
      KillMode = "mixed";
    };
  };
}

terlar avatar Jun 19 '23 01:06 terlar

It looks like wsl-vpnkit could be useful for other distros with nix installed as well. I suggest you try to get the derivation merged into nixpkgs instead of here. Once it's in there, we could add the systemd service here

nzbr avatar Jun 19 '23 12:06 nzbr

As i'm on a coporate Laptop with Windows and VPN i'm very grateful for wsl-vpnkit :) Thanks for adding it to nixpkgs.

fabianpage avatar Aug 17 '23 07:08 fabianpage

Creating a systemd service:

{
  systemd.services.wsl-vpnkit = {
    enable = true;
    description = "wsl-vpnkit";
    after = [ "network.target" ];

    serviceConfig = {
      ExecStart = "${pkgs.wsl-vpnkit}/bin/wsl-vpnkit";
      Restart = "always";
      KillMode = "mixed";
    };
  };
}

adding wantedBy works:

    # see https://github.com/sakai135/wsl-vpnkit/blob/5084c6d/wsl-vpnkit.service
    systemd.services.wsl-vpnkit = {
      enable = true;
      description = "wsl-vpnkit";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];

      serviceConfig = {
        ExecStart = "${pkgs.wsl-vpnkit}/bin/wsl-vpnkit";
        Restart = "always";
        KillMode = "mixed";
      };
    };

573 avatar Aug 25 '23 11:08 573

I explicitly didn't add wantedBy as I'm not always on VPN and when I'm not on VPN it breaks certain things to have this running. For example SSH access to GitHub didn't work when enabled.

terlar avatar Aug 25 '23 14:08 terlar

Is this still needed? There are some experimental network options in the latest WSL which sound like they make wsl-vpnkit obsolete (I haven't used it myself, so I can't test)

nzbr avatar Oct 02 '23 19:10 nzbr

Yes potentially, but there is a while until we get there. I tried to test this new feature, but it requires a bleeding edge windows version which is not under my control where needed.

Please note: You need to be on a Windows Insiders version to use the new networking settings (Any channel of Windows Insiders will do, including release preview). If you see the "These are not supported" messages it means that your current Windows version doesn't have support, and you will need to upgrade. These features will eventually be coming to Windows 11 22H2.

terlar avatar Oct 03 '23 00:10 terlar

Friendly ping @terlar

As of

wsl.exe -v WSL-Version: 2.0.9.0 Kernelversion: 5.15.133.1-1 WSLg-Version: 1.0.59 MSRDC-Version: 1.2.4677 Direct3D-Version: 1.611.1-81528511 DXCore-Version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp Windows-Version: 10.0.19045.3570

❯ cat /etc/wsl.conf [automount] enabled=true mountFsTab=false options=metadata,uid=1000,gid=100 root=/mnt

[boot] command= systemd=true

[interop] appendWindowsPath=true enabled=true

[network] generateHosts=true generateResolvConf=true hostname=DANIELKNB1

[user] default=nixos

wsl-vpnkit stopped working.

... wsl-vpnkit[19026]: /nix/store/ifayrgnd020y38gssz3x4y3sld0sdry5-gvproxy-0.7.1/bin/gvproxy-windows.exe is not executable due to WSL interop settings or Windows permissions

(as in https://github.com/sakai135/wsl-vpnkit#wsl-gvproxyexe-is-not-executable-due-to-wsl-interop-settings-or-windows-permissions)

Which configuration would reenable it ? Currently I have boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; in my configuration.nix

EDIT: Running directly though does seem to prove it is still executable

❯ /nix/store/ifayrgnd020y38gssz3x4y3sld0sdry5-gvproxy-0.7.1/bin/gvproxy-windows.exe time="2023-11-23T22:21:04+01:00" level=info msg="waiting for clients..."

573 avatar Nov 23 '23 21:11 573

@573 Yes, this is an issue with the newer WSL (since 2.0.5) requires you to use WSL_INTEROP environment variable. There is a patch to fix this here: https://github.com/sakai135/wsl-vpnkit/pull/250

I am currently running this override until this gets merged:

{
  lib,
  fetchFromGitHub,
  findutils,
  pstree,
  resholve,
  wsl-vpnkit,
}:
wsl-vpnkit.override {
  resholve =
    resholve
    // {
      mkDerivation = attrs @ {solutions, ...}:
        resholve.mkDerivation (lib.recursiveUpdate attrs {
          src = fetchFromGitHub {
            owner = "sakai135";
            repo = "wsl-vpnkit";
            rev = "28992229fedfa64979faa9ec84b1b4bcf5c8f449";
            sha256 = "sha256-6VKFUoPAhVOmORTGELZu00SnGmYSbumPOZ64giWq14Q=";
          };

          solutions.wsl-vpnkit = {
            inputs =
              solutions.wsl-vpnkit.inputs
              ++ [
                findutils
                pstree
              ];

            execer =
              solutions.wsl-vpnkit.execer
              ++ ["cannot:${pstree}/bin/pstree"];
          };
        });
    };
}

terlar avatar Nov 24 '23 13:11 terlar

Thank you so much, @terlar this now explains why I was able to even run

sudo -i /mnt/c/Windows/system32/wsl.exe -d wsl-vpnkit --cd /app ./wsl-vpnkit

or just plain

/mnt/c/Windows/system32/wsl.exe -d wsl-vpnkit --cd /app ./wsl-vpnkit

but not the exact same commands via systemd service. The env variable (set in NixOS-WSL correctly) was just not exported then.

Glad, you figred it out, even tried setting absolute paths and systemd.services.wsl-vpnkit.environment as suggested there and changed /etc/wsl.conf 🤣

573 avatar Nov 24 '23 13:11 573

I have tested the new options:

[wsl2]
networkingMode=mirrored
dnsTunneling=true

Unfortunately neither worked well with my company VPN and I still had to resort to wsl-vpnkit.

Since WSL 2.0.14, the mentioned issue with wsl-vpnkit has been solved. So WSL 2.0.5-2.0.13 are the problematic ones that can be used together with my mentioned patch. But for later versions it is not needed.

I have also added a custom service like this:

{
  systemd.services = {
    wsl-vpnkit-auto = {
      enable = cfg.autoVPN;
      description = "wsl-vpnkit";

      path = [pkgs.iputils];
      script = ''
        has_internet () {
          ping -q -w 1 -c 1 8.8.8.8 >/dev/null
        }

        has_company_network () {
          ping -q -w 1 -c 1 ${cfg.checkURL} >/dev/null
        }

        is_active_wsl-vpnkit () {
          systemctl is-active -q wsl-vpnkit.service
        }

        main () {
          if is_active_wsl-vpnkit; then
            if has_internet && ! has_company_network; then
              echo "Stopping wsl-vpnkit..."
              systemctl stop wsl-vpnkit.service
            fi
          else
            if ! has_internet; then
              echo "Starting wsl-vpnkit..."
              systemctl start wsl-vpnkit.service
            fi
          fi
        }

        while :
        do
          main
          sleep 5
        done
      '';

      wantedBy = ["multi-user.target"];
    };

    wsl-vpnkit = {
      enable = true;
      description = "wsl-vpnkit";

      serviceConfig = {
        ExecStart = "${pkgs.wsl-vpnkit}/bin/wsl-vpnkit";
        Type = "idle";
        Restart = "always";
        KillMode = "mixed";
      };
    };
  };
}

This way I can automatically connect to the wsl-vpnkit when needed and shut it down when off VPN. I can try to clean it up and make a PR if people think it is useful. At least for me it had made the VPN situation within WSL seemless.

terlar avatar Jan 17 '24 15:01 terlar

@terlar thank you so much! I would really appreciate a PR for this.

julius-boettger avatar Mar 26 '24 11:03 julius-boettger