Pop Art grid of colorful shields with padlocks, symbolizing cybersecurity and protection.

VPN Kill Switch for Linux: 7 Ways to Prevent IP Leaks

A VPN connection can fail without making much noise. The tunnel disappears, Linux still has a perfectly usable default route, and your next browser request can leave through the normal network interface before you even notice. That is exactly why I treat a VPN kill switch for Linux as a firewall problem, not as a comforting toggle that I simply assume works.

I use Parrot OS most of the time in my own VMware lab, with Kali Linux available for comparison. My approach is simple: if WireGuard is down, ordinary internet traffic should have nowhere else to go. I want the machine to fail closed, and then I want to prove that behavior by deliberately breaking the tunnel.

This guide shows my workflow for a VPN kill switch for Linux: identify the interfaces, build a dedicated nftables policy, protect DNS, test WireGuard failures, and only then consider persistence.

If practical lab notes like this are useful, you can join the HackersGhost newsletter for new security guides, lab lessons, and hands-on notes.

Safety checkWhat I verifyFailure sign
1. Network pathUplink, wg0 and VPN endpointWrong interface gets allowed
2. Fail-closed firewallOnly wg0 plus the endpoint can leaveNormal traffic survives tunnel loss
3. DNS and IPv6Resolvers stay inside the protected pathQueries escape outside the tunnel
4. Control scriptsOn, off and panic actions are predictableOld rules linger unnoticed
5. Failure testTraffic dies when WireGuard diescurl still reaches the internet
6. Region switchingOne tunnel at a timeRoutes overlap or endpoint rules mismatch
7. PersistenceRules survive only after validationA bad ruleset locks you out after reboot

Important: this is a manual firewall design for people who want to verify what happens underneath the VPN app. A provider-native VPN kill switch for Linux can be perfectly sensible, but the manual path makes fail-closed behavior easier to inspect and troubleshoot.

If you still need a working tunnel, start with my WireGuard ProtonVPN setup on Kali Linux, then return here for the firewall layer.

HackersGhost Note: I do not call a VPN kill switch for Linux “working” because a setting says it is enabled. I call it working when I can pull the tunnel down on purpose and the machine immediately loses ordinary internet access.

Key Takeaways for a VPN Kill Switch for Linux

  • A real fail-closed policy should allow normal outbound traffic through wg0, not through your physical or virtual uplink.
  • The WireGuard endpoint itself needs a narrow exception, otherwise the VPN cannot establish the tunnel.
  • Do not blindly flush the entire nftables ruleset. Create and remove your own dedicated table so you do not destroy unrelated firewall rules.
  • DNS protection is not just a resolver setting. The firewall should make off-tunnel DNS impossible.
  • The best answer to how to test VPN kill switch behavior is to force a failure and verify that real traffic stops.
  • Only enable persistence after you have tested reconnects, reboots, DNS, IPv6 and recovery.

What You Will Build With This VPN Kill Switch for Linux

The finished setup stays intentionally small: one dedicated nftables table, one narrow WireGuard endpoint exception, simple on/off/status scripts, a separate panic mode, and a test routine that proves your VPN kill switch for Linux actually fails closed.

VPN kill switch for Linux using WireGuard and nftables

What I Use for My Parrot OS VPN Kill Switch Lab

My daily machine is a second-hand HP EliteBook that I upgraded to 32 GB RAM. I chose VMware for my current lab and use Parrot OS most, with Kali Linux as a second reference. Both are Debian-based, which makes the same WireGuard and nftables concepts useful on either distro, even though their exact networking setup can differ.

Upstream, I also use a Cudy WR3000 with Proton VPN WireGuard and Secure Core for part of my lab routing. I still test the VM-level VPN kill switch for Linux separately because router protection and a local fail-closed firewall cover different failure points.

I use standard WireGuard configuration files and wg-quick. The WireGuard project documents the underlying tools, while the nftables documentation is useful when you want to understand the firewall layer rather than merely copy commands.

Install the tools:

sudo apt update
sudo apt install -y nftables wireguard-tools dnsutils curl tcpdump

I do not force-install a particular DNS manager. First inspect whether NetworkManager, systemd-resolved, resolvconf, or another component already manages DNS on your VM.

Step 1 — Start the VPN Kill Switch for Linux Cleanly

The old version of this guide started by running sudo nft flush ruleset. I no longer recommend that. It wipes the entire nftables ruleset, not merely the rules created for the VPN. On a lab VM that may look harmless, but on a machine with Docker, another firewall, custom filtering or remote access rules, it can remove configuration you never intended to touch.

Instead, inspect first:

sudo nft list ruleset
ip route
ip -br link
wg

Now identify three things: your normal uplink interface, your WireGuard interface, and the VPN endpoint. In my VMware setup the uplink may look like ens33; on your system it might be eth0, wlan0 or something else. Never copy my interface name blindly.

ip route show default

grep -E '^Endpoint' /etc/wireguard/protonnl.conf

A WireGuard endpoint is an IP address or hostname plus a UDP port. Do not assume the port is always 51820. WireGuard can use other UDP ports, and the configuration file is the source you should trust.

If the endpoint is a hostname, resolve it before the strict firewall policy is active:

getent ahostsv4 YOUR-ENDPOINT-HOSTNAME | head -n 1

HackersGhost Note: most broken kill-switch tutorials fail at the boring part: they hard-code an interface, endpoint or port. A strict firewall built on the wrong assumptions is still wrong; it is simply wrong with confidence.

Step 2 — Build the nftables VPN Kill Switch for Linux

Create the working directory:

mkdir -p "$HOME/vpn/scripts"

Now create $HOME/vpn/killswitch.nft. Replace ens33, the example endpoint IP, and the example port with the values from your own setup:

define vpn_if = "wg0"
define uplink_if = "ens33"
define vpn_endpoint = 185.159.0.10
define vpn_port = 51820

table inet hg_vpnks {
  chain input {
    type filter hook input priority 0;
    policy drop;

    iifname "lo" accept
    ct state established,related accept
    iifname $vpn_if accept
  }

  chain forward {
    type filter hook forward priority 0;
    policy drop;
  }

  chain output {
    type filter hook output priority 0;
    policy drop;

    oifname "lo" accept
    oifname $vpn_if accept

    oifname $uplink_if ip daddr $vpn_endpoint udp dport $vpn_port accept
  }
}

This is the core of the VPN kill switch for Linux policy. Normal outbound traffic is accepted through wg0. Traffic through the ordinary uplink is dropped unless it is the exact UDP flow required to reach the VPN endpoint. When the tunnel disappears, the default route can still exist, but the firewall refuses to use it for ordinary internet traffic.

Notice what is missing from the output chain: a broad ct state established,related accept. I deliberately avoid that rule for outbound traffic here. If you enable the VPN kill switch for Linux while a normal connection already exists outside the VPN, calling it “established” should not magically exempt it from the policy.

If your endpoint is IPv6, use an ip6 daddr endpoint rule instead of the IPv4 example. If you use multiple VPN profiles with different endpoints, each profile needs the correct endpoint exception or a carefully maintained set of approved endpoints.

Why I Prefer nftables Over an iptables VPN Kill Switch

An iptables VPN kill switch can absolutely work, and plenty of older Linux guides use it. I prefer nftables because the rules are easier for me to read as one policy, and the inet family gives me a cleaner place to reason about IPv4 and IPv6 together.

What About a VPN Kill Switch UFW Setup

A VPN kill switch UFW configuration can also be useful, especially on a simple desktop where you already manage everything through UFW. My reason for not using it here is transparency. For this lab guide I want to see the exact base chains, interfaces and endpoint exception without another abstraction layer between me and Netfilter.

If you are using Proton services for this kind of lab, I generally prefer the complete bundle rather than paying for separate tools one by one.

Proton Unlimited bundles Proton VPN, Proton Mail, Proton Drive, and Proton Pass under one subscription. If you already use Proton services in your lab, the bundle is usually the more practical move.

Colorful control concept for a VPN kill switch for Linux

Step 3 — Add Safe VPN Kill Switch for Linux Control Scripts

I want three boring scripts: on, off and status. Boring is a compliment here. When networking is misbehaving, I do not want a 90-line Bash masterpiece asking me to remember what past-me thought was elegant.

Script 1: $HOME/vpn/scripts/kill-on

#!/usr/bin/env bash
set -euo pipefail

sudo nft delete table inet hg_vpnks 2>/dev/null || true
sudo nft -f "$HOME/vpn/killswitch.nft"

echo "[kill-on] VPN fail-closed policy active."

Script 2: $HOME/vpn/scripts/kill-off

#!/usr/bin/env bash
set -euo pipefail

sudo nft delete table inet hg_vpnks 2>/dev/null || true

echo "[kill-off] HackersGhost VPN kill-switch table removed."

Script 3: $HOME/vpn/scripts/kill-status

#!/usr/bin/env bash
set -euo pipefail

