Routing traffic through OpenVPN using a local SOCKS proxy

Posted by Sven Kiljan on in OpenVPN, Networking

OpenVPN can be used with an obfuscation proxy, such as obfsproxy or obfs4, to avoid identification of VPN traffic through deep packet inspection. In this post I explain a connectivity problem that client-side OpenVPN phases when such a proxy is approached as a local SOCKS proxy by OpenVPN. A solution is provided, of course.


An open-source product I like to work with is OpenVPN. It allows network-savvy people to build robust virtual private network connections across the Internet and any other networks that use the TCP/IP protocol. Secured traffic can include just site-to-site communication, but an OpenVPN client can also be configured to route all other traffic to other (Internet) servers through the VPN. This is useful if one is in an untrustworthy environment. An example is the use of a wireless network which might be vulnerable to the KRACK attack. Alternatively, one might use a wireless card using 802.1x authentication, and one of the used RSA private keys might be stored in a device vulnerable to ROCA. Or maybe it is just that the only available wireless connection is unencrypted. And even if the wireless connection might be completely secure, you might not want to trust the man-in-the-middle that connects the other side of it to the Internet.

OpenVPN can provide a(n additional) security layer to protect traffic confidentiality and integrity. Many Internet users possess an Internet connection at home that can be used to host an OpenVPN server. Once servers and clients are correctly configured, all they have to do is to activate the OpenVPN client with redirect-gateway def1 somewhere in the configuration to secure all traffic from eavesdroppers and other malicious parties.

Sometimes this is not enough. Not all offered Internet connections are open to the use of OpenVPN. Network administrators might be tempted to apply deep packet inspection to block secure connections. An obvious client-side symptom is that the connection is lost soon after it is established. While OpenVPN might protect information confidentiality and integrity, availability can still be negatively influenced.

It is possible to host and use an obfuscation proxy to make detection of an OpenVPN connection using deep packet inspection difficult. Such programs include obfsproxy and obfs4. These programs offer a SOCKS proxy interface on which an OpenVPN client can connect. The result is that VPN network packets are obfuscated, which makes it harder to identify the connection. For this, OpenVPN's configuration file will have a line that will look something like socks-proxy 127.0.0.1 6876. 127.0.0.1 is the localhost address, and 6876 is the chosen local port on which the obfuscation proxy is listening.

Unfortunately, this introduces a complication when routing all traffic through the OpenVPN connection using redirect-gateway def1. To understand this complication, an example OpenVPN client configuration is required. The following example is based on a client running Linux:

script-security 2
client

socks-proxy 127.0.0.1 6876   # Use a local SOCKS proxy on TCP port 6876
proto tcp-client             # Use a TCP connection to the OpenVPN server (through the proxy)
remote my.vpn-server.org     # Address of a server hosting an obfuscation proxy and OpenVPN
port 8080                    # TCP port of the listening obfuscation proxy server

ca   "ca.crt"                # Certificate of the certificate authority
cert "my.crt"                # Client certificate
key  "my.key"                # Client private key

ns-cert-type server          # Pick one of these lines that
remote-cert-tls server       # identifies the server's certificate

persist-tun                  # Avoid TUN/TAP re-initialization upon reconnects
dev tun                      # Use a TUN (OSI layer 3) virtual network device for the VPN
redirect-gateway def1        # Redirect other traffic through the VPN

up /etc/openvpn/update-resolv-conf     # Protect DNS requests from
down /etc/openvpn/update-resolv-conf   # eavesdropping and manipulation.

It is assumed that the local network used by the client has an IP range of 172.17.1.0/24 with an Internet router on 172.17.1.1, and that the internal OpenVPN network uses the IP range 192.168.50.0/24. The resulting routing table would be as follows:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.50.1    128.0.0.0       UG    0      0        0 tun0    <--
0.0.0.0         172.17.1.1      0.0.0.0         UG    600    0        0 enp0s3
127.0.0.1       172.17.1.1      255.255.255.255 UGH   0      0        0 enp0s3  <--!
128.0.0.0       192.168.50.1    128.0.0.0       UG    0      0        0 tun0    <--
192.168.50.0    0.0.0.0         255.255.255.0   U     0      0        0 tun0
172.17.1.0      0.0.0.0         255.255.255.0   U     600    0        0 enp0s3

The routes pointed at by arrows were added by redirect-gateway def1. The problematic route is the one with an exclamation mark. According to the OpenVPN man page:

--redirect-gateway flags...
    Automatically execute routing commands to cause all outgoing IP traffic to be redirected
    over the VPN. This is a client-side option.

    This option performs three steps:

    (1) Create a static route for the --remote address which forwards to the
        pre-existing default gateway. (...)

The configuration has remote my.vpn-server.org, yet the value of socks-proxy is used to create this route. This is not described by the man page.

How redirect-gateway def1 behaves makes sense when a remote SOCKS proxy is used. In this situation, the client only connects to the SOCKS proxy using the information provided by the socks-proxy option, after which the proxy takes care of the rest of the transport using the information provided by the remote and port options. Still, this is something that should have been mentioned by the man page, as it prevents further OpenVPN traffic after a connection has been established when a local SOCKS proxy is used.

OpenVPN supports the execution of user scripts at different connection stages, which can be used to create a workaround. The option remote might not be used for routing by OpenVPN when a SOCKS proxy is used, but its value is still available in the $remote_1 environment variable. A small script can be called by OpenVPN to replace the broken functionality of redirect-gateway def1.

First, the new OpenVPN client configuration:

script-security 2
client

socks-proxy 127.0.0.1 6876   # Use a local SOCKS proxy on TCP port 6876
proto tcp-client             # Use a TCP connection to the OpenVPN server (through the proxy)
remote my.vpn-server.org     # Address of a server hosting an obfuscation proxy and OpenVPN
port 8080                    # TCP port of the listening obfuscation proxy server

ca   "ca.crt"                # Certificate of the certificate authority
cert "my.crt"                # Client certificate
key  "my.key"                # Client private key

ns-cert-type server          # Pick one of these lines that
remote-cert-tls server       # identifies the server's certificate

persist-tun                  # Avoid TUN/TAP re-initialization upon reconnects
dev tun                      # Use a TUN (OSI layer 3) virtual network device for the VPN

up   redirectgateway-def1-socks.sh  # Redirect other traffic
down redirectgateway-def1-socks.sh  # through the VPN

The only changes are at the bottom of the file, where the redirect-gateway def1 line has been removed and the up and down options have been changed.

Create the file redirectgateway-def1-socks.sh with the following content:

#!/usr/bin/env bash

if [ "$script_type" == "up" ]; then
  route add -host $remote_1/32 gw $route_net_gateway
  route add -net 0.0.0.0/1 gw $route_vpn_gateway
  route add -net 128.0.0.0/1 gw $route_vpn_gateway
  /etc/openvpn/update-resolv-conf
elif [ "$script_type" == "down" ]; then
  /etc/openvpn/update-resolv-conf
  route del -host $remote_1/32 gw $route_net_gateway
fi

The used environment variables can be found on the OpenVPN man page. Only a single route is deleted after OpenVPN is issued the disconnect command. The other two routes are removed automatically when the TUN interface is disconnected. Note that the locations of the update-resolv-conf script are critical since non-VPN DNS is required to resolve $remote_1 to an IP address for the routing table.

Now when the OpenVPN connection is created, the routing table will look like this (assuming that the obfuscation proxy and OpenVPN server listen on 213.214.215.1 on the Internet):

Kernel IP routing table  
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.50.1    128.0.0.0       UG    0      0        0 tun0
0.0.0.0         172.17.1.1      0.0.0.0         UG    600    0        0 enp0s3
128.0.0.0       192.168.50.1    128.0.0.0       UG    0      0        0 tun0
192.168.50.0    0.0.0.0         255.255.255.0   U     0      0        0 tun0
172.17.1.0      0.0.0.0         255.255.255.0   U     600    0        0 enp0s3
213.214.215.1   172.17.1.1      255.255.255.255 UGH   0      0        0 enp0s3

The route at the bottom ensures that the client can continue to communicate with the server after the VPN connection is established. This is also how the routing table would look for a connection that uses redirect-gateway def1 without a SOCKS proxy.

Comments