As I mentioned in a previous post, I have a friend who’s heading to China. I have an OpenVPN server. I thought the two would match together well, but then China went and started to filter & kill OpenVPN connections, and block those IP/port combinations. People are reporting that using a random port (as supported by their VPN provider) seems to work, and so I looked into randomizing what port OpenVPN ran on.
The trick that I found was not to make OpenVPN run on multiple ports, but instead to get iptables to forward any and all connections coming into a range of ports to the OpenVPN port. However, since I wanted OpenVPN to be running on both UDP and TCP ports, I had to start another instance, which necessitated more iptables config changes.
First off was getting iptables to forward a port range to the OpenVPN port. To do that, I used Destination NATing, or DNAT:
iptables -t nat -A PREROUTING -p udp --match multiport --dport 10000:40000 -j DNAT --to ip.add.re.ss:1194 iptables -t nat -A PREROUTING -p tcp --match multiport --dport 10000:40000 -j DNAT --to ip.add.re.ss:1194
I’m forwarding both UDP and TCP ports because I’ll have two instances of OpenVPN up and running, one listening on UDP and the other listening on TCP. (As a side note, mangling the output isn’t needed because we’re just changing the destination of the packet – to OpenVPN, it’ll appear like the client is connecting directly to it, so it’ll just send the response back to the client’s port.)
Now, if I was just leaving UDP active, that would be enough, since my existing OpenVPN config works fine over UDP. However, additional configuration is needed to get OpenVPN working on TCP. In particular we need to:
- Configure the TCP instance of OpenVPN not to clobber the UDP instance’s history & log files
- Configure OpenVPN to use a new subnet
- Configure iptables to properly forward packets from the new subnet
- Configure iptables to accept connections to the TCP port OpenVPN will run on
Thankfully, most of it is straightforward. I’m going to go for simplicity and just include the entire OpenVPN server-side config file below:
local ip.add.re.ss port 1194 proto tcp dev tun ca keys/ca.crt cert keys/server.crt key keys/server.key dh keys/dh1024.pem server 10.10.0.0 255.255.255.0 ifconfig-pool-persist ipptcp.txt push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 220.127.116.11" push "dhcp-option DNS 18.104.22.168" keepalive 5 30 comp-lzo persist-key persist-tun status tcp-server-tcp.log verb 3 log /var/log/openvpn-tcp.log tls-auth keys/ta.key 0 link-mtu 1400
The key changes here are to the
status options – I’ve just appended
-tcp to any filenames. I’ve also declared a new subnet –
10.10.0.0/24 for the TCP instance, so it doesn’t conflict with the UDP instance, as well as changing the protocol to
tcp. (Despite people saying otherwise, having proto tcp on the server seems to work.)
I’ve also changed the link-mtu option simply to make it harder for pure protocol detection to detect the packets as OpenVPN packets.
As for the client side,
client dev tun proto tcp-client remote-randomremote ip.add.re.ss 1194resolv-retry infinite nobind persist-key persist-tun ca ca.crt cert client1.crt key client1.key tls-auth ta.key 1 ns-cert-type server comp-lzo verb 3 keepalive 10 120 route-method exe route-delay 2 register-dns link-mtu 1400
Again, the only thing really changed was the proto option – from udp to tcp-client. I’ve also set the link-mtu option to something other than the default, as well as added the remote-random option – this will be explained in a bit.
On the iptables side of things, I ran a few commands:
# Allow packets from the new subnet to make it out to the Internet iptables -A FORWARD -s 10.10.0.0/24 -j ACCEPT # Change the source address on outgoing packets from the new subnet to be the VPS's IP address iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -j SNAT --to-source ip.add.re.ss # Accept incoming packets on the TCP port 1194 - change this to your actual OpenVPN port iptables -A INPUT -p tcp --dport 1194 -j ACCEPT
Combined together, between the two instances, OpenVPN now accepts connections made to any port between 10000 and 40000 (inclusive).
The final step was making the OpenVPN client connect to random ports. To do this, we’re going to make use of connection profiles – essentially, just declaring multiple combinations of IP addresses and ports that you want OpenVPN to connect to. When combined with the remote-random option in the client side config file, OpenVPN should randomly go through the list and connect to a random port. To simplify matters, I turned to Python to generate the 64 random IP/port combinations to paste into the config file:
for i in range(64): print "remote ip.add.re.ss "+str(random.randrange(10000,40000))
( I only printed 64 IP/port combinations, because that’s the maximum the OpenVPN client supports.)