Creating Your Own Superpowered VPN Server

Tuesday, May 9, 2017 @ 5:23 pm

I’ve always been wary of connecting to public wifi hotspots, so the past few days I’ve been playing around with setting up a VPN server for personal use. It took a while, but I’m quite pleased with the result and wanted to share the steps I took.


  • 2017-05-24: updated adblock bash script to properly reload dnsmasq
  • 2017-05-16: updated OpenVPN client configuration to use remote-cert-tls instead of the deprecated ns-cert-type directive (MITM protection)
  • 2017-05-12: added instructions on how to add OpenVPN repo to get latest version (2.4+), optimized cipher/digest choices for reasonable security/speed, added FAQs section

This guide will give you:

  • the ability to use any wifi connection securely and privately, and access any site you wish
  • the ability to circumvent attempts to block/throttle VPN usage through disguising your highly-encrypted data as ordinary HTTPS Internet traffic, even bypassing deep packet inspection (*cough* China)
  • protection against DNS spoofing using secure resolvers located in Iceland
  • not one, not two, but three layers of protection against MITM (man-in-the-middle) attacks (courtesy of OpenVPN, Stunnel, and DNSCrypt)
  • no/minimal logging by the server, in case you have snoopy server admins
  • ad/tracker blocking with a self-updating blocklist, as a bonus

