前言

Linux 防火墙经历了 iptables → nftables → firewalld(前端)的演进。现代 RHEL 系默认 firewalld(底层实为 nftables),但老机器和容器网络里 iptables 依旧无处不在。两个都要会。

一、先搞清三层"墙"

外部流量
  │
  ▼ ① 云安全组(腾讯云/阿里云控制台, 机器之外)
  │
  ▼ ② 系统防火墙(firewalld/iptables, 机器之内)
  │
  ▼ ③ 应用自身监听(0.0.0.0 vs 127.0.0.1)

排查口子问题:从外到内逐层验证——安全组放了吗 → 系统防火墙放了吗 → 进程监听对吗。

二、firewalld:zone 思维

firewalld 把规则按 zone(区域) 组织,每个网卡绑定一个 zone:

firewall-cmd --get-default-zone            # 默认 public
firewall-cmd --get-active-zones            # 网卡与 zone 绑定关系
firewall-cmd --list-all                    # 当前 zone 全部规则
firewall-cmd --get-zones                   # 所有可用 zone
zone 语义
public 公共区域,只放行指定服务(服务器默认)
trusted 全放行
internal/drop/block 内网/全拒/拒绝带响应

服务与端口操作:

# 放行标准服务(本质是 /usr/lib/firewalld/services/*.xml 里定义的端口组)
firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=https --permanent

# 放行自定义端口
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --add-port=60000-61000/udp --permanent

# 端口转发(本机 80 -> 8080)
firewall-cmd --add-forward-port=port=80:proto=tcp:toport=8080 --permanent

# 任何 --permanent 的修改都要 reload 生效
firewall-cmd --reload

# 注意: 不加 --permanent 只对运行时生效(重启丢失); 调试时可先运行时放行

富规则:精细控制

# 只允许特定网段访问 SSH
firewall-cmd --permanent --add-rich-rule='
  rule family=ipv4 source address=10.0.0.0/24 port port=22 protocol=tcp accept'

# 封禁一个恶意 IP
firewall-cmd --permanent --add-rich-rule='
  rule family=ipv4 source address=1.2.3.4 reject'

# 允许 ping
firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp accept'

# 端口转发到另一台机器(需配 masquerade)
firewall-cmd --permanent --add-masquerade
firewall-cmd --permanent --add-rich-rule='
  rule family=ipv4 forward-port port=443 protocol=tcp to-addr=10.0.1.20'
firewall-cmd --reload

三、iptables:表与链的世界

四表(处理优先级从高到低):raw → mangle → nat → filter

五链(规则挂载点):

PREROUTING ──► FORWARD ──► POSTROUTING      (转发路径, nat 表主场)
     │
     ▼
 INPUT ──► [本机进程] ──► OUTPUT              (本机进出, filter 表主场)
链 时机
PREROUTING 刚进网卡(DNAT 在这)
INPUT 发给本机进程
FORWARD 经过本机转发(路由器/网关场景)
OUTPUT 本机进程发出
POSTROUTING 即将发出网卡(SNAT 在这)

规则 = 匹配条件 + 动作:

# 查看(-n 不解析域名, -v 带计数)
iptables -L INPUT -n -v --line-numbers

# 放行 80
iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# 只允许内网访问 22
iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP       # 其余全拒(规则从上到下匹配!)

# 封 IP
iptables -I INPUT -s 1.2.3.4 -j DROP               # -I 插到最前(优先级最高)

# 删除第 3 条规则
iptables -D INPUT 3

# 已建立连接的回包放行(状态防火墙精髓)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# 允许本机回环
iptables -A INPUT -i lo -j ACCEPT

动作速记:ACCEPT 放行 / DROP 丢弃(无响应)/ REJECT 拒绝(回拒绝报文)。

NAT 实战(做一台小路由器/网关):

# 开启转发
echo 1 > /proc/sys/net/ipv4/ip_forward

# SNAT: 内网 10.0.0.0/24 出公网(网卡 eth0 的 IP)
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

# DNAT: 公网访问本机 8443 转给内网机器 443
iptables -t nat -A PREROUTING -p tcp --dport 8443 -j DNAT --to-destination 10.0.1.20:443

MASQUERADE 是动态版 SNAT(出口 IP 会变时用,如拨号/DHCP)。

四、保存与持久化

# iptables 规则默认重启即失
iptables-save > /etc/sysconfig/iptables          # RHEL 系保存
iptables-restore < /etc/sysconfig/iptables       # 恢复

# firewalld 自动持久(--permanent 写入 /etc/firewalld/zones/)
ls /etc/firewalld/zones/

五、firewalld vs iptables vs nftables

firewalld iptables nftables
定位 管理前端(规则易读易管理) 经典工具 内核新框架(iptables 继任者)
规则修改 增量(reload 不断连接) 全量替换 增量
适合 日常服务器运维 老系统/精确控制/K8s 网络底层 新项目/高性能场景

Docker/K8s(kube-proxy iptables 模式)会直接操作 iptables 规则——这就是装了 Docker 后 iptables -L 多出一堆链的原因,别手删。

六、排障速查

# 端口到底谁没放行?
# 1. 计数器证据: 每条规则有 pkts 计数, 看是否命中
iptables -L INPUT -nv | grep 8080

# 2. 抓包确认包到了没
tcpdump -i any -nn port 8080 -c 10

# 3. firewalld panic 模式(全断! 误开排查)
firewall-cmd --query-panic

# 4. 规则对了还不通: 检查内核转发与安全组
sysctl net.ipv4.ip_forward

小结

任务 命令
放行端口 firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload
白名单 rich rule + source address=
封 IP rich rule reject 或 iptables -I INPUT -s x -j DROP
NAT 上网 MASQUERADE + ip_forward
排障 规则计数器 + tcpdump + 别忘安全组

本文是「Linux 运维」系列第 7 篇。