You log into the router, paste a kill-switch rule set from a forum post, and the LAN stops browsing the moment the tunnel drops. Two days later the router is still updating its dynamic DNS record over the ordinary WAN, and the guest laptop is still reaching IPv6 sites. Both behaviours are normal, because the pasted rules covered one chain and one address family, and neither of those is the whole router. Walking the packet paths in order shows exactly what was missed.

Start at the router, not at the laptop

A router-level policy is attractive for a small office because every device behind it inherits the same behaviour and nobody can forget to enable a client switch. It also gives you one place to test after every firmware update. What it does not give you is automatic coverage of the router's own traffic or of a second address family, and those are the two gaps behind most failed deployments.

Before editing anything, note three facts on paper: the WAN interface name, the LAN bridge name, and the VPN endpoint address and port. You will need all three in the rules, and guessing at interface names is the most common reason a correct-looking rule set does nothing.

Walk the three paths a packet can take

Traffic from a laptop to the internet is forwarded, so it passes through the FORWARD chain. Traffic the router generates itself, such as DNS forwarding, NTP queries, DDNS updates and the VPN control channel, passes through OUTPUT. Management traffic addressed to the router arrives through INPUT.

A kill switch built only in FORWARD therefore protects clients and nothing else. The table shows why the leak in the opening scene is predictable rather than mysterious.

Packet pathExampleChain that sees it
LAN client to WAN or tunnelBrowser requestFORWARD
Router-originatedDDNS update, NTPOUTPUT
Addressed to the routerSSH from the LANINPUT

You do not need a kill switch on INPUT in most designs, but you do need to keep management access working while the tunnel is down. Otherwise the first time the VPN fails, you are driving to the office with a console cable.

Order the rules so the tunnel can still come up

A working kill switch is an allowlist that ends in a reject, and the ordering is what makes it a kill switch rather than a brick. Permit loopback, the VPN endpoint, replies for established flows and the tunnel interface first; reject the rest last.

iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -d 198.51.100.7 -p udp --dport 51820 -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o wg0 -j ACCEPT
iptables -A OUTPUT -o eth0 -j REJECT

The endpoint must be an address at this point, not a hostname. Once the final reject blocks DNS, a rule that names the provider's hostname can no longer be resolved, and the tunnel never starts again after a reboot. If the provider rotates endpoints, resolve the name before the policy is applied and refresh that rule from the same script.

Mirror the shape in FORWARD for client traffic, and place an explicit management rule above the reject. Keep that management rule narrow but reachable, and confirm it by opening a second SSH session before you close the first one. Locking yourself out during a kill-switch change is common enough to plan for. On a router running WireGuard, the allowed-ips of the peer decide what enters the tunnel, while these rules decide what is allowed to leave without it.

IPv6 needs its own front door

Everything above is IPv4. If your LAN has working IPv6 and the tunnel carries only IPv4, every covered device still has a direct route to the internet that your new rules never see. A dual-stack laptop will happily prefer it, and the kill switch will look broken only to the person testing with IPv4.

ip6tables -A FORWARD -i br-lan -j REJECT
ip6tables -A OUTPUT -o eth0 -j REJECT
ip6tables -A OUTPUT -o lo -j ACCEPT

Order matters here too: the loopback accept has to precede the WAN reject, or local services on the router start failing in strange ways. If the network does not use IPv6 at all, disabling it on the WAN and LAN is simpler and leaves nothing to forget.

Some VPN clients install a default route only for IPv4 and leave the existing IPv6 default untouched. Check the routing table after the tunnel connects rather than assuming the client handled it.

Prove it with the tunnel down

Stop the tunnel from the router and test from three places in this order:

# systemctl stop wg-quick@wg0
# ip route get 203.0.113.1
# curl -m 5 -sS http://example.com || echo blocked
# iptables -vnL OUTPUT | head
# ip6tables -vnL OUTPUT | head

The route lookup tells you where a packet would go, and the counters tell you whether your rules ever matched. A rule with zero packets after a failed test is not protecting anything, and the usual cause is an interface name that differs from the one you typed. Test router-originated traffic explicitly, because a LAN-side test cannot exercise OUTPUT.

Decide the failure mode before you need it. Fail-closed keeps clients offline until the tunnel returns; fail-open restores ordinary WAN access and defeats the purpose for the traffic you cared about. A blackhole route to the VPN pool with a high metric is a useful second layer for fail-closed designs, since it survives when the tunnel route disappears instead of falling back to the default.

Accept the setup only when a LAN client, a router-originated request and an IPv6 request all fail with the tunnel down, and all three work again once it is up.

Barruulee wal qabatan