拓冰建站拓冰建站
首页 / 资讯中心 / 正文

iptables防火墙完全解读:从Netfilter原理到NAT端口转发实战

1. 先搞清楚iptables管的是哪一段网络路径先说一个很多人问过我的问题iptables到底是个防火墙还是一个命令严格来说iptables是用户态的管理工具真正干活的是Linux内核里的Netfilter框架。你输入的每一条iptables规则本质上是往Netfilter挂载的钩子点上注册了一段处理逻辑。把这两个概念区分开后面学什么都顺。很多初学者拿iptables跟Windows防火墙做类比以为它就是一个放行/拦截某些程序的开关。这个类比害了不少人。Windows防火墙默认是基于应用程序的维度做管控而iptables是基于数据包特征做的管控。它根本不关心这个流量是哪个程序发的它只看源IP、目的IP、源端口、目的端口、协议类型、进出网卡接口这些网络层和传输层的信息。这意味着什么意味着同一个进程产生的流量如果走了不同的端口iptables对它们的处理方式可以完全不同反过来不同进程如果复用了同一个端口iptables也分不清谁是谁。在动手配置iptables之前必须先建立一个核心概念数据包在Linux内核里的流转路径是有固定顺序的。一个数据包从网卡进来之后不是直接交给应用程序它要依次经过内核协议栈中的几个检查点。iptables的所有规则都挂在这些检查点上。以一个最简单的场景为例你本机的一个进程要访问外网数据包从发送到回应经历的过程是这样的请求数据包由本机进程产生走的是OUTPUT链数据包从网卡发出去在发送之前经过POSTROUTING链对端的回应数据包从网卡进来首先经过PREROUTING链然后根据目的IP判断是不是本机地址如果是就走INPUT链交给本机进程。流量如果要转发呢比如你的Linux机器充当路由器内网机器发来的包要转发到外网数据包进来后走PREROUTING然后判断目的IP不是本机于是走FORWARD链最后经过POSTROUTING发出去。这三个方向——发给本机的、本机发出的、经过本机转发的——对应了INPUT、OUTPUT、FORWARD这三条链。搞不清FORWARD和INPUT的区别是后面所有配置混乱的根源。比如你的Linux服务器开了IP转发内网其他机器通过它上网结果你只在INPUT链上放行了某些端口FORWARD链却是默认DROP那么所有经过这台机器的转发流量都会掉包。这类问题我在各种技术群里见过不下十次。数据包经过的检查点在Netfilter里这些点有标准的名称NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING。iptables把用户配置的规则挂在这些点上每经过一个点就检查一遍规则列表匹配到规则就执行对应的动作(ACCEPT、DROP、REJECT等)没匹配到就继续看下一条。我一直强调一个比喻你可以把iptables想象成小区门口的一排保安每个保安负责一道工序有的保安只看进小区的人有的保安只看出门的人有的保安负责在两道门之间的通道上巡逻。数据包这条路必须依次接受所有保安的检查。你配置的每一条规则就相当于给某个保安下了一条具体的指令。弄明白了这条路径再看四表五链就不会晕。2. 四表五链的职责划分iptables的骨架怎么记netfilter提供点位iptables提供规则但规则不是一股脑堆在一起的它按功能用途分了四张表。这就是所谓的四表五链。四张表分别是filter表、nat表、mangle表、raw表。如果你看过一些老教程可能还听说过五张表的说法那是指早期版本的实现现代内核里主要就是这四张。每张表能挂到的链不完全一样。这是一个很多人记混的点我直接列个表格看清楚表能挂载的链核心作用典型动作filterINPUT、FORWARD、OUTPUT进出本机和转发流量的过滤ACCEPT、DROP、REJECTnatPREROUTING、INPUT、OUTPUT、POSTROUTING地址转换改写数据包源/目的地址SNAT、DNAT、MASQUERADE、REDIRECTmangle全部五条链修改数据包头部字段(如TOS、TTL)MARK、TOS、TTLrawPREROUTING、OUTPUT决定数据包是否进入连接跟踪机制NOTRACK刚接触的人最容易犯的一个错误是给filter表写规则时忘了指定-t参数给nat表写规则时也忘了指定-t参数。iptables命令如果不加-t默认操作的是filter表。所以当你执行iptables -A INPUT -p tcp --dport 22 -j ACCEPT时这条规则进的是filter表的INPUT链这个好理解。但当你执行iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE时就必须要带-t nat否则这条规则会落到filter表里而filter表压根没有POSTROUTING链命令直接报错。2.1 filter表与INPUT/OUTPUT/FORWARD的配合逻辑filter表是最常用的一张表。它负责的决策非常纯粹——这个数据包是放行还是丢弃。在实际配置中我建议你把这个表的默认策略想清楚默认ACCEPT还是默认DROP这个决策决定了写规则的思维方式。如果你用的是默认ACCEPT策略那你的filter表现相当于一个黑名单模式只需要写禁止哪些流量的规则。这在测试环境里问题不大但在生产环境里存在一个隐患某天你忘了加某条封禁规则原本应该被拦截的流量就溜进来了。如果你用的是默认DROP策略那filter表就是一个白名单模式只放行明确需要的流量。这种策略安全等级更高但对运维的要求也更高——你漏掉一条放行规则服务就直接不可用了。生产环境里我更推荐默认DROP 显式放行的组合因为不可用比被入侵好处理得多。FORWARD链值得单独说一说。很多只管理单机的人根本没碰过FORWARD链。但只要你开始做NAT网关、做Docker端口映射、做KVM虚拟化FORWARD链就绕不开。举个最常见的场景Docker启动一个容器并映射了端口你以为Docker会把流量转发进容器实际上Docker只是往iptables的DOCKER链里动态加了几条规则而这些规则最终都挂在FORWARD链上。如果你手动清空了FORWARD链的规则或者把FORWARD默认策略改成了DROP你可能会发现容器服务突然从外部访问不了了。2.2 连接跟踪五表之外的隐藏机制在聊raw表和状态匹配之前必须先讲连接跟踪(conntrack)它是理解iptables状态规则的核心。连接跟踪机制会记录每一个经过系统的网络连接的状态Linux把连接状态分为四种NEW、ESTABLISHED、RELATED、INVALID。NEW新发起的连接请求只有第一个包是NEW状态ESTABLISHED连接已经建立后续双向的包都是这个状态RELATED与已有连接相关的新连接典型例子是FTP的数据连接INVALID无法识别的包通常是无效的、伪造的或损坏的包。我见过的最典型的iptables配置错误是只放行了NEW状态的包没放行ESTABLISHED和RELATED的包。你从服务器上主动访问外网服务器发出的响应包回到本地时它是ESTABLISHED状态。如果你在INPUT链上只写了-m state --state NEW -j ACCEPT那么所有回来的响应包都会被丢表现就是服务器能发起请求但收不到任何响应。正确的写法通常是iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 80 -j ACCEPT iptables -A INPUT -j DROP顺序也很重要。放行ESTABLISHED状态的规则一定要放在前面这样已经建立的连接直接被放行不会走到后面的DROP规则那里。conntrack机制虽然强大但它也有代价。每个连接都需要在内存里记录如果连接数特别大conntrack表满了之后新连接会被直接丢弃服务器日志里会出现nf_conntrack: table full, dropping packet的报错。遇到这种情况要么调大net.netfilter.nf_conntrack_max参数要么用raw表的NOTRACK跳过某些高并发流量的连接跟踪。3. 规则增删改查与匹配条件从ping通到限速的完整实践基础操作层面我把最常用的iptables命令按功能分类整理一遍。这些命令看起来多但核心就几个动作增(-A)、删(-D)、插(-I)、替换(-R)、查(-L)、清空(-F)。你可以把每条规则想象成链表里的一个节点iptables的规则是有顺序的从上到下逐条匹配。3.1 放行、拒绝与丢弃ACCEPT、DROP、REJECT怎么选查询当前规则用iptables -L -n -v。-n的作用是不做DNS反向解析直接显示IP地址否则域名解析会拖慢查询速度-v显示每个链上的数据包计数和字节计数这对后续排查问题很有用。看计数器你就能知道某条规则有没有被命中——如果计数一直为零要么规则没匹配到流量要么流量在前面就被别的规则截胡了。添加放行规则的基本姿势iptables -A INPUT -s 192.168.1.100 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -A INPUT -i lo -j ACCEPT第二条命令的含义是允许TCP协议、目标端口为22的流量进入本机也就是放行SSH端口。第三条命令放行回环接口(lo)的所有流量这条规则必须要有。很多诡异的问题——比如本机访问本机的Web服务不通——都是因为回环流量被DROP规则拦了。ACCEPT、DROP、REJECT三者的区别一定要清楚。ACCEPT表示放行数据包进入后续流程DROP表示丢弃直接把包扔掉不回复任何信息REJECT表示拒绝丢弃包的同时给发送方回一个错误信息(默认是ICMP port unreachable)。从发送方的角度看DROP的表现是超时REJECT的表现是立即被拒绝。对外服务推荐用DROP理由很简单不暴露端口的存在减少被扫描的风险对内网的特殊场景有时用REJECT更方便排错——至少你知道对方有没有收到拒绝通知。3.2 匹配条件全拆解IP、端口、协议、网卡、状态组合使用一条iptables规则的条件部分由很多匹配项组成。我来把这几个最常用的匹配项讲透。IP匹配iptables -A INPUT -s 10.0.0.0/8 -j ACCEPT iptables -A INPUT -d 192.168.1.10 -j DROPs指定源地址d指定目的地址。两者可以同时写也可以只写其中一个。地址支持单个IP、CIDR网段也支持非连续匹配比如用!取反-s ! 192.168.1.0/24表示源地址不是这个网段。端口匹配iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p udp --sport 53 -j ACCEPT iptables -A INPUT -p tcp --dport 1024:65535 -j ACCEPT--dport后面跟目的端口--sport后面跟源端口。端口匹配必须配合-p tcp或-p udp使用不指定协议直接写--dport会报错。支持连续端口范围中间用冒号分隔。协议匹配iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j ACCEPT不写-p默认匹配所有协议也可以写-p all。ICMP协议就是ping命令用的协议控制ping的放行和拒绝多是用--icmp-type配合。生产环境建议在INPUT链上放行ICMP的echo-request禁止的话会影响网络连通性排查。网卡接口匹配iptables -A INPUT -i eth0 -p tcp --dport 8080 -j ACCEPT iptables -A FORWARD -o eth1 -d 10.0.0.0/8 -j ACCEPT-i(input interface)匹配数据包进入的网卡-o(output interface)匹配数据包出去的网卡。多网卡服务器上这个匹配项极其有用。比如你有内网网卡eth0和外网网卡eth1内网服务只允许内网访问就直接在规则里限定-i eth0外网网卡来的包即使目的端口对上了也进不来。状态匹配前面提过用-m conntrack --ctstate。注意新旧内核版本的区别老版本用--state新版本推荐用--ctstate功能等价。3.3 规则的插入、删除、替换与顺序陷阱iptables的规则是从上到下逐条匹配的匹配到第一条规则就执行动作不再往下看。这个特性决定了顺序本身就是一种策略。举例说明你写了一条iptables -A INPUT -j DROP想默认拒绝所有流量但在这之前已经有一条iptables -A INPUT -p tcp --dport 22 -j ACCEPT那么SSH访问会被放行因为SSH流量走到DROP之前就匹配了ACCEPT规则。反过来如果你先执行了DROP再执行ACCEPT那么ACCEPT永远不会被命中SSH服务直接断开。这就是先拒绝后放行和先放行后拒绝的经典差异。所以需要用到-I(插入)命令iptables -I INPUT -p tcp --dport 3306 -j ACCEPT-I默认把新规则插到链的最前面。也可以指定插入位置iptables -I INPUT 3 ...表示插入到第三条位置上。删除规则用-D。删除时可以精确指定同样的匹配条件和动作也可以用规则的编号删除iptables -D INPUT -p tcp --dport 3306 -j ACCEPT iptables -D INPUT 3用编号删除前必须先用iptables -L --line-numbers查看规则编号这个--line-numbers在规则多了以后是排查神器。这里提醒一个很多人踩过的坑宁可用新的规则链来实现精细化管控也不要试图通过频繁调整filter表规则顺序来满足需求。规则一多手工维护顺序的复杂度是指数上升的。我习惯的玩法是把一类规则放到一个自定义链里然后主链上留一条跳转规则。比如所有进入的HTTP流量统一走HTTP_CHAIN后续调整只需要改自定义链不会影响主链的其他规则。4. NAT与端口转发让内网服务对外可见的关键配置刚做运维那阵子我对NAT的理解仅限于上网要开NAT。后来被业务方逼着给内网某个服务做端口映射才真正把NAT的用法研究明白。这里直接给出企业环境里最常见的三类需求以及对应的iptables配置。4.1 SNAT与MASQUERADE内网机器共享公网出口内网有几十台机器但只有一台机器有公网IP想让所有内网机器都能上外网。解决方案是在这台双网卡机器上开启IP转发并配置SNAT规则。首先确认内核参数echo 1 /proc/sys/net/ipv4/ip_forward # 或者 sysctl -w net.ipv4.ip_forward1然后配置SNATiptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j SNAT --to-source 203.0.113.5这条规则的意思是从192.168.10.0/24网段来的、要从eth0出去的包把源地址改写成203.0.113.5。内网机器访问外网时外网服务器看到的是公网IP外网服务器回应时数据包回到这台网关网关再把目的地址改回内网IP。整个过程对内网机器来说是透明的。如果你的公网IP是动态获取的(比如PPPoE拨号)用SNAT就麻烦了——每次IP变了都要重写规则。这种情况推荐用MASQUERADEiptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o pppoe0 -j MASQUERADEMASQUERADE会动态获取出口网卡当前的IP地址不需要手动指定。它的代价是每次处理新连接时都要额外查询一次网卡IP性能略低于SNAT。但在家庭宽带、动态IP场景里这个性能损耗可以忽略不计。4.2 DNAT端口映射把内网服务发布到公网内网有一台Web服务器192.168.10.20监听8080端口想通过公网网关的80端口访问它。需要两步配置先配置DNAT(改目的地址)再配合filter表的FORWARD放行。DNAT规则iptables -t nat -A PREROUTING -d 203.0.113.5 -p tcp --dport 80 -j DNAT --to-destination 192.168.10.20:8080这条规则的意思是访问公网IP 203.0.113.5的80端口的数据包把目的地址改成192.168.10.20目的端口改成8080。注意仅仅改目的地址还不够。数据包到了网关后会继续走FORWARD链如果FORWARD链默认DROP数据包会被丢。于是还需要放行对应的流量iptables -A FORWARD -d 192.168.10.20 -p tcp --dport 8080 -j ACCEPT iptables -A FORWARD -s 192.168.10.20 -p tcp --sport 8080 -j ACCEPT第二条放行Web服务器回给客户端的数据包方向相反但同样经过FORWARD链。很多人在配置完DNAT之后发现外网访问不通多半是漏了FORWARD链的放行规则。如果只有一个公网IP想把不同端口映射到内网不同机器上就在规则里区分目的端口iptables -t nat -A PREROUTING -d 203.0.113.5 -p tcp --dport 8081 -j DNAT --to-destination 192.168.10.21:80 iptables -t nat -A PREROUTING -d 203.0.113.5 -p tcp --dport 8082 -j DNAT --to-destination 192.168.10.22:80这是用一个公网IP做多服务发布的常见方案我建议在PREROUTING链里给每个映射写好注释规则多了以后能省很多事。iptables 1.4.20以上版本支持-m comment --comment 描述可以给每条规则附加说明。4.3 REDIRECT在本机内部做流量转向有一种需求是在本机做端口重定向不动源和目的地址只把某个端口的流量转给本机另一个端口。经典场景透明代理。比如你有一个HTTP代理跑在8123端口想让它拦截所有发往80端口的流量iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8123这条规则把进入本机的80端口流量重定向到8123端口源和目的IP地址不变。代理程序可以读取原始的CONNTRACK信息还原出真实的目的地址然后完成请求转发。还有的软件(squid透明代理模式)就会用到这个机制。注意REDIRECT的流量也会经过INPUT链。如果INPUT链有严格限制你得放行8123端口的本地访问否则流量被重定向之后直接被filter表拦掉了。5. 防火墙规则的持久化与动态更新重启不丢配置如果你在终端里敲了一堆iptables规则测试也通过了然后重启了服务器——恭喜你所有规则全部消失。这是iptables默认行为规则存在内存里不落盘。5.1 iptables-save与iptables-restore的组合保存规则最经典的方式是用iptables-save导出iptables-save /etc/iptables.rules恢复规则iptables-restore /etc/iptables.rules这两条命令在不同发行版上的默认路径略有差异。CentOS 6时代习惯保存到/etc/sysconfig/iptablesUbuntu上早期是/etc/iptables.rules后来系统引入了netfilter-persistent服务(底层其实就是iptables-save/restore的封装)配置文件路径变为/etc/iptables/rules.v4和/etc/iptables/rules.v6。要让规则开机自动恢复CentOS系直接执行service iptables save systemctl enable iptablesDebian/Ubuntu系执行apt install iptables-persistent netfilter-persistent save netfilter-persistent reload5.2 同时维护IPv4和IPv6容易被忽略的Ip6tables很多人在服务器上把IPv4的防火墙配得滴水不漏却完全忘了IPv6。如果你的服务器开启了IPv6攻击者完全可以用IPv6地址绕过你的IPv4防火墙。用ip6tables命令配置IPv6版本的规则语法几乎一样只是把iptables换成ip6tables。ip6tables -P INPUT DROP ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ip6tables -A INPUT -p ipv6-icmp -j ACCEPT ip6tables -A INPUT -i lo -j ACCEPT ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT注意IPv6下ping用的协议是ipv6-icmp用来区分IPv4的icmp。如果服务器不需要IPv6干脆在系统层面禁用IPv6省心很多如果需要建议把IPv6和IPv4规则一起纳管不要只配一半。5.3 动态更新规则时的会话保持问题升级防火墙规则最怕的一件事新规则一上现有连接全部断开。比如你更新SSH端口的放行规则如果不小心把当前SSH的ESTABLISHED连接给DROP了那你可能直接断开了自己的远程管理通道。所以在生产环境修改规则前我强烈建议先把两条保底规则加上iptables -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -I INPUT -p tcp --dport 22 -j ACCEPT先确保当前会话不会中断再去做修改。如果你所在的环境不允许临时测试那就准备一个逃生通道——比如带外管理卡、IPMI、或者另一个机房机器上的跳板。网上流传的操作是把规则脚本写成定时任务几分钟后自动恢复防止自己把自己锁在外面。这个思路在没法物理接触服务器时非常实用。5.4 firewalld和iptables到底什么关系这两年问这个问题的同行越来越多了。简单说firewalld是RHEL/CentOS 7之后默认的防火墙服务它底层仍然调用iptables命令只是提供了一套更上层的封装支持动态修改规则而无需刷新整个规则集。也就是说firewalld是管理器iptables是执行器。使用firewalld时你不需要直接写iptables命令而是用firewall-cmd或firewall-config操作富规则典型的例子firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reload但如果你是老运维习惯直接写iptables规则也可以在系统层面禁用firewalld装回iptables-services用传统的方式配置。我个人的观点是新项目直接学firewalld更省力但搞懂iptables底层会让你在遇到复杂问题(比如Docker端口映射、kube-proxy的iptables模式)时不至于抓瞎。很多K8s网络插件至今用的还是iptables这也是为什么现在运维对iptables知识的需求并没有因为firewalld的出现而减少。6. 我踩过的iptables坑与排查方法最后这部分我把这些年线上环境里真实踩过的坑掰开揉碎讲一讲。每个坑背后都是一段排查两小时解决一分钟的惨痛经历。6.1 规则顺序引发的灵异事件有一次线上环境反馈服务器上某个API偶发性超时。我登录上去一看iptables规则里有一条iptables -A INPUT -s 某个内网网段 -j DROP位置在放行规则之前。奇怪的是之前一直好好的怎么突然就出问题了后来排查发现那台服务器的内网IP变了原来被放行的网段恰好落到了新加上的一条DROP规则的匹配范围内。问题不在规则本身对不对而在规则顺序。那件事之后我给自己定了一个纪律每加一条DROP规则先确认它不会影响已有的ESTABLISHED连接再确认它不会拦掉更宽泛网段的合法流量。iptables的规则顺序是自顶向下的前面一旦DROP后面再写什么都是徒劳。排查方法也很简单iptables -L INPUT -n --line-numbers查看顺序iptables -I INPUT 编号插入到正确位置。定位问题时多用-v看计数哪条规则计数在暴涨问题大概率就在那条规则附近。6.2 默认策略是DROP结果把自己锁死在外面这是新手翻车率最高的一件事。场景通常是这样的你为了安全先把INPUT链默认策略改成了DROPiptables -P INPUT DROP然后你意识到自己当前这条SSH连接的响应包还没被放行——因为你只改了默认策略还没有添加任何放行规则。于是啪断了。我自己的做法是任何时候修改默认策略之前先确认当前会话的连续性。具体就是先把对应的放行规则写好再改默认策略。比如你要把INPUT默认策略改成DROP那就先把这几条写进去iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j ACCEPT然后再执行iptables -P INPUT DROP。如果发现自己已经锁死了而服务器是云主机大多数云平台控制台提供VNC/管理终端可以从控制台直接登录进去把规则清掉。物理机就只能依赖带外管理了。这也是我前面建议准备逃生通道的原因。6.3 开启IP转发后FORWARD链不放行转发流量全丢某次帮朋友排查公司内网的问题所有机器能ping通网关但是上不了外网网关机器本身能上。第一反应是NAT没配置好结果一查SNAT规则在。再一查FORWARD链默认策略是DROP而且完全没有放行规则。问题一下就清楚了内网到外网的包走到网关后在FORWARD链被丢了。加了放行规则后问题解决iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -s 192.168.10.0/24 -j ACCEPT为什么FORWARD链会变成默认DROP不用怀疑就是之前某个安全加固脚本干的。这类加固脚本通常把三条链的默认策略全改成DROP但没考虑这台机器是否需要转发流量。所以我一直建议安全基线的配置要结合机器实际的业务角色不能拿同一套模板生搬硬套。6.4 Docker环境下iptables规则被动过手脚用Docker的人大概都遇到过这样的情况明明我把宿主机的安全组/防火墙配置好了但容器端口一映射规则就乱了。这是因为Docker在创建容器时会自动往iptables的nat表和filter表里插入自己的规则。如果你手动清空iptables规则Docker容器可能就失去了端口映射能力。两个高频问题的解法问题一清空规则后容器端口映射失效。原因在于Docker通过DOCKER链和iptables规则完成了端口转发。手动清空规则把这些规则删了。解决办法是重启Docker服务让Docker重建自己的链和规则systemctl restart docker问题二手动启动Docker时外部访问不了映射端口。多数情况下是你把FORWARD链默认策略改成了DROP而Docker的转发流量会被DROP。解决方式有两种一是把FORWARD默认策略改回ACCEPT二是在FORWARD链里显式放行Docker相关的流量。这里我并不建议简单粗暴地把FORWARD默认策略改回ACCEPT因为那样整个防火墙策略等于开了个大口子。更稳妥的做法是了解Docker实际依赖哪些链按需放行。如果你在宿主机上装了firewalld那么firewall-cmd --zonedocker --add-masquerade这类操作也许能解决部分问题但底层还是iptables。6.5 端口通了但业务不通别忘了状态规则和并发限制最后一个常被忽略的坑你放行了端口外部也能连上TCP连接但业务就是响应异常。这种情况我倾向于用conntrack的状态规则来排查。比如你的规则只放行NEW状态的包忘记放行ESTABLISHED和RELATED那么双向通信就会表现得很诡异——建立连接的包能过后面的数据全被丢。排查命令可以直接查conntrack表conntrack -L如果没有安装conntrack命令行工具直接看/proc/net/nf_conntrack也可以。如果发现连接状态大量是INVALID那多半是规则配置有问题或者有伪造IP的流量攻击。另外iptables -L -v能看到每一条规则的包计数和字节计数。如果某条规则计数不断增长说明它一直在被命中对定位问题帮助极大。记住一点iptables的问题先看计数再看顺序最后看默认策略这个顺序能帮你省掉80%的排查时间。7. 再聊几个生产环境常见的iptables优化细节如果只满足于能配规则能放通端口那你和iptables之间还差一层。生产环境里性能和安全同样重要这里分享几个我一直在用的优化习惯。7.1 把匹配频率高的规则放在前面iptables是逐条遍历的规则越多匹配时间越长。虽然现代内核的iptables性能已经很好但高并发场景下规则顺序仍然会影响转发性能。一个实用原则流量命中率高的规则往前放。比如你有一台Web服务器90%的流量都是访问80端口那就把80端口的ACCEPT规则放在最前面。这样大部分数据包在第一二条规则就命中了不用一条一条往下刷。iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT这样的插入方式就能实现。7.2 用自定义链做规则分组规则一多直接在系统链(INPUT、OUTPUT、FORWARD)上堆规则会变得难以维护。自定义链能帮你把规则结构化。举个例子假设INPUT链上需要管理SSH、HTTP、HTTPS、监控采集等好几组规则。我可以建三个自定义链SSH_CHAIN、WEB_CHAIN、MONITOR_CHAIN。主链上只留几条跳转iptables -N SSH_CHAIN iptables -A INPUT -p tcp --dport 22 -j SSH_CHAIN iptables -A SSH_CHAIN -s 192.168.0.0/16 -j ACCEPT iptables -A SSH_CHAIN -j DROP这样规则语义极其清晰SSH流量先走SSH_CHAIN内网放行其余丢弃。后面要调整SSH策略只需要操作SSH_CHAIN不影响其他规则。用-N创建自定义链用-F清空自定义链用-X删除自定义链。7.3 日志审计如何把丢包行为记录下来防火墙最怕静默丢包——包丢了但你没日志可查。生产环境里我建议对关键动作开启日志记录尤其是DROP操作。iptables提供了LOG动作但LOG动作有一个特点它只负责记录日志不改变数据包的走向。如果你想让数据包既被记录又被丢弃需要写两条规则iptables -A INPUT -p tcp --dport 23 -j LOG --log-prefix IPTables-DROP-Telnet: --log-level 4 iptables -A INPUT -p tcp --dport 23 -j DROPLOG产生的日志默认写入内核日志可通过dmesg查看或者通过rsyslog转发到/var/log/messages。日志量大的时候一定要做好日志轮转否则磁盘会被日志灌满。如果只想统计某些规则的命中次数又不愿意看日志用-j ACCEPT加计数器(-v参数看计数)就行。7.4 秒级启停用脚本管理整个规则集手工一条一条敲命令在生产环境太脆弱了。我习惯把一套完整的规则集合写成一个脚本脚本开头先清空当前所有规则然后按顺序重新加载。这样有两个好处一是规则的全貌一目了然二是出问题后可以快速回滚到空规则状态恢复网络。一个简单的脚本结构大致是这样的#!/bin/bash IPT/usr/sbin/iptables # flush all rules $IPT -F $IPT -t nat -F $IPT -t mangle -F $IPT -X # set default policy $IPT -P INPUT DROP $IPT -P FORWARD DROP $IPT -P OUTPUT ACCEPT # loopback $IPT -A INPUT -i lo -j ACCEPT # established connections $IPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # services $IPT -A INPUT -p tcp --dport 22 -j ACCEPT $IPT -A INPUT -p tcp --dport 80 -j ACCEPT $IPT -A INPUT -p tcp --dport 443 -j ACCEPT # ping $IPT -A INPUT -p icmp --icmp-type echo-request -j ACCEPT # save iptables-save /etc/iptables.rules脚本执行完后再验证一下关键端口是否正常。如果脚本有问题马上执行iptables -F清空规则恢复默认状态远程连接不会断(前提是OUTPUT默认策略是ACCEPT)。脚本上线前先在测试机跑一遍这个习惯能救你一命。8. 写在最后iptables学习路径的几点个人体会很多人问我现在有firewalld、有安全组、有云防火墙了还有必要花时间学iptables吗我的回答一直是有必要而且非常有必要。firewalld再方便遇到复杂网络场景时最终仍然会暴露成一条条iptables规则。云平台的安全组再强大到了自建机房、裸金属服务器、容器网络环境里最底层能精确控制数据包的仍然只有Netfilter/iptables。哪怕你打算往K8s方向发展kube-proxy在iptables模式下维护的那一大堆转发规则不懂iptables的话根本看不明白。学习路径上我建议按这个顺序推进先搞清楚数据包流向和四表五链的框架再用filter表练手做端口放行与封锁接着掌握NAT和端口转发然后是状态匹配与连接跟踪最后才是性能优化和自定义链这样的进阶内容。每一步都最好在虚拟机或者云主机上亲手敲命令验证光看不练记不牢。测试环境里随便玩没关系但生产环境有一条底线所有操作前先备份当前规则。iptables-save backup.rules这一条命令成本几乎为零收益却可能是一次原本要通宵处理的事故被缩成十分钟恢复。最后分享一个小技巧写完一套规则后不要急着收工花两分钟逐条检查一遍计数器和规则顺序。iptables -L -n -v --line-numbers这条命令值得你养成肌肉记忆。很多问题在刚配完的时候就会暴露出来当场发现比半小时后业务告警再排查要舒服得多。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门