Captive portals are the worst.
Flaky detection. The OS and browser try to detect these annoying network features but fail quite often, leaving you with broken connections.
> DID YOU KNOW that probe-based captive portal detection really doesn't work very well, with ~30% FP and ~30% FN rate in Chrome? > > -- Emily Stark (@estark37) 15 September 2017
DNS. I want to pick my own DNS server, and more specifically run my own unbound, for a number of reasons: clean results, local zones, overrides, DNSSEC... However most captive portals will block UDP traffic to anything except their DNS resolver (or would be trivially bypassed). So every time getting past a captive portal involves opening Network Settings, removing the custom DNS, logging in, and hopefully (that is, rarely) remember to put the custom DNS server back in.
HTTP. Finally, since a captive portal literally relies on a MitM attack, it results inaccessible when using HTTPS Everywhere in "Block all unencrypted requests" mode.
To scratch this itch I decided to make my own captive portal browser based on Chrome, such that it can be secure and configured as I please.
The main challenge is reaching the DHCP-provided captive portal DNS resolver without changing system settings. Chrome lacks the ability to configure DNS upstreams, but supports SOCKS5 which proxies name resolution.
With 100 lines of Go I built a small SOCKS5 proxy based on
github.com/armon/go-socks5 that handles name resolution via a custom
net.Resolver that always dials a fixed IP for the DNS server.
It automatically discovers the DHCP DNS server on macOS with this command:
ipconfig getoption en0 domain_name_server
Finally it starts a Chrome instance configured to use the SOCKS5 proxy with the following command and waits for it to quit:
open -n -W -a "Google Chrome" --args \ --user-data-dir="$HOME/Library/Application Support/Google/Captive" \ --proxy-server="socks5://$PROXY" \ --host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost" \ --no-first-run --new-window --incognito \ http://example.com
--user-data-dir allows it to run alongside your normal Chrome instance, while still being (separately) configurable and
--incognito ensures that no state is preserved across executions.
The commands can be configured with a TOML file to make the tool work on other operating systems.
Usage boils down to running
captive-browser and logging into the captive portal from a secure, configurable, ephemeral environment. All without touching DNS settings.