NordSecurity / nordvpn-linux

NordVPN Linux client
GNU General Public License v3.0
307 stars 44 forks source link

Request: please support NixOS #355

Open getreu opened 7 months ago

getreu commented 7 months ago

Please support NixOS. It is an one time effort, then it becomes an effortless sure-fire success.

Some exploratory work: NordVPN · Issue #101864

sarahec commented 6 months ago

I'm attempting building from source. @keliramu I may have questions for you from time to time, but most of the help will need to come from other nix package maintainers.

Blatzar commented 1 day ago

I managed to write a nix package to compile from source, the nix package can be contributed to this project and built locally using src = ./. and $ nix-build -E 'with import <nixpkgs> {}; callPackage ./default.nix {}'


default.nix in the project code or (something like) nordvpn.nix in your os config:

{ pkgs, lib, gcc, autoPatchelfHook, ... }:

let
  patchedPkgs = pkgs.appendOverlays [
    (final: prev: {
      # Nordvpn uses a patched openvpn in order to perform xor obfuscation
      # See https://github.com/NordSecurity/nordvpn-linux/blob/e614303aaaf1a64fde5bb1b4de1a7863b22428c4/ci/openvpn/check_dependencies.sh
      openvpn = prev.openvpn.overrideAttrs (old: {
        patches = (old.patches or [ ]) ++ [
          (prev.fetchpatch {
            url =
              "https://github.com/Tunnelblick/Tunnelblick/raw/master/third_party/sources/openvpn/openvpn-${old.version}/patches/02-tunnelblick-openvpn_xorpatch-a.diff";
            hash = "sha256-b9NiWETc0g2a7FNwrLaNrWx7gfCql7VTbewFu3QluFk=";
          })
          (prev.fetchpatch {
            url =
              "https://github.com/Tunnelblick/Tunnelblick/raw/master/third_party/sources/openvpn/openvpn-${old.version}/patches/03-tunnelblick-openvpn_xorpatch-b.diff";
            hash = "sha256-X/SshB/8ItLFBx6TPhjBwyA97ra0iM2KgsGqGIy2s9I=";
          })
          (prev.fetchpatch {
            url =
              "https://github.com/Tunnelblick/Tunnelblick/raw/master/third_party/sources/openvpn/openvpn-${old.version}/patches/04-tunnelblick-openvpn_xorpatch-c.diff";
            hash = "sha256-fw0CxJGIFEydIVRVouTlD1n275eQcbejUdhrU1JAx7g=";
          })
          (prev.fetchpatch {
            url =
              "https://github.com/Tunnelblick/Tunnelblick/raw/master/third_party/sources/openvpn/openvpn-${old.version}/patches/05-tunnelblick-openvpn_xorpatch-d.diff";
            hash = "sha256-NLRtoRVz+4hQcElyz4elCAv9l1vp4Yb3/VJef+L/FZo=";
          })
          (prev.fetchpatch {
            url =
              "https://github.com/Tunnelblick/Tunnelblick/raw/master/third_party/sources/openvpn/openvpn-${old.version}/patches/06-tunnelblick-openvpn_xorpatch-e.diff";
            hash = "sha256-mybdjCIT9b6ukbGWYvbr74fKtcncCtTvS5xSVf92T6Y=";
          })
        ];
      });
    })
  ];
  nordvpn = pkgs.buildGoModule rec {
    pname = "nordvpn";
    version = "3.19.0";

    #src = ./.;
    src = pkgs.fetchFromGitHub {
      owner = "NordSecurity";
      repo = "nordvpn-linux";
      rev = "e614303aaaf1a64fde5bb1b4de1a7863b22428c4";
      sha256 = "sha256-uIzG9QIVwax0Cop2VuDzy033efEBudFnGNj7osT/x2g";
    };

    nativeBuildInputs = with pkgs; [ pkg-config gcc ];

    buildInputs = with pkgs; [ libxml2 gcc ];

    vendorHash = "sha256-h5G5J/Sw0277pDzVXT6b3BX0KUbtyN8ujITfYp5PmgE";

    ldflags = [
      "-X main.Version=${version}"
      "-X main.Environment=dev"
      "-X main.Salt=development"
      "-X main.Hash=${src.rev}"
    ];

    buildPhase = ''
      runHook preBuild
      echo "Building nordvpn CLI..."
      export LDFLAGS="${builtins.concatStringsSep " " ldflags}"
      go build -ldflags "$LDFLAGS" -o bin/nordvpn ./cmd/cli

      echo "Building nordvpn user..."
      go build -ldflags "$LDFLAGS" -o bin/norduserd ./cmd/norduser

      # Fix missing include in a library preventing compilation
      chmod +w vendor/github.com/jbowtie/gokogiri/xpath/
      sed -i '6i#include <stdlib.h>' vendor/github.com/jbowtie/gokogiri/xpath/expression.go

      echo "Building nordvpn daemon..."
      go build -ldflags "$LDFLAGS" -o bin/nordvpnd ./cmd/daemon
      runHook postBuild
    '';

    installPhase = ''
      runHook preInstall

      mkdir -p $out/lib/nordvpn/
      mv bin/norduserd $out/lib/nordvpn/
      ln -s ${patchedPkgs.openvpn}/bin/openvpn $out/lib/nordvpn/openvpn
      ln -s ${pkgs.wireguard-tools}/bin/wg $out/lib/nordvpn/wg

      # Nordvpn needs icons for the system tray and notifications
      mkdir -p $out/share/icons/hicolor/scalable/apps
      cp assets/icon.svg $out/share/icons/hicolor/scalable/apps/nordvpn.svg # Does not follow naming convention
      nordvpn_asset_prefix="nordvpn-" # hardcoded image prefix
      for file in assets/*; do
        cp "$file" "$out/share/icons/hicolor/scalable/apps/''\${nordvpn_asset_prefix}$(basename "$file")"
      done

      mkdir -p $out/bin
      cp bin/* $out/bin

      runHook postInstall
    '';

    meta = with pkgs.lib; {
      description = "NordVPN CLI and daemon application for Linux";
      homepage = "https://github.com/nordsecurity/nordvpn-linux";
      mainProgram = "nordvpn";
      license = licenses.gpl3;
      platforms = platforms.linux;
    };
  };
in pkgs.buildFHSEnv {
  name = "nordvpnd";
  targetPkgs = with pkgs;
    pkgs: [
      nordvpn
      sysctl
      iptables
      iproute2
      procps
      cacert
      libxml2
      libidn2
      zlib
      wireguard-tools
      patchedPkgs.openvpn
      e2fsprogs # for chattr
    ];

  extraInstallCommands = ''
    mkdir -p $out/bin/
    printf "#!${pkgs.bash}/bin/bash\n${nordvpn}/bin/nordvpn \"\$@\"" > $out/bin/nordvpn
    chmod +x $out/bin/nordvpn
  '';

  runScript = ''
    ${nordvpn}/bin/nordvpnd
  '';
}

The daemon can then be activated using this in your os config:

  systemd = {
    services.nordvpn = {
      description = "NordVPN daemon.";
      serviceConfig = {
        ExecStart = "${pkgs.nordvpn}/bin/nordvpnd";
        ExecStartPre = ''
          ${pkgs.bash}/bin/bash -c '\
            mkdir -m 700 -p /var/lib/nordvpn; \
            if [ -z "$(ls -A /var/lib/nordvpn)" ]; then \
              cp -r ${pkgs.nordvpn}/var/lib/nordvpn/* /var/lib/nordvpn; \
            fi'
        '';
        NonBlocking = true;
        KillMode = "process";
        Restart = "on-failure";
        RestartSec = 5;
        RuntimeDirectory = "nordvpn";
        RuntimeDirectoryMode = "0750";
        Group = "nordvpn";
      };
      wantedBy = [ "multi-user.target" ];
      #after = [ "network-online.target" ];
      #wants = [ "network-online.target" ];
    };
  };

If you are having issues importing the package I found it easiest just appending this to the os config:

  nixpkgs.config.packageOverrides = pkgs: {
    nordvpn = pkgs.callPackage ./path-to-your-nordvpn.nix { inherit lib; };
  };

It's probably not the best solution, but it's easy and works :shrug:

sarahec commented 1 day ago

That's beautiful. How would you feel about adding it to nixos/nixpkgs (with plenty of support)? It's about 95% of the way there.

Blatzar commented 1 day ago

That would be great! Unfortunately I am not at all well versed in the PR process to get it into nixpkgs (this is my first package). Feel free to copy the code to make a PR though, just make sure to co-author me :+1:

Perhaps @LuisChDev might be interested. I used your package before this :heart:

getreu commented 12 hours ago

@Blatzar I tested your solution. The package compiles, but I can not start the service:

building the system configuration...
stopping the following units: nordvpn.service
activating the configuration...
setting up /etc...
reloading user units for getreu...
restarting sysinit-reactivation.target
starting the following units: nordvpn.service
warning: the following units failed: nordvpn.service

● nordvpn.service - NordVPN daemon.
     Loaded: loaded (/etc/systemd/system/nordvpn.service; enabled; preset: enabled)
     Active: activating (auto-restart) (Result: exit-code) since Sun 2024-11-17 09:00:51 EET; 143ms ago
    Process: 420641 ExecStartPre=/nix/store/syl4snn859kpqvn9qh91kr7n9i4dws04-bash-5.2p32/bin/bash -c    mkdir -m 700 -p /var/lib/nordvpn;    if [ -z "$(ls -A /var/lib/nordvpn)" ]; then      cp -r /nix/store/30h9kdmxcamma5b83507mbwyhvcd35aw-nordvpnd/var/lib/nordvpn/* /var/lib/nordvpn;    fi (code=exited, status=0/SUCCESS)
    Process: 420646 ExecStart=/nix/store/30h9kdmxcamma5b83507mbwyhvcd35aw-nordvpnd/bin/nordvpnd (code=exited, status=1/FAILURE)
   Main PID: 420646 (code=exited, status=1/FAILURE)
         IP: 0B in, 0B out
        CPU: 83ms
warning: error(s) occurred while switching to the new configuration

$ sudo /nix/store/30h9kdmxcamma5b83507mbwyhvcd35aw-nordvpnd/bin/nordvpnd
2024/11/17 09:01:49 [Info] Daemon has started
2024/11/17 09:01:49 cipher: message authentication failed
2024/11/17 09:01:49 cipher: message authentication failed

Any ideas?

Blatzar commented 9 hours ago

@getreu Your issue is likely due to the changed salt. See https://github.com/NordSecurity/nordvpn-linux/blob/main/BUILD.md?plain=1#L65-L68 I do not know what salt the debian package is compiled with, so you need to remove your old /var/lib/nordvpn and let it regenerate. This also means your need to login and set all your settings again.