echo "=== HackersGhost nftables table ==="
sudo nft list table inet hg_vpnks 2>/dev/null || echo "Kill-switch table not loaded."

echo
echo "=== WireGuard ==="
sudo wg || true

echo
echo "=== Default route ==="
ip route show default

Make them executable:

chmod +x "$HOME/vpn/scripts/kill-on"
chmod +x "$HOME/vpn/scripts/kill-off"
chmod +x "$HOME/vpn/scripts/kill-status"

The important improvement over the old version is that kill-off removes only hg_vpnks. It does not flush every firewall rule on the machine. That makes this VPN kill switch for Linux much safer to experiment with inside a real workstation or a VM that already has other networking components.

Step 4 — Add a Panic Mode Without Flushing nftables

My panic mode is intentionally separate from the normal VPN kill switch for Linux. The VPN kill switch for Linux says “VPN traffic is allowed, everything else is blocked.” Panic mode says “nothing leaves at all.” It is useful when I want an immediate blackout before I investigate a strange network state.

Save this as $HOME/vpn/panic.nft:

table inet hg_panic {
  chain input {
    type filter hook input priority -100;
    policy drop;
  }

  chain forward {
    type filter hook forward priority -100;
    policy drop;
  }

  chain output {
    type filter hook output priority -100;
    policy drop;
  }
}

$HOME/vpn/scripts/panic-on

#!/usr/bin/env bash
set -euo pipefail

sudo nft delete table inet hg_panic 2>/dev/null || true
sudo nft -f "$HOME/vpn/panic.nft"

echo "[panic-on] All network traffic blocked."

$HOME/vpn/scripts/panic-off

#!/usr/bin/env bash
set -euo pipefail

sudo nft delete table inet hg_panic 2>/dev/null || true

echo "[panic-off] Panic table removed."
chmod +x "$HOME/vpn/scripts/panic-on"
chmod +x "$HOME/vpn/scripts/panic-off"

Test this VPN kill switch for Linux from the local console, not through a remote shell you still need. If you activate a policy that blocks every packet while you are connected remotely, your VPN kill switch for Linux has not betrayed you. It has obeyed you with uncomfortable enthusiasm.

How to Test DNS & WebRTC Leaks: 7 Sneaky Checks

Check your VPN for DNS and WebRTC leaks with 7 practical tests that reveal whether your real IP or resolver traffic is escaping the tunnel.

Step 5 — Protect DNS and IPv6 With the VPN Kill Switch

DNS deserves its own check because it can make a VPN look healthier than it is. Your public IP may belong to the VPN while name resolution is still following a path you did not intend. A good VPN kill switch for Linux should prevent DNS from falling back to the ordinary uplink when the tunnel fails.

5A — Find Out Who Manages DNS

resolvectl status 2>/dev/null || true
cat /etc/resolv.conf

If /etc/resolv.conf is a symlink, check where it points. If NetworkManager is managing DNS, inspect the active connection with nmcli. The goal is not to force one DNS architecture on every Linux distro; the goal is to understand yours before you change it.

5B — Use the DNS Value From Your WireGuard Profile

Look at the [Interface] section of your provider-generated configuration. Do not copy a resolver address from an old tutorial simply because it looks familiar.

grep -E '^(Address|DNS|Endpoint|AllowedIPs)' /etc/wireguard/protonnl.conf

Modern Proton WireGuard profiles can include IPv4 and IPv6 settings. That is another reason I prefer inspecting the actual file over hard-coding assumptions into a VPN kill switch WireGuard guide.

5C — Why the Firewall Already Blocks Off-Tunnel DNS

With the Step 2 rules, ordinary traffic on the uplink is denied. That includes UDP and TCP port 53 unless it is encapsulated inside the WireGuard tunnel. In other words, you do not need a decorative DNS drop rule after a broad oifname "wg0" accept; the fail-closed output policy already blocks resolver traffic that tries to bypass wg0.

If you force one resolver inside the tunnel, place its DNS allow rules before the general wg0 accept and reject other port 53 traffic on wg0. Browser DNS-over-HTTPS is different because it uses HTTPS. A working VPN kill switch for Linux still keeps that traffic inside the tunnel, although the browser may use a different DNS provider than the operating system.

For IPv6, check whether your WireGuard profile routes ::/0 and whether your VPN interface receives an IPv6 address. If your tunnel does not carry IPv6, the default-drop policy must still stop native IPv6 from leaving through the uplink. Test it rather than disabling IPv6 reflexively.

