OpenVPN + Tunnelblick XOR Patch

The XOR patch for OpenVPN comes to us courtesy of the Tunnelblick team. They state that “the patch is attractive because it is so easy to implement: simply apply the patch to both the OpenVPN server and the OpenVPN client and add a single, identical option to the configuration files for each.” The XOR patch is automatically included in Tunnelblick OpenVPN software for macOS.

This article will show you how to install and configure OpenVPN with the XOR patch on a Debian server and client. You could also use a recent version of Ubuntu. We therefore sometimes refer to the server or client generically as Debian/Ubuntu.

1. Set Up Server

1.1. Choose Port for OpenVPN

On your server, choose a random port number between 10,000 and 50,000 for OpenVPN.

awk -v min=10000 -v max=50000 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

The example result that we will use in the rest of this article:

16273

1.2. Install and Configure Firewall

There are multiple ways to implement a firewall: nftables, iptables, ufw, and firewalld. The modern way is nftables, and that is what we will use here. Issue each of these commands in turn to install and start nftables:

apt update && apt upgrade -y
apt install nftables -y
systemctl enable nftables
systemctl start nftables

Configure the firewall to accept related traffic and internal traffic:

nft add rule inet filter input ct state related,established counter accept
nft add rule inet filter input iif lo counter accept

Open port 22 for SSH. If you can restrict the port 22 rule so that only certain source IP addresses are whitelisted for SSH access, then so much the better.

nft add rule inet filter input tcp dport 22 counter accept

Add a rule to open the OpenVPN port that you chose at random:

nft add rule inet filter input udp dport 16273 counter accept

Drop all unexpected input:

nft add rule inet filter input counter drop

Now add the table for Network Address Translation (NAT) and masquerading the outgoing IP adddress:

nft add table nat
nft add chain nat prerouting { type nat hook prerouting priority 0 \; }
nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule nat postrouting masquerade

Save the rules:

nft list ruleset > /etc/nftables.conf

1.3. Allow Forwarding

Enable IPv4 forwarding in the Linux kernel. Edit the system control configuration file:

vi /etc/sysctl.conf

Uncomment the line:

net.ipv4.ip_forward=1

Save the file. Apply the new settings by issuing the command:

sysctl -p

1.4. Get OpenVPN and XOR Patch Source

You must use matching versions of OpenVPN and the XOR patch.

Open a browser and visit the GitHub releases page for OpenVPN. Determine the latest release number. At the time of writing, it is version v2.5_beta3. It may be a later version at the time you read this article. We will use v2.5_beta3 in our examples, though you may need to replace this.

Download the OpenVPN tarball for your version:

wget https://github.com/OpenVPN/openvpn/archive/v2.5_beta3.tar.gz
tar -xvf v2.5_beta3.tar.gz

Download and extract the Tunnelblick repository:

wget https://github.com/Tunnelblick/Tunnelblick/archive/master.zip
apt install unzip -y
unzip master.zip

1.5. Apply Patches

Copy the patch files into the OpenVPN directory, replacing openvpn-2.5_beta3 by the current version at the time you run this:

