Proxmox LXC setup to run OpenVPN (2.5.0 & 3.0) with Private Internet Access (PIA)

Neil Pathare
8 min readFeb 15, 2021

A quick how-to guide for getting setup with a LXC container in Proxmox that runs OpenVPN using Private Internet Access as the provider.

At the time of writing, these were the versions in use:

  • Proxmox: proxmox-ve: 6.3–1 (Kernel: 5.4.78–2-pve)
  • Ubuntu: ubuntu-20.10-standard_20.10–1_amd65.tar.gz
  • OpenVPN: OpenVPN 2.5.0 x86_64-pc-linux-gnu
  • OpenVPN3: OpenVPN 3/Linux v13_beta (openvpn3)

To get started, using the Proxmox GUI, create an Ubuntu container — most options can be left at their default values here. I’ve unchecked ‘Unprivileged Container’ to make certain things easier for myself. I’ve also set the network configuration to DHCP instead of the default static.

My container allocation was CPU: 4, RAM: 4096 MB, Swap: 512 MB, Boot disk: 8 GB — these maybe overkill depending on your CPU & your load, I had some resources to spare.

Installing OpenVPN 2.5.0

At the time of writing, running apt-get install openvpn on Ubuntu 20.10 (February, 2021) installs OpenVPN version 2.4.9. Checking OpenVPN’s releases, it looks like we have, available to us, version 2.5.0. The changelog for this version reveals some interesting updates, including “Connections setup is now much faster”. Nice. We’re going to get setup with this version.

Once the LXC container is setup, console into it & get up-to-date on the software required using:

# Get all updates before starting
apt update && apt upgrade -y
# We will need these essential build tools to build:
# OpenSSL & OpenVPN from source
apt-get install build-essential
# Get OpenSSL
cd /home && wget https://www.openssl.org/source/openssl-1.1.1f.tar.gz && cd openssl-1.1.1f
# Start building OpenSSL
./config shared no-ssl2 no-ssl3 no-comp enable-ec_nistp_64_gcc_128 -Wl,-rpath=/usr/local/ssl/lib — prefix=/usr/local/ssl
make -j 4make testsudo make installclear program cachehash -rln -s /usr/local/ssl/bin/openssl /usr/local/bin/openssl# We will need this installed for OpenVPN
apt-get install libpam0g-dev
cd /home && wget https://swupdate.openvpn.org/community/releases/openvpn-2.5.0.tar.gztar -xzvf openvpn-2.5.0.tar.gzcd /openvpn-2.5.0.tar.gzCFLAGS=”-I/usr/local/ssl/include -Wl,-rpath=/usr/local/ssl/lib -L/usr/local/ssl/lib” ./configure — disable-lzomake -j 4make checkmake installhash -rcommand -v openvpn# Done! Verify the version using:
/usr/local/sbin/openvpn — version