HackersGhost Note: I used to treat DNS as a separate checkbox. I now treat it as part of the route. If DNS can only travel through the same protected path as the rest of my traffic, there is less room for a quiet fallback.

Step 6 — One-Click Controls and VPN Kill Switch WireGuard Switching

I like terminal commands, but I also know myself. The more often I type the same network commands manually, the more likely I am to mistype one while distracted. Simple launchers reduce that friction.

Create $HOME/.local/share/applications/vpn-kill-on.desktop:

[Desktop Entry]
Name=VPN Kill-On
Exec=/bin/bash -lc "$HOME/vpn/scripts/kill-on"
Icon=security-high
Type=Application
Terminal=true

You can create matching launchers for status, off, panic-on and panic-off. On my Parrot desktop this turns the Parrot OS VPN kill switch into something I can inspect and control without hunting through shell history every time.

Multi-Region VPN Kill Switch WireGuard Profiles

The old switching script in this post brought one WireGuard profile down and another up, but the firewall still assumed one endpoint. That is a subtle problem. If your US, Belgium and Netherlands profiles use different server IPs or ports, a pinned endpoint rule for one profile can prevent the next profile from connecting.

I now use one of two approaches. The simplest is to maintain a separate killswitch-REGION.nft file for each profile and load the matching firewall policy before bringing that profile up. The more advanced option is an nftables set containing only the approved WireGuard endpoint IPs and ports you actually use.

For beginners, separate small files win. They are obvious. Before switching:

sudo wg-quick down protonnl 2>/dev/null || true
sudo nft delete table inet hg_vpnks 2>/dev/null || true
sudo nft -f "$HOME/vpn/killswitch-be.nft"
sudo wg-quick up protonbe

Then run the verification routine again. A VPN kill switch for Linux is not “configured forever” if the endpoint assumptions change underneath it.

My Cudy router is useful as a separate VPN layer when I want the entire attack-side network routed through Proton VPN. If you are building a similar lab and want the same router model, the Cudy WR3000 is available on Amazon.

I would not buy a router just to solve this local firewall problem. A local VPN kill switch for Linux still matters when the VM’s own tunnel or route changes state.

Fail-closed Parrot OS VPN kill switch policy

Step 7 — How to Test VPN Kill Switch Failure Properly

This is the section I care about most. The VPN kill switch for Linux is theory until I break the tunnel on purpose. If you remember only one part of this guide, remember this: how to test VPN kill switch behavior matters more than how pretty the ruleset looks.

First enable the policy, then bring up your WireGuard profile:

"$HOME/vpn/scripts/kill-on"
sudo wg-quick up protonnl

sudo wg
ip route
curl -4 https://ifconfig.co ; echo
dig example.com

Check that wg shows a recent handshake and transferred bytes. Confirm that the public IP is not your ordinary ISP address. Then inspect DNS and, if you use IPv6, test IPv6 connectivity through the tunnel as well.

Now create the failure:

sudo wg-quick down protonnl

curl --max-time 5 https://example.com
dig example.com

Both should fail while the VPN kill switch for Linux remains active. Your default route may still point to the normal uplink, and that is fine. The firewall is the gatekeeper. If curl still loads a page, you have a VPN kill switch not working situation and should stop before trusting the setup.

Watch the Uplink With tcpdump

For a stronger check, watch the physical or virtual uplink while the tunnel is down. Replace ens33 with your actual interface:

sudo tcpdump -ni ens33

In another terminal, try a web request. You should not see ordinary destination traffic escaping. You may see local-link or infrastructure noise depending on your environment, but the browser request itself should not sail out to the internet over the uplink.

Test Reconnects, Suspend and Network Changes

A good Kali Linux VPN kill switch or Parrot OS VPN kill switch should survive more than one clean disconnect. I test a reconnect, a server switch, a network adapter bounce and a VM suspend/resume cycle. Those are the moments when routing state tends to get interesting.

Finally, check your rules after each test:

"$HOME/vpn/scripts/kill-status"
sudo nft list table inet hg_vpnks

HackersGhost Note: the most useful VPN test I run is the one where I intentionally make the VPN fail. For a VPN kill switch for Linux, successful connections are easy. Safe failures are the part worth engineering.

How Routers Break OPSEC Without You Noticing

See how router settings, fallback paths, DNS behavior, and network segmentation mistakes can quietly undermine OPSEC even when the rest of your lab looks secure.

Make the VPN Kill Switch for Linux Persistent Only When Stable

I delay persistence until every failure test passes. A typo in a temporary VPN kill switch for Linux is annoying; the same typo loaded after every reboot becomes a hobby I never asked for.

Back up the current configuration and inspect it first:

sudo cp /etc/nftables.conf /etc/nftables.conf.backup 2>/dev/null || true
sudo nft list ruleset
sudo systemctl status nftables

Do not blindly export the entire live ruleset to /etc/nftables.conf. Other software may have created dynamic state you do not want to preserve. I prefer adding a reviewed static include or a dedicated service for my VPN kill switch for Linux, then enabling nftables only after I know exactly what will load.

How Does VPN Kill Switch Work on Kali and Parrot OS

If you are asking how does VPN kill switch work, think of it as an allowlist for network paths. The WireGuard interface is allowed, the VPN endpoint receives one narrow uplink exception, and other outbound traffic is denied. Kali is Debian-based and Parrot OS is Debian-based as well, so the firewall concepts translate well even when interface names and DNS handling differ.

Proton also offers kill-switch modes in its supported Linux apps and documents manual WireGuard configuration. I use the manual VPN kill switch for Linux when I specifically want an auditable lab policy I can break and inspect myself.

Why nftables Works Well for a VPN Kill Switch for Linux

An iptables VPN kill switch, a VPN kill switch UFW policy, or a provider-native VPN kill switch for Linux can all work. I choose nftables because I can read the default-drop policy, see the exact endpoint exception, handle IPv4 and IPv6 in one inet table, and remove only my VPN table. In an ethical-hacking VM with several networking layers, that visibility is valuable.

Common VPN kill switch not working mistakes on Linux

VPN Kill Switch Not Working: Mistakes I Check First

When a VPN kill switch not working report lands in front of me, I check assumptions before rewriting VPN kill switch for Linux rules. Most failures are less mysterious than they first appear.

  • Wrong uplink interface: the rule says ens33, but the machine actually leaves through another interface.
  • Wrong endpoint port: a tutorial assumes UDP/51820 while the WireGuard profile uses something else.
  • Endpoint hostname changed: you pinned an old resolved IP and the provider rotated the server address.
  • Broad established-traffic rule: old non-VPN connections remain allowed after the policy is enabled.
  • Multiple WireGuard profiles: the firewall allows the endpoint for profile A while you are trying to start profile B.
  • IPv6 forgotten: IPv4 is blocked correctly but the machine still has another path you never tested.
  • DNS misunderstood: the OS resolver, browser DoH and provider DNS are three related but different pieces.
  • Persistence enabled too soon: a temporary mistake becomes a boot-time problem.

One more VPN kill switch for Linux rule: keep local console access while developing the policy. If this is a remote VPS or remote lab box, a default-drop firewall deserves a recovery path before you press Enter.

My Final VPN Kill Switch for Linux Checklist

  1. Confirm the current uplink, WireGuard interface, endpoint IP and UDP port.
  2. Load only the dedicated hg_vpnks table and leave unrelated firewall rules alone.
  3. Connect WireGuard and verify the handshake, public IP, DNS and IPv6 path.
  4. Disconnect WireGuard while the VPN kill switch for Linux stays active and confirm web traffic fails.
  5. Repeat after reconnecting, switching profiles and resuming the VM.

That is the difference between installing a VPN kill switch for Linux and verifying one. For my Parrot OS workflow, this manual policy is small enough to audit and strict enough to stop normal fallback traffic. Kali users can apply the same logic, but your interface names, DNS stack and endpoint values must come from your own machine.

Frequently asked questions about a VPN kill switch for Linux

Frequently Asked Questions

What is a VPN kill switch for Linux?

How does VPN kill switch work with WireGuard?

How do I test a VPN kill switch on Linux?

Can I build a Kali Linux VPN kill switch with nftables?

Does this Parrot OS VPN kill switch work in VMware?

Is nftables better than an iptables VPN kill switch or UFW?

Do I need a paid Proton plan for WireGuard configs on Linux?

Why is my VPN kill switch not working after switching servers?

VPN & Network Infrastructure Cluster

Some links in this article are affiliate links. If you use them, I may earn a small commission — at no extra cost to you. I only recommend tools I’ve actually tested inside my own cybersecurity lab. Read the full disclaimer.

In many cases, these links unlock better deals than you’ll find on your own.
No paid reviews. No sponsored opinions. Just real testing and real setups.

If you decide to use them, you’re not just getting a discount — you’re helping keep this lab running.

Leave a Reply

Your email address will not be published. Required fields are marked *