1. 为什么我劝你认真学 iptables
先别急着把 iptables 当成老古董。这几年虽然 nftables 越来越常见,容器网络、K8s 的 kube-proxy 底层也大量使用 netfilter 钩子,但直到今天,你排查 Linux 网络问题、配置服务器防火墙、做 NAT 转发、搞双机热备,绕不开的还是 iptables 这一套规则体系。我见过太多人一句“systemctl stop firewalld”就把防护关了,结果线上机器被扫爆;也见过有人写规则从不考虑顺序,最后把自己 SSH 给拦死。说白了,iptables 不是背几条命令的事,而是你要先理解 Linux 内核到底怎么处理数据包,规则是挂在哪个环节上的,才能写出真正可靠、可维护的防火墙配置。
这篇文章我打算从 netfilter 框架讲起,把五条链、四张表、规则匹配顺序这些基础概念掰开揉碎,再给几套可以直接抄的实战配置。适合三类人看:刚入手 Linux 运维的新人,面试前想系统补一遍 iptables 知识点的求职者,以及被“开了防火墙就 ping 不通”“端口映射不生效”这类问题折磨过的人。看完你至少能做到:自己写一套服务器入站防护规则,能讲清楚默认策略为什么重要,遇到网络不通时知道从哪条链开始查。这不是背答案,是建立排查思路。
1.1 这不是老古董:iptables 在 Linux 网络栈里的位置
很多人一听到 iptables 就想到“防火墙”三个字,但它在 Linux 里的真实身份更底层:它是内核 netfilter 框架的用户态管理工具。什么意思?内核的网络协议栈在处理每一个网络报文时,会在特定位置预留几个“钩子点”,netfilter 框架允许你在这些钩子点挂载规则,对经过的报文进行检查、修改、放行或丢弃。iptables 只是把这些规则用命令行写进内核的工具,真正干活的是内核里的 netfilter 模块。
所以理解 iptables,重点不在命令参数,而在那几个钩子点:数据包进来先到哪,转发走哪条路,本机进程收包前在哪触发,回包又从哪里出去。这套流程理解了,后面所有的增删改查都是顺水推舟。这也是为什么我面试时喜欢问“iptables 的链为什么叫 PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING”,因为能答出这几个名字的人,是真的看过数据包流转图,而不是只背过 -A INPUT -j DROP。
1.2 适合谁读,读完能解决什么问题
如果你是运维,最常见的使用场景是保护一台 Linux 服务器的 SSH、Web、数据库端口,只允许白名单 IP 访问,同时把出方向也管起来,防止机器被入侵后变成肉鸡疯狂外联。如果你是做容器、虚拟化或网络设备调度的,iptables 的 NAT 规则、DNAT/SNAT 转发是你躲不开的基础设施。如果你只是想通过面试,那更要把默认策略、规则顺序、状态机制这几个概念说透,因为这些几乎是 Linux 网络方向的高频考点。
这篇文章给的规则都能直接拿去用,但我更希望你在抄的时候,知道每一条规则为什么放在那个位置。因为边界情况才是真正考验人的地方:规则顺序错了、表选错了、-I 插到第几条了、重启之后规则丢了……这些坑我都踩过,下面会挨个讲清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先理解 netfilter 框架:iptables 不是魔法
2.1 数据包从哪里来,到哪里去
我在讲网络的时候,习惯把 Linux 内核处理报文的过程比喻成一条流水线。一个数据包从物理网卡进来,内核做的第一件事不是判断“是谁发给我的”,而是先经过最前面的“登记处”,这个登记处就是 PREROUTING 链。在这里可以对包做 DNAT(目的地址转换),也就是我们常说的端口映射、IP 映射入口。
接着内核要做一个关键决定:这个包是发给本机的,还是要被转发的?如果目的 IP 是本机某个网卡的地址,报文就进入 INPUT 链,经过检查后交给本机进程处理;如果目的 IP 不是本机,而且开启了 ip_forward,报文就会走 FORWARD 链,进入转发流程。至于本机进程自己发出的包,先经过 OUTPUT 链的检查,然后统一走到 POSTROUTING 链,这里是做 SNAT(源地址转换)的地方,比如让内网机器通过这台 Linux 上网,就是在这里把源 IP 改成公网 IP。
记住这张流程图的顺序就够了:PREROUTING -> 路由决策 -> INPUT 或 FORWARD -> OUTPUT -> POSTROUTING。后面所有排查,都先问自己一句:这个包现在走到哪一步了?
2.2 五条链和四张表,先记这三张表
iptables 的链就是上面那些钩子点上规则组成的列表。表则是按功能划分的规则集合,常见的四张表是 filter、nat、mangle、raw。给新手一个建议:90% 的场景你只需要掌握 filter 和 nat 两张表,mangle 用来改包头的 TTL、TOS 等字段,raw 用来跳过连接跟踪,日常工作用得少,但面试时起码要知道它们存在。
filter 表只在 INPUT、FORWARD、OUTPUT 三条链上,负责放行或拒绝;nat 表只在 PREROUTING、OUTPUT、POSTROUTING 三条链上,负责修改源地址或目的地址。所以当你想配置“拒绝某 IP 访问本机 22 端口”,命令要写在 filter 表的 INPUT 链上;当你想做“把公网 8080 映射到内网某台机器的 80 端口”,要写在 nat 表的 PREROUTING 链上。表用错了,规则不报错,但完全不生效,很多老手排查半天都没想到这一点。
2.3 规则的匹配顺序和“最后一条兜底”
iptables 规则是按顺序逐条匹配的,这一点怎么强调都不过分。数据包进入一条链后,从头到尾逐条比对,一旦某条规则完全匹配,就执行这条规则指定的动作(target),然后根据 target 的特性决定是否继续匹配。ACCEPT、DROP、REJECT 这类终结动作会立刻停止本链后续规则的检查;而 LOG 这类非终结动作记录日志后会继续匹配下一条。
所以“放行白名单,拒绝其他所有”的写法,一定是先把白名单规则放在前面,把拒绝或丢弃的规则放在最后。如果你把 -A INPUT -j DROP 写在最前面,那后面的放行规则全都白写了。默认策略(policy)也是一样的逻辑:如果链上所有规则都没匹配,就用链的默认策略。我见过有人把默认策略设为 DROP,然后忘记写放行回环接口 lo 的规则,结果本机连自己都访问不了,数据库、Redis 全都连不上。这种问题排查起来非常反直觉,因为你不是被外部防火墙拦了,是被自己的默认策略坑了。
3. 规则语法拆解:从写出一条能用的规则开始
3.1 iptables 命令各部分的含义
一条完整的 iptables 规则长这样:
bash复制iptables -t filter -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT
拆开看:-t filter 指定操作哪张表,不写默认就是 filter,这个约定导致很多人忘了写 -t nat,所以遇到 NAT 不生效先别怀疑命令,先看表有没有选对。-A INPUT 表示追加到 INPUT 链末尾;如果是 -I INPUT,默认插到链的第一条;-D 是删除,-F 是清空整条链,-L 是列出规则。-s 是源地址,-p 是协议(tcp/udp/icmp),--dport 是目的端口,-j 是动作。
除了这些,还有几个高频参数:-d 是目的地址,-i 是数据包进来的网卡,-o 是出去的网卡,--sport 是源端口,-m state --state 或 -m conntrack --ctstate 用来匹配连接状态。命令本身不复杂,复杂的是你要清楚每个参数作用在哪个环节。
3.2 匹配条件:IP、端口、协议、接口
写匹配条件时,最容易忽略的是“方向”。-s 是源地址,-d 是目的地址,但在不同链上,这两个字段的实际含义会让你绕晕。比如在 INPUT 链上,包是发给本机的,所以 -d 通常是本机地址,你可以不写;但在 FORWARD 链上,-d 是最终目的 IP,多半是内网某台机器,这时候想匹配“转发给某个目标”就要特别小心。
接口参数也一样。-i eth0 表示从 eth0 网卡进入的包,一般用在 INPUT、FORWARD、PREROUTING;-o eth1 表示将从 eth1 网卡出去的包,用在 OUTPUT、FORWARD、POSTROUTING。这里有个经典错误:在 INPUT 链上写 -o eth0 想匹配入站流量,结果完全不生效。因为 INPUT 链的数据包是“进入本机”的,它没有“出去网卡”的概念。
协议和端口的匹配相对直观,但要注意:--dport 只有在 -p tcp 或 -p udp 指定后才有效。另外,ICMP 协议没有端口,只有 type/code,所以写 ping 的规则不要带 --dport。遇到过有人想放行 ping,写 -p icmp --dport 8,这是错的,正确写法是 -p icmp --icmp-type echo-request。
3.3 动作 target:ACCEPT/DROP/REJECT/LOG 的区别
四个常用动作,很多人只知道字面意思,但它们在实战里的体验差别很大。ACCEPT 放行,无需多解释。DROP 是直接把包丢掉,不发任何回应,表现起来就是“连接超时”。REJECT 也是拒绝,但会回一个错误包,表现是“connection refused”。从安全角度,DROP 更隐蔽,因为对方无法通过反馈判断端口是否存活;从排障角度,REJECT 更友好,客户端立刻就能感知失败,不用等超时。所以我个人建议:对外部未知流量用 DROP,对自己测试用的临时规则可以用 REJECT,至少能确认规则生效了。
LOG 是个特殊的非终结动作,它会把匹配的包记录到内核日志,然后继续匹配后续规则。用法是 -j LOG --log-prefix "iptables: " --log-level 4。调试规则时非常有用,但生产环境要控制量,否则 /var/log/messages 会被刷爆。还有一点,LOG 不会放行也不会拒绝,所以写完 LOG 后面通常还要跟一条 ACCEPT 或 DROP,才能真正决定包的命运。
3.4 黑白名单思路:默认策略怎么设
防火墙的黑白名单思路,本质就是“默认允许”还是“默认拒绝”。默认允许意味着你只需要拦截已知的坏人,配置量小,但一旦漏了就出事;默认拒绝意味着你只开放明确允许的访问,安全性高,但配置不当容易把自己锁在外面。实际生产环境,我强烈建议服务器采用默认拒绝的策略:INPUT 和 FORWARD 默认 DROP,OUTPUT 默认 ACCEPT。理由是服务器对外提供的服务是有限的,你完全清楚需要开放哪些端口。
但注意,设默认拒绝之前,一定要先把“基础放行规则”想清楚:放行回环接口 lo,否则本机进程互访会被拦;放行已建立的连接以及相关连接(ESTABLISHED,RELATED),否则你 SSH 连上去之后,回包会被 DROP,表现就是“能连上但卡死”;放行 ping 还是不放行,根据公司规范来。这些基础规则不写,默认拒绝的策略会让你痛不欲生。
下面给一套我常用的基础模板:
bash复制iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
四行设置策略,三行打底。之后再按需追加 SSH、Web 等端口的白名单规则。
4. 实战一:SSH 防护与常用服务器规则
4.1 场景确定:一台公网服务器要开放哪些服务
假设你手里有一台公网 Linux 服务器,IP 是 203.0.113.10,上面跑了 SSH(22)、Nginx(80/443)、一个内部管理后台(8080,仅限公司出口 IP 203.0.113.66 访问)。需求是:白名单 IP 能 SSH,所有人都能访问 80/443,8080 只对公司 IP 开放,其余入站一律丢弃。这个场景非常经典,几乎涵盖了入站防护的所有基础点。
我的建议是先画一个访问矩阵:服务、端口、来源、动作。表格画清楚之后,再翻译成 iptables 规则会非常清晰。
| 服务 | 端口 | 来源 | 动作 |
|---|---|---|---|
| SSH | 22 | 公司 IP/跳板机 | ACCEPT |
| HTTP | 80 | 所有 | ACCEPT |
| HTTPS | 443 | 所有 | ACCEPT |
| 管理后台 | 8080 | 203.0.113.66 | ACCEPT |
| 其他入站 | 全端口 | 所有 | DROP |
很多人在这一步就开始写规则,结果把顺序搞乱了。我的习惯是:先设默认策略和基础放行,再按“放行白名单 -> 放行公开服务 -> 最后拒绝”的顺序追加。顺序的意义在于,白名单和公开服务如果排在拒绝规则后面,等于没写。
4.2 手写规则:从清空到完整配置
先清空已有规则,避免历史规则干扰。这一步在测试机或自己的服务器上做,线上机器最好先备份。
bash复制iptables -F
iptables -X
iptables -Z
iptables -t nat -F
接着设默认策略:
bash复制iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
再写基础放行:
bash复制iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
然后是具体服务:
bash复制iptables -A INPUT -s 203.0.113.66 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -s 203.0.113.66 -p tcp --dport 8080 -j ACCEPT
这时你不需要额外写拒绝规则,因为 INPUT 默认策略已经是 DROP,没匹配到的包全部丢弃。但这里有个关键细节:ESTABLISHED,RELATED 放行必须在前面,否则你从公司 IP 连 SSH,SYN 包被 ACCEPT 了,但后续的 ACK、数据传输包到达时,如果匹配不到任何规则,就会被默认策略 DROP,连接根本建立不起来。这也是“开了防火墙就 ping 不通/SSH 断连”最常见的元凶之一,后面我会重点展开。
4.3 注意事项:不要把自己锁在门外
这一节是最重要的提醒。配置远程服务器防火墙,最怕现场翻车,把自己关在外面。我的习惯是:写规则之前,先开一个长连接的 SSH 会话,比如 ssh -o ServerAliveInterval=30,然后所有配置在这个会话里做。万一规则写崩了,还能靠已有连接抢救。但如果你彻底清了连接状态,那就只能靠带外管理或者去机房了。
第二个保命技巧是“延迟生效”。如果你用的是 firewalld,可以直接 --reload 回滚;但 iptables 没有原生的“测试后自动回滚”功能,所以我通常这样操作:把规则写到一个脚本里,执行前先备份当前规则。
bash复制iptables-save > /root/iptables-backup-$(date +%F).rules
sh /root/iptables-apply.sh
如果执行后 SSH 断了,就说明规则有问题。此时只要你还留着原来的 SSH 连接,立刻恢复备份。但注意,如果你在新会话里执行 iptables-restore,这个新会话连接可能同样会被拦,所以保底手段是:脚本执行前先 sleep 60 再加载防火墙规则,给自己留一个窗口期去恢复。这条经验是我踩过坑换来的,线上操作宁可慢一点,也不要直接一把梭。
第三件事:不要忘了放行你的管理网段。如果你公司有多条出口线路、或者办公地点有变动,写死单个 IP 很容易把自己挡在外面。建议至少放行一个网段,例如 -s 203.0.113.0/24,同时把跳板机单独放行。安全当然重要,但可管理性同样重要,两者要平衡。
5. 实战二:开启防火墙后 ping 不通?NAT 和转发问题
5.1 排查思路:从链到表
“我开了防火墙,服务器 ping 不通了”“端口不通”“容器访问不了了”是运维群里最常见的三类问题。我的排查套路是这样:先确定流量方向是本机入站、本机出站、还是转发经过本机。然后沿着数据包路径,从前到后一条链一条链地看。
举个例子,现象是“开启防火墙后 ping 不通公网 IP”。如果只有本机 ping 不通外网,先看 OUTPUT 链有没有拒绝 ICMP,再看默认策略是不是把 OUTPUT 设成了 DROP。很多人把默认策略设为 DROP 后忘了放行出方向,结果机器完全无法主动访问外网。如果是外部 ping 这台服务器不通,重点查 INPUT 链和 FORWARD 链。对普通服务器来说,入站 ping 走 INPUT;如果这台机器是路由器/网关,那外部 ping 内网机器时,包会走 FORWARD,这时要看 FORWARD 链是否放行,以及路由和 NAT 配置是否正常。
排查工具上,我喜欢用 iptables -L -n -v 看丢包计数。规则列表里如果某条规则计数一直不涨,有两种可能:要么包没走到这条链,要么被前面的规则提前匹配拦截了。配合 counters 清零功能 iptables -Z,可以很方便地确认规则是否真正命中。
5.2 常见故障案例:端口不通、转发失效
举一个我实际处理过的故障。某台 Linux 做内网网关,内网机器要访问外网,结果所有流量不通。从网关本机 ping 外网正常,内网机器 ping 网关正常,但 ping 外网失败。先查 ip_forward:
bash复制sysctl net.ipv4.ip_forward
结果返回 0,转发关了,这是最基础的问题。但很多人开了转发还是不通,这时候查 FORWARD 链:
bash复制iptables -L FORWARD -n -v
发现默认策略是 DROP,而且没有放行转发流量的规则。这里有个关键概念:转发流量是“穿过”本机的,不是“发给”本机的,所以你在 INPUT 链上放行任何规则都没用。必须单独在 FORWARD 链上放行。推荐用连接状态来放行:
bash复制iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT
-i eth0 是内网口,-o eth1 是外网口。生产环境建议只允许内网口到外网口的转发,反向的转发根据需求限制。很多安全要求严格的场景,会限制外网口进入的转发流量,只放行与内网建立过连接的 RELATED 流量,这样内网主动访问外网的响应能回来,但外网主动访问内网会被挡掉。
NAT 转发是另一个高频坑。如果你想让内网机器通过这台网关上网,光有 ip_forward 和 FORWARD 放行还不够,必须加上 SNAT 规则:
bash复制iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j MASQUERADE
这里 MASQUERADE 适合动态获取公网 IP 的场景,比如拨号上网;如果是固定公网 IP,可以用 -j SNAT --to-source 203.0.113.10 更高效。很多新手把这句写到 filter 表里,或者写成 PREROUTING,结果完全不生效,就是这个原因。
5.3 防火墙双机热备和规则持久化
生产环境里单台防火墙不够,通常要做双机热备。Linux 下的 VRRP 或者 keepalived 负责 IP 漂移,iptables 规则本身也要保持一致。实际操作中,我最常采用的方式是:把规则脚本托管到配置管理工具里,主备机同时执行;或者用 iptables-save 导出规则文件,配合定时同步。这里有一个容易忽略的坑:MASQUERADE 规则的 conntrack 状态在故障切换时可能残留,导致新主机收不到回包。所以热备切换后,建议清一下 conntrack 表:
bash复制conntrack -F
或者用 echo 1 > /proc/sys/net/netfilter/nf_conntrack_max 调大表项,但这只是缓解,不是根治。双机热备真正要做好的是规则同步、状态同步、切换检测三层,缺一不可。我见过很多 keepalived 配置没问题,但切换后业务全断,最后发现是备份机的 iptables 规则少了一条放行。所以准备热备脚本时,一定要把规则校验也纳入健康检查。
规则持久化也是绕不开的。iptables 做的修改默认只对当前内核生效,重启后全消失。Debian/Ubuntu 可以用 iptables-persistent,CentOS 7 用 iptables-services,或者干脆在 /etc/rc.local 里执行 iptables-restore < /etc/iptables/rules.v4。我个人的偏好是独立一个 /etc/iptables/rules.v4 文件,规则变更后手动执行 iptables-save > /etc/iptables/rules.v4,这样规则和配置分离,出问题方便 diff。注意:生产环境千万不要裸奔,至少把规则导出到一个备份目录。
6. 进阶:iptables 的保存、恢复与调试
6.1 规则持久化的几种方案对比
规则保存没有银弹,不同场景选不同方案。我只列三种最常用的:
第一种,iptables-save/iptables-restore 配合系统服务。CentOS 7 上装好 iptables-services 后,systemctl save iptables 会把当前规则存到 /etc/sysconfig/iptables,重启后自动加载。优点是简单、原生;缺点是规则文件是 iptables-save 格式,可读性不如命令行直观,而且保存时机是手动触发的,容易忘记。
第二种,把规则写成一个 shell 脚本。每次开机执行一次,脚本里有注释,方便维护。这种方式特别适合规则复杂、需要逻辑判断的场景,比如判断网卡是否存在、从配置中心拉取 IP 白名单。缺点是你得自己保证脚本执行时机正确,最好在 network 服务之后加载。
第三种,用 firewalld 的 rich rule 或者直接拥抱 nftables。如果你用的是 CentOS 8+/RHEL 9,系统默认 nftables 后端,firewalld 提供了一层更友好的封装。但很多老运维还是习惯直接操作 iptables,这里我的建议是:新项目优先学 nftables,老项目保持 iptables 控制,不要混用配置工具,否则规则会被互相覆盖,非常痛苦。
6.2 日志和调试:怎么看一条规则到底命中没有
调试 iptables,最朴素的方法是加 LOG 规则。比如你想确认某个 IP 的包有没有到达 INPUT 链,可以在链的最前面加一条:
bash复制iptables -I INPUT -s 203.0.113.66 -j LOG --log-prefix "INPUT-DEBUG: " --log-level 4
然后去 /var/log/kern.log 或 journalctl -kf 看输出。看到日志说明包到了这条链,没看到日志就往更上游查,比如是不是在 PREROUTING 就被 NAT 改了地址,或者根本没进入本机。
也可以用 tcpdump 配合对比。一个包在网卡上被抓到了,但 iptables 日志里没有,说明问题出在网卡驱动或更底层;日志里有但规则没继续放行,那就在 iptables 规则里找原因。两个工具配合,基本能定位 90% 的网络路径问题。你要记住的顺序是:物理链路 -> 网卡 -> netfilter 钩子 -> 路由决策 -> 应用层。每个层次都有对应的排查工具,不要一上来就猜。
6.3 与 firewalld/nftables 的关系
这三者的关系,可以这样理解:netfilter 是内核框架,iptables 是老的用户态命令,nftables 是新一代内核统一框架和用户态工具,firewalld 是上层管理服务,底层可以用 iptables 也可以切换成 nftables。在主流发行版上,你执行 iptables 命令时,可能实际走的是 nftables 的兼容接口。
所以在实际运维中,首先要搞清楚当前系统用的是哪一层配置。CentOS 7 默认 firewalld,底层 iptables;CentOS 8 默认 firewalld,底层 nftables;Ubuntu 22.04 默认 ufw,底层 nftables。如果你习惯了直接 iptables -A,在 Ubuntu 22.04 上也能用,但规则不会体现在 ufw status 里,容易造成混乱。我的建议是:选择一个管理入口,不要多渠道混用。比如你公司统一用 firewalld,就不要手动改 iptables 文件;如果你习惯 iptables,就把 firewalld 停用并 mask,避免开机启动互相覆盖。
7. 常见问题速查表
7.1 高频问题与排查手段
下面整理一份我日常工作中经常看到的问题速查表,方便你遇到问题时按图索骥:
| 现象 | 可能原因 | 排查命令 / 解决方案 |
|---|---|---|
| SSH 连不上,超时 | INPUT 默认 DROP,没放行 22 端口;或没放行 ESTABLISHED | iptables -L INPUT -n -v,检查 --ctstate ESTABLISHED,RELATED |
| 本机无法访问外网 | OUTPUT 默认 DROP,或没有默认路由 | iptables -L OUTPUT -n -v,ip route |
| 内网机器无法上网 | 没开 ip_forward,或 FORWARD 默认 DROP,或没配 SNAT | sysctl net.ipv4.ip_forward,iptables -L FORWARD -n -v,iptables -t nat -L POSTROUTING |
| 端口映射不生效 | 把 DNAT 写到了 filter 表,或忘记放行 FORWARD | iptables -t nat -L PREROUTING -n -v,确认 FORWARD 有放行对应流量 |
| 同 IP 访问出现随机超时 | conntrack 表满,或 NAT 状态残留 | `dmesg |
| 规则没生效 | 规则顺序不对,被前面的 DROP 匹配到了;或链/表写错 | iptables -L -n -v --line-numbers,逐条核对顺序 |
| 重启后规则丢失 | 没有持久化保存 | iptables-save > /etc/sysconfig/iptables 或装 iptables-services |
这个表里的每一条,都是我线上处理过或者交接班时看到过的真实问题。尤其是“规则没生效”这一条,绝大多数不是命令写错,而是表选错或顺序错。比如有次同事报“Nginx 端口映射不生效”,我上去一看,他把 -t nat 漏了,规则被加到了 filter 表的 INPUT 链上,端口映射当然不会工作,因为 PREROUTING 链上根本没有这条 DNAT 规则。付出一次这样的代价后,我现在看别人的 iptables 规则,第一反应就是先看表名。
7.2 几个我踩过的坑
第一个坑:清空规则时把 nat 表忘了。有一次做规则变更,我执行了 iptables -F,以为全部清干净了,结果 NAT 规则还在,导致新旧规则混在一起,内网访问乱成一团。现在我的清空命令一定是同时清 filter 和 nat,除非我明确只想动其中一张表。
第二个坑:默认策略设成 DROP 之前,没有先放行 ESTABLISHED。当时在给一台数据库服务器加固,想着“把 INPUT 默认改成 DROP 就安全了”,结果改完的一瞬间,所有客户端连接全部断开。因为连接的确立包被 ACCEPT 了,但后续数据包全部 DROP。解决方案就是那句著名的 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT,这句话应该刻在脑子里。
第三个坑:-P OUTPUT DROP 后忘记 DNS 放行。有些安全基线要求出方向也做严格管控,我照着基线配完后,发现服务器域名解析全挂,apt/yum 无法更新,因为 DNS 走 UDP 53 被拦了。所以如果你的出方向默认拒绝,一定要把 DNS、NTP、以及软件源对应的 HTTP/HTTPS 端口放行,否则机器会处于“能开机但什么都干不了”的状态。
如果你看到这里,大概率已经能独立完成一套 iptables 规则配置了。我个人的建议是:第一次配置时,先在虚拟机里把整套流程跑一遍,故意制造几个故障,比如反过来写规则顺序、漏掉 ESTABLISHED、把 NAT 写到 filter,然后逐个排查,这样比你背二十条命令有用得多。iptables 这门手艺,光看是学不会的,得靠动手踩坑。
