跳到主要内容

VPN 只有在隧道存在时才起保护作用。当隧道因为 Wi-Fi 抖动、服务器重启或笔记本从睡眠中唤醒而断开时,操作系统会悄悄回到普通连接,而恰好在这一刻重试的任何请求,都会带着你的真实地址发出去。kill switch 的作用,就是在隧道不存在时封住普通路径,填上这个缺口。

在 Windows 或手机上,kill switch 只是一个复选框。而在 Linux 上,Linux VPN kill switch 指的是另一回事:一条由你自己编写的防火墙规则。听起来比实际要复杂。本文介绍三种实现方法,说明哪些流量必须仍然放行,以免自断后路,并给出安全的测试办法。基本概念在 VPN kill switch 是什么中讲过,这里只负责搭建和验证。

Linux 上的 kill switch 究竟是什么

Linux 在内核里用 netfilter 过滤数据包,nftables、iptables 和 ufw 是同一套引擎的不同前端。kill switch 就是写在这套引擎里的一条策略:

  1. 放行回环接口上的流量。
  2. 放行经 VPN 接口(如 wg0 或 tun0)发出的流量。
  3. 放行用于建立隧道的少量流量:发往 VPN 服务器地址和端口的数据包。
  4. 放行你有意让它留在隧道之外的内容,比如本地打印机所在的网络(如果有的话)。
  5. 丢弃或拒绝其余所有试图外发的流量。

里面没有任何魔法。隧道一消失,第 2 条不再匹配任何东西,第 5 条接住其余流量,应用程序只会超时,而不会在明处建立连接。由于它是防火墙规则,不论隧道是由 wg-quick、NetworkManager 还是一条普通的 OpenVPN 命令管理的,它都有效。

先决定要拦截什么

动手之前先选定级别。

方案 拦截范围 优点 风险
wg-quick 的 PostUp 规则 隧道运行期间,所有不经 WireGuard 接口的流量 写在配置文件里,随隧道出现和消失 隧道启动前和关闭后没有保护
永久的 nftables 策略 任何时候,除隧道、服务器和例外之外的一切 覆盖开机、睡眠和重连的空档 必须自己维护例外,出错会让你断线
ufw 默认拒绝出站 同样的思路,工具更友好 易读,并且重启后依然有效 需要多条放行规则,容易忘记 DHCP
按应用控制 只针对单个程序 对某个应用有用 其他应用仍会泄漏

要看清一个取舍:只在隧道运行时存在的规则,保护不了前后的空档;永久策略能保护这些空档,但意味着在你启动隧道之前,这台电脑完全没有网络。对在公共 Wi-Fi 上使用的笔记本,很多人可以接受;对家里的电脑,另一些人更喜欢较轻的规则。

方法一:写在 wg-quick 里的规则

WireGuard 自己的文档描述了一对可以放在配置文件 Interface 部分的语句。隧道启动时添加规则,关闭时删除规则。

[Interface]
PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT

读法是:拒绝所有不经隧道接口(%i)发出、没有被标记为隧道自身加密流量(WireGuard 设置的 fwmark)、也不是发往本机地址的出站数据包。隧道自己发往服务器的数据包带有这个标记,所以能通过。

需要了解的限制:这个办法适用于 wg-quick,不适用于导入 NetworkManager 的 WireGuard 配置;使用 nft 后端来运行 iptables 的发行版表现一致。由于规则在 PreDown 中被删除,运行 wg-quick down 会恢复正常上网,这在你主动断开时是对的,但如果崩溃跳过了 PreDown,规则可能残留。用 sudo iptables -S OUTPUT 检查。

方法二:永久的 nftables 策略

nftables 允许你用一个易读的文件描述整个策略。请把大写部分替换成你自己的值。

table inet killswitch {
  chain output {
    type filter hook output priority 0; policy accept;
    oif "lo" accept
    oifname "wg0" accept
    ip daddr SERVER_IP udp dport 51820 accept
    ip daddr 192.168.1.0/24 accept
    udp dport { 67, 68 } accept
    counter drop
  }
}

