Appearance
iptables 防火墙
iptables 在 Linux 上做主机侧的包过滤和 NAT。虽然新发行版在往 nftables 迁移,但 iptables 仍然广泛应用——老系统、容器网络(Kubernetes 的 kube-proxy 在很多版本里还是用 iptables)、各种技术文档,都离不开它。所以 iptables 这套东西,运维还是得会。
一、iptables 和 firewalld 的关系
RHEL/CentOS 上经常看到 firewalld,它是个更高层的防火墙管理工具,底层可以调用 iptables 或者 nftables。如果 firewalld 正在管规则,你直接手动改 iptables 规则,可能被 firewalld 覆盖掉——两边打架,规则时有时无。所以处理防火墙之前,先确认到底谁在主控:
bash
iptables -V # 看 iptables 版本
systemctl status firewalld # 看 firewalld 是否在运行
iptables -L -n -v # 看当前 iptables filter 表规则还有个底层概念要理清:iptables 是用户态的管理工具,真正在内核里处理报文的是 netfilter 框架。你敲的 iptables 命令只是把规则写进内核,数据包的匹配和动作全是内核里的 netfilter 干的。理解这点,后面"为什么规则改了立刻生效""为什么重启会丢"这些现象就解释得通了。
二、表、链和规则
iptables 的规则体系分三个层次:表(按功能分类的规则集合)、链(报文经过内核网络栈时的检查点)、规则(匹配条件+动作)。规则在链里按顺序逐条比对,命中就执行动作。
四张常用表,各自管一类功能:filter 表做过滤(放行/阻止端口,最常用)、nat 表做地址转换(SNAT/DNAT)、mangle 表修改报文头(改 TOS、TTL,少用)、raw 表在连接跟踪之前处理(排除特定流量不做状态跟踪)。
五条链对应报文经过内核的不同位置:PREROUTING(报文刚到网卡还没路由决策)、INPUT(路由后确定目标是本机)、FORWARD(路由后确定要转发到别的机器,本机当网关)、OUTPUT(本机进程发出的报文)、POSTROUTING(报文即将离开网卡)。
报文经过哪些链,取决于流量方向:
| 流量方向 | 经过的链 |
|---|---|
| 外部 → 本机服务 | PREROUTING → INPUT |
| 本机 → 外部 | OUTPUT → POSTROUTING |
| 外部 → 本机转发 → 外部 | PREROUTING → FORWARD → POSTROUTING |
开放端口的场景主要看 filter 表的 INPUT 链。排查端口不通时,先判断流量方向(进本机、出本机、还是转发),就知道该看 INPUT、OUTPUT 还是 FORWARD 链了。
三、查看规则
bash
iptables -L -n -v # 默认 filter 表,-n 不解析名称(快),-v 显示计数器
iptables -S # 以接近命令的形式输出(方便复制和回滚)
iptables -t nat -L -n -v # 查看 nat 表
iptables -L INPUT -n --line-numbers # 带行号,方便后续按行号删除iptables -S 的输出格式可以直接当命令重新执行,备份和回滚时比 -L 实用得多。-v 输出里的计数器(pkts、bytes 列)也很重要——某条规则的计数器一直不涨,说明包根本没匹配到这条规则,排查"为什么规则没生效"时,看计数器是个好习惯。
四、规则基本结构
一条规则由匹配条件和动作组成。以"放行 SSH"为例拆解:-A INPUT(追加到 INPUT 链末尾)、-p tcp(只匹配 TCP 协议)、--dport 22(目标端口 22)、-j ACCEPT(匹配后放行)。
规则的顺序至关重要:iptables 从链的第一条开始逐条往下匹配,命中一条就执行动作,不再继续往下看。所以放行规则要写在拒绝规则前面,顺序错了就全废了。
一个安全的最小规则集:
bash
# 放行已经建立和相关的连接——保证现有 SSH 不会断
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 放行本机回环(lo),很多本地服务依赖 127.0.0.1 通信
iptables -A INPUT -i lo -j ACCEPT
# 放行 SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 放行业务端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 默认策略:INPUT 链未匹配的包一律丢弃
iptables -P INPUT DROP远程操作防火墙时有个救命原则:默认策略(-P INPUT DROP)必须最后设置,而且要先确认放行 SSH 的规则已经生效。顺序反了,SSH 立刻断,你被关在外面。操作之前最好在旁边开一个已经登录的 tmux 会话作为兜底——万一主会话断了,tmux 那个还能救。
三种动作的区别要记住:
| 动作 | 行为 | 对方看到的效果 |
|---|---|---|
| ACCEPT | 放行 | 正常通信 |
| DROP | 直接丢弃,不回复任何内容 | 超时(等半天没响应) |
| REJECT | 丢弃并回复错误(ICMP 或 RST) | 立刻收到拒绝 |
公网入口常用 DROP(不回复等于不暴露服务存在,扫描器探测不到),内网排错时用 REJECT 更友好(失败快,不至于一直等超时)。
五、状态跟踪
iptables 通过连接跟踪(conntrack)能识别一个包属于哪个连接。连接状态有几种:NEW(新连接的第一个包)、ESTABLISHED(属于已有连接的包)、RELATED(与已有连接相关,比如 FTP 数据通道)、INVALID(无法归类的异常包)。
ESTABLISHED,RELATED 这条规则的核心价值:允许本机主动访问外部之后,外部返回的响应包能通过防火墙进来。如果没有这条规则,默认 DROP 的机器连自己主动发起的 HTTP 请求的回包都收不到——本机能访问外网这个基本能力就废了。所以这条规则几乎在所有防火墙配置里都是第一条。
新系统推荐用 conntrack 模块的写法:
bash
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTstate 和 conntrack 底层都是 netfilter 的连接跟踪系统,功能基本等价,conntrack 是更现代的模块。维护老机器不用强行改风格,新写的规则优先用 conntrack。
六、规则的插入和删除
追加是 -A(加到末尾),插入是 -I(加到指定位置):
bash
iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT # 插入到 INPUT 链第 1 条按行号删除(先看行号再删):
bash
iptables -L INPUT -n --line-numbers # 先看行号
iptables -D INPUT 3 # 删除第 3 条或者按规则内容匹配删除:
bash
iptables -D INPUT -p tcp --dport 80 -j ACCEPT按行号删之前一定再看一遍行号——链里增删规则之后,后面规则的编号会跟着变,你看的行号可能已经不是刚才那个了。
七、NAT
NAT 修改报文里的 IP 地址或端口,两类常见用法:SNAT/MASQUERADE(内网多台机器共享一个外网 IP 上网,在 POSTROUTING 链)、DNAT(把外网请求转发到内网某台机器,在 PREROUTING 链)。
内网机器共享上网(MASQUERADE 会自动用出口网卡的 IP):
bash
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE端口转发(外部访问本机 8080,转到内网 10.0.0.10:80):
bash
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.10:80做 NAT 转发需要先启用 IP 转发功能:
bash
sysctl -w net.ipv4.ip_forward=1持久化:
bash
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/99-ip-forward.conf
sysctl --system排查 NAT 规则时要同时看 nat 表和 filter 表。DNAT 改写之后,后续链(比如 FORWARD)看到的包已经是改写过的——源端口从 :8080 变成了 :80。这一点容易搞混,排查时心里要有数。
八、保存和恢复规则
iptables 规则存在内存里,重启就丢。所以临时加规则救完火,马上做持久化。不同发行版方式不一样:
RHEL/CentOS:
bash
service iptables save # 保存到 /etc/sysconfig/iptables
iptables-save > /etc/sysconfig/iptablesDebian/Ubuntu:
bash
apt install iptables-persistent -y
iptables-save > /etc/iptables/rules.v4临时加规则救火后忘了持久化,是经典踩坑场景——救完火以为搞定了,机器下次重启规则没了,同样的问题又来一遍。养成习惯:改完规则立刻 iptables-save 持久化。
九、和 firewalld 共存
如果团队统一用 firewalld 管防火墙,别绕过它直接写 iptables:
bash
firewall-cmd --state
firewall-cmd --list-all
firewall-cmd --add-port=80/tcp --permanent
firewall-cmd --reload混着改的结果就是规则互相覆盖,出问题的时候根本追溯不清规则最终是从哪来的——是 firewalld 生成的、还是谁手动 iptables 加的、还是哪个脚本写的。一个团队要么全用 iptables,要么全用 firewalld,别混。
十、端口不通时的排查清单
bash
ss -lntp | grep ':80' # 服务是否在监听
iptables -L INPUT -n -v --line-numbers # filter 表 INPUT 链规则和计数器
iptables -t nat -L -n -v --line-numbers # NAT 表规则(有端口转发时)重点看计数器:如果某条放行规则的计数器始终为 0,说明包根本没走到这条规则——可能是方向不对、表选错了、链选错了、或者上游网络就把包丢了。计数器是排查 iptables 问题最直接的线索,比盯着规则猜"为什么没生效"靠谱得多。