防火墙编辑的死亡开关

家庭实验室自动化中最令人恐惧的一行代码,就是编辑你正通过 SSH 连接的路由器上的防火墙规则那一行。

以下是 Claude Code 和我如何依然进行编辑的方法。


恐惧所在

我需要将 UDM Pro 上名为 “Block inter-VLAN traffic” 的规则从 accept 改为 drop。这条规则长期以来一直配置错误——被设置为 accept,使得上面所有按流量的允许规则都被短路了——而将其重新关闭正是目的所在。执行变更的编排器是我家庭网络上的一台 NixOS 虚拟机。如果这次翻转切断了该虚拟机(或我自己的 SSH)所使用的路径,我将被锁在自己的路由器之外,没有控制台回退。

解决方案不是"小心一点"。解决方案是让危险的编辑在我未确认没问题的情况下自动恢复。

你以前见过这种模式。在 macOS 或 Windows 上更改显示器分辨率,系统会应用十五秒钟,同时显示一个倒计时的"保留这些显示设置?“对话框。如果你的屏幕变黑了,你什么都点不了——这正是重点所在。默认结果是回滚。确认是可选的。

这正是我们在修改规则之前对路由器防火墙翻转所需要的。


技巧

udm_set_firewall_rules.py --flip-with-revert 300 --apply

含义:“翻转规则,但在 300 秒后自动恢复——除非我告诉你不要这样做。”

按顺序执行的操作:

  1. 快照。 GET 当前规则,并将完整的 JSON 负载存储在 UDM 的 /root/.udm-flip-revert/payload-<id>.json
  2. 准备恢复。 在负载旁边写入一个小型 shell 脚本,该脚本将快照重新 PUT 到 UDM API。
  3. 调度恢复。 systemd-run --on-active=300s --unit=udm-flip-revert-<id> 将脚本作为临时计时器排入队列。
  4. 翻转。 PUT 新规则(action: drop)。

现在你有 5 分钟的窗口来验证:

✓ flipped 'Block inter-VLAN traffic' to action='drop'
⏱  auto-revert scheduled (~300s)

TO KEEP THE FLIP:    ssh udm 'systemctl stop udm-flip-revert-66c1d…timer'
TO ROLL BACK NOW:    ssh udm 'systemctl stop …timer; bash /root/.udm-flip-revert/revert-….sh'
WAIT IT OUT:         do nothing — timer will revert in ~300s

三种结果:

第三种情况正是这一切存在的全部原因。


为什么用 systemd-run 而不是 at(1)

UDM Pro(Debian 11)没有附带 atd。但它有 systemd。systemd-run --on-active=Ns 创建一个临时计时器,触发一次后自动清理,这正是"在 N 秒后执行此操作然后消失"的形态。

ssh_udm(
    f"systemd-run --on-active={seconds} --unit={unit} --collect "
    f"--quiet /bin/bash {script_file}"
)

--collect 是关键标志——没有它,单元会停留在 failed/inactive 状态并使 systemctl 变得混乱。


注意事项

临时 systemd 单元存在于 /run 中,这是 tmpfs。如果 UDM 在恢复窗口内重启,计时器就消失了——翻转依然保持应用状态,没有死亡开关看守。

反直觉但真实:保持窗口短暂。带有小重启风险的 5 分钟窗口,比第 12 分钟 UPS 故障导致永久锁定的 1 小时窗口更安全。足够长以便验证,足够短以使"我是否忘记了待处理的恢复?“永远不成问题。


结语

死亡开关模式可以推广到 UDM 防火墙规则之外。任何你将要应用的可能切断你应用它的路径的变更——路由表、sshd_config、nftables、BGP——同样的形态都适用:

  1. 捕获当前状态
  2. 准备一个恢复它的脚本
  3. 安排脚本在 N 秒后运行
  4. 应用变更

如果在 N+ε 秒后你还在那里且一切正常,取消计时器。如果不是,计时器会取消你。

有了这个工具箱,我睡得更好了。