How to use NordVPN in a LXD container

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.

Permanent link to this article: https://blog.simos.info/how-to-use-nordvpn-in-a-lxd-container/

12 comments

Skip to comment form

    • DuckHook on March 15, 2021 at 01:48
    • Reply

    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.

    • DuckHook on March 15, 2021 at 02:02
    • Reply

    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

    • DuckHook on March 15, 2021 at 02:04
    • Reply

    keep making mistakes and there’s no ‘edit’ function for comments:

    sudo openvpn --config /etc/openvpn/client/de505.nordvpn.com.tcp.ovpn
    
    1. 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.

    • DuckHook on March 18, 2021 at 06:32
    • Reply

    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.

    mkdir -R ~/bin/nordvpn
      cd ~/bin/nordvpn
      wget https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip
      unzip ovpn.zip
    

    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:

    Username: xxxxxxxxxxxxxxxxxxxxxx
      Password: 0000000000000000000000
    

    Using those credentials, we create a file:

    sudo nano /etc/openvpn/credentials
    

    It should have only two lines—the Special Credentials username on line 1 and password on line 2:

    xxxxxxxxxxxxxxxxxxxxxx
      0000000000000000000000
    

    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:

      auth-users-pass
    

    Change it to:

      auth-user-pass /etc/openvpn/credentials
    

    Save and exit

    Step 4

    As root, copy the file with a generic name like client.conf, into the proper etc directory:

    sudo cp /your/path/to/de505.nordvpn.com.tcp.ovpn /etc/openvpn/client/client.conf
    

    Step 5

    Both client.conf and the earlier credentials file are stored in the clear. For added security, make them only accessible to root:

    sudo chmod 600 /etc/openvpn/credentials /etc/openvpn/client/client.conf
    

    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:

    [Unit]
      Description=Connect to NordVPN
      Requires=network.target
      After=network-online.target
    
    [Service]
      Type=simple
      ExecStart=/usr/sbin/openvpn --config /etc/openvpn/client/client.conf
    
    [Install]
      WantedBy=multi-user.target
    

    Step 7

    Enable the service module so that it autostarts at boot:

    sudo systemctl enable nordvpn.service
    

    Start the OpenVPN service:

    sudo systemctl start nordvpn.service
    

    Check status:

    sudo systemctl status nordvpn.service
    

    Step 8

    To check if it comes up at boot, reboot the container, then check status again:

    sudo systemctl status openvpn
    

    Check VPN connection:

    curl ipinfo.io
    

    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!

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

    • DuckHook on March 24, 2021 at 04:07
    • Reply

    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.

    • alfo on April 30, 2021 at 16:20
    • Reply

    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?

    1. 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?

    • alfo on April 30, 2021 at 16:57
    • Reply

    s/auth-users-pass/auth-user-pass/ at Step 3

    1. Thanks, I fixed the typo in the comment at Step 3.

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

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.