The main reason why this works is because TCP port 443 is used universally for SSL-enabled websites (e.g. A standalone instance of OpenVPN configured on this port would be sufficient to work with most wifi hotspots.

However, hotspot providers/organizations may use deep packet inspection (DPI) on TCP 443 to determine whether or not the traffic is VPN-related, and throttle/block if it is.

Because of this, I’ve added Stunnel to the mix, which disguises VPN traffic to look like valid SSL traffic, thwarting DPI attempts.

Server configuration details:

  • Stunnel listening publicly on port 443 (TCP)
  • OpenVPN listening locally on 1194 (TCP)
    • VPN clients in the subnet
  • Dnsmasq using Steven Black’s unified blocklist
  • DNSCrypt using resolvers located in Iceland (running on and

When a client connects to the server, the data flow is:

  • Data => OpenVPN (encrypt data) => Stunnel (mask encrypted OpenVPN data)

Then the data is sent to our server and processed in this manner:

  • Stunnel (unmask encrypted OpenVPN data) => OpenVPN (decrypt data) => Dnsmasq (block if an ad/tracker, else allow) => DNSCrypt (send DNS request to a secure DNS resolver)

Once the server retrieves data, it is then encrypted and masked, and subsequently sent back to the client.

Security details:

  • Handshake: TLSv1.2+, cipher TLSv1/SSLv3 DHE-RSA-AES256-GCM-SHA384, 4096 bit RSA
  • Encryption: AES-128-GCM (not 256 because of this)
  • Authentication: (HMAC-)SHA1 (not SHA256 or SHA512 because of this)

Alternative to this guide is Streisand, which automates much of the work in setting up various services to bypass censorship/secure connections. I chose to not use Streisand, and instead set my VPN server up manually to use only the essential services I needed for my purposes.


This guide is geared towards Debian 8, but it also applies to Ubuntu servers. Much of it can be adapted to other Linux distribution systems.

You must be running as root with elevated privileges.

Let’s ensure that we grab the latest OpenVPN version by doing the following:

wget -O -|apt-key add -
echo "deb jessie main" > /etc/apt/sources.list.d/openvpn-aptrepo.list

IMPORTANT: the second command above assumes you are on Debian 8.0. If you are not, check this page for the correct value:

apt-get update && apt-get upgrade

If any packages need upgrading, press y to upgrade them before we proceed.

apt-get install openvpn easy-rsa stunnel4 dnsmasq ufw

Set the Server Hostname

For the purposes of this guide, we’ll be using jackiechan as our server hostname throughout. Feel free to search/replace jackiechan with your desired server name:

hostname jackiechan

The above command will change your server’s hostname to jackiechan.

Disable IPv6

It’s recommended to disable IPv6 if you’re not using it.

nano /etc/sysctl.d/99-sysctl.conf

Add the following lines to the end of the file:

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1
net.ipv6.conf.tun0.disable_ipv6 = 1

Press CTRL+X, Y, then Enter to save the file <= please remember these steps: throughout this guide you will be doing this a lot (“save and exit the file”).

Activate the changes:

sysctl -p

Go into /etc/hosts:

nano /etc/hosts

Add a # in front of ::1 localhost ip6-localhost ip6-loopback

#::1     localhost ip6-localhost ip6-loopback

Save and exit the file.

Next let’s add a rule to reject all v6 traffic:

nano /etc/iptables/rules.v6

Type/paste in the following:




Enforce the ruleset:

ip6tables-restore < /etc/iptables/rules.v6


This is perhaps the more complicated part of this guide due to all the certificates and keys, but once we get through this, the other sections are much simpler.

Let’s generate our server configuration file. You can change jackiechan.conf to anything you want, as long as it ends with .conf:

nano /etc/openvpn/jackiechan.conf

Type/paste in the following configuration:

port 1194
proto tcp
dev tun
ca ca.crt
cert jackiechan.crt
key jackiechan.key
dh dh4096.pem
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS"
push "block-outside-dns"
keepalive 10 120
tls-auth /etc/openvpn/easy-rsa/keys/ta.key 0
tls-version-min 1.2
cipher AES-128-GCM
auth SHA1
user nobody
group nogroup
verb 0
status /dev/null
log /dev/null

Save and exit the file.

Now let’s take care of the certificates and keys.

cp -r /usr/share/easy-rsa/ /etc/openvpn
mkdir /etc/openvpn/easy-rsa/keys
nano /etc/openvpn/easy-rsa/vars

In this file we’ll be changing a few things. First, let’s bump the KEY_SIZE to 4096:

# Increase this to 2048 if you
# are paranoid.  This will slow
# down TLS negotiation performance
# as well as the one-time DH parms
# generation process.
export KEY_SIZE=4096

Optionally, customize the certificate details (default values are shown):

export KEY_CITY="Dallas"
export KEY_ORG="My Company Name"
export KEY_EMAIL=""
export KEY_OU="MYOrganizationalUnit"

Also optional, change the KEY_NAME value to your desired server name (default is “EasyRSA“):

# X509 Subject Field
export KEY_NAME="jackiechan"

Save and exit the file.

Run the following command (note it will take a few minutes to finish):

openvpn --genkey --secret /etc/openvpn/easy-rsa/keys/ta.key
openssl dhparam -out /etc/openvpn/dh4096.pem 4096

Next we will jump right into the fun part: key generation. Let’s go into the /etc/openvpn/easy-rsa folder and then we can get started:

cd /etc/openvpn/easy-rsa


source ./vars

Press enter to each prompt.

Let’s build the certificate and key for the server, assuming that our desired server name is “jackiechan“:

./build-key-server jackiechan

Press enter to all prompts, then press y for the last two prompts:

Sign the certificate? [y/n]
1 out of 1 certificate requests certified, commit? [y/n]

You should then see:

Write out database with 1 new entries
Data Base Updated

Next we’ll copy our certificates and key into the right folder:

cp /etc/openvpn/easy-rsa/keys/{jackiechan.crt,jackiechan.key,ca.crt} /etc/openvpn

We’ve finished configuring our server, now let’s create a client. We’ll name it client1:

./build-key client1

Press enter to all prompts (leave challenge password blank), then press y for the last two prompts:

Sign the certificate? [y/n]
1 out of 1 certificate requests certified, commit? [y/n]

Now that we have created both a server and client, we can create our OpenVPN client configuration file.

touch /etc/openvpn/easy-rsa/keys/client.ovpn
echo '<ca>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
cat /etc/openvpn/ca.crt >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '</ca>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '<cert>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
cat /etc/openvpn/easy-rsa/keys/client1.crt >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '</cert>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '<key>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
cat /etc/openvpn/easy-rsa/keys/client1.key >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '</key>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '<tls-auth>' >> /etc/openvpn/easy-rsa/keys/client.ovpn
cat /etc/openvpn/easy-rsa/keys/ta.key >> /etc/openvpn/easy-rsa/keys/client.ovpn
echo '</tls-auth>' >> /etc/openvpn/easy-rsa/keys/client.ovpn

Almost done. We’ll need to add some lines to our client profile:

nano /etc/openvpn/easy-rsa/keys/client.ovpn

At the bottom of the file, starting from a new line type/paste in:

dev tun
proto tcp
remote 1194
resolv-retry infinite
user nobody
group nogroup
remote-cert-tls server
cipher AES-128-GCM
auth SHA1
verb 3
key-direction 1

Save and exit the file.

Congratulations, you made it through the hard part!

IMPORTANT: grab /etc/openvpn/easy-rsa/keys/client.ovpn from the server onto your local machine as we’ll be using it near the end of this guide when we set up our client devices.


cd /etc/stunnel/
openssl genrsa -out stunnel.key 2048
openssl req -new -x509 -key stunnel.key -out stunnel.crt -days 3650

IMPORTANT: when prompted for a Common Name, enter the hostname of your server (e.g. jackiechan).

Let’s create a stunnel.p12 file which we will be using to set up our Android device as a client:

openssl pkcs12 -export -out stunnel.p12 -inkey stunnel.key -in stunnel.crt

Leave “Export Password” blank when prompted twice. Next let’s configure the Stunnel server:

nano stunnel.conf

Type/paste in the following:

cert = /etc/stunnel/stunnel.crt
key = /etc/stunnel/stunnel.key
syslog = no
debug = 0

accept = 443
connect =

Save and exit the file. Let’s enable Stunnel – run the below command:

nano /etc/default/stunnel4

Change ENABLED=0 to ENABLED=1, then save and exit the file.

That’s it for Stunnel!


Next we’ll make some updates to a couple of files to allow our server to forward traffic from clients to the Internet.

Run the below commands:

echo 1 > /proc/sys/net/ipv4/ip_forward
nano /etc/sysctl.conf





Save and exit the file.

nano /etc/ufw/ufw.conf



And optionally set LOGLEVEL to off (the default value of low will log all blocked packets, which does take up space):


Save and exit the file.

nano /etc/default/ufw





Save and exit the file.

nano /etc/ufw/before.rules

Above the line:

# Don't delete these required lines, otherwise there will be errors


# NAT table rules
# Allow traffic from OpenVPN client to eth0

Save and exit the file.

Run the following commands to open up port 443 (for Stunnel) and 53 (for VPN clients to use DNSCrypt resolvers).

ufw allow proto tcp from any to any port 443
ufw allow from to any port 53

Optionally, if you would also like to limit SSH connections only to clients connected via VPN, run:

ufw allow from to any port 22


We’ll be leveraging a wonderful autoinstall package which takes care of a lot of work in getting DNSCrypt up and running on our server.

Run the following commands:

chmod +x dnscrypt-autoinstall

Follow all the prompts and press y to anything when prompted. When asked for a resolver, you can leave it at the default (

After it’s all done, let’s configure DNSCrypt:

nano /etc/systemd/system/dnscrypt-autoinstall.conf

Change the values to the following:


Save and exit the file. Note: I chose resolvers based in Iceland due to its strict privacy laws. If you want to use different resolvers, you can view:

Dnsmasq and Adblocking

Next let’s set up Dnsmasq to use DNSCrypt, as well as sprinkle in some adblocking.

update-rc.d dnsmasq enable
mv /etc/dnsmasq.conf /etc/dnsmasq-orig.conf
nano /etc/dnsmasq.conf

Type/paste in the following:


Save and exit the file.

We’ll be creating a simple script that grabs Steven Black‘s unified+fake news blocklist variant (I hate fake news) and restarts dnsmasq to load the latest domains.

nano /home/adblockget

Paste/type in the below code:


nc -z 53  >/dev/null 2>&1

if [ $online -eq 0 ]; then
    wget -qO- --no-check-certificate $blocklistdl \
    | sort -u \
    | awk '/^0/ {print}' \
    > $newfile
    new=$(wc -l < $newfile)
    echo "Downloaded blocklist. Checking if empty."
    if [ $new -gt 0 ]; then
        echo "Blocklist is not empty. Checking if updated."
        if cmp -s "$goodfile" "$newfile"
            rm $newfile
            echo "No updates detected."
            good=$(wc -l < $goodfile)
            mv $newfile $goodfile
            /etc/init.d/dnsmasq reload
            echo "Successfully applied blocklist: $new unique domains (formerly $good)."
        rm $newfile
        echo "Downloaded file was empty."

Feel free to visit for other host file variants and update the blocklistdl variable URL in the above code.

Save the new file then run:

chmod +x /home/adblockget

Next we will schedule a cron job so the latest blocklists are downloaded daily.

Run the following command:

crontab -e -u root

Add a new line:

0 12 * * * /home/adblockget > /dev/null

The above will run our script to grab the latest blocklist at 12:00pm every day.

Make It Live

Run the following commands to make all our hard work live:

ufw disable
ufw enable
service dnscrypt-autoinstall restart
service dnsmasq restart
service stunnel4 restart
service openvpn restart


For each client we will need both Stunnel and OpenVPN.

VPN on Windows


Download and install the Stunnel Windows client:

Go to the Stunnel config folder (C:\Users\**YOUR USERNAME**\AppData\Local\stunnel\config or C:\Program Files (x86)\stunnel\config)

Grab /etc/stunnel/stunnel.crt from the server and save it in this \config folder

In the folder, open stunnel.conf in your favourite text editor

Paste in the following:

client = yes
accept =
connect =
verifyChain = yes
CAfile = stunnel.crt
checkHost = jackiechan

In the Start Menu, find stunnel and run “stunnel GUI Start”. It will add a new icon in your system tray. If you open it, the bottom-most lines should be:

2017.05.09 10:49:37 LOG5[main]: Reading configuration from file stunnel.conf
2017.05.09 10:49:37 LOG5[main]: UTF-8 byte order mark detected
2017.05.09 10:49:37 LOG5[main]: FIPS mode disabled
2017.05.09 10:49:37 LOG5[main]: Configuration successful


Download, install, and run the OpenVPN Windows client: (“Installer, Windows Vista and later”)

Remember the client.ovpn file I told you to grab from the server? (/etc/openvpn/easy-rsa/keys/client.ovpn) Its time to shine is now. Find it and copy it to C:\Program Files\OpenVPN\config (or wherever you installed OpenVPN)

Optionally you can rename it to give it a more exciting/meaningful name instead of “client”.

The moment of truth: right-click on the OpenVPN tray icon again and connect to it.

Go to to see if it’s working.

To test if the adblocking is working, try going to it should be blocked.

VPN on Android

Download and install:

Grab /etc/stunnel/stunnel.p12 from your server and copy it, along with client.ovpn (/etc/openvpn/easy-rsa/keys/client.ovpn on your server) onto your Android device.

On your Android, go to Settings => Security => Credential storage => Install from SD card.

Find the stunnel.p12 file you copied and tap on it.

When asked for a password, leave it blank (remember we didn’t set one).

It’ll ask you if you want to set the name: tap OK to leave it as is.

Configure TLS/SSL Tunnel

  1. Open the “Tunnel” app
  2. Tap on the options icon in the top right corner and then “New”:
    1. Name: enter any name you want (e.g. jackiechan)
    2. Connect to: yourServerIPHere:443
    3. Local port: 1194
    4. Root-Certificate: predefined by Android
    5. Tick the three options (Avoid SSL Degration [sic], Force AES usage, Enforce perfect forward secrecy)
  3. Tap Save
  4. You’ll be brought back to the main screen
  5. Long-tap on the entry you just added and the bar to the right of it should turn green

VERY IMPORTANT: do NOT tap “Back” to get out of the app – it will close the tunnel. You MUST tap Home or switch to another window.

Configure OpenVPN

  1. Open the “OpenVPN for Android” app
  2. Tap on the + in the top right corner
  3. Tap on Import
  4. Find the “client.ovpn” file and tap on it
  5. You have the option to give your profile a custom name, feel free to change it to something more meaningful
  6. Tap the save icon
  7. Tap on the pencil icon beside the profile we just imported
  8. Go to the “Allowed Apps” tab
  9. Tick the “Tunnel” app
  10. Press Back to save your update

The moment of truth: single tap on your profile to connect to your VPN.

Go to to see if it’s working. To test if the adblocking is working, try going to it should be blocked.

Creating a Kill-Switch

This is a completely optional, but recommended step. Once you connect to wifi, some apps wait for a connection to be established and immediately start transferring/retrieving data. This is potentially dangerous when you’re connecting to a public wifi/hotspot.

Creating a kill-switch requires your Android device to be rooted. Ignore this section if it isn’t rooted.

  1. Install AFWall+:
  2. Open it and grant root access when requested
  3. Tap on the options icon (the three dots) in the top-right corner
  4. Tap Preferences
  5. Tap Rules/Connectivity
  6. Enable:
    1. Active rules
    2. VPN control
    3. Block IPv6
    4. Everything else should be unticked
  7. Press Back once
  8. Tap Profiles
  9. Enable “Multiple profles”
  10. Tap Manage profiles
  11. Add/rename one of the non-default profiles to “VPN Only”
  12. Remove any extra profiles by long-tapping on them
  13. Press Back three times to go back to the main screen
  14. At the top, tap the Profile drop-down and select “VPN Only”
  15. Tap on the “three horizontal lines with an x” icon in the top bar and select “Block selected”
  16. Tap on the wifi column heading icon
  17. Tap “Check all”
  18. (Optional) Tap on the data column heading icon (up/down arrow) and tap “Check all”
  19. Scroll through the list and uncheck any checked boxes for the following entries to allow them:
    1. CaptivePortalLogin – needed to connect to public hotspots that use captive login
    2. Data Usage Provider […] – needed to connect to wifi
    3. OpenVPN for Android
    4. Tunnel
    5. Any entry starting with a bracket (e.g. (gps) – GPS)
    6. Your preferred browser app
  20. Tap on the options icon (the three dots) in the top-right corner and tap either Save or Apply
  21. At the top, tap the Profile drop-down and select “Default”
  22. Tap on the options icon (the three dots) in the top-right corner and tap Enable
  23. Go back to your home screen and add the “AFWall+ Settings (Old)” widget. There are two, but I find the old variant simpler to use.

How to use:

  1. When you’re expecting to connect to a public wifi hotspot, tap on the AFWall+ widget on your home screen
  2. Activate the “VPN Only” profile
  3. Connect to the hotspot
  4. Open your browser to enter a login or tap “Continue” (in order to use the hotspot)
  5. Proceed to start the tunnel (using the “Tunnel” app; remember to not tap back – tap home or switch to another window)
  6. Once the tunnel is started, open OpenVPN and tap on your profile to connect to your VPN

Only once connected to VPN will your apps will start functioning properly over an encrypted tunnel, thanks to the rules we set in AFWall+.



How do I improve VPN speeds?

If you’d like, you are able to run a second OpenVPN instance on your server which doesn’t require Stunnel. The probability of this connection being blocked is higher, but traffic overhead is reduced:

1. Create a new OpenVPN server file

We will assume we want to name our second OpenVPN instance “ninja”, and have it run on port 20 using the UDP protocol. It will not use Stunnel to further reduce overhead (we can’t use Stunnel anyways because it’s not on the TCP protocol). More importantly, we will assume we want it to use the same certificate and key for the server we created above for ease of use.

cp /etc/openvpn/jackiechan.conf /etc/openvpn/ninja.conf

2. Configure the new server

nano /etc/openvpn/ninja.conf
  • proto: change tcp to udp
  • remote: change 1194 to 20
  • server: change to

Save and exit the file.

3. Run a second OpenVPN instance

systemctl start openvpn@ninja
systemctl enable openvpn@ninja.service

Note: the name in the commands must match the name we gave to the file in step #1.

4. Update the firewall to allow connections

ufw allow 20/udp

5. Create a new OpenVPN client configuration

Copy the client.ovpn file we created above, rename it to ninja.ovpn, open it in your favourite text editor and modify:

  • proto: change tcp to udp
  • remote: change 1194 to *YourServerIP* 20

Save and now you have two configurations you can choose from. The beauty of this is that you can attempt to connect to “ninja” first on a connection. If it works, you’ll get quick and light access.

If it is blocked, you can open an Stunnel tunnel and connect to “jackiechan” which is guaranteed to work in most cases.

Why AES-128-GCM and SHA-1?

AES-128-GCM and SHA-1 were chosen instead of AES-256, SHA-256/SHA-512 because of practicality.

Why AES-128: “The attack exploits the fact that the key schedule for 256-bit version is pretty lousy — something we pointed out in our 2000 paper — but doesn’t extend to AES with a 128-bit key.”

Why (HMAC-)SHA-1: “HMAC itself is resistant to collisions if they are found”. OpenVPN uses HMAC SHA-1, not SHA-1. In addition, “No known extensions attacks have been found against the current HMAC specification which is defined as H(key ∥ H(key ∥ message)) because the outer application of the hash function masks the intermediate result of the internal hash”.

Why AES-GCM and not AES-CBC: “Use an authenticated encryption algorithm, like AES-GCM, that combines checksumming and ciphering”.

Psychologically there is always the “bigger is better” perception (“Pfft, SHA1? That’s puny. I’m going to go with SHA512! It’s 512x better!”). You can choose to customize the configuration in this blog post if you wish, but I recommend AES-128-GCM and SHA1.

This is awesome, I can now go ahead and do illegal things via VPN right?

I don’t condone illegal activities, and the main reason behind having a VPN server is to protect myself against having my traffic logged when using public wifi.

When using a VPN, you’re just changing the actual requestor for your destination data. Without a VPN, the requestor would be the public wifi hotspot provider or your Internet service provider. With a VPN, the requestor is your VPS or server that OpenVPN is set up on.

Your requestor sends your request to the destination, and then data from your destination is sent back to the requestor, which is then sent back to your device.

What this means is that the requestor has full access to your data (with the exception of traffic sent over HTTPS), and you must ask yourself: do you trust the public wifi hotspot provider or your Internet service provider more than the server your VPN is set up on?

In most cases personally identifiable information is required to purchase a server, and not through anonymous means (e.g. Bitcoin), so do assume that activity done through the VPN can be traced back to you.