Enforce VPN usage on Windows

I spend a lot of time visiting my boyfriend in another town and a lot of time in coffee shops and airports - the end result is that I use public WiFi networks a lot. Because these are often unencrypted and have dubiously trustworthy operators, I do not trust them with any of my internet traffic in plaintext.

The first part of solving this problem is to use a VPN. I run the F/OSS OpenVPN server on a spare VPS with good internet connectivity and the vanilla OpenVPN client on my devices. OpenVPN has the advantage of fairly broad platform support, strong PKI-based authentication, and relatively easy configuration for small environments (though if you're managing a lot of users the PKI can become a big headache without bringing in additional management tools).

I am often left with a problem though: when I initially connect to public WiFi networks, I don't necessarily remember to bring up the VPN connection, and often on launching my web browser to authenticate to a captive portal, pinned tabs to several web services load and expose my session info (albeit TLS protected) to the network before the VPN can be established. To avoid this problem, I want to enforce that on untrusted networks all traffic go through a VPN interface. Using the Windows Firewall with Advanced Security this is fairly easy to do.

First, we will exploit Windows' sense of public vs private networks, which is exposed in the interface as file sharing and device discovery. For networks identified as private (exposed in Windows as having file sharing and device discovery enabled), I allow direct traffic as usual. For networks identified as public, I will prohibit all traffic on the network except for traffic required to establish and maintain a VPN connection. Note that we can toggle a network between public and private configuration by going to Settings, Network, Wifi, clicking on the network name, and toggling the “Make this PC discoverable” option.

Here's how to set this up:

  • Open the full configuration interface for the Windows firewall. This can easily be found by searching for “Windows Firewall.”
  • Select “outbound rules” in the left bar. We will primarily be concerned with outbound traffic here, since we are restricting our local applications.
  • Click “New Rule” in the right-side actions menu. Create a “Program” type rule, select the OpenVPN client as the program, set the rule to allow traffic, and set the rule to apply to all network profiles.
  • Next, I recommend that you also create a rule allowing a web browser you don't use very often (e.g. iexplore.exe) to connect directly over all networks. This is a quick and dirty way to make it still possible to complete captive portal authentication, since your normal web browser will not be permitted to communicate on public networks.
  • Next, go back to the main page (select “Windows Firewall…” in the left menu) and click “Windows Firewall Properties.” Go to the Public Profile tab and set the default outbound action to block.

Now we're done with the firewall side, but there's one catch. Windows has a curious bug or perhaps feature (I'm honestly not sure which) where it will only consider non-wireless networks to be of 'private' type if there is a default route associated with them. So, to make your OpenVPN connection trusted and thus allow traffic on it, have the server push the following config, or put it directly in your client config file:

route-metric 512
route 0.0.0.0 0.0.0.0

This just adds a default route to the interface with a very high metric so that it won't be used. The OpenVPN interface will now be considered a private network (default for non-wireless interfaces), so the public profile won't apply and there will be a default allow on outbound traffic.