GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage
Edit page

Setting Up An Obfuscated VPN Service (1/2)

This tutorial sets an obfuscated VPN gateway, and walks you through all the steps in a very manual way. It does not assume any previous knowledge of system administration (but being comfortable on the terminal is a plus). You will need access to a VPS provider of your choice to create new virtual machines (ideally, three, but you can make it do with two, or even one for testing purposes). If you prefer to go directly to the technical documentation, you might prefer to skip this tutorial and have a look directly at the docker compose in obfsvpn.



Last edited

25 Mar 2023


In this tutorial, we will be setting up one OpenVPN server (a.k.a "The Gateway"), and two other obfs4 bridges to connect to this gateway in an obfuscated manner.

Why on Earth would we need three extra servers to just browse the internet? Short answer: because there is more than one attacker we want protection from. First is the admin of the destination website itself: websites can log user IPs and try to use browser fingerprinting. Second is the user’s ISP: it logs the websites which the user tries to visit and can block requests at will.

The Gateway provides improved privacy for the user when browsing the public internet.

Websites see all user’s traffic as originating from the Gateway, which acts as a facade address for several people at once.

While we do try to conceal user’s true IP, at the network layer we can’t stop browser fingerprinting, this is for browser to handle.

Gateway also makes it harder for ISP to see which sites are accessed. Because user contacts only one server - the Gateway.

However, if the user is trying to access services from a censored country, it is easy for a censor to block the user from connecting to the gateway.

  1. First of all, becase OpenVPN traffic is fingerprintable.

  2. Secondly, gateways are globally listable for high amounts of outgoing traffic, or even for moderately known services like RiseupVPN.

We can’t use the OpenVPN protocol to the gateway if, for example, connecting from Iran, Russia or China.

To hide the OpenVPN protocol to the eyes of a potential censor, we are going to use another protocol, obfs4, to encapsulate (think:hide) OpenVPN data. Obfs4 is designed to look like nothing, a random encrypted flow of bytes, and is one of the most widely used Pluggable Transports used for circumvention in the Tor community nowadays.

Pluggable Transports is a community-led initiative that does research and development on new, easy to use ways to circumvent censorship around the world.

We will create a separately hosted obfs4 service to be a bridge between the user and the gateway.

User will send to it inconspicious, obfuscated bytes and then the bridge will unwrap them into an OpenVPN connection to the gateway, and then the gateway will forward them to the public internet.

To extra-confuse censors, we support automatic change of bridges at random intervals, this will be covered in Part 2 of this tutorial.

😁 😁 😁

So…​ let’s get started!

Setting up the OpenVPN Gateway

In this tutorial we are going to use a common cloud provider like Digital Ocean, just to illustrate how would you install the software in a more or less off-the-shelf service of the many available out there.

A word of caution
If you are thinking about putting your VPN service to real use (as in real life stuff), you need to think twice about where you are going to host your VPN service. This is quite important for VPNs, because while you’re trying to shift how much your ISP or other actors might interfere with your traffic, now you’ll be putting a lot of trust on the networks and hardware that your VPN service will depend on. That’s it, you’re becoming your own (VPN) provider, and you’re placing trust on the people at the datacenter. So choose carefully ;)

Log in to your cloud provider and create new machines

  1. In the create tab, select "create new droplet".

  2. Choose default ubuntu.

  3. Choose the cheapest plan (we do not need anything special for OpenVPN to work).

  4. Let’s call the droplet something meaningful, like "obfsvpn-gateway". But it’s fine if you want to use your cat name, as long as you remember 😉 .

  5. Do not finish creation yet!

Add an SSH key

This and the next section assume an unix-like environment.

  1. Click "add ssh key". You should be shown a field to put the key into.

  2. In terminal: cd .ssh.

  3. Make a new key with ssh-keygen.

  4. Copy your public key into the field.

  5. Finish making the droplet. After completion, you should be able to see and copy the IP of the new machine.

Connect to your new machine

Open your terminal, and ssh into the new box:

ssh -i yourkeyfile root@gateway-ip

After entering the passphrase that you set on ssh key generation stage, you should see a root console. We will only need to install openvpn and easy-rsa to prepare for next stage.

apt install openvpn easy-rsa

Create a Public Key Infrastructure directory for OpenVPN

Ok, now we have to generate some keys. Why? Because we want to be sure that our openvpn clients will be connecting to our gateway (and not to some other dubious machine that an adversary might have put in the middle). We also want that our openvpn server accepts only the clients that we have previously authorized. You can have a look at the Public Key infrastructure page in Wikipedia, but if you’re in a rush, the gist is basically this. You’ve done this a million times - it’s actually almost the same process than for the ssh keys we’ve just used above.

This step will store the key pairs; if you even want to change the certificate authority you should re-do this step, but also remember to generate new client certificates.

Ubuntu’s easy-rsa package does not put easyrsa on the PATH, so we copy the whole easyrsa dir to our would-be key dir:

cp -r /usr/share/easy-rsa /etc/openvpn
cd /etc/openvpn/easy-rsa
# now we can init our empty pki dir
./easyrsa init-pki

Create a Certificate Authority (CA)

When two parties want to communicate securely, they exchange public encryption keys.

However, how would one know if they use a CORRECT key that truly belongs to another party?

A TLS certificate contains not only the key itself, but also a signature from some trusted entity that this key belongs to what it claims to belong to.

In this tutorial, we will make this trusted entity, the almighty Certificate Authority all by ourselves.

With exactly one command:

./easyrsa build-ca

Copy the generated ca.crt somewhere! we will need it later on the client machine.

Generate a key for the OpenVPN Server

We generate the request and sign it:

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

Yes, this machine will be both our vpn server AND certificate authority (in a more permanent installation you probably want to take the CA offline, and just copy certificates and keys). Now, as an extra layer of security besides TLS, OpenVPN offers an extra protection (adding security to the control channel). For that we need a preshared static key. Generate it with:

openvpn --gen-key > ta.key

This ta.key we also will need to copy to client afterwards.

Finally, generate parameters for Diffie Hellman handshakes. These won’t be shared:

openssl dhparam -out dh2048.pem 2048

Compose our OpenVPN server config file

Start by copying an example config from the openvpn documentation folder.

cd ..
cp /usr/share/doc/openvpn/examples/configs/server.conf .

Then edit server.conf to look like:

proto tcp

ca easy-rsa/pki/ca.crt
cert easy-rsa/pki/issued/server.crt
key easy-rsa/pki/private/server.key

For clients, you will need pki/issued/server.crt and ca.crt.

Start OpenVPN

  • openvpn --config server.conf

  • Normal output ends with "completed"

  • If this works, stop it with ctrl+c, then

  • systemctl enable openvpn@server.service

  • systemctl start openvpn@server.service

This will make it run on background and autostart at machine boot

Enable routing to internet

Right now the packets that go to our VPN can only reach our VPN, you cannot use duckduckgo over it. To change this, we alter iptables rules.

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Setting up OBFS4 Bridges

Ok, we have a working VPN gateway. Now we setup bridges, to "connect obscurely" to the VPN.

Create a new machine

You could add the bridge in the same machine as the OpenVPN gateway for tests, but that’s not a good idea. Censors will quickly notice, in a scan, that you have one port open sitting next to some other thing that clearly is an OpenVPN server, which kind of defeats the purpose of being stealthy. In fact, in a realistic system you want these bridges to be in different IP ranges than your gateways.
  • Log into Digital Ocean

  • Make a new droplet with minimal specifications. We’ll call it obfs-bridge.

  • Note down its IP.

  • Connect to it with ssh similarly to as you did in the openvpn setup above.

  • You are root now!

Prepare binaries

apt install golang make git
git clone https://0xacab.org/leap/obfsvpn
cd obfsvpn/cmd/server
make build

Generate obfs certificate

To do a quick test, we can copy over some placeholder obfs4 keys: see Appendix to know how to generate them.

  • cp -r ../../server/test_data .

Set target gateway

In the similar vein of easy reuse, we will export some variables.

export LHOST=<your bridge ip>
export LPORT=<any port you want>
export RHOST=<gateway ip:port>
Remember to open the port for your bridge in the machine firewall.

Start the bridge!

make certs
./server -addr ${LHOST} -port ${LPORT} -vpn ${RHOST} -state test_data -c test_data/obfs4.json

Running the obfs4 client

On any machine you want to connect from:

apt install golang make git
git clone https://0xacab.org/leap/obfsvpn
cd obfsvpn
make build-client
./obfsvpn-client -c <certificate string from your obfs4.json> -r <your bridge ip, LHOST> -rp <your bridge port, LPORT>

If all goes good, this reports that the socks5 port is open at 8080. However, to be able to use it, we need to set up our openvpn client.

OpenVPN Client setup

Repeat the steps for openvpn server but stop after generation of pki dir.

  • Copy ta.key from server to openvpn folder

  • Copy ca.crt from server to pki dir

  • From /etc/openvpn/easy-rsa, generate a client request-for-certificate:

  • On client: ./easyrsa gen-req server

  • Copy it over to server’s pki/reqs directory:

  • On server: ./easyrsa sign-req client client client

  • Copy pki/issued certificate to client’s pki folder

  • Copy sample client file

  • cp /usr/share/doc/openvpn/examples/configs/client.conf .

  • Edit it to point to your files and to your gateway

proto tcp
remote <gateway host> <gateway port>

ca easy-rsa/pki/ca.crt
cert easy-rsa/pki/client.crt
key easy-rsa/pki/private/client.key
  • Check that direct connection to gateway succeeds:

  • openvpn -c client.conf

  • Check that output ends with completed.

  • On the gateway, ip addr

  • Find the IP of tun0 interface

  • On client: ping -I tun0 <gateway tun0 ip>

If that works, you can try pinging the outside internet:

ping -I tun0

Congrats, at least direct openvpn tunnel is working.

Now, let’s check the obfuscated OpenVPN tunnel!

openvpn -c client.conf --remote <obfs4_ip> <obfs4_port> --socks-proxy 8080

If it succeeds

ping -I tun0

Is it working? Yes? You are golden! No? Send us your error so we write a troubleshooting section.

Appendix: Making your own obfs4 certificate

apt install python3-pysodium
wget -O gen-shapeshifter-state.py https://0xacab.org/leap/container-platform/lilypad/-/raw/main/playbooks/scripts/gen-obfs4-state.py
python3 gen-shapeshifter-state.py statedir

And fetch your files from the statedir folder.

To the next step!
We could stop here, but this is a good moment to drink some chai, pat ourselves on the back, and proceed to the second part of the tutorial. In it, we will add another bridge and we teach our client how to jump between both bridges.