Next, enable TUN (see here for more information: https://www.kernel.org/doc/Documentation/networking/tuntap.txt)

mkdir -p /dev/net
mknod /dev/net/tun c 10 200

I was able to run these commands as my container was privileged. If your container is unprivileged, you will not be able to create device nodes due to kernel enforced policies. Instead, what you will need to do is to bind-mount the device from the host.

Next up, its time to configure OpenVPN to use PIA. Navigate to /etc/openvpn & download PIA’s configuration files. These files provide the certificates & connection parameters required to connect to PIA’s servers.

cd /etc/openvpn
wget https://www.privateinternetaccess.com/openvpn/openvpn.zip
apt-get install unzip && unzip openvpn.zip -d PIA

If wgetfails with “Unable to locally verify the issuer’s authority” as the message, add the — no-check-certificate flag.

Now that we have all the configuration saved in its own directory, copy over the RSA CRT & PEM files as well as the VPN configuration of your choosing. This example uses the US New York OVPN configuration.

cp /etc/openvpn/PIA/ca.rsa.2048.crt /etc/openvpn/ca.rsa.2048.crt
cp /etc/openvpn/PIA/crl.rsa.2048.pem /etc/openvpn/crl.rsa.2048.pem
cp /etc/openvpn/PIA/us_new_york.ovpn /etc/openvpn/pia-vpn.conf

Leaving the file extension as “ovpn” will only work if you manually start the OpenVPN service. If you want your configuration to be loaded from an initialization script, the configuration file needs a “conf” extension.

That’s all that’s needed so far — we should begood to launch the service.

openvpn — config “/etc/openvpn/pia-vpn.conf”

This should launch OpenVPN with your PIA configuration & immediately prompt you for authentication. After entering your username & password, you should see some time-stamped log messages on the terminal that confirm the CRL load, UDP links local & remote information, ROUTE & ROUTE6 information, TUN/TAP device(s) opened, etc.

The last log message should be something along the lines of: “Initialization Sequence Completed”. This signifies that your configuration was loaded, initialized & your VPN connection is up & running. Nice.

Launch another terminal to your container & run:

apt-get install curl && curl ipinfo.io/ip

This should show you your VPN IP — your public IP from this container. Check your public IP from a different container or any other network device to ensure that you are truly in a VPN session on your OpenVPN container.

Another way to confirm is to run: wget -q -O — ipecho.net/plain which should show your public facing IP.

Now that everything is setup & confirmed to be working, let’s go back & optimize a few things.

Open the VPN configuration file:

nano /etc/openvpn/pia-vpn.conf

Locate ‘auth-user-pass’ & append to it, the name of your credentials store. When done, the line should look like: `auth-user-pass /etc/openvpn/credentials.conf`. This parameter will let OpenVPN auto-load the username & password from the configuration file you’ve specified.

NOTE: This is generally always a bad practice. Passwords should not be stored in plaintext, even on your own containers. As of OpenVPN 3, this feature to load user-password combination has been discontinued.

Read more about the issue & the proposed workaround: https://openvpn.net/openvpn-3-linux-and-auth-user-pass/

For this section of the guide though, we’re going ahead with it. The next configuration change that you might want to do is to add the auth-nocache parameter. This line can be added just below the auth-user-pass line.

With your configuration updated, save it & exit out. Then create a new file to store your PIA credentials.

nano /etc/openvpn/credentials.conf

This file will have the username on the first line & the password on the second line & nothing else.

If you created it from a non-Linux environment, you will want to run this after creation:

apt-get install dos2unix & dos2unix /etc/openvpn/credentials.conf

Next, running the command below will remove the group/accessible warning when you start the VPN service (WARNING: file ‘credentials.conf’ is group or others accessible)

chmod 0400 /etc/openvpn/credentials.conf

One major drawback at this point is that following a reboot, you will notice that OpenVPN now throws an error when you attempt to start it.

ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)

Exiting due to fatal error

To avoid this fatality & to also avoid having to run the `mkdir` & mknod` commands every time the container is restarted, we will need to update our container’s configuration from our Proxmox host. From the GUI, go to the shell for your node.

Navigate to `/etc/pve/lxc` & open the configuration file for your OpenVPN container (mine was 102.conf).

nano /etc/pve/lxc/102.conf

Add these lines:

lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
lxc.cgroup.devices.allow: c 10:200 rwm

This will ensure that when this container is started the mount entries are set for TUN.

We’re almost set with our VPN service. The final step is to automate the starting of the service with our configuration for PIA. OpenVPN comes installed with an initialization script under `/etc/init.d`. We will be updating the `default` script. /etc/default contains some parameters that the end user or administrator is likely to change, so rather than embedding the values in the actual boot scripts. they are placed here instead. In this way, changes will persist even if you upgrade the package and the boot script is replaced. As we have only one configuration file set, we just need to make one entry/update into this script.

nano /etc/default/openvpn

Add a line above/below the commented out #AUTOSTART line:

AUTOSTART=”pia-vpn”

This tells the initialization script to auto-load & use the configuration defined, which in our case is the configuration file. Note that the file extension, “conf” is omitted from the line. It is implied & thus not needed to be added.

This should be it. You can reboot to confirm that OpenVPN starts up when rebooted & that it connected to PIA servers.

You can check up on the service using something like systemctl status openvpn@pia.service. systemctl status shows you all the services. Use that to find your OpenVPN service & check its status.

You can confirm that the service created a `tun0` interface:

ip addr show dev tun0

Also, check out your new routes using ip route.

Troubleshooting

This section is sparse as I didn’t really encounter any issues post-install. If you are having issues with VPN connectivity, a starting point might be to check the VPN configuration — specifically the remote endpoints defined. Refer here: https://www.privateinternetaccess.com/pages/network/ for the current/up-to-date PIA network connection endpoints.

Installing OpenVPN3

These commands should get you started with OpenVPN3

apt update && apt upgrade -yapt-get install apt-transport-https # This ensures that your apt supports the https transportcd /homewget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pubapt-get install gnupg2 && apt-key add openvpn-repo-pkg-key.pubwget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-groovy.listapt update && apt upgrade -yapt-get install openvpn3openvpn3 version

Note that I used “openvpn3-groovy” in the wget command as I’m running this on Ubuntu 20.10. Replace “groovy” with your Ubuntu distribution codename (eg: focal for 20.04, buster for Debian 10, etc.).

mkdir -p /dev/netmknod /dev/net/tun c 10 200cd /etc/openvpn3wget https://www.privateinternetaccess.com/openvpn/openvpn.zipapt-get install unzip && unzip openvpn.zip -d PIAcp /etc/openvpn3/PIA/ca.rsa.2048.crt /etc/openvpn3/ca.rsa.2048.crtcp /etc/openvpn3/PIA/crl.rsa.2048.pem /etc/openvpn3/crl.rsa.2048.pemcp /etc/openvpn3/PIA/us_new_york.ovpn /etc/openvpn3/pia-vpn.ovpnopenvpn3 session-start — config “/etc/openvpn3/pia-vpn.ovpn”

The last command to start the service fails. Unfortunately, there isn’t a descriptive error message on the console, we will probably have to take logs somehow to understand what’s going wrong. There is another option available though, OpenVPN3 can be launched using OpenVPN2 (wrapper)

openvpn2 — config “/etc/openvpn3/pia-vpn.ovpn”

This also fails, but lets us know that there is an invalid option in the configuration. Let’s fix that.

nano /etc/openvpn3/pia-vpn.ovpn

Remove the `disable-occ` parameter (usually the last line of the configuration). Launch the service again:

openvpn2 — config “/etc/openvpn3/pia-vpn.ovpn”

Finally, we should see the service up & running successfully. The console should display a ‘CONNECTED’ message.

2021–02–15 19:11:51.569733 [STATUS] (StatusMajor.CONNECTION, StatusMinor.CFG_OK) config_path=/net/openvpn/v3/configuration/<configId>2021–02–15 19:11:51.569768 [STATUS] (StatusMajor.CONNECTION, StatusMinor.CONN_CONNECTING)2021–02–15 19:11:51.614846 [STATUS] (StatusMajor.CONNECTION, StatusMinor.CONN_CONNECTING)2021–02–15 19:11:51.670068 [STATUS] (StatusMajor.CONNECTION, StatusMinor.CONN_CONNECTED

From another terminal, you can verify the running session by using: `openvpn3 sessions-list`. The output should show you the TUN device & session information.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —Path: /net/openvpn/v3/sessions/<configId>Created: Mon Feb 15 00:00:00 0000 PID: 00000Owner: root Device: tun0Config name: /etc/openvpn3/pia-vpn.ovpn (Config not available)Session name: us-newyorkcity.privacy.networkStatus: Connection, Client connected— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — 

That’s about as far as I got with OpenVPN3. Their documentation provides a way for the credentials to be autoloaded without having to hardcode them in the OpenVPN /etc directory, which I recommend to try.

Wrapping this up, there is a container template available on Proxmox for OpenVPN, debian-10-turnkey-openvpn. TurnKey LXCs are intended to provide a polished & streamlined experience for the functionality that they offer, so that might prove a better choice for some people.

Reference

--

--