AZiT

VPN and kill switch is highlighted; its page is below.

VPN and kill switch

Every network except the PS5 and guests reaches the internet only through a WireGuard tunnel to a commercial VPN provider. If the tunnel goes down, those networks lose the internet rather than falling back to the provider's line.

Protocol
WireGuard, in the OpenBSD kernel
Fails
Closed: no tunnel, no internet
Enforced by
Separate routing tables, and pf on the WAN port
Exceptions
PS5 and guests, by policy routing
DNS
Through the tunnel too, with ad and tracker blocking

Two independent layers#

Routing domain 0 its only default route is the tunnel Internal networks admin, Wi-Fi, IoT, media, servers wg0 tunnel encrypted PS5 and guests rtable 1, tag DIRECT Routing domain 1 the WAN port, alone igc0, WAN pf lets out only the tunnel, DHCP and DIRECT traffic Provider, internet wgrtable 1 rtable 1
The tunnel's encrypted packets, and traffic tagged DIRECT, are the only things that ever reach routing domain 1.

Routing. The WAN port lives alone in its own routing domain. Every internal network, and the tunnel, live in another, whose only default route is the tunnel. When the tunnel is down, packets have nowhere to go: that routing table simply has no path to the WAN.

pf. On the WAN port, only three things may leave: the encrypted tunnel to the VPN server, DHCP, and traffic explicitly tagged as an exception. Anything else is blocked and logged as block_wan_leak, which raises an alert. If the first layer were ever misconfigured, the second would catch it, and say so.

The key never leaves the firewall#

The WireGuard private key was generated on the firewall, and only the public key was registered with the provider. Git holds a template with a placeholder; if the firewall is lost, a new key is generated rather than restored.

Watching the tunnel#

A small script runs every minute and logs an error when the last WireGuard handshake is more than three minutes old. The SIEM alerts on it, so an outage is explained before anyone wonders why the internet stopped.

Configuration#

/etc/hostname.igc0
# WAN: alone in routing domain 1, address from the provider by DHCP
description "WAN"
rdomain 1
inet autoconf
/etc/hostname.wg0
description "VPN"
wgkey <private key>
wgrtable 1  # the encrypted packets themselves use routing domain 1
wgpeer <server key> wgendpoint <server address> 51820 wgaip 0.0.0.0/0 wgpka 25
inet <tunnel address>/32
!route -q add -inet default -link -iface wg0  # the only default route in domain 0
  • wgrtable 1 is the trick: the tunnel's outer packets travel through the WAN's table, while everything inside it belongs to the internal table.
/etc/pf.conf
match out on $vpn inet from <internal_nets> nat-to ($vpn)
match on $vpn scrub (max-mss 1380) # room for WireGuard's overhead
pass out quick on $wan inet proto udp to <vpn_server> port 51820 \
    label "allow_wan_wireguard"
pass out quick on $wan inet proto udp from any port bootpc to any port bootps \
    label "allow_wan_dhcp_out"
pass out quick on $wan inet tagged DIRECT label "allow_direct_out"
block out log quick on $wan label "block_wan_leak"
  • The last line is the safety net: anything else heading out of the WAN port is stopped and becomes an alert.

Go deeper#