Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions maintainers/maintainer-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6631,6 +6631,11 @@
githubId = 2096594;
email = "Dietrich@Daroch.me";
};
different-error = {
name = "Sanfer D'souza";
github = "different-error";
githubId = 9338001;
};
different-name = {
name = "different-name";
email = "hello@different-name.dev";
Expand Down
3 changes: 3 additions & 0 deletions nixos/doc/manual/redirects.json
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,9 @@
"sec-kubernetes": [
"index.html#sec-kubernetes"
],
"module-services-nordvpn": [
"index.html#module-services-nordvpn"
],
"ch-running": [
"index.html#ch-running"
],
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2605.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
designed to run on affordable, low-power devices. Available as [services.meshtasticd]
(#opt-services.meshtasticd.enable).

- [NordVPN](https://github.com/NordSecurity/nordvpn-linux), a NordVPN client for linux. Available as [services.nordvpn](options.html#opt-services.nordvpn.enable).

- [knot-resolver](https://www.knot-resolver.cz/) in version 6. Available as `services.knot-resolver`. A module for knot-resolver 5 was already available as `services.kresd`.

- [ImmichFrame](https://immichframe.dev/), display your photos from Immich as a digital photo frame. Available as `services.immichframe`.
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,7 @@
./services/networking/nncp.nix
./services/networking/nntp-proxy.nix
./services/networking/nomad.nix
./services/networking/nordvpn.nix
./services/networking/nsd.nix
./services/networking/ntopng.nix
./services/networking/ntp/chrony.nix
Expand Down
65 changes: 65 additions & 0 deletions nixos/modules/services/networking/nordvpn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# NordVPN {#module-services-nordvpn}

*Source:* {file}`modules/services/networking/nordvpn.nix`

*Upstream documentation:* <https://github.com/NordSecurity/nordvpn-linux>

NordVPN offers a paid virtual private network (VPN) service.
The service operates as closed-source,
but the Linux client uses open-source code licensed under GPLv3.
A minimal configuration in NixOS appears as follows:

```nix
{
services.nordvpn.enable = true;
networking.firewall.enable = true;
networking.firewall.checkReversePath = "loose";
}
```

When using a firewall, set `networking.firewall.checkReversePath` to `"loose"` or `false`.
NordVPN includes a `kill-switch` feature that blocks all packets not associated with the VPN connection.

Additionally, add your user to the `nordvpn` group.

```nix
{
users.users.yourUser = {
#..
extraGroups = [
#..
"nordvpn"
];
};
}
```

If you prefer to use your own user and group, you can do so using

```nix
{
services.nordvpn.user = "SOME-USER";
services.nordvpn.group = "SOME-GROUP";
}
```

NordVPN provides several useful CLI commands, including:

```bash
nordvpn login # Log in using an OAuth URL
nordvpn login --token <token> # Log in with a token obtained from your NordVPN account
nordvpn c # Connect to the VPN
nordvpn c ie # Connect to a NordVPN server in Ireland
nordvpn d # Disconnect from the VPN
nordvpn set technology openvpn # Switch to OpenVPN technology
nordvpn c # Reconnect after changing technology
```

Additionally, if you prefer to use the friendly GUI,

```bash
nordvpn-gui
```

**Disclaimer:** NixOS currently does not support meshnet.
Contributions welcome!
171 changes: 171 additions & 0 deletions nixos/modules/services/networking/nordvpn.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.nordvpn;
defaultUser = "nordvpn";
defaultGroup = "nordvpn";

nordvpn =
let
cli = cfg.package.cli.overrideAttrs (old: {
preBuild =
let
extraPreBuild = lib.optionalString (cfg.group != defaultGroup) ''
substituteInPlace internal/permissions.go \
--replace-fail \
'string{"nordvpn"}' \
'string{"${cfg.group}"}'

substituteInPlace internal/constants.go \
--replace-fail \
'NordvpnGroup = "nordvpn"' \
'NordvpnGroup = "${cfg.group}"'
'';
in
extraPreBuild + (old.preBuild or "");

# postFixup wraps nordvpnd so that it can find binaries that it calls.
# here, instead, use systemd to update the path to those binaries.
postFixup = "";
});
in
pkgs.symlinkJoin {
inherit (cfg.package) pname version meta;
paths = [
cli
cfg.package.gui
];
};
in
{
options.services.nordvpn = {
enable = lib.mkEnableOption "Enable NordVPN";
package = lib.mkPackageOption pkgs "nordvpn" { };
user = lib.mkOption {
type = lib.types.str;
default = defaultUser;
description = ''
The User that owns the `nordvpnd` systemd process.
If overriding the default, a user with the same name must exist.
'';
};
group = lib.mkOption {
type = lib.types.str;
default = defaultGroup;
description = ''
The Group that owns the `nordvpnd` systemd process.
If overriding the default, a group with the same name must exist.
'';
};
};

config = lib.mkIf cfg.enable {

# create the default user if that's the one used
users.users = lib.mkIf (cfg.user == defaultUser) {
${defaultUser} = {
description = "User that runs `nordvpnd`.";
group = cfg.group;
isSystemUser = true;
};
};

# create the default group if that's the one used
users.groups = lib.mkIf (cfg.group == defaultGroup) {
${defaultGroup} = { };
};

# nordvpnd uses resolved to configure dns
services.resolved.enable = true;

# policy that allows nordvpnd to configure dns
security.polkit = {
enable = true;
extraConfig = ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.resolve1.set-dns-servers"
&& subject.isInGroup("${cfg.group}")) {
return polkit.Result.YES;
}
});
'';
};

environment.systemPackages = [
nordvpn
];

systemd.services.nordvpnd = {
after = [ "network-online.target" ];
description = "NordVPN daemon.";
path = (
with pkgs;
[
e2fsprogs
iproute2
iptables
libxslt
procps
wireguard-tools
]
++ [ nordvpn ]
);
serviceConfig = {
# nordvpnd needs CAP_NET_ADMIN to configure network interfaces
AmbientCapabilities = "CAP_NET_ADMIN";
CapabilityBoundingSet = "CAP_NET_ADMIN";
ExecStart = lib.getExe' nordvpn "nordvpnd";
Group = cfg.group;
KillMode = "process";
NonBlocking = true;
Requires = "nordvpnd.socket";
Restart = "on-failure";
RestartSec = 5;
RuntimeDirectory = "nordvpn";
RuntimeDirectoryMode = "0750";
StateDirectory = "nordvpn";
StateDirectoryMode = "0750";
User = cfg.user;
};
wantedBy = [ "default.target" ];
wants = [ "network-online.target" ];
};

systemd.sockets.nordvpnd = {
description = "NordVPN Daemon Socket";
listenStreams = [ "/run/nordvpn/nordvpnd.sock" ];
partOf = [ "nordvpnd.service" ];
socketConfig = {
DirectoryMode = "0750";
NoDelay = true;
SocketGroup = cfg.group;
SocketMode = "0770";
SocketUser = cfg.user;
};
wantedBy = [ "sockets.target" ];
};

systemd.user.services.norduserd = {
after = [ "network-online.target" ];
description = "NordUserD Service";
serviceConfig = {
ExecStart = lib.getExe' nordvpn "norduserd";
NonBlocking = true;
Restart = "on-failure";
RestartSec = 5;
};
wantedBy = [ "graphical-session.target" ];
wants = [ "network-online.target" ];
};

};

meta = {
doc = ./nordvpn.md;
maintainers = with lib.maintainers; [ different-error ];
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,7 @@ in
nominatim = runTest ./nominatim.nix;
non-default-filesystems = handleTest ./non-default-filesystems.nix { };
non-switchable-system = runTest ./non-switchable-system.nix;
nordvpn = runTest ./nordvpn.nix;
noto-fonts = runTest ./noto-fonts.nix;
noto-fonts-cjk-qt-default-weight = runTest ./noto-fonts-cjk-qt-default-weight.nix;
novacomd = handleTestOn [ "x86_64-linux" ] ./novacomd.nix { };
Expand Down
Loading
Loading