恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
iptables安装配置与生产基线实战避坑
首页
资讯中心
/
iptables安装配置与生产基线实战避坑
iptables安装配置与生产基线实战避坑
发布时间:2026/10/2 10:50:08
凌晨两点被电话叫醒运维群里说业务端口从公网直接能连上。登上机器一看iptables 压根没装防火墙是裸的。这种场景我遇到过不止一次很多人装完系统、跑起服务之后就默认外面连不进来直到某天被扫到端口开放才回过神来。iptables 是 Linux 上最基础、也最容易被绕过去的一道门——装起来只要一条命令配起来却能配出各种花样。这篇就把 iptables 的安装和配置按真实落地流程走一遍怎么判断系统该装哪个包、四表五链到底怎么读、一份能直接抄的生产基线规则长什么样、conntrack 状态为什么必须先放行、53 端口的出站策略怎么设计、规则怎么持久化以及那些把 SSH 会话当场锁死的经典坑。不管你是刚接手一台云主机的开发还是手里管着几十台机器的运维这些内容都能直接拿去用。1. 先把 iptables 的定位说清楚它到底是什么不装行不行1.1 从一次端口裸奔的排查说起那次事故的根因其实很简单镜像用的是精简版系统iptables这个包根本没进基础依赖服务装完之后监听在0.0.0.0:6379公网直接可连。当时我做的第一件事不是急着写规则而是先确认现在这套系统到底有没有防火墙在管事。很多人会下意识敲systemctl status iptables看到Unit iptables.service could not be found就以为系统没有防火墙其实这个判断是错的——服务没注册不代表内核里没有过滤规则在跑。这里要先建立一个基本认知iptables 不是防火墙本身它只是操作防火墙的那把螺丝刀。真正干活的是 Linux 内核里的 netfilter 框架它挂在内核网络协议栈的若干关键位置数据包每经过一个位置就会被拦一下问一句这条包放不放。iptables 是用户态工具负责把这些位置上的规则读出来、写进去。所以你完全可能遇到iptables 命令都装不上但机器其实已经有一堆过滤规则的情况因为另一套工具nftables、firewalld在往同一个内核框架里塞规则。排查时我一般按这个顺序走先iptables --version看命令存不存在再iptables -L -n -v看现有规则数量接着nft list ruleset | head -50看 nftables 有没有在动最后cat /proc/net/ip_tables_names看内核里实际激活了哪几张表。这四步走完基本能判断出这台机器的过滤体系是什么状态再决定是装 iptables、还是改用系统自带的方案。1.2 iptables 与 netfilter、nftables 的关系这三者的关系经常把人绕晕我用一个类比说清楚。netfilter 是内核里的一套关卡系统关卡位置是固定的比如进机器之前、路由决策之后、出去之前。iptables 是这套关卡系统最早的遥控器用起来直观但效率一般规则一多匹配就慢。nftables 是后来重新设计的遥控器语法更统一、性能更好同时兼容旧的操作习惯。现在的问题是新版发行版默认把iptables这个命令指向 nftables 后端。你在 Debian 12、Ubuntu 22.04 及以上、RHEL 9 这些系统上敲iptables实际操作的可能是 nftables。判断方法很直接iptables --version # iptables v1.8.7 (nf_tables) - 走 nftables 后端 # iptables v1.8.4 (legacy) - 走传统后端括号里那两个字就是答案。如果系统上两套都装了还可以用update-alternatives --config iptables切换。这个信息为什么重要因为 nftables 后端下少数老扩展模块的行为会不一样某些依赖/proc/net/ip_tables_names的老脚本也可能读不到东西。我在一台 Ubuntu 22.04 上就遇到过-m recent的计数文件和预期对不上换回 legacy 后端立刻正常。所以如果你的规则里用了冷门模块先把后端确认清楚能省掉大量瞎猜的时间。1.3 规则匹配的三段式模型写规则之前必须理解匹配顺序否则你写出来的规则看着对、跑起来不对。iptables 的匹配模型可以概括成三段先选表再选链最后从上到下逐条比。选表靠-t参数默认是filter表不写-t nat的话你的 NAT 规则会被写进 filter 表然后你会发现它完全不生效——这个错误我见过太多次。选链靠-A追加到末尾、-I插入到指定位置默认最前面、-D删除。最后的逐条匹配是最关键的一旦某条规则匹配成功并给出了终止动作ACCEPT、DROP、REJECT 等后面的规则就完全不再看。这意味着规则的书写顺序就是策略本身。把DROP写在放行规则前面等于后面全白写。这也是为什么所有教程都强调先把放行规则写全最后再改默认策略为 DROP顺序错了改策略那一下就是你把自己关在门外的时刻。2. 安装前的三件确认后端、发行版、当前规则状态2.1 确认后端iptables-legacy 还是 iptables-nft上一步已经说过怎么判断后端这里补充几个实操细节。第一iptables-save的输出在两个后端下格式基本一样可以互相导入但 nftables 后端会在注释里带上句柄号手动编辑时别去动那些注释。第二如果你要通过/etc/sysconfig/iptables或/etc/iptables/rules.v4这类文件做持久化两个后端的恢复命令都能吃但恢复时的解析器不同罕见的扩展模块参数可能会有差异。第三也是最容易被忽略的同一台机器上不要混用两套工具去写规则。用iptables写了规则又用nft命令行改一遍两边看到的规则集可能不一致排查起来非常痛苦。我给自己定的规矩是一台机器只选一种方式管理要么全 iptables要么全 nftables要么交给 firewalld绝不上手混着改。如果想统一到传统后端Debian 系可以这样切update-alternatives --set iptables /usr/sbin/iptables-legacy update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy切完之后记得重新执行一遍iptables -L -n确认规则有没有跟着换壳。老规则在新后端下通常不会自动带过去需要提前iptables-save备份再 restore。2.2 各发行版安装命令对照与内核模块检查装包这件事本身没难度难的是记住不同发行版的包名差异。下面这张表是我实际用过的对照可以直接抄发行版安装命令持久化配套包备注Debian / Ubuntuapt install -y iptablesiptables-persistent装持久化包时会弹交互框可预置 debconf 跳过RHEL / CentOS 7yum install -y iptables-services同左会注册iptables.serviceRHEL 8/9 / Rocky / Almadnf install -y iptables-nft iptables-services同左默认后端已是 nftablesAlpineapk add iptables iptables-openrc同左需rc-update add iptablesopenSUSEzypper install iptables一般配合自写 systemd 单元系统默认 firewalldDebian 系装iptables-persistent时那个交互提示经常卡住自动化脚本预置方法是这样echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections DEBIAN_FRONTENDnoninteractive apt install -y iptables-persistent装完包别急着写规则先看内核模块在不在位。iptables的功能是靠一堆nf_*、ip_tables、iptable_nat、nf_conntrack模块实现的精简镜像里这些模块经常被裁掉或者被列进了黑名单lsmod | grep -E ip_tables|nf_conntrack|iptable_nat cat /proc/net/ip_tables_names第二条命令会列出当前激活的表名如果输出为空说明还没有任何规则占用这些表属于干净状态。正常情况下你写第一条规则的时候内核会自动按需加载对应模块但如果modprobe iptable_nat报 Module not found那就是内核裁剪问题了得换内核或者上云厂商的通用内核。2.3 动手之前先备份iptables-save 是保命符这条我放在安装环节里讲是因为它是整个流程里最容易被跳过、又最救命的一步。在任何清空、修改默认策略的操作之前先执行一次保存mkdir -p /root/fw-backup iptables-save /root/fw-backup/rules-$(date %F-%H%M).v4这个文件里有完整的表、链、规则和计数器加了-c参数才有计数器恢复就是一条命令iptables-restore /root/fw-backup/rules-2024-05-11-0230.v4我建议把这个备份动作写进你的操作习惯里改规则前存一份改完测通了再存一份新的。云主机还有一层保险——很多厂商控制台提供救援模式真把自己锁在外面了从控制台 VNC 登录进去 restore 就行。但这条路有前提你得知道 VNC 密码而且得确认控制台能进。我自己会把备份文件顺手扔一份到对象存储或者另一台机器上多花十秒省一次深夜救火。3. 四表五链与常用参数把规则当成数据包的一段旅程3.1 四张表各管什么iptables 的规则是按表组织的每张表解决一类问题。新手最常犯的错是只知道 filter 表然后被 NAT 和连接跟踪搞得莫名其妙。四张表的分工是这样filter 表负责过滤决定放行还是丢弃。日常 90% 的需求都在这张表里INPUT、FORWARD、OUTPUT 三条链。nat 表负责地址转换包括端口转发、源地址伪装、目的地址改写。链是 PREROUTING、OUTPUT、POSTROUTING。mangle 表负责改包头的特殊字段比如 TTL、TOS、打标记MARK。做策略路由或者多线路分流时才会用到。raw 表优先级最高用来在连接跟踪之前做处理主要用途是给某些流量打NOTRACK标记跳过 conntrack减轻大流量场景的表压力。优先级顺序是 raw → mangle → nat → filter。这个顺序只影响同一张链上的处理先后不影响你写规则的语法。实际工作中我基本只在 raw 表里干一件事给超大流量的备份同步流量打 NOTRACK避免 conntrack 表被这些长连接撑爆。3.2 五条内置链与数据包的完整流向理解流向比背参数重要得多。假设有一台主机网卡是eth0一个数据包从外面进来要发给本机的 Nginx它的路径是到达网卡进入PREROUTING链raw → mangle → nat 依次处理内核做路由决策判断这个包是给本机的还是需要转发的如果是给本机的进入INPUT链mangle → filter本机进程处理后生成响应包走OUTPUT链raw → mangle → nat → filter响应包在离开网卡前经过POSTROUTING链mangle → nat完成源地址转换之类的最后操作。如果这个包是要转发给另一台内网机器的路径就变成 PREROUTING →FORWARD链mangle → filter→ POSTROUTING。FORWARD 链管的是路过的流量不是到达的流量这个区分特别重要Docker 容器、Kubernetes Pod、内网网关的流量全都要过 FORWARD如果你的FORWARD默认策略设成了 DROP 又没放行容器之间立刻互相不通。画成表格会更清楚链处理对象涉及的表PREROUTING刚到达网卡、尚未路由决策raw、mangle、natINPUT目的地是本机的包mangle、filterFORWARD需要转发给其他主机的包mangle、filterOUTPUT本机进程发出的包raw、mangle、nat、filterPOSTROUTING即将离开网卡的包mangle、nat3.3 高频参数速查与易错点参数的记忆诀窍是先定位表、链、动作再描述协议、地址、端口最后扩展模块、匹配条件。常用的我整理成一张速查表参数作用易错点-t指定表不写默认 filter写 nat 规则时必须带-L -n -v --line-numbers查看规则不加-n会做反向解析卡顿且看不清-A/-I追加 / 插入-I默认插到第 1 条容易打乱顺序-D删除可用规则全串或行号行号会随删除变化-P默认策略改 DROP 前务必确认放行规则已生效-p协议写tcp才能用--dport--dport/--sport目的/源端口源端口过滤在多端口场景里很容易写反-i/-o入/出接口只在特定链有效OUTPUT 链不能用-i-m加载扩展模块用了--state就必须先-m state-j动作ACCEPT/DROP/REJECT/LOG/RETURN 等-m comment --comment加注释强烈建议每条规则都加否则一周后自己都看不懂关于-s和-d还有个容易踩的坑-s 10.0.0.5默认会被解释成10.0.0.5/32而-s 10.0.0.0/24才是网段。如果手快写成-s 10.0.0.0iptables 会当成10.0.0.0/32这个具体地址处理规则照样生效但拦不住你以为的网段排查时特别迷惑。养成习惯写网段就明确带掩码。4. 一份可以直接上生产的基线规则集4.1 清空、默认策略与顺序原则套路很清楚清空 → 写放行 → 改默认策略。三步的顺序不能颠倒。清空用-F清规则、-X删自定义链、-Z归零计数器iptables -F iptables -X iptables -Z注意-F只清规则不清默认策略所以清完之后 INPUT 还是之前的策略。如果你上一轮的策略是 DROP清空规则的瞬间机器就处于全丢状态——你如果正通过 SSH 连着会话会立刻断因为你用来连接的这条 ESTABLISHED 连接的回包被默认策略丢掉了。这是第一条铁律远程操作时永远不要先清空再写规则。正确做法是先写放行、确认无误最后才动默认策略。改默认策略的命令是iptables -P INPUT DROP改之前建议用下面这个自锁保险# 10 分钟后自动把策略恢复成放行防止把自己锁死 at now 10 minutes iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT系统没装at的话用screen或者nohup起一个延迟任务也一样nohup bash -c sleep 600; iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT 十分钟够不够如果规则已经提前写在脚本里一般两分钟就验证完了。这个保险的成本几乎为零收益是不用打飞的去机房。4.2 放行回环与 conntrack 已建立连接基线规则的前两条几乎是固定的iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT第一条放行回环接口。别小看它很多本地服务之间通过127.0.0.1通信放行 lo 还兼着解决一部分本地 IPC 的连通问题。第二条是整份规则集里最关键的一条它放行所有已经建立的连接和与已有连接相关的包。为什么它关键因为 iptables 是无状态匹配的它不认识这是刚才那个请求的响应。如果没有 conntrack 这条规则你的 INPUT 默认策略是 DROP那么你在本机发起的出站请求比如curl一个接口回来的响应包会被 INPUT 链拦下丢掉——表现就是出网能连上但收不到数据卡住不返回。加上这条之后conntrack 模块会把五元组相同的响应包识别为 ESTABLISHED 状态直接放行。注意这条规则的顺序必须在任何 DROP 动作之前也必须在 SSH 放行规则之前因为它要处理的是所有返回流量。RELATED状态主要覆盖 FTP 的数据连接、ICMP 的错误报文这类派生连接。比如你ping出去的 ICMP 请求回来的echo reply是 ESTABLISHED但中间路由器返回的destination unreachable是 RELATED没有它你会看到ping报奇怪的错误。4.3 SSH 放行的防锁死做法SSH 是唯一一条你必须无条件先放行的规则因为它是你进机器的路。写法本身简单iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT但我强烈建议加上来源限制不要对全网开放iptables -A INPUT -p tcp -s 203.0.113.0/24 --dport 22 -m conntrack --ctstate NEW -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP第二行的兜底 DROP 是可选的但加了之后日志会告诉你有人在尝试从其他地址连 SSH对判断是否被扫很有帮助前提是接上了日志规则后面第 5 节讲。还有一个细节如果你的 SSH 端口改成了非 22一定要在写规则时改成实际端口如果用了跳板机要把跳板机的地址放进去。我见过最离谱的一次是规则里放行的是 22但 sshd 实际监听 2222改完默认策略之后当场失联靠云控制台的 VNC 才救回来。4.4 业务端口放行、ICMP 限速与多端口写法业务端口放行建议按来源 协议 端口三维描述别图省事全放。举几个实际写法# 只允许内网访问数据库 iptables -A INPUT -p tcp -s 10.0.0.0/16 --dport 3306 -j ACCEPT # 允许公网访问 Web 服务 iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT # ICMP 限速允许 ping 但防止被刷 iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 10/s --limit-burst 20 -j ACCEPT-m multiport一次可以写最多 15 个端口比写一堆规则清爽匹配效率也更好。-m limit的--limit是平均速率--limit-burst是允许的瞬时突发量两个参数配合才能既防刷又不误伤正常请求。ICMP 这条我一般都会加原因很现实完全禁 ping 之后链路上的问题排查会变得很难你没法快速判断是网络不通还是服务没起。最后一条收尾规则是把默认策略改成 DROPiptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPTOUTPUT 我通常保持 ACCEPT除非有明确的出站管控需求。原因很简单出站限制配错会让服务出现一堆莫名其妙的超时而且排查方向很难第一时间想到防火墙。要管控出站可以像下一节那样做定向收敛别一刀切。5. 进阶场景conntrack 状态、53 端口出站策略与暴力破解防护5.1 conntrack 状态机详解与 --ctstate 的正确用法conntrack 是 iptables 最有价值的模块之一它维护一张连接状态表把无状态的数据包匹配变成了有状态的连接匹配。状态有五种搞清楚它们的区别能省下大量排查时间状态含义典型场景NEW新连接的第一个包主动发起的 SYNESTABLISHED已建立连接的双向包三次握手完成后的数据RELATED与已有连接有派生关系FTP 数据连接、ICMP 错误INVALID无法识别、不属于任何连接畸形包、序列号异常UNTRACKED被 raw 表标记为不跟踪打 NOTRACK 的大流量--state是旧写法--ctstate是新写法两者功能重叠新写法能多识别一些状态。现在写规则我一律用--ctstate兼容性已经没问题了。这里有个实用技巧加一条显式的 INVALID 丢弃规则。被标记为 INVALID 的包正常业务不会产生丢掉它对正常流量没影响却能挡掉一部分扫描和畸形包探测iptables -A INPUT -m conntrack --ctstate INVALID -j DROP另外要监控 conntrack 表的使用率表满了会直接丢包而且报错在应用层完全看不出来cat /proc/sys/net/netfilter/nf_conntrack_count cat /proc/sys/net/netfilter/nf_conntrack_max如果count逼近max就要调大上限或者缩短超时时间sysctl -w net.netfilter.nf_conntrack_max262144 sysctl -w net.netfilter.nf_conntrack_udp_timeout30这些值要写进/etc/sysctl.d/下的配置文件否则重启就回默认值。表满的典型症状是dmesg里反复刷nf_conntrack: table full, dropping packet服务表现为间歇性连不上非常像网络抖动很容易查错方向。5.2 53 端口出站收敛为什么要限制 DNS 出口内网里有个很常见的管控需求限制主机只能向指定的 DNS 服务器发查询不允许私自连外部 DNS。原因不是技术洁癖而是两点现实问题。一是内网的解析策略、内部域名、访问控制都挂在自建 DNS 上主机绕过它直接查询外部内网域名就解析不了业务会时好时坏。二是出口流量的可审计性——DNS 查询本身是很小的包如果不限定目的地出口流量里就混进了一堆不受控的查询日志里根本看不出谁在查什么。具体的规则这样写假设内网 DNS 是10.0.0.53# 放行到内网 DNS 的查询 iptables -A OUTPUT -p udp -d 10.0.0.53 --dport 53 -j ACCEPT iptables -A OUTPUT -p tcp -d 10.0.0.53 --dport 53 -j ACCEPT # 其余目标一律拒绝用 REJECT 而不是 DROP让客户端快速失败 iptables -A OUTPUT -p udp --dport 53 -j REJECT --reject-with icmp-port-unreachable iptables -A OUTPUT -p tcp --dport 53 -j REJECT --reject-with tcp-reset三个细节值得说清楚。第一TCP 53 也要管。DNS 在大响应和区域传送时会走 TCP只封 UDP 等于留了个口子。第二用 REJECT 而不是 DROP。DROP 会让客户端一直等到超时默认 5 秒甚至更长应用层表现为打开网页卡几秒用户体感很差REJECT 会立刻返回错误客户端立刻切换到备用解析方案体验完全不同。第三别忘了 conntrack 的配合。DNS 走 UDPconntrack 会为每次查询建立一条短超时条目默认 30 秒响应包的状态是 ESTABLISHED所以只要你前面放行了ESTABLISHED,RELATED回包是能进来的不需要额外放行--sport 53。还有一种情况这台机器本身就是 DNS 服务器。那就要区分清楚方向。对外的 INPUT 方向要放行别人来查它出站方向仍然要收敛它去哪里做递归查询# 本机作为 DNS 服务端放行外部查询 iptables -A INPUT -p udp --dport 53 -j ACCEPT iptables -A INPUT -p tcp --dport 53 -j ACCEPT这种服务端和客户端两个方向的混淆是我在真实环境里见过最多的 53 端口配置错误。写完规则之后一定要从一台客户端机器上实际dig 目标IP 域名验证一次别只看规则列表觉得对了就收工。5.3 recent 模块做 SSH 爆破防护公网机器上 SSH 被扫是常态几天不看日志就是几十万条失败记录。iptables 自带的recent模块能做简单有效的限流同一来源在时间窗内失败或新建连接次数超过阈值就临时封禁。写法如下iptables -N SSH_GUARD # 60 秒内新建连接超过 5 次直接丢弃 iptables -A SSH_GUARD -m recent --name ssh_zone --rcheck --seconds 60 --hitcount 5 -j DROP # 未超限的记录下来并放行 iptables -A SSH_GUARD -m recent --name ssh_zone --set -j ACCEPT # 挂到 INPUT 上只对新连接生效 iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j SSH_GUARD参数的含义是--rcheck检查来源地址是否在列表里且满足条件--seconds 60是时间窗--hitcount 5是命中次数阈值--set把来源地址记进列表。当前被封的来源可以从/proc/net/xt_recent/ssh_zone里看到调试时很有用。几个实测经验。第一--hitcount从 5 往上调之前先想清楚如果你的运维同事在跳板机上反复重连很容易被误伤所以我一般把--seconds放大到 60、--hitcount放到 5 到 10 之间别设太狠。第二recent模块依赖内核和 nftables 后端的具体实现前面提到过某些环境下--rcheck的行为和预期不一致验证时一定要用一台测试机实际触发封禁看/proc/net/xt_recent/里的计数是否变化。第三recent是单机方案面对分布式来源的扫描基本无效真要防住得靠上层统一防护但作为兜底的第一道门槛它的性价比足够高。5.4 LOG 日志与限速不加 limit 会把磁盘写满要看到谁在被拦就得用 LOG。但 LOG 有两个必须知道的特性它不终止匹配执行完 LOG 后规则会继续往下走以及它会写内核日志量大了能把磁盘写爆。所以标准写法一定带限速iptables -N LOG_DROP iptables -A LOG_DROP -m limit --limit 5/min --limit-burst 10 -j LOG --log-prefix IPT-DROP: --log-level 4 iptables -A LOG_DROP -j DROP # 在 INPUT 末尾引用 iptables -A INPUT -j LOG_DROP--log-prefix一定要写且要有辨识度方便后面用grep捞。日志落在哪取决于系统的日志配置用 systemd-journald 的系统可以直接journalctl -k | grep IPT-DROP用 rsyslog 的系统要确认kern.*或kern.warning有没有被写进文件否则日志只在内存里转一圈就没了。我在一台测试机上真的把日志盘写满过原因就是当时的 DROP 规则没加--limit一条每秒几百个包的扫描直接把分区撑爆连带数据库都写不进数据。这个教训很直白任何面向公网的 DROP 规则旁边都必须有一条带 limit 的 LOG。6. 持久化、验证与外置组件冲突的排障6.1 让规则活过重启三种持久化方案iptables 的规则是内存里的重启就没了这一点和很多人配置完就永久生效的直觉不符。持久化有三条路按系统选方案一Debian 系用 iptables-persistent。装好之后规则文件在/etc/iptables/rules.v4和rules.v6保存命令netfilter-persistent save # 或者 iptables-save /etc/iptables/rules.v4恢复是netfilter-persistent reload。这套方案的好处是开机自动加载不用自己写服务。方案二RHEL/CentOS 用 iptables-services。service iptables save # 或者 iptables-save /etc/sysconfig/iptables systemctl enable --now iptables方案三通用方案自己写一个 systemd oneshot 单元。适合 Alpine、openSUSE 或者不喜欢装额外包的环境[Unit] DescriptionRestore iptables rules Beforenetwork-pre.target Wantsnetwork-pre.target [Service] Typeoneshot ExecStart/sbin/iptables-restore /etc/iptables.rules RemainAfterExityes [Install] WantedBymulti-user.target三条路选一条就行别同时用两套否则开机时到底谁最后写入就成了玄学问题。无论用哪个方案保存之后一定要重启验证一次我在云主机上遇到过 cloud-init 在启动后覆盖防火墙规则的情况本地方案一切正常结果一开机规则就被冲掉了最后是把规则写进了 cloud-init 的自定义脚本里才稳定下来。6.2 验证规则真的生效本机、外部、脚本三种方式规则写完之后最忌讳的就是看了下iptables -L觉得没问题。规则列表只能证明规则存在不能证明匹配顺序和行为符合预期。我一般做三层验证。第一层是本机检查重点看顺序和计数器iptables -L INPUT -n -v --line-numbers iptables -t nat -L -n -v-v会带出每条规则命中的包数和字节数。如果你刚测过一次连接对应规则的计数应该从 0 涨上去——计数不动说明根本没匹配到这时候该查的是顺序或者条件写错了而不是怀疑服务。第二层是从另一台机器实际测。用curl、nc或者直接连业务端口验证放行和拦截都符合预期。这里有个小技巧测拦截的时候观察是超时还是立刻拒绝能立刻判断出你的规则用的是 DROP 还是 REJECT比翻规则文件快。第三层是脚本化验证适合纳入变更流程iptables -C INPUT -p tcp --dport 22 -j ACCEPT || echo SSH 规则缺失 iptables -C INPUT -i lo -j ACCEPT || echo 回环规则缺失-C是检查规则是否存在返回码 0 表示存在。把这几个检查写进发布脚本能在规则被误删时第一时间报警。这个用法知道的人不多但实战价值很高。6.3 踩坑实录规则不生效、Docker 断网、conntrack 表满坑一规则写了但完全不生效。最常见的两个原因是后端不对iptables 命令和实际生效的规则集不是一套和被更上层的工具接管firewalld、云厂商的安全组代理。排查顺序iptables --version看后端nft list ruleset看有没有另一套规则systemctl status firewalld看有没有服务在管最后看云控制台的安全组是不是在更外层拦着。这里必须强调云主机的安全组和本机 iptables 是两层独立的过滤安全组放行了不代表本机放行反过来也一样两边都要看。坑二写完之后 Docker 容器全断网。这个坑非常经典。Docker 会在 FORWARD 链上插自己的规则同时依赖net.ipv4.ip_forward1。如果你按基线把FORWARD默认策略设成 DROP容器和外部之间的转发流量就被拦了表现为容器起得来、docker exec进得去但容器里访问不了外面外面也访问不了容器端口。正确做法是给 Docker 留口子iptables -A FORWARD -i docker0 -j ACCEPT iptables -A FORWARD -o docker0 -j ACCEPT更规范的做法是在DOCKER-USER链里写规则因为 Docker 每次重启都会重建自己那几条链直接往 FORWARD 里插的规则可能在重建时丢失或被挪位置。另外要记住Docker 的端口映射走的是 DNAT不经过 INPUT 链所以你把 INPUT 锁死并不能阻止别人访问-p 8080:80暴露的端口要拦就得在DOCKER-USER或者 nat 层拦。坑三conntrack 表满导致间歇性丢包。症状是服务偶发超时、dmesg里刷table full但 CPU 内存都很正常。处理方式是调大nf_conntrack_max和缩短nf_conntrack_tcp_timeout_established。根源往往是有大量短连接或者被扫前者要在应用层用连接池解决后者靠限流规则挡住光调参数是治标。坑四改默认策略把自己锁死。前面反复提过这里再给一个具体的判断方法如果你执行完iptables -P INPUT DROP之后 SSH 还能用说明ESTABLISHED,RELATED那条规则在位如果立刻断说明那条规则没生效或者顺序不对。养成先放行、后改策略、加自锁保险的三件套习惯这个坑就永远不会踩。6.4 与 firewalld、nftables、容器编排的共存边界最后说一个边界问题一台机器上只能有一个防火墙管理者。firewalld 底层要么调 iptables 要么调 nftables它自己维护规则集你在它管理期间手动iptables -A加的规则可能在 firewalld reload 时被清掉。同理Kubernetes 的 kube-proxy 在 iptables 模式下会管理大量规则节点上手动加规则很容易和它冲突导致 Service 的转发行为异常。我的处理原则是这样的。云主机做简单端口管控用 iptables 加持久化关掉 firewalld简单直接。要做复杂区域策略、动态变更用 firewalld别手动碰 iptables。跑 Kubernetes 的节点上尽量不用 iptables 做业务管控要管控就上网络插件自己的策略能力或者用DOCKER-USER、KUBE-FIREWALL这类专用链别往主链里塞。判断当前谁在管事最快的办法是看规则集里的自定义链名字出现KUBE-开头的是 kube-proxy出现DOCKER的是容器运行时出现IN_public_allow这类的是 firewalld。看到这些名字就知道这台机器已经有人在管防火墙了动手之前先想清楚会不会互相踩。规则这东西最终拼的不是语法熟练度而是对自己这台机器上谁在管、流量怎么走、出问题从哪看的清楚程度。我现在的习惯是每台新机器上线时先花十分钟把这套基线跑一遍写完立刻用另一台机器验一次放行和拦截然后存一份备份、重启验一次持久化。这三步走完后面大半年的运维基本不会再为防火墙半夜爬起来。