This is my approach for running NordVPN in a network namespace, and then running rtorrent in that netns.
I did this on Fedora 39 in November 2023.
You need to get the wireguard details that the NordVPN client uses to get the private key, local ip, and local port number. This post is helpful.
Basically, run the official nordvpn client in Nordlynx mode and connect. Run wg
to collect the local port
number in use -- it may be different from the standard, and its helpful to know which one is definitely working.
You also need to sudo wg show nordlynx private-key
to get the private key. It should go without saying that if
anyone in the world gets this key they can use your NordVPN account, so keep it secret & safe. NordVPN doesn't
have an official way to issue these private keys, so I don't know how you might get a new one if you screw up...
NOTE: If you change your NordVPN password, your private key might get changed too.
You can also collect the public key & endpoint:port of the server you're connecting too. If you want an API to get public keys from NordVPN, this gist has a method.
After you collet all that data, you'll want to disconnect from the vpn and systemctl stop nordvpnd.service
and
disable it as well after you get this setup working.
I was originally working from this blog
and this repo of systemd services. However, these examples are
from 2019 and systemd changed a little, so the services in this gist are what I am using. In particular,
PrivateNetwork=yes
seems to be out of vogue.
/etc/netns/nordvpn/resolv.conf sets up DNS resolution for stuff in the nordvpn namespace. [email protected] creates a new network namespace and nothing more. wireguard-nordvpn.service gets the vpn running in the namespace. Wireguard is the only iterface in the namespace, so anything that goes into the nordvpn namespace can only talk through the VPN.
One thing you need to realize is that NetworkManager isn't going to be able to touch this thing, so its also not managing routing or dns resolution or telling anything over D-Bus about it.
First you can check your 'normal' environment to obvserve it isn't VPN'd:
$ curl -s 'https://api.nordvpn.com/vpn/check/full' | jq
{
"ip": "1.2.3.4",
"isp": "ISP",
"status": "Unprotected",
"country": "United States",
"code": "US"
}
Then you can check from within the namespace:
$ sudo ip netns exec nordvpn curl -s 'https://api.nordvpn.com/vpn/check/full' | jq
{
"ip": "4.5.6.7",
"isp": "WhateverNordvpnCallsItself",
"status": "Protected",
"country": "United States",
"code": "US"
}
Personally I run rtorrent in a shell, so here's how I run it:
sudo ip netns exec nordvpn sudo -u MYUSERNAME rtorrent
Just to explain what that command does if you have copy and pasted everything until now without understanding...
ip netns exec nordvpn CMD
will run CMD
in an environment where it is in the nordvpn network namespace. One of
the features of this command is that /etc/netns/nordvpn/resolv.conf will be bind mounted to /etc/resolv.conf,
so things should magically work even if they don't know about network namespaces.
ip netns exec
has to be run as root, thus we used sudo. But that means CMD
will be run as root.
That's why CMD
is sudo -u MYUSERNAME rtorrent
there's no reason to run rtorrent as root after all!
If you are starting rtorrent as a systemd service, just add NetworkNamespacePath
to the unit file, e.g.
NetworkNamespacePath=/run/netns/nordvpn
That will set the namespace. The rest of your unit file should already take care of setting the proper user or whatever. Keep in mind I'm not using this method myself, so maybe its missing something; you might need to bind mount resolv.conf yourself.
Normally, Fedora is running systemd-resolved at something like 127.0.0.53. This address is unreachable from inside the network namespace! That's why you need to set a DNS server for inside the namespace. Its reasonable to use NordVPN's dns servers for your NordVPN connection, but you could set it to anything else if you really desire 'DNS leaks'
I'd like to improve this setup to route IPv6 too, but I don't think NordVPN supports it yet. Here's the minimum of what has to change:
- Add a local ipv6 address
--
ip -n nordvpn addr add SOME_KIND_OF_LOCAL_IPV6_ADDRESS/128 dev wg-nordvpn0
- Changed AllowsIPs in wg-nordvpn.conf
--
AllowedIPs = 0.0.0.0/0,::0/0
My first attempt was to simply ip netns exec nordvpn nordvpn connect
, which is to say to run the official client in
a network namespace. What this meant was actually getting nordvpnd.service into the network namespace. But ultimately
it didn't work because nordvpnd tries to talk to NetworkManager to set the DNS, and NetworkManager doesn't know about
managing network namespaces. For other vpn clients this might be an easier option than snooping through all the wireguard
secrets yourself.
Actually it might be possible to spin up a new NetworkManager instance in that namespace, but I was afraid that'd be too deep of a rabbit hole to run down.