# How Amnezia VPN Client Implements Kill Switch: Architecture and Code Deep Dive

> Discover how Amnezia VPN client implements its kill switch. Learn about the cross-platform architecture and code used to block traffic on Windows, Linux, and macOS.

- Repository: [Amnezia VPN/amnezia-client](https://github.com/amnezia-vpn/amnezia-client)
- Tags: architecture
- Published: 2026-07-29

---

**Amnezia-client implements its kill switch through a cross-platform `KillSwitch` singleton that coordinates strict traffic blocking across Windows, Linux, and macOS by delegating packet filtering to platform-specific firewall helpers.**

The kill switch in the [amnezia-vpn/amnezia-client](https://github.com/amnezia-vpn/amnezia-client) repository ensures no traffic leaks when the VPN disconnects or when users explicitly enable strict block-all mode. The implementation centers on the `KillSwitch` class found in [`service/server/killswitch.h`](https://github.com/amnezia-vpn/amnezia-client/blob/main/service/server/killswitch.h) and [`service/server/killswitch.cpp`](https://github.com/amnezia-vpn/amnezia-client/blob/main/service/server/killswitch.cpp), which provides a unified API while leveraging OS-native firewall technologies for actual packet filtering.

## Core Architecture: The KillSwitch Singleton

The implementation follows the **singleton pattern** to maintain a single point of control for firewall rules across the entire application. The `KillSwitch::instance()` method lazily creates one `KillSwitch` object attached to the Qt application (`qApp`), ensuring thread-safe access to firewall state.

Initialization occurs through `KillSwitch::init()`, which checks for a persisted *strict-kill-switch* flag. On Linux and macOS, this uses `SecureQSettings` to read from the Qt settings file; on Windows, it queries the registry. If strict mode is already enabled at startup, `init()` immediately invokes `disableAllTraffic()` to block all packets before any other network operations begin.

## Platform-Specific Firewall Integration

While the `KillSwitch` class provides the high-level logic, actual packet filtering relies on dedicated firewall helpers for each operating system:

### Windows Implementation

On Windows, the `KillSwitch` delegates to **`WindowsFirewall`** (defined in [`client/platforms/windows/daemon/windowsfirewall.h`](https://github.com/amnezia-vpn/amnezia-client/blob/main/client/platforms/windows/daemon/windowsfirewall.h) and implemented in the corresponding `.cpp` file). This wrapper interacts with the Windows Filtering Platform to create allow/deny rules.

For strict blocking, `WindowsFirewall::create(this)->enableInterface(-1)` disables all network interfaces. When restoring connectivity, `allowAllTraffic()` reverts to default permissive rules. For split-tunnel configurations, `enablePeerTraffic()` processes an `InterfaceConfig` object built from JSON configuration to permit specific traffic flows.

### Linux and macOS Implementation

Both Unix-like platforms use **PF (Packet Filter)** based implementations:

- **Linux**: `LinuxFirewall` in [`client/platforms/linux/daemon/linuxfirewall.h`](https://github.com/amnezia-vpn/amnezia-client/blob/main/client/platforms/linux/daemon/linuxfirewall.h) manages anchor-based rule sets
- **macOS**: `MacOSFirewall` in [`client/platforms/macos/daemon/macosfirewall.h`](https://github.com/amnezia-vpn/amnezia-client/blob/main/client/platforms/macos/daemon/macosfirewall.h) handles the BSD PF subsystem

These implementations organize rules into anchors such as `100.blockAll`, `110.allowNets`, and `120.blockNets`. The `setAnchorEnabled()` method toggles specific rule blocks, while `setAnchorTable()` (macOS) or direct anchor manipulation (Linux) manages allowed IP ranges.

## Strict Mode vs. Standard Kill Switch

The client supports two operational modes controlled through `KillSwitch::refresh(bool enabled)`:

**Strict Mode (Block-All)**
When enabled, the method writes `strictKillSwitchEnabled = true` to persistent storage (registry or settings file) and calls `disableAllTraffic()`. This immediately activates the `100.blockAll` anchor on Unix systems or disables all interfaces on Windows, guaranteeing zero network connectivity regardless of VPN state.

**Standard Mode**
When disabled, `refresh()` calls `disableKillSwitch()`, which restores normal networking by:
- Re-enabling the loopback anchor and permissive anchors (`allowDHCP`, `allowLAN`, `allowDNS`) on Linux
- Calling `MacOSFirewall::uninstall()` on macOS (unless strict mode persists)
- Executing `WindowsFirewall::allowAllTraffic()` on Windows
- Clearing the internal `m_allowedRanges` list

## Managing Allowed Traffic and Split-Tunneling

Even with the kill switch active, certain traffic must flow (such as DHCP, DNS, or user-specified split-tunnel sites). The `KillSwitch` manages these exceptions through:

- **`resetAllowedRange()`**: Clears the whitelist before rebuilding it
- **`addAllowedRange(QStringList ranges)`**: Adds IP subnets (e.g., `"10.0.0.0/24"`, `"2001:db8::/32"`) to the internal `m_allowedRanges` list and updates platform-specific firewall tables

During VPN activation, `enableKillSwitch()` evaluates the split-tunnel type to determine whether to `blockAll`, `allowNets`, or `blockNets`, automatically programming the appropriate PF anchors or Windows rules.

## Integration Points: Peer Traffic and VPN Activation

The `KillSwitch` integrates with the VPN connection lifecycle through several key methods:

**`enablePeerTraffic(QJsonObject config)`**
Called primarily on Windows during VPN activation, this method parses the JSON configuration to construct an `InterfaceConfig` object. It passes this to `WindowsFirewall::enablePeerTraffic()` to permit traffic to VPN servers while maintaining blocks on other destinations.

**`enableKillSwitch(int vpnAdapterIdx, QJsonObject config)`**
This comprehensive activation method:
1. On Windows: Calls `allowAllTraffic()` for certain split-tunnel types, then enables the specific VPN interface
2. On Linux/macOS: Computes the effective rule set based on configuration and activates the corresponding anchors (handling DNS servers, LAN traffic, and user-specified ranges)

## Summary

- **Centralized Control**: The `KillSwitch` singleton in [`service/server/killswitch.h`](https://github.com/amnezia-vpn/amnezia-client/blob/main/service/server/killswitch.h) provides a single API for all platforms, handling persistence through `SecureQSettings` or Windows registry.
- **Platform Abstraction**: Actual packet filtering delegates to `WindowsFirewall`, `LinuxFirewall`, or `MacOSFirewall` depending on the operating system.
- **Strict Mode**: Can be toggled at runtime via `refresh()`, immediately blocking all traffic through `disableAllTraffic()` or restoring connectivity via `disableKillSwitch()`.
- **Flexible Rules**: `addAllowedRange()` and `resetAllowedRange()` support split-tunnel configurations by whitelisting specific subnets while maintaining the kill switch for all other traffic.
- **Lifecycle Integration**: Methods like `enablePeerTraffic()` and `enableKillSwitch()` coordinate firewall rules with VPN connection states to prevent leaks during connection establishment or teardown.

## Frequently Asked Questions

### How does amnezia-client prevent traffic leaks during VPN disconnections?

Amnezia-client prevents leaks through the `KillSwitch` class which maintains active firewall rules even when the VPN tunnels drops. On disconnection, unless the user disables the kill switch entirely, the `disableKillSwitch()` method restores a safe state that still blocks non-VPN traffic, or in strict mode, `disableAllTraffic()` maintains a complete block until explicitly released.

### What is the difference between strict mode and regular kill switch in amnezia-client?

Strict mode, activated via `KillSwitch::refresh(true)`, immediately blocks all network traffic regardless of VPN state using `disableAllTraffic()`. The regular kill switch only blocks traffic when the VPN is disconnected or misconfigured, allowing normal internet use when the VPN is intentionally turned off. Strict mode persists across application restarts by storing the flag in Windows registry or Qt settings.

### Where does amnezia-client store kill switch settings?

On Windows, the strict kill switch flag is stored in the Windows Registry. On Linux and macOS, the implementation uses `SecureQSettings` to persist the `strictKillSwitchEnabled` boolean in the application's Qt settings file, ensuring the preference survives client restarts.

### Can the kill switch allow specific IPs while blocking everything else?

Yes, through the `addAllowedRange()` method in [`killswitch.cpp`](https://github.com/amnezia-vpn/amnezia-client/blob/main/killswitch.cpp). This method accepts a `QStringList` of CIDR ranges (e.g., `"192.168.1.0/24"`) and updates the platform-specific firewall rules to permit traffic to those destinations while maintaining the block on all other addresses, supporting split-tunnel configurations.