NordVPN is one of many VPN services. I was asked to have a look at how to make it work in a LXD container and as a result I am writing this post. I am not advertising this service, nor do I use affiliate links, etc. Up to now, NordVPN have refused to fix their official Linux client to work in a container.
Installing the official client
Let’s install the official client in a LXD container. Create a container and get a shell into it. Then, download the Deb package and install it. The initial Deb package is really small. It just has instructions to setup the NordVPN repository to your system. After you install this package, you apt update
to refresh the package list and then you can install the actual nordvpn
package.
$ lxc launch ubuntu:18.04 nordvpn Creating nordvpn Starting nordvpn $ lxc ubuntu nordvpn ubuntu@nordvpn:~$ wget https://repo.nordvpn.com/deb/nordvpn/debian/pool/main/nordvpn-release_1.0.0_all.deb ubuntu@nordvpn:~$ sudo apt install -f ./nordvpn-release_1.0.0_all.deb ... ubuntu@nordvpn:~$ sudo apt update ... ubuntu@nordvpn:~$ sudo apt install -y nordvpn ... NordVPN for Linux successfully installed! To get started, type 'nordvpn login' and enter your NordVPN account details. Then type 'nordvpn connect' and you’re all set! If you need help using the app, use the command 'nordvpn --help'. ... ubuntu@nordvpn:~$ nordvpn Welcome to NordVPN Linux client app! Version 3.7.3 Website: https://nordvpn.com Usage: nordvpn [global options] command [command options] [arguments…] ... ubuntu@nordvpn:~$
Running the official NordVPN client
Let’s attempt to run the official NordVPN client. We log in, and then we connect. Does not work! Something is wrong.
ubuntu@nordvpn:~$ nordvpn login --username myusername@example.com --password mypassword Welcome to NordVPN! You can now connect to VPN by using 'nordvpn connect'. ubuntu@nordvpn:~$ nordvpn connect Connecting to Germany #500 (de500.nordvpn.com) transport is closing ubuntu@nordvpn:~$ nordvpn connect Whoops! Cannot reach System Daemon. ubuntu@nordvpn:~$
We look into /var/log/syslog
. Here are the offending lines. The official client crashes due to some array bounds error.
Jun 16 19:59:31 nordvpn nordvpnd[1423]: debug: Tue Jun 16 19:59:31 2020 MANAGEMENT: Connected to management server at /var/run/nordvpn-openvpn.sock Jun 16 19:59:31 nordvpn nordvpnd[1423]: 2020/06/16 19:59:31 [INFO] Tue Jun 16 19:59:31 2020 MANAGEMENT: Connected to management server at /var/run/nordvpn-openvpn.sock Jun 16 19:59:31 nordvpn nordvpnd[1423]: panic: runtime error: index out of range [1] with length 1 Jun 16 19:59:31 nordvpn nordvpnd[1423]: goroutine 117 [running]: Jun 16 19:59:31 nordvpn nordvpnd[1423]: nordvpn/daemon.ruleParsing(…) Jun 16 19:59:31 nordvpn nordvpnd[1423]: #011/builds/nordvpn/apps-source/linux-app/src/daemon/vpn_ipv6.go:117 Jun 16 19:59:31 nordvpn nordvpnd[1423]: nordvpn/daemon.(Ipv6).Disable(0xc000390844, 0x0, 0x0) Jun 16 19:59:31 nordvpn nordvpnd[1423]: #011/builds/nordvpn/apps-source/linux-app/src/daemon/vpn_ipv6.go:43 +0x6c7 Jun 16 19:59:31 nordvpn nordvpnd[1423]: nordvpn/daemon.(OpenVPN).Start(0xc0004466e0, 0x2b89520, 0xc00042dec0, 0x18, 0xc00042dee0, 0x18, 0x0, 0x0, 0x0, 0x0, …) Jun 16 19:59:31 nordvpn nordvpnd[1423]: #011/builds/nordvpn/apps-source/linux-app/src/daemon/vpn_openvpn.go:159 +0xd11 Jun 16 19:59:31 nordvpn nordvpnd[1423]: created by nordvpn/daemon.Connect Jun 16 19:59:31 nordvpn nordvpnd[1423]: #011/builds/nordvpn/apps-source/linux-app/src/daemon/rpc.go:288 +0x882 Jun 16 19:59:31 nordvpn systemd[1]: nordvpnd.service: Main process exited, code=exited, status=2/INVALIDARGUMENT Jun 16 19:59:31 nordvpn systemd[1]: nordvpnd.service: Failed with result 'exit-code'. Jun 16 19:59:36 nordvpn systemd[1]: nordvpnd.service: Service hold-off time over, scheduling restart.
The same error appears whether you run the client with sudo -H
, whether you run it in a privileged container. Something is wrong in this official NordVPN client.
So, what do we do now? Apparently, their client is based on OpenVPN, therefore let’s use the OpenVPN client directly.
Using the OpenVPN client
We exit from the container, remove it and create a new one. One without the official client.
ubuntu@nordvpn:~$ logout
$ lxc stop nordvpn
$ lxc delete nordvpn
$ lxc launch ubuntu:18.04 nordvpn
Creating nordvpn
Starting nordvpn
$ lxc ubuntu nordvpn
ubuntu@nordvpn:~$
We then update the package list and install the openvpn
package.
ubuntu@nordvpn:~$ sudo apt update ubuntu@nordvpn:~$ sudo apt install -y openvpn
OpenVPN requires configuration files for the VPN servers. NordVPN has this list online at https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip
Let’s download it. It’s a 21MB file.
ubuntu@nordvpn:~$ wget https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip
We unzip into the /etc/openvpn/client
directory. The ZIP has folders, so we instruct unzip
to ignore folders and dump them all in the given folder.
ubuntu@nordvpn:~$ sudo unzip -d /etc/openvpn/client/ -j ovpn.zip
We are ready to connect to some VPN server. But which one? We can select any. This page, https://nordvpn.com/servers/tools/, will auto-detect the closest VPN server. Let’s assume the result is de505.nordvpn.com
. We do not connect yet. Do the following section to avoid DNS leakage, then we connect on the next section.
Avoiding DNS leakage
By default, OpenVPN does not set the DNS server and keeps the existing DNS configuration. The result of this, is DNS leakage; that is, name resolutions do not happen through the VPN but use the local network.
What needs to happen, is to add the appropriate script to OpenVPN to configure the DNS when the VPN is established, and when the VPN is teared down.
We use Ubuntu 18.04 LTS in the container, therefore, we configure systemd-resolved
for this. Here are the commands. We install the helper, and edit the .ovpn
file to use the helper.
ubuntu@nordvpn:~$ sudo apt install openvpn-systemd-resolved
Then, edit the OpenVPN configuration file, in our case, /etc/openvpn/client/de505.nordvpn.com.tcp.ovpn
and add the following lines,
script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
down-pre
We are ready now to connect. Note that if you change server, you need to edit the corresponding file manually as above.
Making the connection
We are ready to make the connection.
ubuntu@nordvpn:~$ sudo openvpn --config /etc/openvpn/client/de505.nordvpn.com.tcp.ovpn Tue Jun 16 21:17:17 2020 OpenVPN 2.4.4 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on May 14 2019 Tue Jun 16 21:17:17 2020 library versions: OpenSSL 1.1.1 11 Sep 2018, LZO 2.08 Enter Auth Username: myusername@example.com Enter Auth Password: *********** ... <14>Jun 16 21:17:42 update-systemd-resolved: Adding DNS Routed Domain . <14>Jun 16 21:17:42 update-systemd-resolved: Adding IPv4 DNS Server 103.86.96.100 <14>Jun 16 21:17:42 update-systemd-resolved: Adding IPv4 DNS Server 103.86.99.100 ... Tue Jun 16 21:17:42 2020 Initialization Sequence Completed
Looks good. The VPN circuit is active until you hit Ctrl+C here to interrupt OpenVPN. You need to open a new terminal to the LXD container to use the VPN. We can see that the proper new DNS entries have been added to systemd-resolved.
$ lxc ubuntu nordvpn ubuntu@nordvpn:~$ systemd-resolve --status ... Link 7 (tun0) Current Scopes: DNS LLMNR setting: yes MulticastDNS setting: no DNSSEC setting: no DNSSEC supported: no DNS Servers: 103.86.96.100 103.86.99.100 DNS Domain: ~. ubuntu@nordvpn:~$
Automating the setup with systemd
DuckHook in a comment below describes how to setup OpenVPN so that the NordVPN VPN can start automatically. Have a look at the comments for more. Thanks DuckHook!
Conclusion
The official NordVPN client does not work in a LXD container, and it appears that it’s just a bug that they know about and do not intend to fix. The developers did not envision the client to run in a container, nor did they test it. We work around this issue by installing the OpenVPN client and using it to connect to one of their VPN servers.
12 comments
Skip to comment form
Hi Simos,
Your awesome tutorials have opened up whole worlds for me. So very much appreciated, and keep up the good work!
In my case, I needed the –configure flag to make this work, hence, sudo openvpn –configure /etc/openvpn/client/de505.nordvpn.com.tcp.ovp
Otherwise, it worked perfectly.
Another tip: invoke the VPN session with screen or tmux, then detach to free up the console. Works a treat. If you could post a tutorial on starting this as a service module, that would be so useful. Of course, this sort of constrains one to only a single geographical server, but that’s all that many of us need.
Really appreciate the thought, work and generosity that you put into this.
Oops. That last was incomplete. Missing the final ‘n’. It’s also hard to see the double hyphen before “configure”:
sudo openvpn –configure /etc/openvpn/client/de505.nordvpn.com.tcp.ovpn
keep making mistakes and there’s no ‘edit’ function for comments:
Author
Thanks for the kind words!
I fixed the omission of
--config
.I do not have a NordVPN account; I wrote this post thanks to a temporary account I was given at https://discuss.linuxcontainers.org/t/nordvpn-in-lxc-unpriv-priv-container-doesnt-work/8128/13
If you can work out the systemd configuration, feel free to post below. I suppose it should be easy as the OpenVPN package is aware of systemd.
Simos posed me the challenge of creating a systemd service to autostart NordVPN at boot. After a bit of experimentation, I think the instructions below should work.
Follow Simos’s instructions up to just before downloading the zip file.
Step 1
We wish to unzip the ovpn files into a local directory. I made mine a directory nested one level below my local bin, but yours can be elsewhere. Just remember where you put it.
This will create two subfolders in ~/bin/nordvpn owned by the normal user. One is for TCP connections, the other is UDP. The reason I don’t like to unpack directly into /etc/openvpn/client is because I like to keep my system directories as clean as possible. And since NordVPN’s servers number almost six thousand at last count, I do not want all of those files spamming my etc directory. Moreover, there is another advantage to keeping the etc directory clean, which is discussed in Step 9 below. So instead, we will copy only the single server config that we wish, as per the instructions in Step 4 below.
Step 2
To automatically connect our VPN at start, we must supply username and password from a file. The problem is that Ubuntu stores these credentials in the clear. Since the username and password for your NordVPN master account is sensitive and should never be revealed to anyone, it is not advisable to store them in plain text. Fortunately, NordVPN provides a separate set of Service Credentials for use cases like ours. This is available on your NordVPN dashboard. Should this alternate set of username/password ever be compromised (say, your laptop gets stolen), they can be replaced by NordVPN without the need to change your main account username or password. Instructions are here: https://support.nordvpn.com/Connectivity/Linux/1047409422/How-can-I-connect-to-NordVPN-using-Linux-Terminal.htm
Let’s say our Special Credentials are:
Using those credentials, we create a file:
It should have only two lines—the Special Credentials username on line 1 and password on line 2:
Save and exit.
Step 3
Find your optimal NordVPN server by following Simos’s link above. Let’s continue using Simos’s example. Add the security script block to /your/path/to/de505.nordvpn.com.tcp.ovpn to stop DNS leakage. Recall that our files are under our ownership within our home directory, so do not invoke editing with sudo.
Now find the line containing:
Change it to:
Save and exit
Step 4
As root, copy the file with a generic name like
client.conf
, into the proper etc directory:Step 5
Both client.conf and the earlier credentials file are stored in the clear. For added security, make them only accessible to root:
Step 6
Though openvpn is systemd‑aware, I was unable to produce a working syntax given my rudimentary systemd skills. Instead, I built a systemd service module that I named nordvpn.service containing the following contents:
Step 7
Enable the service module so that it autostarts at boot:
Start the OpenVPN service:
Check status:
Step 8
To check if it comes up at boot, reboot the container, then check status again:
Check VPN connection:
Step 9
Note that by providing a single default client.conf file in the etc directory, it is easy to change the VPN server by repeating Step 4 but using a different .ovpn file. Just make sure that it contains the security script block and the pointer to the credentials file. The new server can be reached by cycling nordvpn.service with the systemd restart command.
Summary
That’s it: so versatile, so many possibilities. Clone the container, change the VPN server, and one could have any number of different containers tunnelled to different VPN servers at once, limited only by bandwidth or your VPN provider’s device limits. Go nuts!
Author
Thank you for taking the time and composing the instructions on how to automate the VPN with systemd!
I edited the instructions to add markdown markup.
I am adding a note in the post about your instructions.
Hi Simos,
One further correction/adjustment:
The script block for preventing DNS leakage is missing an element. I had to add:
dhcp-option DOMAIN-ROUTE .
Especially note the ending dot which is easily missed.
The complete block looks like this:
script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
dhcp-option DOMAIN-ROUTE .
down-pre
After adding dhcp-option, no further leakage.
Hi, I’ve a question (https://discuss.linuxcontainers.org/t/systemctl-disable-service-operation-not-permitted/10946)
I’m using NordVPN on a container with LXC 3.0.3 and Ubuntu 18.04.
I’d like to disable NordVPN daemon with “systemctl disable nordvpnd” by root.
Searching inside logs I found into “/var/log/syslog”:
systemd[1]: nordvpnd.socket: Failed to reset devices.list: Operation not permitted
systemd[1]: nordvpnd.service: Failed to reset devices.list: Operation not permitted
Can I fix/force it?
Author
My best understanding of the Failed to reset devices.list: Operation not permitted warning or error, is that is innocuous (it is indeed annoying to see, but it still works). The system containers use the same runtime as your standard Ubuntu, and with standard Ubuntu on a VM or baremetal computer you do not get that message.
Either it is too big of a change to make on the runtime, or the change should happen upstream at the
systemd
project and for some reason either it cannot be done, or it will take time for such a change to reach the Ubuntu images.I believe it should have been discussed before, and there should be some background information on the Internets on this.
If something does not work for you and you think it is related to this, then it should be investigated. Can you verify that you cannot disable the VPN using
systemctl disable nordvpnd
?s/auth-users-pass/auth-user-pass/ at Step 3
Author
Thanks, I fixed the typo in the comment at Step 3.
Hmmm I followed the steps and this hit me:
ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)
2022-04-10 15:30:30 Exiting due to fatal error