Configuring a VPN Kill Switch with nftables on Linux: Completely Prevent IP and DNS Leaks

Network tutorial - IT technology blog
Network tutorial - IT technology blog

When a VPN connection drops abruptly, the Linux operating system automatically redirects network packets to the physical network interface. As a result, both your real IP address and DNS queries are exposed to the internet without your knowledge. This happens in a split second—more than enough to expose your identity while working remotely or browsing privately.

A kill switch is designed to solve this exact problem. It works like an automatic circuit breaker: whenever the VPN connection drops, all outbound data traffic is instantly blocked.

VPN Kill Switch Implementation Options on Linux

Currently, there are 4 primary ways to implement this mechanism:

  • Built-in VPN client features: Toggling the kill switch button in a commercial app or using up/down script hooks in configuration files.
  • UFW (Uncomplicated Firewall): Easy to use with simplified syntax, though it fundamentally still generates rules for iptables.
  • Traditional iptables: Linux’s oldest solution, operating directly on the packet filtering tables.
  • nftables: The modern packet-filtering framework that completely replaced iptables starting with Linux kernel 3.13 and became the default in Debian 10, Ubuntu 20.04, and later.

Practical Comparison of Solutions

Each approach has its own operational trade-offs:

1. Built-in VPN Client Kill Switch

  • Pros: Fast to enable with a single click or a simple command.
  • Risks: Vulnerable when the VPN process crashes unexpectedly or is terminated by the system due to low memory (OOM killer). When the process terminates abnormally, cleanup scripts never execute. The operating system simply resumes routing traffic over standard Wi-Fi or Ethernet interfaces.

2. UFW Firewall

  • Pros: Intuitive and easy-to-remember syntax for Linux newcomers.
  • Risks: Inflexible when virtual network interfaces (such as wg0, tun0) repeatedly cycle up and down. When a router reassigns an IP via DHCP, UFW’s static rules can easily cause the machine to lose local network connectivity.

3. Legacy iptables

  • Pros: Widely adopted with extensive documentation across older distributions like CentOS 7.
  • Risks: Requires writing and maintaining two separate files for IPv4 (iptables) and IPv6 (ip6tables). Modifying even a single line forces the kernel to reload the entire rule table, which wastes CPU cycles and can disrupt active connections.

4. Modern nftables

  • Pros: Combines IPv4 and IPv6 into a single unified table (family inet). Applies rules via atomic transactions—loading instantaneously within microseconds without dropping ongoing connections. Superior packet filtering performance thanks to its in-kernel bytecode engine.
  • Cons: Unfamiliar block-based syntax that takes time to learn if you are used to single-line iptables commands.

Why nftables is the Most Reliable Failsafe

A core principle of network security: Failsafe protections must remain independent of end-user applications. If VPN client software crashes, your kernel-level defenses must stay intact.

In reality, on spotty coffee shop Wi-Fi or cellular connections, VPNs frequently disconnect and reconnect within 1 to 3 seconds. That multi-second window is when the system silently leaks dozens of DNS queries and TCP packets across standard network cards. With nftables, you set a default policy: Drop all outbound traffic except for the port connecting directly to your VPN server. This eliminates the risk of data leakage entirely.

Step-by-Step Implementation Guide for an nftables Kill Switch

Step 1: Identify Network Parameters

Open a terminal and check your current network interfaces:

ip -brief link
# enp3s0   UP      52:54:00:12:34:56 <BROADCAST,MULTICAST,UP,LOWER_UP>
# wg0      UNKNOWN 

Gather your actual network details according to this template:

  • Physical internet-facing interface: enp3s0 (or wlan0 for Wi-Fi)
  • Virtual VPN interface: wg0 (WireGuard) or tun0 (OpenVPN)
  • VPN server IP: 203.0.113.50
  • VPN port and protocol: 51820 UDP (WireGuard) or 1194 UDP (OpenVPN)
  • Local LAN subnet: 192.168.1.0/24 (for printing, NAS, or router access)

Step 2: Install nftables

On Debian, Ubuntu, or Linux Mint, run:

sudo apt update && sudo apt install -y nftables
sudo systemctl enable nftables

Step 3: Write the Firewall Ruleset

Open the nftables configuration file:

sudo nano /etc/nftables.conf

Replace the entire file content with the following standard ruleset:

#!/usr/sbin/nft -f

flush ruleset

table inet vpn_killswitch {

    chain output {
        # Drop all outbound traffic by default
        type filter hook output priority filter; policy drop;

        # 1. Allow local loopback traffic
        oifname "lo" accept

        # 2. Allow established and related connections
        ct state established,related accept

        # 3. Allow DHCP requests for local IP assignment
        udp dport { 67, 68 } oifname "enp3s0" accept

        # 4. Allow access to local network resources (printers, router, NAS)
        ip daddr 192.168.1.0/24 oifname "enp3s0" accept

        # 5. Allow only the VPN handshake connection to the VPN server via physical interface
        ip daddr 203.0.113.50 udp dport 51820 oifname "enp3s0" accept

        # 6. Allow all traffic through the VPN tunnel
        oifname "wg0" accept
    }

    chain input {
        # Drop unauthorized inbound traffic
        type filter hook input priority filter; policy drop;

        # Allow loopback and responses from established connections
        iifname "lo" accept
        ct state established,related accept

        # Allow DHCP responses from the local router
        udp sport { 67, 68 } iifname "enp3s0" accept

        # Allow packets from the local subnet (LAN)
        ip saddr 192.168.1.0/24 iifname "enp3s0" accept
    }
}

Step 4: Load and Activate the Configuration

Apply the ruleset immediately without rebooting:

sudo nft -f /etc/nftables.conf

Verify the active ruleset running in the kernel:

sudo nft list ruleset

Step 5: Test for Leaks in Practice

Never trust a configuration without testing it. Simulate network drop scenarios using these 3 steps:

  1. Test when the VPN is operating normally:
    curl -4 ifconfig.me
    # Output must return the VPN server's IP address: 203.0.113.50
  2. Simulate a VPN failure:
    sudo wg-quick down wg0
    curl --connect-timeout 5 https://ifconfig.me

    If the terminal returns Connection timed out or Could not resolve host after 5 seconds, the kill switch is working correctly. Outbound packets via enp3s0 have been completely dropped by the kernel.

  3. Test against IPv6 leaks:

    Many ISPs automatically assign IPv6 alongside IPv4. Because the configuration uses table inet with a policy drop, all IPv6 resolution and traffic outside the VPN tunnel are blocked, eliminating any chance of an IPv6 identity leak.

With just a single configuration file, you have a rock-solid kernel-level protection mechanism. Data can only leave your computer when the secure VPN tunnel is active.

Share: