Skip to content

nordvpn: add meshnet support - #564752

Closed
novalkun wants to merge 3 commits into
NixOS:masterfrom
novalkun:nordvpn-meshnet
Closed

novalkun wants to merge 3 commits into
NixOS:masterfrom
novalkun:nordvpn-meshnet

Conversation

@novalkun

@novalkun novalkun commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Upstream sources: nordvpn-linux 5.4.0, libtelio 6.2.4, libdrop 9.0.0.

Background

The nordvpn package and module were originally written by different-error, whose GitHub account is currently suspended (a suspension they dispute). I (@novalkun) took over maintainership at 5.4.0 and am picking up meshnet as unfinished work flagged from the module's first commit. This PR uses techniques from different-error's parallel implementation, credited via Co-authored-by.

Changes

  • Add top-level libtelio and libdrop packages, built from source with Rust tests enabled. Their versions track nordvpn-linux's lib-versions.env; automatic independent updates are disabled.
  • Build nordvpn-linux with the public/FOSS telio, drop, and cdnrc Go tags, and install cmd/fileshare as nordfileshare.
  • Add a --hosts-readonly flag backed by NoopHostsSetter, so the daemon leaves /etc/hosts unchanged. Peer hostnames do not resolve; use meshnet IP addresses instead. The upstream submission and previously reported rejection are documented in the review thread.
  • Give the norduserd and fileshare sockets group nordvpn and mode 0660, allowing the unprivileged main daemon to communicate across users. Start the norduserd user service only for members of the configured group, since non-members cannot assign their socket to that group. Custom groups remain supported.
  • Add CAP_NET_RAW for libtelio's ping support and trust the nordlynx firewall interface.
  • Set main.RemotePath=/apps/linux/config to match upstream's documented production configuration; otherwise remote configuration downloads use the wrong CDN path.
  • Use LD_LIBRARY_PATH wrappers because adding an RPATH with patchelf corrupts these Go binaries' ELF layout.
  • Run the DNS package tests, including the read-only hosts setter. Exclude only TestDiscoverNameserverIp from that package because the build sandbox has no default gateway.
  • Extend the VM test with non-member service checks and explicit fileshare startup/socket assertions, and add a NixOS release note.

Live testing previously caught cargo test overwriting the release libtelio.so with a mock-adapter variant despite successful builds and VM tests. The package snapshots the production shared object before checkPhase and installs that snapshot.

This PR remains independent of the hardening PR. NordWhisper remains unsupported because it requires the proprietary quench library; the proprietary moose tag is also excluded.

How to test

Enable services.nordvpn.enable = true;, add the testing user to the nordvpn group, and set networking.firewall.checkReversePath = "loose"; when using the firewall. Build against this PR with:

nixos-rebuild build-vm \
  -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/refs/pull/564752/head.tar.gz \
  -I nixos-config=./configuration.nix

Inside the VM, check nordvpn status. A real NordVPN account is required for nordvpn login and nordvpn set meshnet on; use nordvpn mesh peer list and nordvpn fileshare list to inspect the services.

AI disclosure

Originally prepared with Claude Code (Claude Sonnet 5 / claude-sonnet-5), then reviewed and revised with OpenCode using GPT-6.1 Sol (openai/gpt-6.1-sol). AI assistance covered code and test changes, review, commit messages, and this description. Build and VM checks and live-test commands were executed through OpenCode; I performed the privileged activation and restoration of test configurations on my hardware. Relevant commits carry Assisted-by: trailers, and different-error's work retains Co-authored-by: attribution.

Things done

Testing performed locally

  • Built nordvpn.cli on x86_64-linux, including the libdrop and libtelio Rust test suites and Go tests with the meshnet/fileshare build tags. The DNS package tests pass with the gateway-dependent discovery test excluded.
  • Ran nixosTests.nordvpn: all five scenarios pass, covering the disabled module and four user/group combinations. Tests check trusted interfaces, socket ownership/mode, the expected fileshare exit code when meshnet is disabled, and that non-members are skipped without socket creation or restarts while authorized users continue to work.
  • Reproduced the non-member chown failure in a separate VM with ConditionGroup disabled, then verified the condition avoids it in all four user/group scenarios.
  • Ran the repository formatter with --fail-on-change on the changed files: zero changes. git diff --check passes.
  • Live-tested on an x86_64-linux NixOS 26.05 host with only the NordVPN module/package replaced: the native adapter creates nordlynx, connects to a DERP relay, and survives two additional off/on cycles. /etc/hosts stays byte-identical throughout.
  • Verified both user sockets have group nordvpn and mode 0660, the fileshare socket is listening, and nordvpn fileshare list successfully queries the daemon. No peers were registered, so peer-to-peer traffic and actual file transfers were not tested.
  • Disabled meshnet and restored the original system generation. Confirmed the original package is active, nordvpn mesh and nordfileshare are unavailable again, no nordlynx interface or fileshare process/listener remains, and HTTPS access works.

