别再被iptables搞懵了!一文彻底搞懂Linux防火墙的底层逻辑

最近看到好多粉丝小伙伴都在问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。

四表的详细说明

image-20250822202919826

四个表按照功能分工,每个表包含不同的链:

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四表五链中的流转流程图

POSTROUTING
FORWARD
OUTPUT
INPUT
PREROUTING
发往本机
需要转发
mangle表 POSTROUTING链
POSTROUTING 钩子点
nat表 POSTROUTING链
mangle表 FORWARD链
FORWARD 钩子点
filter表 FORWARD链
raw表 OUTPUT链
OUTPUT 钩子点
mangle表 OUTPUT链
nat表 OUTPUT链
filter表 OUTPUT链
mangle表 INPUT链
INPUT 钩子点
nat表 INPUT链
filter表 INPUT链
raw表 PREROUTING链
PREROUTING 钩子点
mangle表 PREROUTING链
nat表 PREROUTING链
外部数据包
网络接口
路由决策
本地进程/应用
路由决策
网络接口
外部网络

实际应用中的表链选择

理解了四表五链的关系,在实际应用中就知道该在哪里写规则了。

想要阻止某个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,明天可能就被新技术替代了。但是学习的过程和思维方式是相通的,掌握了一种技术的本质,学习其他技术也会更容易。


如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!

公众号:运维躬行录

个人博客:躬行笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值