Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: please support NixOS #355

Open
getreu opened this issue Apr 10, 2024 · 10 comments
Open

Request: please support NixOS #355

getreu opened this issue Apr 10, 2024 · 10 comments
Labels
enhancement New feature or request

Comments

@getreu
Copy link

getreu commented Apr 10, 2024

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

Some exploratory work: NordVPN · Issue #101864

@keliramu keliramu added the enhancement New feature or request label Apr 18, 2024
@sarahec
Copy link

sarahec commented May 14, 2024

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
Copy link

Blatzar commented Nov 16, 2024

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 🤷

@sarahec
Copy link

sarahec commented Nov 16, 2024

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
Copy link

Blatzar commented Nov 16, 2024

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 👍

Perhaps @LuisChDev might be interested. I used your package before this ❤️

@getreu
Copy link
Author

getreu commented Nov 17, 2024

@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
Copy link

Blatzar commented Nov 17, 2024

@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.

@getreu
Copy link
Author

getreu commented Nov 18, 2024

@Blatzar thank you for your hint. I tried, but it did not solve my problem. I still can not start the nordvpn service.

@getreu
Copy link
Author

getreu commented Nov 19, 2024

@Blatzar Could you post a minimal configuration.nix that I can debug mine?

@Blatzar
Copy link

Blatzar commented Nov 19, 2024

@getreu

I am absolutely positive its the hash, I can replicate your error by changing the hash myself, but if you want to debug go ahead, I've made it easy with a single config file. I built a vm with this config using nixos-rebuild build-vm -I nixos-config=/path/to/this/configuration.nix --no-flake
and it works fine

Stuff to note:

Username and password is "alice"
Make sure you change the VM specs to what your computer can handle
You might need to use nordvpn click <yourloginlink> if the click to login doesnt work in firefox
You might need to start nordvpnd as root once to let it generate the data files, not sure why the service doesn't

configuration.nix

# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).

{ config, lib, pkgs, ... }:

{
  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  nixpkgs.config.packageOverrides = pkgs: {
    nordvpn = pkgs.callPackage ({ 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
            mkdir -p $out/share/icons/hicolor/scalable/apps
            nordvpn_asset_prefix="nordvpn-" # hardcoded image prefix
            cp assets/icon.svg $out/share/icons/hicolor/scalable/apps/nordvpn.svg # Does not follow convention
            for file in assets/*.svg; 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
        '';
      }) { inherit lib; };
  };

  # networking.hostName = "nixos"; # Define your hostname.
  # Pick only one of the below networking options.
  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
  # networking.networkmanager.enable = true;  # Easiest to use and most distros use this by default.

  # Set your time zone.
  # time.timeZone = "Europe/Amsterdam";

  # Configure network proxy if necessary
  # networking.proxy.default = "http://user:password@proxy:port/";
  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

  # Select internationalisation properties.
  # i18n.defaultLocale = "en_US.UTF-8";
  # console = {
  #   font = "Lat2-Terminus16";
  #   keyMap = "us";
  #   useXkbConfig = true; # use xkb.options in tty.
  # };

  # Enable the X11 windowing system.
  services.xserver.enable = true;

  # Enable the Plasma 5 Desktop Environment.
  services.displayManager.sddm.enable = true;
  services.xserver.desktopManager.plasma5.enable = true;

  # Configure keymap in X11
  # services.xserver.xkb.layout = "us";
  # services.xserver.xkb.options = "eurosign:e,caps:escape";

  # Enable CUPS to print documents.
  # services.printing.enable = true;

  # Enable sound.
  # hardware.pulseaudio.enable = true;
  # OR
  # services.pipewire = {
  #   enable = true;
  #   pulse.enable = true;
  # };

  # Enable touchpad support (enabled default in most desktopManager).
  # services.libinput.enable = true;

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.groups.nordvpn.gid = 1000;
  users.users.alice = {
    password = "alice";
    isNormalUser = true;
    extraGroups = [ "wheel" "nordvpn" ]; # Enable ‘sudo’ for the user.
    packages = with pkgs; [ firefox tree ];
  };

  # Nordvpn doesnt like when you dont have a dns
  networking.nameservers = [ "1.1.1.1" "1.0.0.1" ];
  services.resolved = {
    enable = true;
    dnssec = "true";
    domains = [ "~." ];
    fallbackDns = [ "1.1.1.1" "1.0.0.1" ];
    dnsovertls = "true";
  };

  # Disable the firewall to prevent fuckery with nordvpn rules
  # It is possible to use it at the same time but I have spent enough time on this config as is
  networking.firewall.enable = false;

  # Change this according to your specs
  virtualisation.vmVariant = {
    # following configuration is added only when building VM with build-vm
    virtualisation = {
      memorySize = 8000; # Use 8000MiB memory.
      cores = 4;
    };
  };

  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" ];
    };
    services.NetworkManager-wait-online.enable = lib.mkForce false;
  };

  # List packages installed in system profile. To search, run:
  # $ nix search wget
  environment.systemPackages = with pkgs; [
    vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
    wget
    nordvpn
  ];

  # Some programs need SUID wrappers, can be configured further or are
  # started in user sessions.
  # programs.mtr.enable = true;
  # programs.gnupg.agent = {
  #   enable = true;
  #   enableSSHSupport = true;
  # };

  # List services that you want to enable:

  # Enable the OpenSSH daemon.
  # services.openssh.enable = true;

  # Open ports in the firewall.
  # networking.firewall.allowedTCPPorts = [ ... ];
  # networking.firewall.allowedUDPPorts = [ ... ];
  # Or disable the firewall altogether.
  # networking.firewall.enable = false;

  # Copy the NixOS configuration file and link it from the resulting system
  # (/run/current-system/configuration.nix). This is useful in case you
  # accidentally delete configuration.nix.
  # system.copySystemConfiguration = true;

  # This option defines the first version of NixOS you have installed on this particular machine,
  # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
  #
  # Most users should NEVER change this value after the initial install, for any reason,
  # even if you've upgraded your system to a new NixOS release.
  #
  # This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
  # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
  # to actually do that.
  #
  # This value being lower than the current NixOS release does NOT mean your system is
  # out of date, out of support, or vulnerable.
  #
  # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
  # and migrated your data accordingly.
  #
  # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
  system.stateVersion = "24.05"; # Did you read the comment?

}

Working picture

image

@getreu
Copy link
Author

getreu commented Nov 30, 2024

Maybe my problem is, that I have a flake based configuration? Everything compiles fine, but I still can not start the service:

sudo nordvpnd
2024/11/30 14:04:01 [Info] Daemon has started
2024/11/30 14:04:01 cipher: message authentication failed
2024/11/30 14:04:01 cipher: message authentication failed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants