Proxmox : OpenVPN deployment through LXC

In this post, we’ll see how to install a popular open-source VPN server on a Proxmox hypervisor : OpenVPN.

We’ll be using a LXC container for this project, a technology natively supported by the Proxmox VE hypervisor. Containers are a lightweight alternative to fully virtualized machines. They use the kernel of the host system that they run on, instead of emulating a full operating system. This means that containers can access resources on the host system directly.

This technology provides a fast and easy deployment solution, and simplified management (hardware resources hot changes, very easy migration and backup, large template library).

And to make things even easier, we’ll use the famous Angristan’s OpenVPN installation script. I’ve used this script numerous times in various environments (home network, small VPS, etc) and it has proven to be very reliable in my opinion.

I tend to prefer OpenVPN for its compatibility and long-time proven reliability, even though I’ve also been using Wireguard for a few months now with my Android phone to connect to my home network remotely, and it works like a breeze.

I’ll assume you have a working Proxmox server running, and that you know your static public IP and how to forward a port on your router. Let’s get started.

LXC configuration

First things first, log in to your Proxmox Server WebUI, and if you don’t already have one running, let’s set up a LXC template repository.

There should already be one Container location available on the local Proxmox installation volume : depending on your needs and how many templates you’d like to store, this could be enough, but you may want to add another custom location for your templates storage.

So let’s add another container repo, by adding a new storage to your Proxmox Installation. On the main UI screen, go to “Datacenter”, “Storage”, and “Add”.

I’m using a CIFS storage pointing to a shared folder on my NAS in this example :

Create the share if you haven’t already done it (with read/write permissions), and fill the fields with ID, share’s address, username/password (if you have set one), and which share to use.

Pay attention to the “Content” field : select “Container template” to make this mount a CT repo. You can also use this share to store your ISOs and VMs backup dumps for instance, by selecting the corresponding options in “Content”.

This repo should now be available to store your templates. There are a few of them available by default, so go to your server’s shell in WebUI or connect to your server with SSH. List the available templates by typing :

pveam available

You should see a few templates appearing, so let’s update this by typing :

pveam update

You should get an “update successful” output, and if you retype the previous command, the template list should be way bigger. In our case, type this command :

pveam available --section system

It outputs the system images only, and you should see the ubuntu-20.04-standard_20.04-1_amd64.tar.gz image we’re going to use as a template in this guide. You could download the template from here, but let’s do it through the WebUI.

In the WebUI, go to mounted CIFS storage you added earlier, and select “CT templates”. Click “Templates” on the top, it opens a window and shows you all the available templates :

LXC Templates list in PVE

You can now download any template available here, and as stated before, we’re going to use the Ubuntu Server 20.04 one. Debian or any other distro would work here too, I’m just used to use Ubuntu Server so I’ll go with it.

OpenVPN’s container setup

So let’s create our first container :

  • Give it a name, an ID if you wish (default is fine too), and set a root password for it. Uncheck “unprivileged container“.
  • Next, choose the Ubuntu Server 20.04 template you downloaded earlier.
  • Disk : 2Gb is plenty for a small home network usage. Leave the others options to default.
  • CPU : 1 CPU core is more than enough in my case (Ryzen 2700).
  • Memory : 256Mb should be enough, for both main and swap

Regarding the network options : you can set up a static IP for your container here. You can do it this way, or use your router to assign a static IP to a specific MAC address (a MAC address will be automatically generated for your container during setup). Since all routers are different, I won’t dig into this process in this guide, so let’s assign a static IP during the container creation.

CT network settings. Adapt it to your own network configuration

We’ll keep the default vmbr0 bridge here, set the IPv4 to static, then type the desired IP in CIDR notation, according to your network configuration. Fill the “gateway” field with your router’s IP. IPv6 won’t be needed in this guide, I’ll leave it on “DHCP”.

Leave the DNS setting to default “host settings”, and tick the “Start after created” box. Review your settings, click “confirm” and the container creation will begin. On my system (on a Nvme drive) it only takes a couple of seconds.

Your container should be up and running, click on it (the list is under your host) and then on “summary”. Have a look at RAM and disk usage : it’s lightweight compared to a full VM :

Now click on “console”, login as root + the password you set during container creation. Let’s check the IP with the ip a command, it should output the right IP configuration :

If you want to change this IP later, go to the container network options and replace the current IP with the new one. As simple as that.

Now we’re going to update the system before installing anything on it :

apt update && apt upgrade

A couple minutes later, the updating process is done, and depending on what was updated you may need to reboot the container with the reboot command.

Now we could only work from the Proxmox console (you can copy/paste in it), but you can also use your favorite SSH client if you wish. Therefore you should enable root login by editing the open-ssh server configuration file :

nano /etc/ssh/sshd_config

In this file, uncomment the #PermitRootLogin prohibit-password line and change the setting to yes :

Save the change you made (Ctrl + O) and exit nano (Ctrl + X). Restart the SSH service :

systemctl restart sshd.service

And you should now be able to connect as root to your container with any SSH client. I’ll use Konsole (KDE terminal emulator) from now on.

One more step before moving to OpenVPN installation : we’ll need Curl to grab some files, and it’s not installed by default on this Ubuntu Server template. Type this command to fix it :

apt install curl

We also need to enable to tun/tap interface for our container, as this is disabled out of the box and needed to run OpenVPN. You can read more here about it. Go to your Proxmox console, and edit the container .conf file :

nano /etc/pve/lxc/xxx.conf

Replace “xxx” with your container’s ID. Now append these lines at the end of the file :

lxc.cgroup.devices.allow = c 10:200 rwm
lxc.hook.autodev = sh -c "modprobe tun; cd ${LXC_ROOTFS_MOUNT}/dev; mkdir net; mknod net/tun c 10 200; chmod 0666 net/tun"

In the same file, double check if unprivileged is set to 0, else you’ll get an error later.

Your container is now ready for OpenVPN server installation. Final touch : in the container’s options, tick the “start at boot” option, it’s self-explanatory.

Your container will start automatically at Proxmox reboot

OpenVPN Installation

Let’s install OpenVPN server using Angristan’s script :

curl -O https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh 
chmod +x openvpn-install.sh

These commands will download and make the script executable, and it’s pretty much straight forward from there, just type :

./openvpn-install.sh

…and the script will execute :

  • Address : it should detect the container’s IP address, accept
  • Public IPv4 address : since you’re most probably behind NAT, the script will detect your public IP. If not, fill it here.
  • I didn’t need to enable IPv6 support, answer no.
  • Port : default is 1194, but you can customize it here.
  • Protocol : default UDP is good.
  • DNS resolvers : default is AdGuard DNS, I’m used to use uncensored Quad9 DNS, it’s up to you.
  • Don’t enable compression.
  • Don’t customize encryption settings

The script will now install and configure all required dependencies. You’ll be asked to choose between a password or passwordless client, it depends on your needs and the level of security you want to achieve.

Once the installation is over, you will get an .ovpn file located in the same folder as the script : this is the configuration file your VPN client will be using to connect to your server.

You have various options to get the file. Linux users will use a simple scp or sftp transfert (most probably using the file browser of their choice, or terminal), Windows users can use WinSCP or MobaXTerm for instance to connect to the container to grab the file. Mac users, I don’t know cause I don’t own one, but I’m sure you’ve got options too (using MacOS’s terminal sounds like the easy path).

The two last steps before testing is setting up the firewall and port forwarding.

We’re using Ubuntu Server, so ufw is included. We’re going to add an additional layer of security, as we’ll allow only SSH and OpenVPN, then enable the firewall, and then check the enabled rules :

ufw allow 22
ufw allow 1194
ufw enable
ufw status --numbered

Finally, you have to forward port 1194 (or the one you chose during OpenVPN installation) to your newly created container. Process varies depending on your router brand and model, so please refer to the documentation of your own hardware. Basically, you have to open port 1194 on your router and forward all incoming traffic on this port to the container IP. Without doing this, you won’t be able to connect to your OpenVPN server when you’ll be outside your home LAN network.

Port forwarding on my Asus Router

Connecting to your VPN Server

The easiest way to test your server is to copy your .ovpn file on your phone for instance, and then remotely connecting to your network over 3G/4G. On my Android phone, I’ve been using this app over the years across my various devices.

On Windows machines, the official OpenVPN client works nicely.

On my Linux laptop, I’ve installed the openvpn client package and I connect directly from the terminal : sudo openvpn myprofile.ovpn

Use a site like ifconfig.me to get your public IP before and after connecting to your server, and enjoy the successful IP change result.

Conclusion

You can re-run the script as much as you want to create new profiles or to delete existing ones, so keep it in your container.

Remember to perform updates once in a while on your container, like with any other OS. Or, if you feel lazy, enable unattended-upgrades to do the work for you.

Having a working VPN server to connect to your home network while on the go always comes in handy : file transfer, remote access to non-public-facing services, securing your connection on a public hotspot, doing some maintenance…You name it. You’re basically at home, on your network, behind a tunneled encrypted connection. Happy surfing.

1 Comment

  1. Jed W.

    Following these instructions on proxmox >7.0 I ended up not being able to access the tun dev.

    I switched to https://pve.proxmox.com/wiki/OpenVPN_in_LXC for the LXC and tun setup, then back to your post for the openvpn script

Leave A Comment

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