最近看到好多粉丝小伙伴都在问iptables的问题,什么"为什么我的规则不生效"、"怎么老是被自己的防火墙规则锁在门外"之类的。说多了都是泪,我刚开始接触iptables的时候没注意规则的顺序,回车敲的相当自信。结果悲催了,这道墙直接六亲不认的把我挡在了外面。记住!每次敲回车都得确认,确认,再确认。不要养成顺手敲回车的习惯,有时候慢就是快。快不一定快!!!后面发现是自己写的iptables规则把自己给封了只能灰溜溜跑去机房接显示器。。。。
什么是iptables?
iptables其实不是防火墙本身,它只是一个用户空间的工具,真正的防火墙功能是由内核中的netfilter框架提供的。你可以把netfilter想象成一个在内核网络栈中设置的检查站,而iptables就是用来配置这些检查站规则的工具。
我记得有次面试的时候,面试官问我iptables和netfilter的关系,我当时就懵了,以为它们是一回事。后来才明白,netfilter是内核模块,负责实际的数据包过滤工作;iptables是用户态程序,用来管理netfilter的规则。
netfilter的五个钩子点
要理解iptables,必须先搞清楚netfilter在网络栈中设置的五个钩子点(hook points)。数据包在内核中传输时,会经过这些钩子点,每个钩子点都可以对数据包进行处理。
这五个钩子点分别是:
PREROUTING:数据包刚进入网络栈,还没有进行路由决策的时候。在这里可以修改数据包的目标地址,也就是DNAT(目标地址转换)。
INPUT:数据包经过路由决策后,如果目标是本机,就会到达INPUT钩子点。
FORWARD:如果数据包的目标不是本机,需要转发给其他主机,就会经过FORWARD钩子点。
OUTPUT:本机产生的数据包在离开本机之前会经过OUTPUT钩子点。
POSTROUTING:数据包离开本机之前的最后一个钩子点,通常在这里做SNAT(源地址转换)。
我画个简单的图来说明数据包的流向:
外部数据包 -> PREROUTING -> 路由决策 -> INPUT -> 本机应用
|
v
FORWARD -> POSTROUTING -> 外部网络
^
本机应用 -> OUTPUT -> 路由决策 -------|
iptables的四表五链详解
这里是重点了,很多人搞不清楚iptables就是因为没理解四表五链的关系。我当初也是在这里卡了很久,后来想明白了其实很简单。
五链的详细说明
五链就是对应netfilter的五个钩子点,每个链都是规则的集合:
INPUT链:处理进入本机的数据包。比如别人SSH到你的服务器,或者访问你服务器上的Web服务,这些数据包都会经过INPUT链。
OUTPUT链:处理从本机发出的数据包。比如你在服务器上ping别的机器,或者服务器主动连接数据库,这些数据包会经过OUTPUT链。
FORWARD链:处理经过本机转发的数据包。只有当你的机器作为路由器或网关时,这个链才会被用到。比如你搭建了一个NAT网关,内网机器访问外网的数据包就会经过FORWARD链。
PREROUTING链:在路由决策之前处理数据包。主要用于DNAT,比如把访问公网IP的80端口转发到内网服务器。
POSTROUTING链:在数据包离开本机之前处理。主要用于SNAT,比如把内网IP转换成公网IP。
四表的详细说明
四个表按照功能分工,每个表包含不同的链:
filter表(过滤表):
这是默认表,主要用于决定数据包是否允许通过。包含三个链:
- INPUT链:过滤进入本机的数据包
- OUTPUT链:过滤从本机发出的数据包
- FORWARD链:过滤经过本机转发的数据包
我们平时说的"防火墙规则",大部分都是在filter表里。比如:
# 这条命令默认就是在filter表的INPUT链中添加规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
nat表(地址转换表):
专门用于网络地址转换,包含四个链:
- PREROUTING链:用于DNAT,修改数据包的目标地址
- INPUT链:处理发往本机的数据包的地址转换(用得比较少)
- OUTPUT链:处理本机发出的数据包的地址转换(用得比较少)
- POSTROUTING链:用于SNAT,修改数据包的源地址
典型的应用场景:
# DNAT:把访问本机80端口的请求转发到内网服务器
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:80
# SNAT:把内网访问外网的数据包源地址改成公网IP
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 1.2.3.4
mangle表(修改表):
用于修改数据包的头部信息,比如TTL、TOS、DSCP等。包含所有五个链:
- PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING
这个表用得相对较少,主要在需要精细控制数据包属性的场景下使用:
# 修改数据包的TTL值
iptables -t mangle -A OUTPUT -j TTL --ttl-set 64
# 给数据包打标记,配合tc做流量控制
iptables -t mangle -A OUTPUT -p tcp --dport 80 -j MARK --set-mark 1
raw表(原始表):
主要用于配置数据包,决定是否进行连接跟踪。包含两个链:
- PREROUTING链
- OUTPUT链
这个表的优先级最高,主要用于性能优化:
# 跳过连接跟踪,提高性能
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
表和链的关系矩阵
我画个表格来说明哪些表包含哪些链:
PREROUTING INPUT FORWARD OUTPUT POSTROUTING
raw ✓ ✗ ✗ ✓ ✗
mangle ✓ ✓ ✓ ✓ ✓
nat ✓ ✓ ✗ ✓ ✓
filter ✗ ✓ ✓ ✓ ✗
数据包处理的优先级
当数据包到达某个钩子点时,会按照表的优先级依次处理:
raw → mangle → nat → filter
这个顺序很重要!我之前就遇到过一个问题,在filter表里写了规则拒绝某个IP,但是在nat表里又写了DNAT规则转发这个IP的请求。结果发现DNAT规则生效了,因为nat表的优先级比filter表高,数据包在到达filter表之前就被转发了。
数据包在iptables四表五链中的流转流程图
实际应用中的表链选择
理解了四表五链的关系,在实际应用中就知道该在哪里写规则了。
想要阻止某个IP访问服务器:
# 在filter表的INPUT链中添加规则
iptables -A INPUT -s 1.2.3.4 -j DROP
想要做端口转发:
# 在nat表的PREROUTING链中做DNAT
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# 还需要在filter表的FORWARD链中允许转发
iptables -A FORWARD -d 192.168.1.100 -p tcp --dport 80 -j ACCEPT
想要做网关共享上网:
# 在nat表的POSTROUTING链中做SNAT
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# 在filter表的FORWARD链中允许转发
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
想要跳过连接跟踪提高性能:
# 在raw表中添加NOTRACK规则
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --sport 80 -j NOTRACK
连接跟踪系统
iptables有一个很重要的特性就是连接跟踪(connection tracking),它能记住网络连接的状态。这个功能是由内核模块nf_conntrack提供的。
连接状态主要有:
- NEW:新建连接的第一个数据包
- ESTABLISHED:已建立的连接
- RELATED:与已有连接相关的新连接,比如FTP的数据连接
- INVALID:无法识别的连接
利用连接跟踪,我们可以写出很简洁的规则。比如:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
这条规则允许所有已建立的连接和相关连接通过,这样就不用为每个服务单独写返回数据包的规则了。
但是连接跟踪也有代价,它会消耗内存来存储连接信息。在高并发的环境下,连接跟踪表可能会满,导致新连接无法建立。这时候可能需要调整内核参数或者使用raw表来跳过连接跟踪。
实际应用中的一些坑
说了这么多理论,来聊聊实际使用中容易踩的坑。
关闭firewall,不然会冲突
# 关闭firewalld
[root@node1 ~]# systemctl stop firewalld
[root@node1 ~]# systemctl disable firewalld
规则顺序很重要。我见过不少人写规则的时候不注意顺序,把DROP规则放在前面,ACCEPT规则放在后面,结果ACCEPT规则永远不会被执行到。
比如这样的规则就有问题:
iptables -A INPUT -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
第二条规则永远不会生效,因为所有数据包都被第一条规则DROP了。
默认策略要小心设置。我刚开始学iptables的时候,经常把INPUT链的默认策略设置为DROP,然后忘记添加SSH的规则,结果把自己锁在外面,只能去机房重启服务器。
现在我的习惯是先添加必要的规则,最后再修改默认策略:
# 先允许SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 允许已建立的连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许本地回环
iptables -A INPUT -i lo -j ACCEPT
# 最后设置默认策略
iptables -P INPUT DROP
回环接口别忘了。lo接口(127.0.0.1)的流量一定要允许,很多应用都依赖本地回环通信。如果把lo接口的流量给阻断了,可能会出现各种奇怪的问题。
ICMP不要全部禁止。有些人为了"安全",把所有ICMP流量都禁止了,结果导致路径MTU发现机制失效,出现一些诡异的网络问题。至少要允许ICMP的错误消息通过。
表的选择要正确。我见过有人在filter表里写SNAT规则,当然不会生效,因为filter表根本没有POSTROUTING链。还有人在nat表里写DROP规则,这也是不对的,nat表是用来做地址转换的,不是用来过滤的。
四表五链的实战案例
来看几个实际的配置案例,帮助大家理解四表五链的应用。
案例1:Web服务器防火墙配置
假设你有一台Web服务器,需要开放80和443端口,允许SSH管理,其他端口都要封闭:
# 清空现有规则
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
# 设置默认策略(注意顺序)
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP
# 允许本地回环(在filter表的INPUT链)
iptables -A INPUT -i lo -j ACCEPT
# 允许已建立的连接(在filter表的INPUT链)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许SSH(在filter表的INPUT链)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 允许HTTP和HTTPS(在filter表的INPUT链)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 允许ping(在filter表的INPUT链)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# 最后设置INPUT默认策略为DROP
iptables -P INPUT DROP
这个配置只用到了filter表的INPUT链,因为我们只需要过滤进入服务器的流量。
案例2:NAT网关配置
假设你要搭建一个NAT网关,内网是192.168.1.0/24,外网接口是eth0:
# 开启IP转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 在nat表的POSTROUTING链做SNAT
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# 在filter表的FORWARD链允许内网到外网的流量
iptables -A FORWARD -i eth1 -o eth0 -s 192.168.1.0/24 -j ACCEPT
# 在filter表的FORWARD链允许已建立连接的返回流量
iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
# 设置FORWARD链默认策略为DROP
iptables -P FORWARD DROP
这个配置用到了nat表的POSTROUTING链(做SNAT)和filter表的FORWARD链(控制转发)。
案例3:端口转发配置
假设你要把访问服务器8080端口的请求转发到内网192.168.1.100的80端口:
# 在nat表的PREROUTING链做DNAT
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# 在nat表的POSTROUTING链做SNAT(如果需要的话)
iptables -t nat -A POSTROUTING -d 192.168.1.100 -p tcp --dport 80 -j SNAT --to-source 192.168.1.1
# 在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 --sport 80 -j ACCEPT
这个配置用到了nat表的PREROUTING链(DNAT)、POSTROUTING链(SNAT)和filter表的FORWARD链(控制转发)。
案例4:高性能Web服务器配置
假设你有一台高并发的Web服务器,想要跳过连接跟踪来提高性能:
# 在raw表跳过连接跟踪
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --sport 80 -j NOTRACK
# 在filter表允许HTTP流量(注意不能使用state模块了)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
# 在mangle表给数据包打标记,配合tc做QoS
iptables -t mangle -A OUTPUT -p tcp --sport 80 -j DSCP --set-dscp 46
这个配置用到了raw表(跳过连接跟踪)、filter表(基本过滤)和mangle表(QoS标记)。
NAT的工作原理深入解析
既然说到了四表五链,就不能不深入讲讲NAT的工作原理,因为很多人对DNAT和SNAT在哪个链生效搞不清楚。
DNAT的工作流程
DNAT(目标地址转换)发生在PREROUTING链,也就是路由决策之前。这样设计是有道理的,因为要先修改目标地址,然后内核才能根据新的目标地址做路由决策。
比如这条规则:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:8080
数据包的处理流程是:
1. 外部数据包到达,目标是本机的80端口
2. 在PREROUTING链,DNAT规则生效,目标地址改为192.168.1.100:8080
3. 路由决策:发现目标是192.168.1.100,需要转发
4. 数据包进入FORWARD链进行过滤
5. 如果通过过滤,数据包被转发到192.168.1.100
SNAT的工作流程
SNAT(源地址转换)发生在POSTROUTING链,也就是数据包离开本机之前的最后一步。这样设计也是有道理的,因为要等路由决策完成,确定了出口接口,才能决定用哪个IP做源地址。
比如这条规则:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 1.2.3.4
数据包的处理流程是:
1. 内网机器192.168.1.10发出数据包,目标是外网
2. 数据包到达网关,经过路由决策,确定从eth0接口发出
3. 在POSTROUTING链,SNAT规则生效,源地址改为1.2.3.4
4. 数据包发送到外网
MASQUERADE的特殊性
MASQUERADE是SNAT的一个特殊形式,它会自动使用出口接口的IP地址作为源地址。这在动态IP环境下很有用:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
MASQUERADE会自动获取eth0接口的IP地址,不需要手动指定。
连接跟踪在NAT中的作用
NAT的实现完全依赖连接跟踪系统。当一个连接的第一个数据包经过NAT规则时,连接跟踪系统会记录下地址转换的映射关系。后续的数据包(包括返回的数据包)都会根据这个映射关系自动进行地址转换。
这就是为什么NAT规则只需要写一条,返回的数据包会自动被转换。比如:
# 只需要这一条DNAT规则
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:80
当外部访问本机80端口时:
1. 请求数据包:外部IP:随机端口 → 本机IP:80,经过DNAT后变成:外部IP:随机端口 → 192.168.1.100:80
2. 响应数据包:192.168.1.100:80 → 外部IP:随机端口,会自动变成:本机IP:80 → 外部IP:随机端口
这个自动转换就是连接跟踪系统的功劳。
调试和排错技巧
iptables的调试一直是个难点,数据包在复杂的规则中穿行,很难追踪到底在哪里被处理了。
使用LOG target记录日志是最常用的调试方法:
# 在每个关键位置添加LOG规则
iptables -t nat -A PREROUTING -p tcp --dport 80 -j LOG --log-prefix "PREROUTING: "
iptables -A FORWARD -p tcp --dport 80 -j LOG --log-prefix "FORWARD: "
iptables -t nat -A POSTROUTING -p tcp --sport 80 -j LOG --log-prefix "POSTROUTING: "
然后在/var/log/messages或者/var/log/kern.log中查看日志。
使用TRACE target可以跟踪数据包的完整路径:
# 开启TRACE(需要加载xt_LOG模块)
modprobe xt_LOG
iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE
TRACE会显示数据包经过的每个表、每个链、每条规则,非常详细,但也会产生大量日志。
查看连接跟踪表:
cat /proc/net/nf_conntrack
这个文件显示当前所有被跟踪的连接,可以帮助理解NAT的工作情况。
使用iptables-save查看所有规则:
iptables-save
这个命令会按照表和链的结构显示所有规则,比iptables -L更清晰。
我记得有一次排查NAT问题,怀疑是规则写错了,用iptables -L看了半天都没发现问题。后来用iptables-save一看,发现有人在不同的表里写了冲突的规则,把整个逻辑搞乱了。
性能优化的一些技巧
iptables在高并发环境下可能会成为性能瓶颈,这里分享一些优化技巧。
减少规则数量。规则越多,匹配越慢。可以用ipset来处理大量IP地址的匹配,ipset使用哈希表,查找效率比逐条匹配高很多。
# 创建ipset
ipset create blacklist hash:ip
ipset add blacklist 1.2.3.4
ipset add blacklist 5.6.7.8
# 在iptables中使用
iptables -A INPUT -m set --match-set blacklist src -j DROP
合理使用连接跟踪。连接跟踪虽然方便,但是会消耗内存和CPU。对于一些无状态的服务,可以考虑跳过连接跟踪:
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --sport 80 -j NOTRACK
规则顺序优化。把匹配频率高的规则放在前面,把DROP规则尽量放在前面,这样可以减少不必要的匹配。
使用专用的硬件或软件。在流量很大的环境下,可能需要考虑使用专门的防火墙设备,或者基于eBPF的解决方案,比如Cilium。
与其他防火墙工具的关系
现在很多发行版都默认使用firewalld或者ufw这些高级工具,但它们底层还是基于iptables的。
firewalld是Red Hat系列发行版的默认防火墙管理工具,它提供了zone的概念,把网络接口分配到不同的安全区域。但是firewalld生成的规则最终还是通过iptables来执行的。
你可以用iptables -L
看到firewalld生成的规则,通常会有很多以FWDI_
、FWDO_
开头的自定义链。
ufw(Uncomplicated Firewall)是Ubuntu的默认防火墙工具,设计理念是简化iptables的使用。比如:
ufw allow 22/tcp
这条命令会自动生成对应的iptables规则。
nftables是新一代的包过滤框架,设计用来替代iptables。它的语法更简洁,性能也更好。但是目前还没有完全普及,很多工具和脚本还是基于iptables的。
理解iptables的工作原理,对于使用这些高级工具也是很有帮助的。当出现问题时,你可以直接查看底层的iptables规则来排查。
实践建议
最小权限原则。默认拒绝所有流量,只开放必要的端口和服务。这样即使有新的漏洞,攻击面也比较小。
分层防护。不要把所有安全措施都寄托在iptables上,要结合应用层防护、入侵检测等多种手段。
定期审计规则。iptables规则会越来越多,要定期清理不需要的规则,避免规则冲突。
备份配置。在修改规则之前,一定要备份当前配置:
iptables-save > /etc/iptables/rules.backup
使用配置管理工具。对于多台服务器,建议使用Ansible、Puppet等工具来统一管理iptables配置,避免手工配置出错。
监控和告警。要监控iptables的DROP统计,异常的DROP数量可能表示有攻击或者配置错误:
iptables -L -v -n
测试环境验证。重要的规则变更要先在测试环境验证,确认没问题再上生产环境。
我见过太多因为iptables配置错误导致的故障,有些甚至影响了业务。所以在这方面一定要谨慎,宁可保守一些,也不要冒险。
写在最后
iptables作为Linux网络安全的基石,虽然学习曲线比较陡峭,但是掌握了它的原理和使用方法,对于理解整个Linux网络体系都很有帮助。
四表五链的概念是理解iptables的关键。记住:
- raw表处理连接跟踪,优先级最高
- mangle表修改数据包属性,包含所有五个链
- nat表做地址转换,DNAT在PREROUTING,SNAT在POSTROUTING
- filter表做过滤,是默认表,包含INPUT、OUTPUT、FORWARD三个链
数据包在内核中的流向是固定的:PREROUTING → 路由决策 → INPUT/FORWARD → OUTPUT → POSTROUTING。理解了这个流程,就能知道规则应该写在哪个表的哪个链里。
现在虽然有很多更高级、更易用的网络工具,但是它们大多数还是基于iptables或者netfilter框架。理解底层原理,有助于更好地使用这些高级工具,也有助于在出现问题时快速定位和解决。
网络安全是个复杂的领域,iptables只是其中的一个工具,但它是一个很重要的工具。希望这篇文章能帮助大家更好地理解和使用iptables,在实际工作中少踩一些坑。
记住,任何工具都有它的适用场景和局限性。iptables也不例外,要根据实际需求选择合适的解决方案。有时候简单的ufw就够用了,有时候可能需要专业的硬件防火墙。
技术在不断发展,我们也要保持学习的心态。今天的iptables,明天可能就被新技术替代了。但是学习的过程和思维方式是相通的,掌握了一种技术的本质,学习其他技术也会更容易。
如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!
公众号:运维躬行录
个人博客:躬行笔记