1. 项目概述:为什么iptables依然是Linux网络安全的基石
最近在社区里看到不少关于容器网络、云原生安全的讨论,很多新工具层出不穷。但无论底层技术怎么变,当你真正需要精细控制服务器流量、排查网络问题时,绕不开的还是那个“古老”而强大的工具——iptables。我处理过无数次服务器被扫、服务异常、Docker网络冲突的问题,最终解决问题的钥匙,往往就是几条精准的iptables规则。很多人觉得它复杂难懂,命令一堆参数看着就头大,其实一旦理解了它的设计哲学和核心结构,你就会发现它逻辑清晰、功能强大,是每个Linux运维和开发必须掌握的底层技能。这篇文章,我就结合自己十多年的踩坑经验,带你从零开始,彻底搞懂iptables的表链结构和规则编写,让你不仅能看懂别人的配置,更能写出高效、安全的规则,真正掌控你的Linux网络。
2. iptables核心设计哲学:表、链、规则的三层架构
很多人一上来就死记iptables -A INPUT -p tcp --dport 22 -j ACCEPT这样的命令,结果换一个场景就懵了。要精通iptables,必须先理解它的设计思想。你可以把它想象成一个物流分拣中心。
2.1 核心组件拆解:表、链、规则的关系
- 规则:这是最基本的操作单元,就像分拣中心流水线上的一个“判断指令”。例如:“如果包裹来自IP 192.168.1.100,就把它放到‘签收区’(ACCEPT)”。一条规则通常由匹配条件(来自哪、去往哪、什么协议、哪个端口)和目标动作(ACCEPT接受、DROP丢弃、REJECT拒绝等)构成。
- 链:链是规则的有序集合,就像物流中心里一条条特定的流水线。每条链有固定的“检查点”。iptables内置了五条核心链,对应数据包生命周期的不同阶段:
- PREROUTING: 数据包刚进入网络接口,在进行路由判断之前。常用于修改目标地址(DNAT),比如端口转发。
- INPUT: 数据包的目标地址是本机,即将交给本机的上层应用程序。这是保护本机服务最重要的链。
- FORWARD: 数据包的目标地址是其他机器,本机充当路由器,需要转发它。这是实现网络共享或防火墙网关的关键链。
- OUTPUT: 本机应用程序产生的数据包,即将发送出去。
- POSTROUTING: 数据包离开网络接口之前,路由判断之后。常用于修改源地址(SNAT),比如让内网机器上外网。
- 表:表是链的容器,它决定了链具备哪些“功能”。不同的表承载不同类型的链,用于实现不同的网络控制目的。这是最容易被忽略也最关键的一层。
2.2 四大功能表详解
iptables主要包含四张表,理解它们的分工是写出正确规则的前提。
| 表名 | 主要功能 | 包含的链 | 典型应用场景 |
|---|---|---|---|
| filter | 过滤数据包,决定是否允许通过。这是最常用的表,也是默认表。 | INPUT, FORWARD, OUTPUT | 服务器安全:只开放22、80、443端口;家用路由器防火墙。 |
| nat | 网络地址转换,修改数据包的源或目标地址。 | PREROUTING, OUTPUT, POSTROUTING | 端口映射:将公网IP的80端口转发到内网服务器;共享上网:内网机器通过网关访问外网(SNAT)。 |
| mangle | 修改数据包内容(如TTL、TOS标记)或给数据包打标签。属于高级应用。 | 所有五条链:PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING | 流量整形(QoS)的前期标记;结合tc命令进行带宽控制。 |
| raw | 决定数据包是否被连接跟踪机制处理。 | PREROUTING, OUTPUT | 提升性能:对某些流量(如大量并发)关闭连接跟踪;或确保某些特殊协议不被跟踪。 |
注意:不是每条链在每个表里都存在。例如,
nat表就没有INPUT链,因为地址转换通常发生在路由前后(PREROUTING/POSTROUTING),或者本机发出数据时(OUTPUT)。
2.3 数据包在表链中的流转流程
这是理解iptables的“任督二脉”。一个数据包是如何依次经过这些表和链的呢?记住这个核心路径:
- 数据包进入->raw表的PREROUTING链->mangle表的PREROUTING链->nat表的PREROUTING链。
- 路由决策:系统根据目标IP判断,这个包是发给本机的,还是要转发的?
- 目的地为本机:
- 经过mangle表的INPUT链->filter表的INPUT链->本地进程。
- 本地进程回复数据包:mangle表的OUTPUT链->nat表的OUTPUT链->filter表的OUTPUT链->mangle表的POSTROUTING链->nat表的POSTROUTING链-> 发出。
- 目的地为其他机器(转发):
- 经过mangle表的FORWARD链->filter表的FORWARD链->mangle表的POSTROUTING链->nat表的POSTROUTING链-> 发出。
实操心得:这个流程不用死记硬背,但要有概念。当你写规则不生效时,首先问自己:我的数据包处在哪个阶段?我应该把规则加到哪个表的哪条链?比如要做端口转发,包在进入时目标地址就要被修改,所以规则必须加在nat表的PREROUTING链。
3. 规则编写核心语法与实战技巧
理解了架构,我们来动手写规则。iptables命令的基本格式是:iptables [-t 表名] 命令选项 链名 [规则匹配条件] -j 目标动作
3.1 常用命令选项
-A:在链的末尾追加一条规则。-I:在链的指定位置插入一条规则,如-I INPUT 1表示插入为第一条(优先级最高)。-D:从链中删除一条规则,可以指定序号或完整匹配条件。-L:列出链中的所有规则,-v显示详细信息,-n以数字形式显示IP和端口(强烈建议始终加上-n,避免DNS解析拖慢速度)。-F:清空链中的所有规则。-P:设置链的默认策略,如-P INPUT DROP。警告:在远程连接时,错误设置默认策略为DROP可能导致你立刻断线!-N:新建一条用户自定义链。-X:删除一条空的自定义链。
3.2 核心匹配条件解析
匹配条件是规则的“筛选器”,写得好才能精准控制。
- 通用匹配:
-p:协议,如tcp,udp,icmp,all。-s:源IP地址,可以是一个IP(192.168.1.1)、网段(192.168.1.0/24)或域名(不推荐,影响性能)。-d:目标IP地址。-i:数据包进入的网络接口名,如eth0,ens33。仅能用于PREROUTING, INPUT, FORWARD链。-o:数据包离开的网络接口名。仅能用于FORWARD, OUTPUT, POSTROUTING链。
- 隐含扩展匹配(无需
-m显式指定):-p tcp后可跟:--sport:源端口,--dport:目标端口。支持单端口(80)、范围(1000:2000)。--tcp-flags:匹配TCP标志位,用于高级状态检测。
-p udp后可跟:--sport,--dport。-p icmp后可跟:--icmp-type,匹配ICMP类型(如8为请求回显,即ping)。
- 显式扩展匹配(必须用
-m指定模块):-m state:状态匹配,这是防火墙性能优化的关键!--state NEW,ESTABLISHED,RELATED,INVALID。NEW:新连接的第一个包。ESTABLISHED:已建立的连接。RELATED:与已有连接相关的连接,如FTP的数据连接。- 规则示例:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT。这条规则能极大提升效率,因为它允许所有回包,而无需为每个服务单独开放高端口。
-m multiport:匹配多个不连续的端口。- 示例:
-m multiport --dports 22,80,443。
- 示例:
-m iprange:匹配一个IP地址范围。- 示例:
-m iprange --src-range 192.168.1.100-192.168.1.200。
- 示例:
-m limit:限制匹配速率,用于防止洪水攻击。- 示例:
-m limit --limit 3/minute --limit-burst 5,限制每分钟最多3个新连接,突发允许5个。
- 示例:
3.3 目标动作详解
ACCEPT:接受数据包。DROP:丢弃数据包,不返回任何信息。对客户端来说就像连接超时。更安全。REJECT:拒绝数据包,会返回一个错误响应(如connection refused)。对用户更友好。SNAT:源地址转换,用于nat表的POSTROUTING链。格式:-j SNAT --to-source [IP]。DNAT:目标地址转换,用于nat表的PREROUTING链。格式:-j DNAT --to-destination [IP:PORT]。MASQUERADE:动态源地址转换,用于拨号等IP不固定的出口。是SNAT的一种特例。LOG:将匹配的数据包信息记录到系统日志(如/var/log/messages),然后继续执行后续规则。用于调试。
4. 从零构建一个安全的服务器防火墙规则集
理论说再多,不如动手配一套。假设我们有一台新装的Web服务器(CentOS/RHEL/Ubuntu等),公网IP是203.0.113.10,需要开放SSH(22)、HTTP(80)、HTTPS(443)端口,并做好基本安全防护。
4.1 准备工作与重要警告
在开始之前,务必确保你有一个不会被中断的本地控制台连接(如通过服务器供应商的VNC、本地虚拟机终端),或者先设置一个定时恢复的脚本。因为一旦规则错误导致SSH断开,你将无法远程登录。
一个简单的保险做法是,先写一个清空所有规则并设置默认允许的策略脚本,并设置为5分钟后执行:
echo "iptables -F; iptables -X; iptables -t nat -F; iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT; iptables -P OUTPUT ACCEPT" | at now + 5 minutes如果5分钟内配置成功,记得用atrm命令取消这个任务。
4.2 规则配置实战步骤
我们按逻辑顺序,在filter表上构建规则。
清空所有现有规则和计数器:
iptables -F # 清空filter表所有链的规则 iptables -X # 删除filter表所有用户自定义链 iptables -t nat -F # 清空nat表 iptables -t mangle -F # 清空mangle表 iptables -Z # 将所有链的计数器归零设置默认策略(白名单思想): 安全的最佳实践是“默认拒绝,显式允许”。
iptables -P INPUT DROP # 默认丢弃所有进入本机的数据包 iptables -P FORWARD DROP # 默认丢弃所有转发的数据包 iptables -P OUTPUT ACCEPT # 默认允许所有从本机发出的数据包(通常这样设,也可设为DROP但配置更复杂)注意:此时如果你在远程SSH,连接会立刻断开!所以这一步必须在控制台做。
允许本地回环接口流量: 这是系统内部通信必需的。
iptables -A INPUT -i lo -j ACCEPT允许已建立和相关连接: 这是性能与便利性的关键规则。允许所有对外请求的回应包进来。
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT开放必要的服务端口:
- SSH (22端口):建议限制源IP,比如只允许办公室IP
198.51.100.0/24访问。iptables -A INPUT -p tcp -s 198.51.100.0/24 --dport 22 -m state --state NEW -j ACCEPT - HTTP (80端口) 和 HTTPS (443端口):
iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT iptables -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
- SSH (22端口):建议限制源IP,比如只允许办公室IP
防御常见攻击(可选但推荐):
- 限制ICMP (ping):完全禁止或限速。
# 完全禁止ping iptables -A INPUT -p icmp --icmp-type 8 -j DROP # 或者,限制ping的速率(每秒1个,突发5个) iptables -A INPUT -p icmp --icmp-type 8 -m limit --limit 1/second --limit-burst 5 -j ACCEPT - 防止端口扫描和洪水攻击:
# 记录并丢弃每秒超过5个新连接的SSH尝试(记录前5个) iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 --name SSH -j LOG --log-prefix "SSH Attack: " iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 --name SSH -j DROP iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
- 限制ICMP (ping):完全禁止或限速。
保存规则: iptables规则默认重启后失效,必须保存到配置文件中。
- RHEL/CentOS 7+:
iptables-save > /etc/sysconfig/iptables # 或使用firewalld的兼容层 service iptables save - Ubuntu/Debian:
apt-get install iptables-persistent # 安装时会询问是否保存当前规则,之后可用以下命令手动保存 netfilter-persistent save
- RHEL/CentOS 7+:
4.3 一个完整的脚本示例
将上述步骤保存为一个脚本(如firewall.sh),并赋予执行权限chmod +x firewall.sh,在控制台执行即可。
#!/bin/bash # 重置所有规则 iptables -F iptables -X iptables -t nat -F iptables -t mangle -F iptables -Z # 设置默认策略 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 允许本地回环 iptables -A INPUT -i lo -j ACCEPT # 允许已建立和相关的连接 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 开放SSH(请替换为你的可信IP段) iptables -A INPUT -p tcp -s 198.51.100.0/24 --dport 22 -m state --state NEW -j ACCEPT # 开放Web服务 iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT iptables -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT # 限制ICMP (可选) iptables -A INPUT -p icmp --icmp-type 8 -m limit --limit 1/second --limit-burst 5 -j ACCEPT iptables -A INPUT -p icmp --icmp-type 8 -j DROP # 记录并保存规则(根据系统选择) echo "Firewall rules applied." # iptables-save > /etc/sysconfig/iptables # For RHEL/CentOS5. 高级应用场景与排错指南
掌握了基础配置,我们来看几个更复杂的实战场景和遇到问题怎么排查。
5.1 实现端口转发(DNAT)
场景:内网有一台Web服务器192.168.1.100:80,网关/防火墙公网IP是203.0.113.1,需要将公网IP的8080端口转发到内网服务器。
# 1. 开启内核IP转发(永久生效需修改 /etc/sysctl.conf) echo 1 > /proc/sys/net/ipv4/ip_forward # 或 sysctl -w net.ipv4.ip_forward=1 # 2. 在nat表的PREROUTING链做目标地址转换 iptables -t nat -A PREROUTING -d 203.0.113.1 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80 # 3. 在filter表的FORWARD链允许该转发的流量 iptables -A FORWARD -d 192.168.1.100 -p tcp --dport 80 -j ACCEPT iptables -A FORWARD -s 192.168.1.100 -p tcp -j ACCEPT # 4. (可选)在nat表的POSTROUTING链做源地址转换(MASQUERADE),让回包能正确返回 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE5.2 共享上网(SNAT/MASQUERADE)
场景:内网机器192.168.1.0/24通过网关192.168.1.1(公网IP为203.0.113.1)上网。
# 开启IP转发(同上) # 在nat表的POSTROUTING链做源地址转换 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE # 如果公网IP固定,也可以用SNAT,性能稍好 # iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.15.3 与Docker等容器网络共存
这是最常见的坑之一。Docker默认会操作iptables,创建自己的DOCKER链和规则,有时会与你的自定义规则冲突。
- 现象:你配了防火墙,但Docker容器服务无法访问或被访问。
- 排查:用
iptables -L -n -v和iptables -t nat -L -n -v仔细查看所有链,特别是DOCKER,DOCKER-USER链。 - 解决:Docker提供了
DOCKER-USER链供用户插入自定义规则,且该链在Docker规则之前被检查。应将需要影响容器流量的规则放在这里。# 例如,禁止外部访问Docker容器的3306端口 iptables -I DOCKER-USER -p tcp --dport 3306 -j DROP重要:修改Docker相关的iptables规则前,最好先停止Docker服务,或者非常清楚Docker的规则逻辑,避免网络瘫痪。
5.4 规则不生效?通用排错思路
- 检查规则顺序:iptables规则是从上到下逐条匹配的。用
iptables -L -n --line-numbers查看规则序号。你的ACCEPT规则是否被前面的DROP规则覆盖了?新规则用-I插入到合适位置。 - 确认表和链:端口转发规则写到
filter表的INPUT链是没用的,必须写到nat表的PREROUTING链。 - 检查内核参数:IP转发是否开启?
cat /proc/sys/net/ipv4/ip_forward。 - 查看详细日志:在关键规则前添加
LOG目标,如iptables -I INPUT -p tcp --dport 8080 -j LOG --log-prefix "[IPTABLES 8080] ",然后去/var/log/messages或journalctl -f查看数据包是否匹配到了这条规则。 - 使用tcpdump抓包:这是终极武器。在源端、目标端、网关设备上抓包,看数据包到底在哪一步被丢弃或修改了。
tcpdump -i any port 80 -nnvv
5.5 性能优化建议
- 多用状态规则:
-m state --state ESTABLISHED,RELATED一条规则抵得上无数条开放高端口的规则,大幅提升性能。 - 规则顺序优化:将最频繁匹配的规则(如允许已建立连接)放在前面,匹配条件最严格的规则放在前面。
- 避免使用DNS解析:始终使用
-n选项,并在规则中使用IP地址而非主机名。 - 减少冗余规则:定期审查和合并规则。
- 考虑使用ipset:当需要匹配大量IP或端口时,使用
ipset创建集合,然后在iptables中匹配集合,效率远高于多条独立规则。
iptables的深度远超一篇文章所能涵盖,但掌握了表链结构、规则语法和这套从基础到进阶的实战思路,你已经有能力应对绝大多数Linux网络管控需求。记住,复杂配置都是从简单的规则累加而来的,先理解流程,再动手实践,遇到问题按部就班地排查,这才是从入门到精通的正确路径。