Securely Tunneling WireGuard VPN's UDP Traffic
It is sometimes not possible to establish a VPN connection due to a country or an organization's restrictions and their counter-measures. This article outlines how UDP traffic can be routed through a proxy to encrypt data, enabling the connection of a UDP-based VPN, such as those using the WireGuard protocol.
I was recently faced with an issue of being unable to establish a VPN connection in a country that used Deep Packet Inspection (DPI) to examine datagrams and their common patterns.
My project started as a simple chat about VPN’s and how simply one could host their own at home, through the use of a small computer (such as a Raspberry Pi). I set up a VPN at home that very evening with an old Raspberry Pi, using a dynamic DNS service (my ISP did not provide a static IP) and a port-forward on my router (to make it reachable over the internet).
There was just one problem, I was traveling soon and I knew VPNs were hard to connect to, due to the use of DPI, at my destination. I set out to find a way to obfuscate the data, such as using a stunnel (tunnel over SSL), but that was slow and only worked with TCP. I opted not to use a VPN over TCP since OpenVPN is a very large codebase and is quite a bit slower, compared to WireGuard.
What if there was a proxy program that could intercept the datagrams, encrypt their data, then send it to a server which is running a similar proxy program to decrypt that data and send it to where it needs to be.
Typical WireGuard Flow
D are WireGuard peers.
Proxied WireGuard Flow
D are WireGuard peers, they do not communicate directly over the internet, only the proxy servers communicate remotely with the encrypted data.
C are the proxy programs running a server on each machine.
C receive data from
D, respectively, they encrypt and send it to each other,
C receive data from each other, they decrypt it and send it to their local peers,
I loosely say the term encrypt as a means to hide the VPN data from any packet inspection. WireGuard has its own cryptography implementations so we do not need to secure our data. We just need hide the WireGuard protocol pattern (e.g. type bits and reserved bits that are prepended to every WireGuard message) from anything that may be inspecting the data.
Since the server cannot know the client’s address beforehand, it awaits a secret message to set its remote peer. Both the client and server know this secret message.
The server’s UDP handler flow looks like this:
When a UDP datagram is received: If it was sent from the locally running WireGuard server: Encrypt and send it to the client If it is the secret message: Set the client to the sender If it was sent from the client: Decrypt and send it to the locally running WireGuard peer
The client knows the server’s address beforehand, so there is no need to negotiate it.
The client’s UDP handler flow looks like this:
When a UDP datagram is received: If it was sent from the locally running WireGuard server: Encrypt and send it to the server If it was sent from the server: Decrypt and send it to the locally running WireGuard peer
# wg0.conf [Interface] PrivateKey = REDACTED Address = 10.55.33.1/32,fd11:5ee:bad:c0de::1/64 # it is important to make the IP subnet mask /32 so it is one IP MTU = 1420 ListenPort = 51820 ### begin client1 ### [Peer] PublicKey = REDACTED PresharedKey = REDACTED Endpoint = 127.0.0.1:51821 # the local proxy server is running on this address AllowedIPs = 10.55.33.2/32,fd11:5ee:bad:c0de::2/128 ### end client1 ###
# client1.conf [Interface] PrivateKey = REDACTED ListenPort = 51820 Address = 10.55.33.2/24, fd11:5ee:bad:c0de::2/64 DNS = 220.127.116.11, 18.104.22.168 MTU = 1420 [Peer] PublicKey = REDACTED PresharedKey = REDACTED AllowedIPs = 0.0.0.0/0 DisallowedIPs = 127.0.0.1/32, the.remote.server.address/32 # localhost and the remote server address Endpoint = 127.0.0.1:51821 # the local proxy server is running on this address
You might notice that
DisallowedIPs is not a valid WireGuard field. It’s purpose is to allow some IPs to not be routed through WireGuard. Using
DisallowedIPs on this allowed IP calculator will give you a valid
UFW Server Configuration
If you are using
ufw on your server which also runs a WireGuard peer, you should run the following to get the expected behaviour (you may need
sudo if you are not root).
ufw allow 51820/udp # not required unless you also want to connect without the proxy tunnel ufw allow 51821/udp # 51821 is the port that your proxy server runs on ufw route allow in on wg0 out on eth0 ufw route allow in on eth0 out on wg0
With the above concepts and configurations, one should be able to hide WireGuard traffic from any DPI and connect to their VPN. It is simple to create such UDP-forwarders with encrypt/decrypt capabilities and any custom obfuscation features that a developer wishes to implement. The resulting traffic appears to be random data with little to no identifying features. Additionally, added overhead for each datagram is up to the developer’s implementation of encryption and/or obfuscation.