用 sudo nft -f killswitch.nft 加载,用 sudo nft delete table inet killswitch 删除。各行说明如下。

  • oif "lo" 让本机服务(如 DNS 存根)继续工作。
  • oifname "wg0" 放行经隧道发出的一切。OpenVPN 请改用 tun0。
  • SERVER_IP 那一行让加密数据包能到达 VPN 服务器。请用地址而不是主机名,因为主机名需要在隧道之外先解析才行。
  • 本地子网那一行是可选的:它让打印机和 NAS 保持可访问。不需要的话就删掉。
  • DHCP 端口让机器能在 Wi-Fi 上续租地址。没有它们,租约到期后连接就会中断。
  • counter drop 结束这条链,并显示被拦截的数据包数量,测试时很有用。

由于表名为 inet,它同时覆盖 IPv4 和 IPv6。不经隧道发出的 IPv6 流量会被丢弃,这能防止许多人忽视的 IPv6 泄漏。IPv6 定义在 RFC 8200 中,如今许多网络默认就在使用,所以不要跳过这一部分。

方法三:ufw,适合喜欢简单命令的人

ufw 适合已经在用它的桌面系统。

sudo ufw default deny outgoing
sudo ufw default deny incoming
sudo ufw allow out on wg0
sudo ufw allow out to SERVER_IP port 51820 proto udp
sudo ufw allow out 67/udp
sudo ufw allow out to 192.168.1.0/24
sudo ufw enable

ufw 的默认规则已经处理了回环接口。用 sudo ufw status verbose 检查结果。需要恢复时,用 sudo ufw default allow outgoing 和 sudo ufw disable。ufw 的规则重启后依然有效,这对保护是好事,但如果你忘了它的存在,又纳闷为什么新网络用不了,就麻烦了。另一点:笔记本移到有认证门户(captive portal)的网络时,需要先加载门户页面隧道才能启动,因此你可能需要暂时放宽规则。

放行必须通过的流量

大多数锁死情况都源于漏掉了下面某一项。

  1. VPN 服务器本身。 没有针对其地址和端口的规则,隧道永远无法启动。
  2. DHCP。 机器通过 UDP 67 和 68 端口向路由器申请地址。
  3. 本地网络访问。 只有在你需要时才放行,否则不要加。
  4. 配置中使用的主机名。 如果配置里写的是名称,先把它解析成地址,再写入地址。
  5. 认证门户。 酒店和咖啡馆的网络需要先加载一个页面,隧道才能工作。请预留手动放宽规则的步骤。
  6. 备用通道。 在远程机器上,来自特定地址的 SSH 会话可能需要单独的放行规则,否则策略一加载你就会丢掉会话。

关于 SSH 的情形,请特别阅读命令行指南中的警告。

安全地测试

不要加载新的 kill switch 就走开。用计时器让错误的规则自己消失。

  1. 打开两个终端。在第一个里安排删除:sleep 300; sudo nft delete table inet killswitch(或 ufw 的撤销命令)。
  2. 在第二个终端加载规则。
  3. 隧道开启时,检查普通流量是否正常:curl -s https://example.org -o /dev/null -w "%{http_code}\n"。
  4. 在不删除规则的前提下关闭接口:sudo ip link set wg0 down。
  5. 再试一次 curl,应该失败或超时。这说明 kill switch 在工作。
  6. 检查 DNS 也无法在隧道外解析:resolvectl query example.org 不应通过你的 Wi-Fi 链路返回结果。
  7. 再次启动隧道,确认能恢复。如果计时器先触发了,用更长的等待时间重做。

之后再按如何测试 VPN中更完整的检查来做,包括公网地址和 IPv6 的检查。

持久化与移除

要在开机时加载 nftables 规则,把文件保存到发行版期望的位置(通常是 /etc/nftables.conf),并启用 nftables 服务。在使用 ufw 的系统上,ufw enable 本身就是持久的。对于 wg-quick 的钩子规则,启用 wg-quick@wg0 会在每次启动时添加它们。

把移除方法记下来。在笔记里写一句“sudo nft delete table inet killswitch”,能让你免去一个焦躁的夜晚。如果你要出售、借出或转让这台机器,请先移除该策略。

