OpenVPN + Obfsproxy

Obfsproxy was produced by the Tor project in order to hide Tor traffic from censors. This article will show you how to use Obfsproxy and a non-standard port to obfuscate your OpenVPN traffic. This will work in some countries and not in others.

Both server and client in this article run recent versions of Debian/Ubuntu.

1. Set Up Server

1.1. Choose Port for Obfsproxy

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

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. Implement Firewall

There are multiple ways to implement a firewall: nftables, iptables, ufw, and firewalld. The modern way is nftables. However, in a moment we are going to install OpenVPN with a script that uses iptables. Therefore we will use iptables to build our basic firewall. Issue each of these commands in turn:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j 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.

iptables -A INPUT -p tcp --dport 22 -j ACCEPT

Add a rule to open the random port you chose for Obfsproxy:

iptables -A INPUT -p tcp --dport 16273 -j ACCEPT

Drop all unexpected input:

iptables -P INPUT DROP

Save the iptables rules so that they persist after reboots:

apt update && apt upgrade -y
apt install iptables-persistent -y

1.3. Implement BBR

Bottleneck Bandwidth and Round-trip propagation time (BBR) is a TCP congestion control algorithm developed at Google. Under certain types of network congestion, it will improve your latency. Implement BBR TCP congestion control on your server with the following commands:

cat >> /etc/sysctl.d/50-bbr.conf <<EOF
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl -p /etc/sysctl.d/50-bbr.conf

1.4. Download Angristan OpenVPN Install Script

Get the Angristan OpenVPN script from GitHub:

wget https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh

Make the script executable:

chmod +x openvpn-install.sh

1.5. Run Script and Create Client OVPN File

Run the Angristan OpenVPN script:

./openvpn-install.sh

You can accept the suggested default answers to most of the questions, but Obfsproxy will require OpenVPN to use TCP rather than UDP:

  1. IP address should accurately reflect the IP address of your server
  2. Port choice should be 1194 (it will be hidden behind Obfsproxy anyway)
  3. Protocol must be TCP to use Obfsproxy
  4. For DNS servers, you can choose your favorite, e.g. Cloudflare or Google
  5. For enable compression, recommend n for no
  6. For customized encryption settings, recommend n for no

After answering all the questions, press any key to continue and run the script. At the end of the installation, you are asked about the first client:

  1. For the first client name, we will put debian10
  2. For the private key file, we will put passwordless

You can check that OpenVPN is active (running) after the script finishes:

systemctl status openvpn@server

Check also that OpenVPN is listening for TCP input on port 1194:

ss -tulpn | grep 1194

1.6. Adjust Firewall Rules

If you look at the iptables firewall, you will see that the script has opened port 1194 to the public:

iptables -vnL

We want to make the OpenVPN server as invisible as possible. Therefore we will close port 1194 to the public. In a few minutes, we’ll add Obfsproxy to handle incoming traffic and pass it to OpenVPN, on the loopback interface port 1194.

Edit the iptables rules that get added when OpenVPN is brought up:

vi /etc/iptables/add-openvpn-rules.sh

Delete the line to insert an input rule accepting packets on port 1194. Save the file.

Edit the iptables rules that get removed when OpenVPN is brought down:

vi /etc/iptables/rm-openvpn-rules.sh

Delete the line deleting the input rule accepting packets on port 1194. Save the file.

Make sure that everything still works after you reboot the server:

reboot

Check the revised iptables rules:

iptables -vnL

You should see that port 1194 is no longer open to the public. Traffic that is passed from Obfsproxy to OpenVPN will still be accepted, since it uses the loopback interface (-i lo), for which we added a special iptables rule at the beginning.

1.7. Install Obfsproxy

Install Obfsproxy from the repositories:

apt install obfsproxy -y

1.8. Configure Obfsproxy

Create an Obfsproxy systemd service file:

vi /usr/lib/systemd/system/obfsproxy.service

Insert the following lines. These make Obfsproxy listen on port 16273 and forward the unobfuscated traffic to localhost port 1194, where OpenVPN is listening:

[Unit]
Description=Obfsproxy obfs3 service

[Service]
ExecStart=/usr/bin/obfsproxy --log-min-severity=info --log-file=obfsproxy.log obfs3 --dest=127.0.0.1:1194 server 0.0.0.0:16273
Restart=always

[Install]
WantedBy=multi-user.target

Save the file.

1.9. Start Obfsproxy

Start Obfsproxy, check that it is working, and exit your SSH session with the server:

systemctl enable obfsproxy
systemctl start obfsproxy
systemctl status obfsproxy
ss -tulpn | grep 16273
exit

2. Set Up Client

2.1. Copy Client Configuration File from Server to PC

Now go to work on your PC. Assuming that your server has IP address yy.yy.yy.yy and that you named the client file debian10.ovpn, copy the OVPN file down from the server to the client like this:

scp root@yy.yy.yy.yy:debian10.ovpn ~/Downloads/debian10.conf

2.2. Install Obfsproxy Client

Install Obfsproxy from the repositories:

sudo apt update && sudo apt upgrade -y
sudo apt install obfsproxy -y

2.3. Install OpenVPN Client

Install OpenVPN for GNOME and all its dependencies from the repositories:

sudo apt install network-manager-openvpn-gnome -y

2.4. Configure OpenVPN Client

Edit the downloaded OpenVPN client configuration file:

vi ~/Downloads/debian10.conf

Change the remote port to be your server’s obfsproxy port. For example, if your server is at IP address yy.yy.yy.yy and your server’s obfsproxy port is 16273:

remote yy.yy.yy.yy 16273

Also insert a line to use Obfsproxy, which is listening on localhost port 1080:

socks-proxy 127.0.0.1 1080

Save the file.

From GNOME Settings, go to the Network page.

  1. Click the plus sign to add a VPN
  2. Select the option Import from file...
  3. Select your downloaded ~/Downloads/debian10.conf file
  4. Click Add

2.5. Add Static Route to Server

In a few minutes, we will have OpenVPN and Obfsproxy running at the same time. We must prevent a routing loop. We do not want traffic for the OpenVPN server to go to the OpenVPN client, then to the Obfsproxy client, and then back to the OpenVPN client again.

On Windows, you can add route yy.yy.yy.yy 255.255.255.255 net_gateway to the OpenVPN client configuration. This does not work on a Linux client running Network Manager due to #1417169. Network Manager for OpenVPN does not support special route keywords such as vpn_gateway or net_gateway.

Therefore on a Linux client, we will add a static route at this stage.

Determine your default gateway. In your terminal, issue the command:

ip route | grep default

Let’s say, for example, that your OpenVPN server is yy.yy.yy.yy, your default gateway is 192.168.1.254, and your real interface is enp0s3. Add a temporary static route:

sudo ip route add yy.yy.yy.yy via 192.168.1.254 dev enp0s3

The route will persist until the next reboot (or until you explicitly delete the route).

2.6. Start Obfsproxy

In your terminal, start Obfsproxy listening for SOCKS input on port 1080:

sudo obfsproxy --log-min-severity=info --log-file=obfsproxy.log obfs3 socks 127.0.0.1:1080

Leave the terminal open with obfsproxy running in it.

2.7. Test End-to-End

In GNOME Network Settings, toggle your newly added VPN connection to the ON position.

Open Firefox, and visit IP Chicken.

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

3. Get Help and Report Issues

Look in the client log file obfsproxy.log. If this does not solve your problem: