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 184.108.40.206 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 <--! 220.127.116.11 192.168.50.1 18.104.22.168 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.
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
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
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
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 22.214.171.124/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 126.96.36.199 on the Internet):
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.50.1 188.8.131.52 UG 0 0 0 tun0 0.0.0.0 172.17.1.1 0.0.0.0 UG 600 0 0 enp0s3 184.108.40.206 192.168.50.1 220.127.116.11 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 18.104.22.168 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.