iptables (legacy|nf_tables) 会导致 ssh 连接断开的问题

68 阅读3分钟

image.png

非常好的问题,这里你已经抓到了关键差异。总结如下👇


🔍 现象总结

  • aarch64 + iptables (legacy) 的控制节点 k8s-ctrl 上执行脚本 不会断 SSH
  • x86_64 + iptables (nf_tables) 的工作节点 k8s-work1 上执行同样的脚本后,SSH 立即断联且无法重新连接

脚本本质上做的是:

  1. 备份现有规则;

  2. 执行:

    iptables-save | grep -v -i ovn | iptables-restore
    

    即:重新加载一份去掉包含 “ovn” 的规则;

  3. 删除与 “ovn” 相关的 ipset。


🧠 根因分析

1️⃣ legacy vs nf_tables 模式差异

这两种模式虽然命令相同,但内部实现完全不同:

对比项legacynf_tables
实现层经典 iptables 内核模块使用 nftables 后端
规则保存/恢复行为严格按文本匹配和顺序转换为 nftables 规则集后再重建
默认链与 policy保留系统已有链可能清空表后重新定义默认链

👉 在 nf_tables 模式 下,执行

iptables-save | grep -v -i ovn | iptables-restore

会触发整个 filternatmangle 表被重新创建。
iptables-restore 默认不保留现有规则和默认链的 policy,除非你手动保留。

这意味着:

你的 SSH 允许规则(例如 ACCEPT tcp -- anywhere anywhere tcp dpt:ssh)如果没有匹配 ovn 字样,也可能被清空掉,因为整个表被替换。

legacy 模式下,由于规则链和表行为略不同,部分默认规则(例如 INPUT ACCEPT)仍被保留,因而不会断 SSH。


2️⃣ iptables-restore 的行为细节

iptables-restore 默认行为是:

重写整个表内容,而不是在原有规则上“增删改”。

grep -v -i ovn 过滤掉的只是包含 “ovn” 的行,但你忘了一点:
iptables-save 的输出里包含如下内容:

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
...
COMMIT

nf_tables 模式下,这些链定义、引用、计数器都很脆弱。
grep -v -i ovn 过滤后可能打断某个 COMMIT 块或去掉部分链引用,从而导致加载异常或默认 DROP。


3️⃣ SSH 掉线的直接原因

k8s-work1 上:

  • 执行 iptables-restore 重新加载时,INPUT 链可能被清空;
  • 没有任何允许 22/tcp 的规则;
  • 甚至可能 INPUT policy 默认为 DROP
    → 于是 SSH 连接瞬间被切断。

而断联后无法重新连上,是因为 iptables 状态已经被持久化在内核中,没有 ACCEPT 规则可再匹配。


✅ 安全修正版脚本

可以改成这样来安全清理 OVN 相关规则,而不影响 SSH:

#!/usr/bin/env bash
set -e

# 备份
iptables-save > "ovn-iptables-backup-$(date +%Y%m%d%H%M%S).txt"
ip6tables-save > "ovn-ip6tables-backup-$(date +%Y%m%d%H%M%S).txt"

# 临时保存当前 SSH 规则
SSH_RULES=$(iptables-save | grep dpt:ssh || true)

# 清理含 ovn 的规则(仅删除匹配的行,而不整体重载)
iptables-save | grep -v -i ovn > /tmp/iptables-no-ovn.txt

# 确保保留 SSH 规则
echo "$SSH_RULES" >> /tmp/iptables-no-ovn.txt

# 确保有基本链定义和 COMMIT
if ! grep -q "COMMIT" /tmp/iptables-no-ovn.txt; then
    echo "COMMIT" >> /tmp/iptables-no-ovn.txt
fi

# 安全恢复
iptables-restore < /tmp/iptables-no-ovn.txt

# 清理 OVN ipsets
for s in $(ipset list -name | grep -i ovn); do
    ipset destroy "$s" || true
done

🔧 额外建议

  1. 不要直接管道 grep | restore
    → 这样会把 iptables-restore 置于非交互状态,任何语法错误或链缺失都可能立即生效(破坏 SSH)。

  2. 在远程执行前测试 dry-run

    iptables-save | grep -v -i ovn > /tmp/new.rules
    iptables-restore --test < /tmp/new.rules
    
  3. 尽量使用 iptables-nft 明确后端
    若系统默认 nf_tables,你也可以通过:

    update-alternatives --set iptables /usr/sbin/iptables-legacy
    

    临时切换到 legacy 模式执行清理。


💡结论

节点后端类型结果原因
k8s-ctrl (aarch64)legacySSH 正常iptables-restore 保留默认 ACCEPT
k8s-work1 (x86_64)nf_tablesSSH 断联iptables-restore 重写全表导致 INPUT policy 或规则丢失