cp Tunnelblick-master/third_party/sources/openvpn/openvpn-2.5_beta3/patches/*.diff openvpn-2.5_beta3

Apply the patches to the OpenVPN source:

cd openvpn-2.5_beta3
apt install patch -y
patch -p1 < 02-tunnelblick-openvpn_xorpatch-a.diff
patch -p1 < 03-tunnelblick-openvpn_xorpatch-b.diff
patch -p1 < 04-tunnelblick-openvpn_xorpatch-c.diff
patch -p1 < 05-tunnelblick-openvpn_xorpatch-d.diff
patch -p1 < 06-tunnelblick-openvpn_xorpatch-e.diff
patch -p1 < 10-route-gateway-dhcp.diff

1.6. Build OpenVPN with XOR Patch

Install the prerequisites for the build:

apt install build-essential libssl-dev iproute2 liblz4-dev liblzo2-dev libpam0g-dev libpkcs11-helper1-dev libsystemd-dev resolvconf pkg-config autoconf automake libtool -y

Compile and install OpenVPN with the XOR patch:

autoreconf -i -v -f
./configure --enable-systemd --enable-async-push --enable-iproute2
make
make install

The program is installed to /usr/local/sbin/openvpn.

1.7. Create Configuration Directories

Create directories that will store your OpenVPN key, certificate, and configuration files:

mkdir -p /etc/openvpn/{server,client}

1.8. Create Keys and Certificates with EasyRSA

Install the EasyRSA package:

apt install easy-rsa -y

On Debian 10 and Ubuntu 20.04, this installs EasyRSA 3.0.6.

Make a copy of the EasyRSA scripts and configuration files:

cp -r /usr/share/easy-rsa ~
cd ~/easy-rsa

Make a copy of the example variables:

cp vars.example vars

You can edit the vars file if you wish, but we will just use the defaults. Initialize the public key infrastructure:

./easyrsa init-pki

Build your Certificate Authority (CA):

./easyrsa build-ca nopass

Give the CA a common name of your choosing, or just press Enter to accept the default name of Easy-RSA CA.

Generate and sign your server key and certificate. We use the example server name of server in the example below:

./easyrsa gen-req server nopass
./easyrsa sign-req server server
yes

Generate and sign your client key and certificate. We use the example name debian10 in the example below. You can change this to a name of your own choosing.

./easyrsa gen-req debian10 nopass
./easyrsa sign-req client debian10
yes

Generate the Diffie-Hellman parameters. This can take a long time.

./easyrsa gen-dh

Generate a preshared key to encrypt the initial exchange:

openvpn --genkey secret pki/tls-crypt.key

Copy all the keys and certificates into position in the OpenVPN directory:

cp pki/ca.crt /etc/openvpn
cp pki/private/server.key /etc/openvpn/server
cp pki/issued/server.crt /etc/openvpn/server
cp pki/private/debian10.key /etc/openvpn/client
cp pki/issued/debian10.crt /etc/openvpn/client
cp pki/tls-crypt.key /etc/openvpn
cp pki/dh.pem /etc/openvpn

1.9. Generate Scramble Obfuscation Code

For the scrambling obfuscation, generate a 192-bit (24-byte) code, expressed as 32 base-64 characters:

openssl rand -base64 24

The example result that we will use in the rest of this article:

r7EaFR2DshpQT+QMfQGYO5BXC2BAV8JG

1.10. Configure OpenVPN Server

Edit the OpenVPN configuration file:

vi /etc/openvpn/server.conf

The model for you to adapt to your situation is as follows.

port 16273
proto udp
dev tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/dh.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /etc/openvpn/ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 120
cipher AES-128-GCM
tls-crypt /etc/openvpn/tls-crypt.key
persist-key
persist-tun
status openvpn-status.log
verb 3
scramble obfuscate r7EaFR2DshpQT+QMfQGYO5BXC2BAV8JG

Save the file.

1.11. Configure Systemd

Create a systemd service file for OpenVPN:

vi /lib/systemd/system/openvpn@.service

Insert contents like this:

[Unit]
Description=OpenVPN connection to %i
PartOf=openvpn.service
ReloadPropagatedFrom=openvpn.service
Before=systemd-user-sessions.service
After=network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO

[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn
ExecStart=/usr/local/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --config /etc/openvpn/%i.conf --writepid /run/openvpn/%i.pid
PIDFile=/run/openvpn/%i.pid
KillMode=process
ExecReload=/bin/kill -HUP $MAINPID
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
LimitNPROC=100
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
RestartSec=5s
Restart=on-failure

[Install]
WantedBy=multi-user.target

Save the file.

Create the directory for the process identification (pid) file:

mkdir /run/openvpn

1.12. Start OpenVPN

Start OpenVPN on the server:

systemctl enable openvpn@server
systemctl start openvpn@server

Check that it is active and listening on the expected port:

systemctl status openvpn@server
ss -tulpn | grep openvpn

Server work is done:

exit

2. Set Up Client

2.1. Download Keys and Certificates

Now go to work on your PC. Assuming that your server has IP address yy.yy.yy.yy and that you named the client key and certificate debian10.*, copy the required files down from the server to the client like this:

scp root@yy.yy.yy.yy:/etc/openvpn/client/debian10.key ~/Downloads/debian10.key
scp root@yy.yy.yy.yy:/etc/openvpn/client/debian10.crt ~/Downloads/debian10.crt
scp root@yy.yy.yy.yy:/etc/openvpn/ca.crt ~/Downloads/ca.crt
scp root@yy.yy.yy.yy:/etc/openvpn/tls-crypt.key ~/Downloads/tls-crypt.key

2.2. Get OpenVPN and XOR Patch Source

For a Debian/Ubuntu client, this is the pretty much same process as on the server. The version number of OpenVPN and the XOR patch will be the same as on the server. We will use v2.5_beta3 in our examples, though you may need to replace this.

Download the OpenVPN tarball for your version:

cd ~/Downloads
wget https://github.com/OpenVPN/openvpn/archive/v2.5_beta3.tar.gz
tar -xvf v2.5_beta3.tar.gz

Download the Tunnelblick repository:

wget https://github.com/Tunnelblick/Tunnelblick/archive/master.zip
sudo apt install unzip -y
unzip master.zip

2.3. Apply Patches

Copy the patch files into the OpenVPN directory, replacing openvpn-2.5_beta3 by the current version:

cp Tunnelblick-master/third_party/sources/openvpn/openvpn-2.5_beta3/patches/*.diff openvpn-2.5_beta3

Apply the patches to the OpenVPN source:

cd openvpn-2.5_beta3
sudo apt install patch -y
patch -p1 < 02-tunnelblick-openvpn_xorpatch-a.diff
patch -p1 < 03-tunnelblick-openvpn_xorpatch-b.diff
patch -p1 < 04-tunnelblick-openvpn_xorpatch-c.diff
patch -p1 < 05-tunnelblick-openvpn_xorpatch-d.diff
patch -p1 < 06-tunnelblick-openvpn_xorpatch-e.diff
patch -p1 < 10-route-gateway-dhcp.diff

2.4. Build OpenVPN with XOR Patch

Install the prerequisites for the build:

sudo apt install build-essential libssl-dev iproute2 liblz4-dev liblzo2-dev libpam0g-dev libpkcs11-helper1-dev libsystemd-dev resolvconf pkg-config autoconf automake libtool -y

Compile and install OpenVPN with the XOR patch:

autoreconf -i -v -f
./configure --enable-systemd --enable-async-push --enable-iproute2
make
sudo make install

The program is installed to /usr/local/sbin/openvpn.

2.5. Fix DNS Resolution

There is a problem with the pushed DNS servers being ignored in OpenVPN on Linux. The solution depends on what is managing the nameservers. Here is a solution that worked on a Debian 10 client with NetworkManager. There is an alternative solution for Ubuntu with Systemd.

Edit the NetworkManager configuration:

sudo vi /etc/NetworkManager/NetworkManager.conf

In the [main] section, insert a line:

dns=none

Save the file. Then edit the /etc/resolv.conf file:

sudo vi /etc/resolv.conf

Change the contents of the file to specify nameservers that will be accessible while the VPN is on, e.g.:

nameserver 8.8.8.8
nameserver 8.8.4.4

Save the file. Restart NetworkManager:

sudo systemctl restart NetworkManager

2.6. Create OpenVPN Client Configuration File

Create a client configuration file for OpenVPN:

cd ~/Downloads
vi debian10.conf

Insert the configuration details below, tailoring them to your situation:

client
dev tun
proto udp
remote yy.yy.yy.yy 16273
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert debian10.crt
key debian10.key
remote-cert-tls server
cipher AES-128-GCM
tls-crypt tls-crypt.key
verb 3
scramble obfuscate r7EaFR2DshpQT+QMfQGYO5BXC2BAV8JG

Save the file.

2.7. Start OpenVPN Client

Open a terminal on your client PC, and start OpenVPN running:

cd ~/Downloads
sudo /usr/local/sbin/openvpn --config debian10.conf

Leave the terminal open with OpenVPN running in it.

2.8. Test End-to-End

Open Firefox.

Visit IP Chicken.

You should see the IP address of your remote server, not your local client.

3. Get Help and Report Issues