1. 背景: etcd API 漏洞现形

在安全审计中,我们发现 PD 节点(TiDB 的元数据管理组件)的 2379 端口(etcd API)存在未授权访问漏洞。这意味着任何客户端都能通过 HTTP 请求获取集群的元信息。

紧急任务: 针对 2379 端口实施严格的 IP 白名单准入,仅允许 TiDB 内部节点及中控机访问,阻断其余所有流量,且不能影响 SSH(22)等其他业务端口。


2. 深度复盘:iptables 里的“障眼法”

在这次排查过程发现了如下两个让让我疑惑或者说让我产生写下这篇文章的的2个问题:

就是在排查的时候发现这个端口在之前已经通过 iptables 设置了规则并且还执行了 iptables-save,但是现在却失效了,进一步排查发现这个主机最近更换硬件有过重启,也就是重启失效了,以下是具体的两个问题点:

疑点 A:history 里明明有 save,重启后规则却丢了

通过 history 看到之前的操作记录中看到了 iptables-save,但机器重启后规则却消失了。

  • 真相: iptables-save 默认只是将当前规则打印到屏幕(Stdout)。
  • 教训: 如果没有配合重定向符号(如 sudo iptables-save | sudo tee /etc/sysconfig/iptables),这行命令就只是在终端里“闪了一下”,根本没有写入磁盘。重启时,内核内存重置,规则自然烟消云散。我请教了下 AI ,AI 给了我一个形象的解释:在 Linux 中,直接运行 sudo iptables-save 就像你在文档里按了“全选+复制”,但没有按“保存”。 哈哈,形象不。

疑点 B:服务是 inactive 状态,为什么规则却在生效?

然后进一步检查其他正常运行的主机,发现 systemctl status iptables 也是 inactive (dead),但它们的过滤规则却稳如泰山。

  • 真相: iptables 的规则直接运行在 Linux 内核的 Netfilter 框架中。
  • 逻辑: iptables.service 并不是一个常驻后台的守护进程,而是一个 一次性脚本。它在开机的那一瞬间运行,负责把磁盘配置文件“搬”进内核,干完活就退出了。
  • 结论: 只要内核里的规则没被清空且机器没重启,即使服务显示 inactive,防火墙依然在工作。但如果开机时服务没能成功加载规则,内存里就是一片空白。

3. 架构演进:转向 Firewalld 的理由

考虑到维护成本与配置的直观性,我们决定弃用 iptables,转向 firewalld

  • 默认放行策略: 通过 set-target=ACCEPT 模拟“全开放”环境,仅对高危端口做“精准打击”。
  • 原生持久化: 使用 --permanent 参数即可确保规则直接写入 XML 配置文件,无需担心忘记重定向。
  • 顺序自动化:firewalld 的富规则中,accept 逻辑会自动优先于 reject/drop 处理,降低了因命令顺序错误导致“把自己锁在外面”的风险。

4. 生产级加固脚本

以下脚本实现了“全局放行 + 2379 端口严管”的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/bash
# TiDB PD 节点安全加固脚本 (firewalld 方案)

# 1. 环境清理:停止并禁用旧的 iptables 服务,防止规则冲突
echo "清理旧的 iptables 环境..."
sudo systemctl stop iptables
sudo systemctl disable iptables

# 2. 激活服务:启动并启用 firewalld
echo "启动并启用 firewalld..."
sudo systemctl start firewalld
sudo systemctl enable firewalld

# 3. 设置策略:全局默认放行 (ACCEPT)
# 这保证了除了 2379 以外的所有端口(如 SSH)依然可以正常连接
echo "设置默认策略为 ACCEPT..."
sudo firewall-cmd --permanent --set-target=ACCEPT

# 4. 定义白名单 IP (已脱敏处理)
WHITELIST_IPS=(
"127.0.0.1" # 本机回环
"10.10..x.86" "10.10..x.41" "10.10..x.22" # PD 节点
"10.10..y.126" "10.10..y.127" "10.10..y.14" # TiDB 节点/中控机
)

echo "注入 2379 端口白名单..."
for ip in "${WHITELIST_IPS[@]}"; do
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='$ip' port protocol='tcp' port='2379' accept"
done

# 5. 精准打击:拒绝其余所有 IP 访问 2379 端口
echo "注入 2379 端口默认拒绝规则..."
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port protocol="tcp" port="2379" reject'

# 6. 配置生效
echo "重载规则..."
sudo firewall-cmd --reload

echo "=========================================================="
echo "配置完成!当前 2379 端口防护已生效且已持久化。"
sudo firewall-cmd --list-rich-rules
echo "=========================================================="

5. 经验总结

  1. 内核状态 $\neq$ 磁盘配置:在 iptables 体系下,sudo iptables -S 看到的只是幻象,只有写入 /etc/sysconfig/iptables 的规则才是永恒。
  2. 善用工具特性firewalld 的富规则(Rich Rules)在处理这种“白名单+末尾拒绝”的场景时,比 iptables 的链式顺序更不容易出错。
  3. 安全即流程:加固完毕后,务必在另一台非白名单机器上使用 curl 进行连通性验证,并重启一台非核心节点测试自启动状态,确保安全策略“真的”活在系统里。

原文作者: liups.com

原文链接: http://liups.com/posts/6395c953/

许可协议: 知识共享署名-非商业性使用 4.0 国际许可协议