局限与坦率的提醒

  • kill switch 防止隧道故障时的泄漏。它不能让你匿名,也不能保护账户免受钓鱼攻击。
  • 已经保持连接的应用可能需要一会儿才会失败,出现短暂超时是正常的。
  • 像方法一那样依赖 fwmark 的策略只适用于 WireGuard。OpenVPN 请使用基于接口的策略。
  • 一些桌面工具、虚拟机和容器有自己的网络,可能绕开你的 OUTPUT 规则,也要检查它们的流量。
  • 不要照抄网上自己看不懂的规则。丢弃流量的语句很容易写,却很难排错。

总结

在 Linux 上,kill switch 就是一条防火墙策略:放行回环、隧道接口、能到达 VPN 服务器的数据包以及你需要的少数例外,其余全部丢弃。想要轻量、随隧道存在的规则,用 wg-quick 的钩子;想要同时覆盖 IPv4 和 IPv6 的永久策略,用 nftables;偏好简单命令则用 ufw。用会自我清除的计时器测试,确认关闭隧道后流量真的停止,并写下撤销的办法。如果你想用桌面应用搭配预付通行证,请查看下载页面和价格页面。

常见问题

Linux 有内置的 VPN kill switch 吗?

内核或桌面环境里没有这样一个单独的开关。Linux 上的 kill switch 是一条防火墙规则:只允许流量经 VPN 接口发出,外加连接 VPN 服务器所需的数据包。可以用 nftables、iptables、ufw 或 wg-quick 的钩子来实现。

kill switch 会把我锁在自己的网络之外吗?

如果你忘了放行回环接口、DHCP、本地网络或 VPN 服务器地址,就有可能。请用会自动删除规则的计时器来测试,并在远程机器上保留控制台通道。

IPv6 需要单独的规则吗?

需要。如果隧道不承载 IPv6,访问 IPv6 网站的流量可能绕过隧道。要么像 IPv4 一样阻止 IPv6 出站,要么在物理接口上禁用 IPv6。

kill switch 也能防止 DNS 泄漏吗?

它能阻止 DNS 数据包从普通接口发出,因为除隧道外的所有出站流量都被拦截。但你仍需把隧道里的 DNS 设置好,才能在隧道内成功解析。

同样的规则能用于 OpenVPN 吗?

nftables 和 ufw 的方法适用于任何隧道接口(如 tun0),只要放行 OpenVPN 服务器的地址和端口。wg-quick 的钩子语句只适用于 WireGuard。

关于作者

Bob Kanokphon Thongdee,XPASS VPN 创始人

Bob Kanokphon Thongdee

  • XPASS VPN 创始人
  • eSIMonline.asia 联合创始人
  • 7 年以上网络营销经验
  • Cojourney Visa 与 Cojourney Travel 创始人

短途出行或忙碌的一个月,需要 VPN?

XPASS 提供按天数计算的预付通行证。一次付款,不会自动续费。

查看通行证

继续阅读

VPN 安全

什么是 VPN 断网保护(Kill Switch)?

Kill Switch 能防止 VPN 断开时设备悄悄改用不受保护的连接。本文介绍它的原理,以及如何检查它是否有效。

阅读约 12 分钟

设备指南

如何在 Windows 上使用 VPN Kill Switch

Windows 自带 VPN 没有 Kill Switch 开关。看看如何在应用里查找、手动防火墙方案涉及什么,以及怎样测试结果。

阅读约 10 分钟

VPN 安全

如何自己评测 VPN:一份测试清单

许多 VPN 评测是为赚取佣金而写的。这份清单教你自己测试任何 VPN:哪些能验证,哪些不能,以及如何给结果打分。

阅读约 9 分钟

VPN 安全

IPv6 泄漏与 VPN:实用指南

一份动手指南:两套地址体系为何会造成缺口、怎样测试和解读结果,以及按顺序可以尝试的修复办法。

阅读约 10 分钟

VPN 安全

WireGuard 与 OpenVPN 有什么区别

WireGuard 和 OpenVPN 都能建立加密隧道,但设计、复杂度和灵活性不同。本文用通俗的方式比较,并说明该怎样选择。

阅读约 9 分钟