1. 项目概述:为什么iptables依然是Linux防火墙的基石
在云原生和容器化大行其道的今天,你可能听过很多关于新一代防火墙、云安全组、服务网格安全策略的讨论。但如果你深入到任何一台Linux服务器,无论是物理机、虚拟机还是容器宿主机,最终兜底的那个网络流量管控层,十有八九还是iptables。这个诞生于Linux内核2.4时代(2001年)的工具,至今仍是Linux网络安全事实上的标准。很多人觉得它命令行复杂、规则链抽象,远不如firewalld或者一些Web管理界面直观。但我想说的是,真正理解并掌握iptables,是你从“系统使用者”迈向“系统掌控者”的关键一步。它让你能清晰地看到数据包在你服务器里的“行走路径”,能精准地定位网络问题,能实现从端口开放到复杂流量整形、从NAT转发到DDoS简易防护等一系列高级功能。更重要的是,它是理解Kubernetes的kube-proxy、Docker的网络模型乃至众多云平台底层网络实现的基础。今天,我就以一个老运维的视角,带你从零开始,彻底搞懂iptables,并分享那些手册里不会写的实战技巧和踩坑经验。
2. iptables核心架构与四表五链深度解析
很多人学iptables,一上来就记命令,结果规则一多就晕了。我的建议是,先花时间把它的“世界观”建立起来。iptables的核心是“表”(Table)、“链”(Chain)和“规则”(Rule)。
2.1 四张表:各司其职的规则容器
你可以把“表”理解为不同用途的规则集合。iptables默认有四个内置表,但最常用的是前三个:
- filter表:这是默认表,也是最核心的表。它负责过滤数据包,决定是放行(ACCEPT)还是丢弃(DROP/REJECT)。我们常说的“开防火墙端口”,主要就是在操作这个表。
- nat表:网络地址转换表。当你的数据包需要改变源或目标地址时,就用到它。比如做路由器、做端口映射(DNAT)、让内网机器通过本机上网(SNAT)都离不开它。
- mangle表:修改表。这个表功能强大且“危险”,它可以修改数据包的内容,比如修改TTL(生存时间)、设置MARK标记(用于后续的路由或流量控制)。一般初学者接触较少,但在做策略路由、QoS时很有用。
- raw表:原始表。主要用于配置数据包不被连接跟踪机制处理。在需要极高性能或者处理某些特殊协议(如VPN)时可能会用到。
注意:不是所有链都能挂载所有表。例如,
FORWARD链通常只用于filter表和mangle表,而PREROUTING和POSTROUTING链主要用于nat表和mangle表。理解这个对应关系很重要。
2.2 五条链:数据包的必经关卡
“链”是数据包传输路径上的检查点。数据包会像过安检一样,依次经过这些链。五条内置链对应了数据包的生命周期:
- PREROUTING链:数据包刚进入网络接口(网卡),在路由决策之前。这里适合做DNAT(改变目标地址)和标记数据包。
- INPUT链:数据包经过路由决策,目标是本机(例如,SSH连接到这台服务器)。我们保护服务器,主要就是在INPUT链上设置过滤规则。
- FORWARD链:数据包经过路由决策,目标是其他机器(本机充当路由器或网关)。如果你的服务器开启了IP转发功能(
net.ipv4.ip_forward=1),就需要在这里管理转发的流量。 - OUTPUT链:由本机进程产生的数据包,在离开本机之前。
- POSTROUTING链:数据包即将离开网络接口,在路由决策之后。这里适合做SNAT(改变源地址),比如让内网机器共享一个公网IP上网。
2.3 数据包流向全景图
这是理解iptables的钥匙。一个数据包从进入网卡到离开,其旅程如下:
- 来自外部,目标为本机:
PREROUTING->INPUT->本机应用程序 - 来自本机,发往外部:本机应用程序->
OUTPUT->POSTROUTING - 来自外部,穿越本机发往另一台外部主机(本机是网关):
PREROUTING->FORWARD->POSTROUTING
把这个流程图刻在脑子里。当你写一条规则时,必须非常清楚:我这个规则,是想在数据包的哪个“人生阶段”(链),做什么“类型”的处理(表)?比如,你想做端口映射,那是在数据包进入时、路由前改变它的目标地址,所以你应该在nat表的PREROUTING链上写一条DNAT规则。
3. iptables基础语法与规则管理实战
理解了架构,我们来看怎么操作。iptables命令的基本格式是:iptables [-t 表名] 命令选项 链名 匹配条件 -j 目标动作
-t 表名:指定操作哪张表,省略时默认为filter表。命令选项:告诉iptables你要做什么,比如添加(-A)、插入(-I)、删除(-D)、查看(-L)等。链名:指定操作哪条链。匹配条件:用来筛选数据包,比如源IP、目标IP、协议、端口等。-j 目标动作:匹配后执行的动作,如ACCEPT(接受)、DROP(丢弃,无响应)、REJECT(拒绝,返回拒绝包)、DNAT、SNAT等。
3.1 规则管理核心命令
查看规则:这是最常用的命令之一。
# 查看filter表所有链的规则(默认就是filter表) iptables -L -n # 加上-v查看更详细信息(数据包和字节计数) iptables -L -n -v # 查看nat表规则 iptables -t nat -L -n -v # 以更清晰的格式、带行号查看,便于后续删除 iptables -L -n --line-numbers添加规则:
# 在INPUT链末尾添加一条规则:允许来自192.168.1.0/24网段的TCP流量访问本机22端口 iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT # 在INPUT链开头插入一条规则(优先级最高):允许本地回环接口(lo)的所有流量 iptables -I INPUT 1 -i lo -j ACCEPT实操心得:
-A是追加到链尾,-I是插入到指定位置(不指定位置则默认为链首)。防火墙规则是从上到下顺序匹配的,第一条匹配到的规则就会生效,后面的不再检查。所以,通常会把允许的、范围精确的规则放在前面,把拒绝的、范围宽泛的规则(如默认策略)放在最后。-i lo -j ACCEPT这条规则务必放在最前面,很多本地服务(如MySQL、Redis本地连接)依赖回环接口,如果被误拦,排查起来很头疼。
删除规则:
# 方法1:通过完整的规则描述来删除(必须和添加时完全一致) iptables -D INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT # 方法2(推荐):通过链名和行号删除(先用--line-numbers查看) iptables -D INPUT 3修改链的默认策略: 链的默认策略(Policy)是指当数据包不匹配任何一条规则时,采取的动作。
# 将INPUT链的默认策略设置为DROP(严格安全模式) iptables -P INPUT DROP # 将FORWARD链的默认策略设置为ACCEPT(在作为网关时常用) iptables -P FORWARD ACCEPT重要警告:在远程连接(如SSH)的服务器上,千万不要直接执行
iptables -P INPUT DROP,除非你已经提前在INPUT链里添加了允许SSH的规则。否则你会立刻断开连接并再也连不上去!正确的做法是,先添加允许规则,再设置默认拒绝策略。
清空所有规则与计数器:
# 清空指定表所有链的规则 iptables -F # 清空nat表所有规则 iptables -t nat -F # 删除所有用户自定义的链 iptables -X # 将所有链的包和字节计数器归零 iptables -Z3.2 常用匹配条件详解
匹配条件是规则的灵魂,决定了规则作用的范围。
基于网络接口:
-i eth0 # 数据包进入的网卡是eth0(仅用于PREROUTING, INPUT, FORWARD链) -o eth1 # 数据包出去的网卡是eth1(仅用于FORWARD, OUTPUT, POSTROUTING链)基于IP地址:
-s 192.168.1.100 # 源IP地址 -s 192.168.1.0/24 # 源IP网段 -d 10.0.0.1 # 目标IP地址 ! -s 192.168.1.100 # 非192.168.1.100的源IP(感叹号表示取反)基于协议和端口:
-p tcp # TCP协议 -p udp # UDP协议 -p icmp # ICMP协议(如ping) --dport 80 # 目标端口(常用) --sport 1024:65535 # 源端口范围 -p tcp --dport 22:25 # 目标端口范围(22到25) -p icmp --icmp-type 8 # 匹配ping请求(type 8)基于连接状态(state):这是iptables最强大的特性之一,实现了“有状态检测”。
-m state --state RELATED,ESTABLISHED -j ACCEPTNEW:新建连接的第一个包。ESTABLISHED:已建立的连接。RELATED:与已有连接相关的连接,如FTP的数据连接。INVALID:无效的包。 这条规则的含义是:允许所有属于已建立连接或相关连接的数据包通过。把它放在允许规则的开头,可以极大地简化规则集。因为一旦内网主机主动发起一个对外请求(比如浏览网页),其回来的流量状态就是ESTABLISHED,会被这条规则直接放行,而无需为每个服务单独设置回包规则。
4. 经典实战场景配置指南
光说不练假把式,下面我们通过几个最常见的场景,把上面的知识串联起来。
4.1 场景一:配置一台安全的Web服务器
假设你有一台公网服务器(IP: 203.0.113.10),上面运行着Nginx(80/443端口)和SSH服务(22端口)。你需要允许所有人访问Web服务,但只允许来自办公室IP(例如 198.51.100.0/24)的SSH连接。
配置思路:
- 清空旧规则,设置默认策略。
- 允许本地回环。
- 允许状态为
ESTABLISHED,RELATED的包,这是关键。 - 允许访问80和443端口。
- 允许来自特定IP段的SSH连接。
- 拒绝其他所有入站流量(INPUT链默认策略设为DROP)。
- 允许所有出站流量(OUTPUT链默认策略设为ACCEPT,通常服务器主动对外访问是需要的)。
操作步骤:
# 1. 设置默认策略(先设ACCEPT防止被踢,生产环境请谨慎) iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT # 2. 清空所有规则 iptables -F iptables -t nat -F iptables -t mangle -F # 3. 允许本地回环 iptables -A INPUT -i lo -j ACCEPT # 4. 允许已建立和相关连接(至关重要!) iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 5. 允许HTTP和HTTPS iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 6. 允许来自办公室IP段的SSH iptables -A INPUT -s 198.51.100.0/24 -p tcp --dport 22 -j ACCEPT # 7. 配置默认策略 iptables -P INPUT DROP # 不匹配以上规则的入站流量全部丢弃 iptables -P FORWARD DROP # 不转发任何流量 iptables -P OUTPUT ACCEPT # 允许所有出站流量 # 8. 保存规则(根据发行版不同) # CentOS/RHEL 6: service iptables save # CentOS/RHEL 7+/Ubuntu: 需要安装iptables-persistent或使用其他方法,见下文“规则持久化”章节。现在,你的服务器只开放了80、443端口,并且22端口只对办公室IP开放,其他所有入站连接都会被静默丢弃(DROP),安全性大大提升。
4.2 场景二:实现端口转发(DNAT)
你的服务器有公网IP(203.0.113.10),内网有一台IP为192.168.1.100的机器,上面运行着Web服务(80端口)。你想让外网用户访问203.0.113.10:8080时,流量被转发到内网的192.168.1.100:80。
配置思路:
- 在
nat表的PREROUTING链上做DNAT,改变目标地址和端口。 - 因为数据包是“穿过”本机(目标不是本机),所以还需要
filter表的FORWARD链允许该流量通过。 - 确保服务器已开启IP转发功能。
操作步骤:
# 1. 开启Linux内核的IP转发功能(临时生效) echo 1 > /proc/sys/net/ipv4/ip_forward # 永久生效:编辑 /etc/sysctl.conf,设置 net.ipv4.ip_forward = 1,然后执行 sysctl -p # 2. 在nat表的PREROUTING链添加DNAT规则 iptables -t nat -A PREROUTING -d 203.0.113.10 -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 -j ACCEPT # 4. (可选)如果内网服务器需要主动访问外网,还需要做SNAT(伪装) iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE # MASQUERADE是SNAT的一种自动形式,适用于动态获取公网IP的情况(如家庭宽带、拨号上网)。完成以上设置后,外网访问http://203.0.113.10:8080的请求就会被透明地转发到内网的Web服务器上。
4.3 场景三:限制连接速率与防简单CC攻击
iptables不仅可以做静态的允许/拒绝,还能通过limit和recent模块实现简单的流量整形和攻击防护。
示例1:限制SSH每分钟最多连接3次新连接,超过则拒绝
# 使用limit模块限制速率 iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m limit --limit 3/min --limit-burst 3 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j DROP--limit 3/min:平均每分钟最多允许3个新连接。--limit-burst 3:初始的“令牌桶”容量为3,允许短时间内突发连接。- 第一条规则:符合速率限制的新连接被接受。
- 第二条规则:超出速率限制的新连接被丢弃。
示例2:使用recent模块防止对Web端口的暴力访问
# 创建一个“黑名单”,60秒内对80端口发起超过10次新连接的IP将被加入黑名单并拒绝5分钟 iptables -N HTTP_FLOOD # 新建一个自定义链 iptables -A HTTP_FLOOD -m recent --set --name BADGUY --rsource iptables -A HTTP_FLOOD -m recent --update --seconds 300 --hitcount 1 --name BADGUY --rsource -j DROP iptables -A HTTP_FLOOD -j ACCEPT # 在INPUT链中引用这个自定义链 iptables -A INPUT -p tcp --dport 80 -m state --state NEW -m recent --update --seconds 60 --hitcount 10 --name BADGUY --rsource -j DROP iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j HTTP_FLOOD这个规则集稍微复杂一些,但效果很好。它记录了每个IP在60秒内对80端口新建连接的次数,超过10次就将该IP标记为“BADGUY”,并在接下来的300秒内直接丢弃其所有到80端口的新连接。这能有效缓解简单的CC攻击或扫描器。
5. 高级技巧与性能优化
5.1 规则的优化与排序
规则顺序直接影响性能。iptables是线性匹配,所以:
- 最常用的规则放前面:比如那条
-m state --state ESTABLISHED,RELATED -j ACCEPT,应该放在很靠前的位置,因为服务器上大部分流量都是回包。 - 匹配范围最小的规则放前面:比如“允许某个特定IP访问SSH”的规则,应该放在“允许整个网段访问SSH”的规则前面。
- 使用多端口匹配:减少规则数量。
# 不推荐:为每个端口写一条规则 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT iptables -A INPUT -p tcp --dport 8080 -j ACCEPT # 推荐:使用multiport模块 iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT
5.2 连接跟踪(conntrack)的坑与解决
state模块依赖内核的conntrack(连接跟踪)表。这个表有大小限制。在高连接数的服务器(如负载均衡器、NAT网关)上,可能会遇到conntrack table full的错误,导致新连接无法建立。
- 查看当前状态:
cat /proc/sys/net/netfilter/nf_conntrack_count(当前连接数),cat /proc/sys/net/netfilter/nf_conntrack_max(最大限制)。 - 调整最大值:编辑
/etc/sysctl.conf,增加net.netfilter.nf_conntrack_max = 655350(或更大),然后sysctl -p。 - 调整超时时间:对于某些协议,可以缩短连接跟踪的超时时间,让条目更快释放。例如,在
/etc/sysctl.conf中设置net.netfilter.nf_conntrack_tcp_timeout_established = 1200(将ESTABLISHED状态的TCP连接超时从默认的5天改为1200秒)。
5.3 规则持久化:让配置在重启后依然生效
这是新手最容易踩的坑。用iptables命令配置的规则是保存在内存中的,重启服务器就会丢失。
- CentOS 6 / RHEL 6:使用
service iptables save命令,规则会保存到/etc/sysconfig/iptables。 - CentOS 7+ / RHEL 7+:默认使用firewalld。如果你想继续用iptables,需要先禁用firewalld,安装
iptables-services。systemctl stop firewalld systemctl disable firewalld yum install iptables-services -y systemctl start iptables systemctl enable iptables # 配置好规则后,使用以下命令保存 service iptables save # 或 /usr/libexec/iptables/iptables.init save - Ubuntu / Debian:安装
iptables-persistent包。apt-get update apt-get install iptables-persistent -y # 安装过程中会询问是否保存当前规则。之后,每次用`iptables`命令修改规则后,需要手动保存: netfilter-persistent save # 或 iptables-save > /etc/iptables/rules.v4 ip6tables-save > /etc/iptables/rules.v6
6. 故障排查与日常维护命令
当网络不通时,按以下思路排查iptables:
- 查看所有规则和计数器:
iptables -L -n -v。重点看对应链的包计数(pkts和bytes),如果某个拒绝规则的计数器在增长,说明有流量被它挡住了。 - 查看NAT规则:
iptables -t nat -L -n -v。 - 查看规则顺序:
iptables -L -n --line-numbers。 - 临时放行所有流量进行测试(危险,仅在测试环境或确定影响后操作):
如果此时网络通了,问题就在iptables规则上。然后一条条规则加回去,直到问题复现。iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F iptables -t nat -F - 使用日志记录被拒绝的包:在关键的
DROP规则前插入一条LOG规则,帮助定位。
日志默认会输出到# 记录被丢弃的SSH新连接尝试,日志前缀为“SSH-DROP” iptables -I INPUT 5 -p tcp --dport 22 -m state --state NEW -j LOG --log-prefix "SSH-DROP: "/var/log/messages或/var/log/kern.log,你可以用tail -f命令实时查看。
7. 从iptables到nftables:未来的方向
虽然iptables依然强大,但Linux社区已经推出了它的继任者:nftables。从Linux内核3.13开始引入,旨在替代iptables/ip6tables/arptables/ebtables等一系列工具,提供统一的配置框架和更高效的性能。它的语法更简洁,规则集处理效率更高。主流发行版的新版本都已支持。例如,在CentOS 8/RHEL 8上,iptables命令只是一个兼容层,底层已经是nftables了。
学习nftables是未来的趋势,但理解iptables的概念(表、链、规则、状态跟踪)对于学习nftables有巨大的帮助,因为核心思想是相通的。当你彻底玩转了iptables,再去看nftables的语法,会有一种“豁然开朗”的感觉。
iptables就像Linux系统工程师的一把瑞士军刀,看起来其貌不扬,但用熟了几乎能解决所有网络层面的管控问题。它的价值不在于界面有多花哨,而在于给你了对网络数据包最底层的、最直接的控制力。这份控制力,是构建稳定、安全、可调试的服务器环境的基石。希望这篇长文能帮你把这把刀磨得更亮一些。