The previous nixpkgs-review report built the earlier HEAD on x86_64-linux and aarch64-linux. This revision was tested locally on x86_64-linux; I have not run nixpkgs-review myself.

@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 11.by: package-maintainer This PR was created by a maintainer of all the package it changes. llm-assisted Contributions that include code generation with LLMs 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` labels Sep 19, 2026
@novalkun
novalkun force-pushed the nordvpn-meshnet branch 2 times, most recently from d363fa0 to 8652a05 Compare September 19, 2026 01:10
@novalkun novalkun changed the title nordvpn: add meshnet support (+ opt-in mutable /etc/hosts) nordvpn: add meshnet support Sep 19, 2026
@novalkun
novalkun marked this pull request as ready for review September 19, 2026 02:46
@nixpkgs-ci nixpkgs-ci Bot added the 9.needs: reviewer This PR currently has no reviewers requested and needs attention. label Sep 19, 2026
@nixos-discourse

Copy link
Copy Markdown

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/7646

@nixpkgs-ci nixpkgs-ci Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing to the documentation.

Follow the documentation style guide, especially:

  • Show, don’t tell. Start with a minimal working example. Explain after the code.
  • Avoid meta-commentary. Don’t write "This section explains how to…".
  • Use present tense, imperative mood, and active voice. For example: "Run the command," not "The user should run the following command."
  • Avoid vague references like "this" when the referent is unclear. Write "This command creates a folder," not "This will create a folder."
  • Be direct. Avoid "should", "might", "typically", and "usually".
  • Cut filler words like "simply", "just", "easily", and "basically". Use "to" instead of "in order to".

For larger changes, such as adding or removing whole sections, ask the NixOS documentation team for review.

@novalkun

novalkun commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

cc @andersonjoseph, would appreciate a review.

@nixpkgs-review-gha

Copy link
Copy Markdown

✅ nixpkgs-review result

Generated using nixpkgs-review-gha (a0a3a8d)

Command: nixpkgs-review pr 564752
Commit: d7735bac98319bc1cc1cecb0ff0bd1fdc7722466 (subsequent changes)
Merge: 5ab7ac411402b636d3895af3e57db4ce20a0f72b

Triggered by @novalkun (43009254)
Logs: https://github.com/novalkun/nixpkgs-review-gha/actions/runs/37017910698/attempts/1


x86_64-linux

⏩ 2 packages blacklisted:
  • nixos-install-tools
  • tests.nixos-functions.nixos-test
✅ 1 package built:
  • nordvpn

aarch64-linux

⏩ 2 packages blacklisted:
  • nixos-install-tools
  • tests.nixos-functions.nixos-test
✅ 1 package built:
  • nordvpn

Comment thread nixos/tests/nordvpn.nix Resolved
Comment thread nixos/tests/nordvpn.nix Resolved
Comment thread nixos/tests/nordvpn.nix Resolved
@nixpkgs-ci nixpkgs-ci Bot removed the 9.needs: reviewer This PR currently has no reviewers requested and needs attention. label Oct 3, 2026
@nixpkgs-ci nixpkgs-ci Bot added the 8.has: package (new) This PR adds a new package label Oct 4, 2026
@novalkun
novalkun force-pushed the nordvpn-meshnet branch 2 times, most recently from 1c4ca84 to 5426c55 Compare October 4, 2026 01:00
@novalkun

novalkun commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Thank you very much @SuperSandro2000
Reworked per review: split into 3 commits (libtelio/libdrop now proper top-level packages with nixpkgs-update: no auto update, feature wiring separate), applied all suggestions.
Lint / treefmt shows as failed but it's actually cancelled (stuck 15min then cancelled, no push after) — ran nix fmt --fail-on-change locally, 0 changes, so should be a CI flake rather than a real issue. i do not know why
Edit: it was restarted

@sheeeng

sheeeng commented Oct 12, 2026

Copy link
Copy Markdown
Member

✅ nixpkgs-review result

Generated using nixpkgs-review-gha (c28ca54)

Command: nixpkgs-review pr 564752
Commit: 5426c55de128bc1647aad56e3da45e1c9567fdb3 (subsequent changes)
Merge: 8ff8a571641d68f95e647b8ea244b59f6d7e4987

Logs: https://github.com/sheeeng/defelo-nixpkgs-review-gha/actions/runs/38228922869/attempts/1


x86_64-linux

⏩ 2 packages blacklisted:
  • nixos-install-tools
  • tests.nixos-functions.nixos-test
✅ 3 packages built:
  • libdrop
  • libtelio
  • nordvpn

aarch64-linux

⏩ 2 packages blacklisted:
  • nixos-install-tools
  • tests.nixos-functions.nixos-test
✅ 3 packages built:
  • libdrop
  • libtelio
  • nordvpn

x86_64-darwin

✅ No rebuilds


aarch64-darwin

✅ No rebuilds

@nixpkgs-ci nixpkgs-ci Bot added 8.has: changelog This PR adds or changes release notes 8.has: documentation This PR adds or changes documentation labels Oct 14, 2026
@nixpkgs-ci nixpkgs-ci Bot removed the 8.has: changelog This PR adds or changes release notes label Oct 19, 2026
@novalkun

Copy link
Copy Markdown
Contributor Author

Rebased onto master now that #547437 is merged — CI is green and this is conflict-free. @different-error, would appreciate your review since the meshnet design here is based on your original work.

@different-error different-error left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @novalkun, thank you for the changes. After review I've realized that many of my request changes effectively mean use what I have instead. Let me create a meshnet PR with most of my present meshnet branch and some of the new changes I've obtained in this PR. Feel free to close this.

'TestTransports|'\
'TestUpWGInterface|'\
'TestVPNRuleset'
skipPattern='Test.*FileList_RealURL|TestAddAllowlistRules|TestAddDevice|TestConnectionInfo_TransferRatesShallBeProvidedOnlyForConnectedState|TestCustomTable|TestDiscoverNameserverIp|TestFwmarkRule|TestH1Transport_RoundTrip|TestMeshnetRuleset|TestMultiFwmarkRule|TestNetlinkManager_SetUnset|TestRouter_Add|TestSetMTU|TestSuppressRule|TestTransports|TestUpWGInterface|TestVPNRuleset'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the old format as it reads better.

Comment thread pkgs/by-name/li/libdrop/package.nix Outdated
description = "Native fileshare library used by NordVPN's meshnet feature";
homepage = "https://github.com/NordSecurity/libdrop";
license = lib.licenses.gpl3Only;
maintainers = with lib.maintainers; [ novalkun ];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add me here.

Comment thread pkgs/by-name/li/libtelio/package.nix Outdated
description = "Native meshnet library used by NordVPN's meshnet feature";
homepage = "https://github.com/NordSecurity/libtelio";
license = lib.licenses.gpl3Only;
maintainers = with lib.maintainers; [ novalkun ];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add me here


# save shared object before cargo test relinks it with a test only variant
postBuild = ''
install -Dm755 "$(find target -name libtelio.so -path '*/release/*' | head -1)" "$TMPDIR/libtelio.so"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a code smell. Rust packages don't normally install .so files in their postBuild

Comment on lines +88 to +100
postFixup = ''
python3 -c "
path = '$out/lib/libtelio.so'
placeholder = b'VERSION_PLACEHOLDER' + b'@' * 109 + b'\0'
assert len(placeholder) == 129
new = ('${finalAttrs.version}'.encode() + b'\0' * 129)[:129]
with open(path, 'r+b') as f:
data = f.read()
assert data.count(placeholder) == 1
f.seek(0)
f.write(data.replace(placeholder, new))
"
'';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find directly modifying a .so file a little strange.

Comment thread pkgs/by-name/no/nordvpn/cli.nix Resolved
require making `/etc/hosts` writable, which conflicts with NixOS's
declarative `/etc`. As a workaround you can connect to peers by their meshnet IP
instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an example. Something like

  networking.hosts = {
    "1.2.3.4" = [ "some-alias" ];
  };

Comment on lines +115 to +120
export LD_LIBRARY_PATH=${
lib.makeLibraryPath [
libtelio
libdrop
]
}''${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this to make meshnet operational.

Comment on lines +122 to +124
go test -tags "telio drop cdnrc" ./cli/...
go test -tags "telio drop cdnrc" ./fileshare/...
go test -tags "telio drop cdnrc" ./norduser/...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until proven otherwise we should simply run the entire test suite (not just these tagged ones) as some tests cases may not belong in the tag but impact end-user functionality.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@novalkun

novalkun commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Hello @novalkun, thank you for the changes. After review I've realized that many of my request changes effectively mean use what I have instead. Let me create a meshnet PR with most of my present meshnet branch and some of the new changes I've obtained in this PR. Feel free to close this.

after you open the PR, i will close this PR. thank you

novalkun and others added 3 commits October 20, 2026 18:15
Native (Rust) meshnet library consumed by nordvpn-linux's cgo
bindings (github.com/NordSecurity/libtelio-go, already vendored as a
Go dependency). Version must stay in lockstep with LIBTELIO_VERSION
in nordvpn-linux's lib-versions.env, hence "nixpkgs-update: no auto
update".

Co-authored-by: different-error <[email protected]>
Assisted-by: Claude Code (Sonnet 5, claude-sonnet-5)
Native (Rust) fileshare library consumed by nordvpn-linux's cgo
bindings (github.com/NordSecurity/libdrop-go, already vendored as a
Go dependency). Version must stay in lockstep with LIBDROP_VERSION
in nordvpn-linux's lib-versions.env, hence "nixpkgs-update: no auto
update".

Co-authored-by: different-error <[email protected]>
Assisted-by: Claude Code (Sonnet 5, claude-sonnet-5)
Adds meshnet (libtelio) and fileshare (libdrop) support, both fully
open-source/GPLv3 and gated behind upstream's own telio/drop/cdnrc Go
build tags (moose/quench stay out of scope, proprietary-only).

- nordvpnd gains a --hosts-readonly flag backed by a NoopHostsSetter
  (daemon/dns/hosts.go), so /etc/hosts is never written to. Peer hostnames
  can be mapped declaratively via networking.hosts instead (documented).
- builds and installs cmd/fileshare as nordfileshare; its and
  norduserd's unix sockets are chowned to the nordvpn group so
  processes running as a different user than nordvpnd (which runs
  unprivileged) can reach them.
- only start the norduserd user service for members of the configured
  group, since other users cannot assign their socket to that group.
- adds CAP_NET_RAW (libtelio needs it for ping) and nordlynx to
  networking.firewall.trustedInterfaces.
- sets the RemotePath ldflag to upstream's documented default
  (.env.sample: /apps/linux/config). Without it, remote-config
  downloads that gate meshnet/libtelio resolve to the wrong CDN path.
- run daemon/dns tests, including the read-only hosts setter, except
  nameserver discovery, which needs a gateway absent from the sandbox.
- expand nixosTests.nordvpn to check trusted interfaces, socket modes
  and groups, the expected fileshare exit code while meshnet is disabled,
  and skipped services for non-members across all user/group combinations.
- document meshnet support and its hostname limitation in the release notes.

Rebased onto master's merged nixos/nordvpn: make more secure and robust
(NixOS#547437): reconciled the refactored nixosTests.nordvpn
UserGroupTestCase, the broader checkPhase daemon test coverage, and the
full systemd sandboxing directive set with this branch's meshnet-specific
additions on top.

Based on independent work by different-error, who could not push this
directly due to a disputed account suspension. Credited via Co-authored-by.

Co-authored-by: different-error <[email protected]>
Assisted-by: Claude Code (Sonnet 5, claude-sonnet-5)
Assisted-by: OpenCode (GPT-6.1 Sol, openai/gpt-6.1-sol)
Assisted-by: Claude Code (Opus 5.5, claude-opus-5-5)
@novalkun

Copy link
Copy Markdown
Contributor Author

Closing in favor of #576413. Thank you @different-error

@novalkun novalkun closed this Oct 20, 2026
@novalkun
novalkun deleted the nordvpn-meshnet branch October 20, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: documentation This PR adds or changes documentation 8.has: module (update) This PR changes an existing module in `nixos/` 8.has: package (new) This PR adds a new package 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 11.by: package-maintainer This PR was created by a maintainer of all the package it changes. llm-assisted Contributions that include code generation with LLMs

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

6 participants