1. 从零开始理解iptables:它到底解决了什么问题
搞 Linux 运维或者开发的人,迟早都会碰到 iptables。我第一次真正正视它,是因为一台测试服务器莫名其妙连不上外网,排查到最后发现是别人在机器上顺手敲了一条默认拒绝 INPUT 链的命令,把自己关在了门外。那会儿才意识到,防火墙不是装个图形界面点点点,真正的底层控制,是靠 iptables 这套规则框架来完成的。
iptables 是什么?一句话说清楚:它是 Linux 内核 netfilter 模块的命令行管理工具。netfilter 是内核里真正干活的那套数据包处理框架,iptables 只是用户态下用来往这个框架里添加、删除、修改规则的接口。你通过 iptables 写的每一条规则,最终都会被翻译成内核里数据包流向的匹配与动作指令。
它能解决什么问题?往大了说有三类:
- 包过滤:决定哪些数据包可以进来、哪些可以出去,基于 IP、端口、协议、网卡等条件。
- 网络地址转换(NAT):做内网共享上网(源地址转换)、端口映射(目的地址转换),这是家用路由器和云主机安全组底层的经典原理。
- 流量标记和修改:给特定包打标记,配合路由策略实现策略路由;修改 TTL、TOS 等报文头字段,做特殊场景的流量控制。
适合谁来学?如果你是 Linux 运维、后端开发、网络工程或者做云原生相关工作的,iptables 绝对绕不开。即使你平时用的是云厂商的安全组,那只是更方便的白名单控制,底层在 Linux 主机上,依然需要理解规则的表达逻辑。入门学的不是背诵命令,而是搞懂它怎么思考:数据包从哪来、到哪去、路过哪条链、该怎么处置。
需要先说明一点:iptables 虽然正在被新一代的 nftables 取代,但在存量服务器、各类教程、老系统维护中仍然是绝对主流。理解 iptables,不光是为了操作老机器,更是为了理解整个 Linux 网络数据包处理的基础心智模型。这套模型一旦建立,再学 nftables、firewalld、甚至云安全组,都会觉得是一通百通。
2. 一张图看懂iptables的骨架:表、链、规则怎么配合
iptables 入门最大的门槛不是命令复杂,而是它的名词体系容易让人头晕。表、链、规则、匹配、目标,乍一看像绕口令。我用一个生活类比来拆解:数据包就像一辆货车,防火墙规则就是货运公司的分拣流程,表是按照业务类型划分的不同分拣车间,链是车间里的传送带,规则是传送带上的每一个分拣指令。
2.1 四张表:filter、nat、mangle、raw各管哪一摊
iptables 默认有四张表,精度从高到低依次是 raw、mangle、nat、filter。绝大部分场景你只会用到 filter 和 nat,但另外两张也得知道是干嘛的。
- filter 表:做的是纯粹的允许/拒绝裁决,也就是包过滤。字段是 ACCEPT、DROP、REJECT 这些动作。日常写的防火墙规则里,八成都是 filter 表的。
- nat 表:负责修改数据包的源地址或目的地址,实现 NAT。它里面的动作主要是 SNAT、DNAT、MASQUERADE、REDIRECT。只要你做了端口映射、内网上网共享,走的必然是这张表。
- mangle 表:负责修改数据包的 QoS 字段,比如 TTL、TOS,以及给包打 mark 标记。它不太常用,但在策略路由场景里几乎是必需的。
- raw 表:优先级最高,负责决定数据包是否要进入连接跟踪机制。日常运维基本碰不到,除非你遇到性能优化或特殊绕过场景。
2.2 五条链:数据包必经的“检查站”
链是规则的容器。iptables 内置了五条链,分别对应数据包流经的检查点:
- INPUT:数据包进入本机的检查点。
- OUTPUT:本机发出数据包的检查点。
- FORWARD:数据包经过本机转发,不进入本机应用的检查点。
- PREROUTING:数据包刚进入网卡,路由决策之前做处理。主要用于 DNAT 和 mangle。
- POSTROUTING:数据包即将离开本机网卡前做处理。主要用于 SNAT 和 MASQUERADE。
理解数据包走哪条链,是写出靠谱规则的起点。数据包发往本机,走的是 PREROUTING -> INPUT -> 应用;数据包从本机发出去,走的是 OUTPUT -> POSTROUTING;数据包作为中转被转发,走的是 PREROUTING -> FORWARD -> POSTROUTING。
2.3 规则的匹配顺序:先看链再看规则号
同一张表同一条链里的规则,从上到下逐条匹配。数据包命中了某条规则,就立刻执行该规则指定的动作,不再继续往下看。这就是所谓的“匹配即停止”。这个特性极其重要,因为它意味着规则顺序本身就是一种逻辑。如果你不小心把“全部放行”写在了“封禁某个 IP”的前面,那条封禁规则就永远不会生效。
这也是新手最容易踩的坑。很多人以为防火墙规则像云安全组那样无序求和,实际上 iptables 规则是线性扫描。你可以用-I把规则插到最前面,用-A把规则追加到最后面。想让某条规则优先生效,就得用-I。
我在实际布控中习惯把规则分成三类按顺序组织:先放行已建立的连接(状态匹配规则),再放行明确要开放的端口和服务,最后兜底拒绝所有其他流量。这个顺序既符合常规业务逻辑,又不会把自己锁在门外。
2.4 匹配与目标:规则的两段式结构
每一条 iptables 规则,可以拆成两段看:匹配条件 + 目标动作。
匹配条件包括协议(-p,如 tcp/udp/icmp)、源地址(-s)、目的地址(-d)、源端口(--sport)、目的端口(--dport)、进入网卡(-i)、流出网卡(-o)、连接状态(-m state --state)等。这些条件可以组合使用,多个条件之间的关系是“并且”:必须同时满足才算命中。
目标动作主要有:
- ACCEPT:放行。
- DROP:丢弃,不回任何消息。表现是连接超时。
- REJECT:拒绝,并通过 ICMP 或 TCP RST 告知对端。表现是立即被拒。
- LOG:记录日志,继续匹配下一条规则。
- SNAT:修改源地址,常用于内网上网。
- DNAT:修改目的地址,常用于端口映射。
- MASQUERADE:动态 SNAT,适用于出口 IP 不固定的情况(如拨号上网)。
- REDIRECT:在本机内做端口重定向。
提示:DROP 和 REJECT 的选择是有讲究的。对外服务端口建议用 REJECT,客户端能立刻感知失败;对疑似恶意扫描的来源,直接用 DROP,让对方慢慢超时,增加对方的探测成本。
3. 动手实操:从零开始写第一条iptables规则
前面框架理解了,后面就顺了。这一节我带你把常用的 iptables 场景从头到尾操作一遍。环境假设是一台 CentOS 7/8 或兼容系统的 Linux 主机,你有一个 root 权限的终端。建议先开一台虚拟机来练习,别直接在线上业务机器上试。
3.1 查看当前规则:动手之前先摸清家底
不管你要干什么,第一步永远是看现状。查看规则的命令有两个常用姿势:
# 查看所有表的全部规则(带详细信息) iptables -L -n -v # 只用最精简的方式看看当前 filter 表 iptables -L -n参数含义说明:-L是列出规则,-n表示不做域名反解,直接用 IP 和端口显示(不加-n会尝试反解域名,又慢又容易卡住),-v显示更详细的信息,包括每个规则匹配了多少包、多少字节,还有进入和流出的网卡。
执行完你可能看到像下面这样的输出:
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0这里有个关键信息:Chain INPUT 后面的(policy ACCEPT代表这条链的默认策略是 ACCEPT。如果某条链没有任何规则命中,最终就按默认策略处理。很多入门教程不讲策略,但实际上它才是防火墙的“最后一道防线”。建议在配置防火墙初期,策略先保持 ACCEPT,等规则全部写好了,最后再把策略改成 DROP。顺序搞反了,很容易把自己锁在门外。
3.2 最基础的三条规则:放行、拒绝、丢弃
现在来写真正的规则。三条入门必会:
# 允许来自 192.168.1.0/24 网段的所有流量访问本机 iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # 拒绝 10.0.0.5 这个来源 IP 访问本机的 22 端口(SSH) iptables -A INPUT -s 10.0.0.5 -p tcp --dport 22 -j REJECT # 丢弃所有来自 172.16.0.0/16 网段的流量,且不回复任何消息 iptables -A INPUT -s 172.16.0.0/16 -j DROP这条-A INPUT规则要拆开看:-A是追加到链尾,INPUT是链名,-s指定源地址,-p tcp限定协议,--dport 22限定目的端口,-j REJECT指定动作。注意--dport必须配着-p tcp或-p udp一起用,单独写目的端口是不生效的,因为端口概念只存在于 TCP/UDP 协议里,ICMP 没有端口这个概念。这正是新手常见的报错点。
3.3 状态防火墙:让规则“聪明”起来
单纯写死 IP 和端口的规则是静态防火墙,实际场景里还不够用。比如:你开放了本机的 8080 端口,如果只写一条“允许所有访问 8080”,那么所有外部请求都能直接打进来,一旦有扫描器盯上你,压力会很大。
更稳妥的做法是配合连接状态跟踪(conntrack)来写。核心思想是:只放行新建立的合法连接,以及已经建立的连接和它们的回包。
# 放行所有与已建立连接或相关连接相关的流量 iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 放行 SSH 端口的新连接 iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT # 最后拒绝所有其他新连接 iptables -A INPUT -m conntrack --ctstate NEW -j DROP这组规则的逻辑是:我不是无条件开放 22 端口,而是严格限制只有“新建的到 22 端口的连接”才放行。一旦连接建立,后续所有回包都走第一条规则。这就形成了非常经典的有状态防火墙,比单纯裸开放端口安全得多,同时日志也干净得多。
你可能会问,为啥回包能自动匹配 ESTABLISHED?因为 conntrack 机制维护了一张连接状态表,记录每个 TCP/UDP/ICMP 会话的双向元组。内核看到回包,会自动关联到已有的连接记录,从而命中 ESTABLISHED 规则。这也是为什么 iptables 的 NAT 能工作——没有 conntrack 的映射记录,NAT 根本不对称。
3.4 内网共享上网与端口映射:NAT 的两个高频场景
在 Linux 上做 NAT,是 iptables 最体现价值的地方之一。两个场景最常碰到:内网机器通过这台 Linux 共享上网;外部通过公网 IP 访问内网某台机器的服务。
先看共享上网(源地址转换)。
# 开启内核 IP 转发 sysctl -w net.ipv4.ip_forward=1 echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf # 让从内网网卡(eth1)进来的数据,出去时统一换成外网网卡(eth0)的 IP iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j MASQUERADE这里要特别说明-t nat和MASQUERADE选择的逻辑。-t参数指定操作哪张表,默认是 filter,这里必须切到 nat。MASQUERADE会自动获取 eth0 当前的 IP,哪怕你动态拨号导致 IP 变了,它也能自适应;如果你出口 IP 是固定的公网 IP,用SNAT更高效,因为 MASQUERADE 每次都要额外查一次出口 IP。固定 IP 的写成这样:
iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j SNAT --to-source 203.0.113.5再看端口映射(目的地址转换)。
# 把公网网卡进来的、访问 80 端口的数据,转给内网的 192.168.10.10 这台机器 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 192.168.10.10:80这条规则写在 PREROUTING 链,因为必须在路由决策之前就把目的地址改掉,否则内核不知道要把包转给谁。配合上面的 SNAT 规则,内网机器访问外网和外部访问内网服务就能同时工作了。
3.5 规则的查看、删除与持久化
临时规则的增删改查很简单:
# 按规则序号删除。先查出序号: iptables -L INPUT --line-numbers # 假设要删 INPUT 链第 3 条规则: iptables -D INPUT 3 # 清空某条链的所有规则: iptables -F INPUT # 清空所有链的所有规则: iptables -F这里强烈建议:删除规则之前,一定先--line-numbers看清楚序号。我因为偷懒直接-D INPUT 3删错过规则,结果把放行 SSH 的规则删了,然后预设的兜底策略又是 DROP,当场断连。这种惨痛教训多说无益,操作时多看一眼,绝对值得。
重启机器后,手写的 iptables 规则会全部丢失。要持久化,需要按发行版方式保存:
# CentOS / RHEL 系 iptables-save > /etc/sysconfig/iptables systemctl enable iptables # Debian / Ubuntu 系 iptables-save > /etc/iptables/rules.v4iptables-save的输出是内核里当前全部规则的“导出文件”,也可以用它来备份和审计。注意不要直接手改这个文件,格式解析繁琐且容易出错,规范流程是:命令行修改规则 -> 验证生效 -> 再用 save 覆盖导出。
4. 常见问题与排查技巧实录
iptables 使用中的坑不少,我把实操中反复出现的问题整理成速查表,并附上我的排查思路。
| 现象 | 常见原因 | 排查思路 |
|---|---|---|
| 规则加进去了不生效 | 规则顺序不对,前面有 ACCEPT 兜底把它跳过了 | iptables -L -n --line-numbers看顺序 |
| SSH 突然连不上 | 默认策略被改成 DROP,或防火墙里拒绝规则的顺序问题 | 有带外管理/物理控制台先恢复;否则重启前先测试 |
| SNAT 不生效,内网机器上不了网 | 没开启 ip_forward;或 FORWARD 链默认策略 DROP | 确认sysctl net.ipv4.ip_forward;检查 FORWARD 链 |
| 端口映射外部访问不到 | DNAT 写在 filter 表;或 FORWARD 链拦了转发流量 | 确认是-t nat -A PREROUTING;再查 FORWARD 链 |
| 重启后规则没了 | 没做持久化保存 | iptables-save > 文件并设置开机加载 |
| 加了 DROP 规则,应用卡超时 | DROP 不回包,TCP 只能等超时 | 对可识别异常来源可用 REJECT 代替 DROP |
4.1 规则顺序的“拦路虎”
先给你看一个真实的反面案例。有人想把 80 端口开放,写了:
iptables -A INPUT -j DROP iptables -A INPUT -p tcp --dport 80 -j ACCEPT结果外部访问 80 端口依然超时。原因很直白:-A是追加到链尾,先追加的 DROP 在最上面,所有流量都已经被 DROP 了,后面那条 ACCEPT 永远轮不到。这类问题用--line-numbers一眼就能看出来。
正确逻辑是,把放行规则插到 DROP 之前:
iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT-I后面必须带插入的位置编号,不写编号默认插第 1 条。这也解释了为什么强烈建议规则按“放行已建立连接 -> 放行必要端口 -> 拒绝其他”的顺序编排。
4.2 做了端口映射却访问不到:多半是FORWARD链漏了
每次有朋友跟我反馈“外面访问不了我映射的服务”,我第一反应都是让他查 FORWARD 链。原因很简单:到达公网网卡的包,经过 PREROUTING DNAT 后,要由本机内核转发给内网服务器,这一步属于 FORWARD,不是 INPUT。
如果 FORWARD 链默认策略是 DROP,或者没有放行相关流量,那么 DNAT 之后的包就直接被丢了。典型的完整配置是三段式:
# 1. DNAT 改写目的地址 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.10.10:80 # 2. 放行 FORWARD 链的已建立连接流量 iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 3. 放行 FORWARD 链的新连接 iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 80 -j ACCEPT这三条缺一不可。很多时候你只写了第 1 条,然后怪 DNAT 不生效,实际上是第 2、3 条没写。
4.3 别把DROP写进OUTPUT链把自己锁死
新手另一个高频误区是:为了限制服务器对外访问,在 OUTPUT 链上加了大量 DROP,然后发现 yum 装不了包、curl 全都超时、DNS 解析失败。
常见配置是:
iptables -A OUTPUT -j DROP iptables -A OUTPUT -p udp --dport 53 -j ACCEPT问题又出在顺序上。而且就算你把 53 端口放行了,DNS 返回的包走的是 INPUT 链,如果 INPUT 链也是全 DROP,DNS 响应依然进不来。写 OUTPUT 链的规则,一定要同步考虑 INPUT 链和 conntrack 状态。
我的建议是:没有特殊需求,不要动 OUTPUT 链。服务器主动访问外网是正常行为,限制出站是安全加固的手段,但属于微调而非入门玩法。先把 INPUT 链管明白,比什么都强。
4.4 规则量大了怎么看日志
调试 iptables 时,加LOG规则是定位问题的利器。比如不确定某个来源 IP 的流量有没有到 INPUT,可以在链的开头暂时加一条:
iptables -I INPUT 1 -s 203.0.113.0/24 -j LOG --log-prefix "input-check: " --log-level 4然后在/var/log/messages或journalctl -f里观察。看到日志后,input-check这个前缀可以帮你按 grep 过滤,定位到具体包。用完记得删除这条 LOG 规则,否则数据量大了,磁盘会被日志灌爆。
LOG 规则最大的价值是确认数据包是否真的到达某条链。如果包根本没到,防火墙规则写得再完美也没用,问题多半出在网络链路或者对端。
5. 学完iptables之后,下一步该怎么走
掌握 iptables 之后,我建议你再往前走两步,让这套知识真正串成体系。
第一步是学会 firewalld 和 ufw 这两层“壳”。它们本质上是 iptables(或 nftables)封装出来的易用层。CentOS 上你敲一条firewall-cmd --zone=public --add-port=80/tcp,背后就是自动生成了一条 filter 表 INPUT 链的规则。理解了 iptables 再看这些壳,会非常通透:所谓 zone 就是一套预设的链和规则集合,所谓 rich rule 就是更细的复杂匹配组合。
第二步是接触 nftables。内核从 3.13 起就引入了 nftables,新一代系统默认都基于它了。nftables 的命令体系更统一,配置更简洁优雅,但概念模型与 iptables 共用 netfilter 框架,表、链、规则这些抽象都能对得上。有了 iptables 的基础,学 nftables 只需要懂语法差异,不需要重新理解网络原理。
我个人在实际操作中的体会是:iptables 最珍贵的不是命令本身,而是它逼迫你搞清楚“一个数据包在 Linux 内核里到底走了一条什么路”。过去我只知道服务器能上网,不知道上网的背后还有 PREROUTING、FORWARD、POSTROUTING 这么多道处理环节。正是因为亲手敲过了 DNAT、SNAT 和 FORWARD 的组合规则,我才真正理解了“路由”“转发”“NAT”这三个概念之间的边界与联系。
最后再分享一个小技巧:在实验环境里练习 iptables 时,建议装一个轻量虚拟机快照。每次改规则卡到连不上了,直接回滚快照,比在物理机上想要怎么接串口恢复要踏实得多。把这个习惯带到生产环境,就是先写好规则验证完再持久化,永远给 SSH 留一条“救急通道”,而不是等到断连了才想为什么。防火墙配置本质上是在写“允许策略”,不是写“拒绝清单”,想清楚了这一点,你离熟练使用 iptables 就不远了。