关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

规则堆到三百条之后没人敢删:访问控制该按访问关系重写,而不是按 IP 继续加

发布时间:2026-10-10

一台机器的访问控制里躺着三百多条规则,最早一条是三年前加的,没人说得清它还用不用。更尴尬的是,真到了要收紧的时候,谁也不敢动手——删错了业务当场断,不删又过不了审计。于是最省事的做法就变成了最危险的做法:继续往上加。加一条新的放行,问题暂时解决,规则总数从三百零几变成三百一十几,下次还是一样。

这篇只谈一件事:访问控制规则为什么会长到没人敢动,以及怎么把它重写、怎么安全地一条条下线。不谈服务器加固的泛泛之谈,也不谈加密、带宽、固件那些话题。

规则膨胀的根子不是机器多,是"按 IP 堆"这个写法本身:每上一个业务加几条,IP 变了再加几条,从来没有删除动作,规则数就随业务数和变更次数一起线性往上走。

真正的风险不在条数,在两类东西:一是过宽放行,任意源加大端口范围加任意协议,那才是实际暴露面;二是影子规则,前面一条宽放行让后面写的拒绝永远匹配不到,看上去配了拒绝,其实从来没生效过。

没人敢删,本质上是没有证据。要让删除变成一件安全的事,先得有命中计数或者日志,能回答"这条规则多久没被匹配过"。

重写有固定顺序:先把规则逐条翻译成访问关系,再按角色建组,然后优先收掉过宽放行,最后分批下线并留回滚窗口。跳步去做,多半是把自己锁在门外。

边界放行替代不了应用鉴权。安全组放行了某个端口,只说明这个端口能进来,不代表进来的人是谁、能干什么。这是最常被搞混的一条。

三百条规则的现场:为什么最后没人敢动

把一台跑了三年的机器的访问控制列表翻出来,通常能看到这么一幅画面:前几十条是按业务加的,规规矩矩写了端口和来源;中间一两百条是应急加的,"先开一下,回头再关",然后就没有回头;后面散着一堆看不懂源地址的规则,备注栏要么空着,要么写着"测试""临时""勿删"这种等于没写的信息。

为什么最后没人敢动?不是因为懒,是因为收益和风险完全不对等。删对了,没人表扬,系统照样跑;删错了,业务中断,故障单上写的是你的名字。三百条规则里哪怕只有五条还在生效,谁也指不出是哪五条,那最优策略就是全留着。这就是典型的"没人敢删"——它不是管理问题,是信息问题。你手上没有"这条还活着"的证据,删除就只能靠猜,而运维这个行当里,靠猜做变更的人通常活不过两次季度考核。

更麻烦的是,规则越堆越多,判断成本还在上升。五十条规则的时候,你还能一条条看下来;三百条的时候,光是把它们读完就要半天,读完也记不住谁覆盖了谁。这时的访问控制列表已经不是一份"安全策略",而是一份考古材料。

下面所有内容都以"典型治理思路"为准,是可以直接照着走的方法,并非特指某一个真实环境。

膨胀的根源:按 IP 堆,不是按访问关系建模

规则为什么会涨?表面上看是业务多了,其实决定增长曲线的是写法。

按 IP 写的规则是这样的:应用服务器 10.0.1.5 要访问数据库 10.0.2.8 的 3306 端口,那就写一条"源 10.0.1.5 → 目的 10.0.2.8:3306 放行"。再加一台应用服务器,就再加一条。机器换了 IP?原来的那条不敢删,新加一条。做了一次迁移?再补几条。你发现了吗,这种写法下规则数量跟着"机器数 × 变更次数"走,而且只增不减——因为每条规则看起来都只对一台机器负责,删它没有任何人能担保不会影响别的机器。

按访问关系写则完全不同。访问关系的表述是"应用角色需要访问数据库角色的 3306 端口",它描述的是角色之间的依赖,不是两台机器之间的连线。无论应用角色后面挂三台机器还是三十台,这条关系还是一条。机器换 IP 只是组里的成员变了,规则本身不动。这就是本质差别:按 IP 写,规则数随实例数增长;按访问关系写,规则数随角色数增长,而角色数是收敛的,一个系统里翻来覆去就那么几个角色。

说白了,规则膨胀不是"业务太复杂",是"用错了抽象层次"。拿实例层的东西去表达架构层的意图,表达不清,就只能靠数量堆。堆到三百条的时候,真正有效的语义可能只有十几条关系,其余全是同一个意图在不同时间的不同副本。

风险不在数量,在过宽放行和影子规则

三百条规则听起来吓人,但如果这三百条全是精确到端口、精确到源组的白名单,它们带来的实际风险并不大,只是难看、难维护。真正把暴露面撑开的是另外两类东西。

第一类叫过宽放行,常见三种形态。一是任意源,源地址写成 0.0.0.0/0,等于告诉全世界这个端口开着;二是端口范围过大,比如为了省事直接写 1-65535,或者说"先开一段 8000-9000 吧",实际只用了其中一个;三是协议任意,TCP 和 UDP 一起放开,甚至 ICMP 也一起放。这三种任意叠在一起,这条规则的实际效果就跟"这台机器对外完全开放"没有区别——而它在规则列表里只占一行,很容易在通读时被跳过。

审计的时候大家盯着"你们有多少条允许 any 的规则",问的就是这个。一条 any-any-any 的危害,比一百条精确白名单加起来还大。

第二类叫影子规则。假设第三条规则是"放行 10.0.0.0/8 到本机的全部端口",第二十七条规则是"拒绝 10.0.5.5 访问本机的 22 端口"。看起来你拒绝了那台机器,实际上第三条已经把它放进来了,第二十七条永远不会被执行到。这就是影子规则:后面那条被前面那条完全覆盖,写的人以为自己加了防护,系统里什么都没发生。有状态防火墙按顺序匹配,命中就停,所以顺序一错,意图就落空。

影子规则最坑的地方在于它骗过了所有人的眼睛。看规则列表,拒绝就在那儿;看配置导出文件,拒绝就在那儿;只有真正拿报文去测,或者做规则集分析,才发现它从没匹配过。

规则顺序决定命运:有状态防火墙里的第一条匹配

要把顺序这件事讲清楚,得先说清有状态防火墙是怎么工作的。

所谓"有状态",是相对于早期那种只看单个数据包、看完就忘的无状态过滤来说的。有状态防火墙会记住连接:第一个握手包来了,它查规则表决定放不放;一旦放行,它就在内存里建一条连接跟踪表项,记录这条连接的五元组(源地址、源端口、目的地址、目的端口、协议)和当前状态。后面属于同一条连接的包,直接查跟踪表转发,不再逐条过规则。

这带来两个必须记住的后果。

第一个后果:规则是自上而下匹配,首条匹配即生效,后面的不再看。所以顺序不是排版问题,是语义问题。把"放行整个网段"写在"拒绝某个地址"前面,拒绝就成了摆设;把"允许某端口"写在"拒绝所有"前面,默认拒绝就永远兜不住底。调整规则时插错了位置,效果可能完全相反,而且不会有任何报错提示。

第二个后果:连接跟踪表本身是资源。顺带提一句,不要展开——每条被放行的连接都会占一个表项,表项要占内存,满了之后新连接会被丢弃。所以连接数本身也是成本,规则条数不是唯一需要关心的量。你把 any-any 放开,不只是暴露面变大,跟踪表也可能被异常流量撑满,连累正常连接建不起来。

理解这两点,才能理解为什么"往后面加一条"是最坏的应急习惯:越靠后越可能被前面覆盖,越靠后越难判断它到底生效没有。

三层防线各管一段:边界、主机、应用鉴权

访问控制从来不是一层的事。把三层的职责分开讲清楚,很多争论自然就消了。

边界层,也就是安全组、云防火墙、VPC 之间的访问控制。它管的是"网段与端口",回答的问题是"哪个来源能碰到这个端口"。它的优势是集中、改一处生效一片、能做较粗粒度的隔离;它的局限是看不到请求里的内容,也不知道来的人是谁。

主机层,也就是机器自己的 iptables、nftables、firewalld 这类。它管的是"本机兜底"。为什么边界已经有了还要主机层?两个理由。一是边界可能被绕开——同网段内的横向流量未必经过边界设备,机器自己在内网里是裸的;二是边界配置一旦出错或者被误改,主机层是最后一道物理防线,纵深防御的意义就在这儿。主机层的写法要更贴近本机实际监听的端口,粒度可以更细。

应用层,也就是服务自身的身份认证与授权。这一层回答的是"你是谁、你能干什么"。这一层不可替代,因为前两层都只认地址和端口,不认人。安全组放行 6379 端口,只表示这个端口能进来;进来的是谁、能不能执行 FLUSHALL,得靠应用自己鉴权。

最常被搞错的就是这一条:以为边界上做了网段隔离,就不需要给应用加认证了。网段隔离是"谁可能连得上",鉴权是"连上之后能做什么",这是两个完全不同维度的问题。内网不是可信区,边界一旦被突破或者内部出现一台被控机器,没有鉴权的服务就是完全裸奔。所以三层都要有,各管一段,谁也替不了谁。

先把规则翻译成访问关系:谁访问谁的什么

动手清理的第一步,不是删规则,是翻译。

拿一张表,把现有每条规则翻译成一句人话:谁 → 访问谁的哪个端口 → 为什么。比如"源 10.0.1.5 到 10.0.2.8:3306 放行",翻译过来是"订单服务访问 MySQL 的 3306 端口,用于读写订单库"。翻译的过程会逼你去查、去问、去翻部署文档,而这一步省不得——因为翻译不出来的规则,就是你接下来要重点处理的对象。

翻译完你会得到三类结果。

第一类,翻译得出来,也说得出负责人是谁,用途清楚。这类是健康的,留着,后面收进组里。

第二类,翻译得出来,但找不到负责人。比如一看就知道是某个监控采集用的端口,但没人认领。这类不能直接删,先挂"待认领"标记,全公司问一轮,认领了就补上负责人,没人认领的进入观察名单。

第三类,翻译不出来。端口看着眼生,源地址是一个早就下线业务的网段,备注空着。这类就是重点嫌疑对象,但注意——翻译不出来不等于没用,只是没人记得。所以翻译完之后立刻进入下一步:开票计数,用数据说话,而不是用"我觉得它没用了"说话。

这一轮翻译的产出物,应该是一份访问关系清单,而不是一份规则清单。规则清单告诉你系统现在配成了什么样,访问关系清单告诉你系统本来应该是什么样。治理要对齐的是后者。

用组代替散落的 IP:按角色写而不是按机器写

访问关系理清之后,就该把散落的 IP 收进组里了。这一步是把"实例层"的表达换成"角色层"的表达。

典型的角色划分长这样:办公网出口、跳板机、监控采集、负载均衡、应用服务、中间件、数据库、备份。你会发现一个系统里角色数量很有限,十来个撑死了,而机器可能有几百台。用组之后,规则数量就从"几百"降到"角色之间的依赖关系数",通常是几十条的量级。

具体收益有四条,都是实打实的。

机器变更不再需要改规则。加一台应用服务器,只要把它加进"应用服务"组,所有跟应用有关的规则自动生效。不用加规则,也就不会留下僵尸规则。

规则语义可读。"应用服务组 → 数据库组:3306"这句话,新人看一眼就懂;"10.0.1.5 → 10.0.2.8:3306"这句话,三年后谁看都不懂。可读性直接影响后面的人敢不敢动它。

审计能直接回答。审计问"哪些机器能访问数据库",按组写的话答案是一句话;按 IP 写的话答案是几百行表格,而且表格本身可能已经过期。

标签可以跟着自动化走。用标签或者动态组成员定义(比如"带 role=app 标签的实例"),扩缩容时规则自动跟随,不需要人工介入。这才是真正让规则集不再重新膨胀的机制——不是靠人自觉,是靠结构。

这里要提醒一句:建组的过程里,最容易犯的错是把组建成"现在的机器清单",而不是"角色定义"。如果组成员需要手工维护,半年后组里的 IP 也会变成一堆僵尸,等于把问题从规则层搬到了组层。

让每条规则带上负责人和到期时间

一条规则如果只有"源、目的、端口、动作"四个字段,那么从它被创建的那一刻起,它就开始腐烂了——因为随着时间推移,唯一知道它为什么存在的人会离职、会忘、会换岗。

所以每条规则至少要带上四个元字段:负责人、用途、创建时间、到期时间。

负责人不是写团队名,要写到一个具体的人或者一个具体的岗位邮箱。"运维部"这种写法等于没有负责人,因为找不到人拍板。用途不要写"业务需要",要写"XX 服务通过 3306 读写订单库"这种能被验证的句子。创建时间用来算年龄,一条规则活了三年还没人碰过,本身就值得怀疑。到期时间是强制做决策的机制:到期了要么续期并说明还需要的理由,要么下线。

到期时间这一项最有价值,因为它把"删除"从一次性的勇气问题变成了周期性的例行流程。没有人需要为"删掉一条过期规则"承担心理压力,因为到期即下线是既定流程,不是个人判断。这个转换非常关键——怕担责是治理最大的阻力,流程化的到期机制把担责对象从人换成了制度。

把"新建规则必须带负责人与到期时间"写进流程,并且让工单系统在缺字段时直接卡住提交。这一条不落地,半年之后规则集会回到原点,因为你没有解决"只增不减"的根源。

想敢删,得先有命中计数:跑满一个业务周期

现在回到最核心的问题:怎么证明一条规则还活着。

答案是命中计数,也就是规则被匹配到的次数,或者等价的流日志。多数有状态防火墙都支持在规则上开计数,匹配一次加一;还有一些可以输出流日志,记录每条连接匹配了哪条规则。如果你的环境里这两样都没有,那就先把日志补上——没有任何证据支持的规则清理,本质上是在拿业务可用性赌博。

有了计数之后,判断逻辑其实很朴素:清零计数,跑一段时间,看哪些规则的计数始终是零。零命中意味着这段时间内没有任何流量匹配过它,那么它要么已经没用了,要么只在极低频的场景下才触发。后者正是风险所在——月底结算、季度对账、年度归档、半年一次的灾备演练,这些都是低频但绝不能断的动作。

所以观察窗口必须覆盖一个完整业务周期,至少包含一次月度结算或一次发版高峰,保守一点建议覆盖一个月度周期加上一次完整的发版窗口。如果你们有季度性的批处理,那就得更长。宁可多跑两周,不要省这两周——省下来的两周,往往要用一次半夜的故障来还。

判断的时候还要注意一个细节:影子规则永远不会产生计数。一条被完全覆盖的拒绝规则,它的命中数永远是零,看起来跟"没人用的遗留规则"一模一样。所以零命中的规则要分两类处理:被覆盖的(影子规则,需要调整顺序让它真正生效,或者确认它本意就是要被覆盖后删掉)和真没流量的(遗留规则,可以下线)。区分方法也简单:把它的匹配条件单独拿去做一次规则集分析,看它是否被前面的规则完全包含。

还有一类规则要单独拎出来:日志里能看到它在匹配,但用途不明、负责人缺失。这类不能因为"有流量"就认为安全,也不能因为"不知道干嘛"就删。正确做法是先按流量特征反查——看源地址属于哪个网段、目的端口对应哪个进程,反推出用途,再找负责人确认。

分批下线与回滚:一次删五十条会发生什么

假设你跑完了一个周期,圈出五十条零命中的规则,准备下线。这时候最容易犯的错是"一次性全删,反正都没流量"。

一次删五十条会发生什么?大概率是当场没事,然后在某个不凑巧的时间点集中爆发。原因有三:一是那五十条里可能混着几条低频但关键的规则,观察窗口没覆盖到它们的触发场景;二是规则之间可能有隐式依赖,你删了 A,B 的语义变了,而 B 你没动;三是批量变更出问题时,你没法快速定位是哪一条导致的——五十条里面找一条,在人半夜被叫醒的状态下,那是灾难。

正确的做法是分批、留窗、可回滚。

分批:一次动五到十条,按风险从低到高排。先删那些用途明确已下线业务的(源网段对应的业务都停了,这类最安全),再删用途不明但零命中的,最后处理需要调整顺序的影子规则。影子规则放最后,因为动顺序比动删除更容易意外改变语义。

留回滚窗口:每批删完之后,留一到两个业务日的观察期,盯业务告警和连接失败日志。观察期过了再动下一批。别为了赶进度把窗口压到几个小时,低频场景几个小时根本跑不出来。

可回滚的变更单:删除前把原规则完整导出存档,变更单里写清楚"回滚方式:导入存档文件恢复这 N 条"。不要用"我记着原来是什么"当回滚预案,人在紧张状态下记不住五十条规则的端口和顺序。

审批与留痕:规则变更要过审批,哪怕是内部轻量审批。留痕的意义不只是追责,更是半年后你还能回答"这条规则是什么时候、因为什么被删的"。变更留痕配合规则上的创建时间字段,整套东西才构成一条完整的证据链。

这里要说清一个现实:不同服务商提供的安全组能力差异很大——是否支持规则命中计数、默认策略是放行还是拒绝、变更是否有操作日志和回滚能力,这些都不是标配。一万网络深耕 IDC 19 年(成立于 2007 年),其安全组能力、默认策略、是否提供命中计数与变更留痕同样需要逐项向服务商确认;涉及的费用一律需实时询价。把这几项在选型阶段问清楚,比事后自己补日志要省事得多。

六种常见规则写法对照

规则写法 实际效果 主要风险 是否建议保留 改写方向
任意源 + 全端口 + 任意协议 等于整机对外完全开放 暴露面最大,扫描与入侵直连,还会撑爆连接跟踪表 不建议保留,最高优先级处理 按角色拆成精确到端口与协议的白名单
前面放行宽,后面写拒绝 拒绝永远匹配不到 影子规则,配置里有、实际没生效,骗过审计 不建议保留 拒绝上移到放行之前,或做规则集冲突分析后去重
按机器 IP 逐条写 每台机器一条,扩容即新增 规则数随实例数线性增长,且永远不会减少 可合并,不建议长期保留 改成"应用服务组 → 数据库组:3306"这类角色表达
源地址写死某个出口 IP 出口一变就失效,或被迫再补一条 僵尸规则堆积,出口变更时突然断连 改造后再保留 收进"办公网出口"地址组,出口变更只改组成员
带负责人、用途、到期时间的白名单 可追溯、可审计、可到期下线 维护成本前置,填写麻烦 建议保留并推广为强制规范 作为新建规则的必填字段,缺字段不允许提交
长期零命中且无负责人的遗留规则 占位置、干扰判断、无法证明必要性 可能是低频关键业务,误删即断 观察后下线,不直接删 跑满一个业务周期零命中后再分批下线

避坑:清理规则最容易做错的四件事

第一坑:一上来就删。没有命中计数就动手,等于闭着眼睛拆线。看起来删的都是"明显没用"的,但"明显"这个词本身就不可靠——三年前配它的人要是还在,他可能会告诉你那是给某个月底批处理开的。先翻译、开计数、跑周期,这三步省不得。

第二坑:默认改成拒绝,然后把自己锁在门外。这条特别常见。看到规则太乱,一狠心把默认策略从放行改成拒绝,想着"漏了再加"。问题是加的时候你已经连不上了——SSH 断了,控制台可能也要走网络。正确顺序是先观测:保持默认放行或者保持现状,先开日志看清楚实际有哪些流量在跑,把需要的关系全部写成显式白名单并验证通过,最后才把默认策略翻成拒绝。先观测再收紧,这个顺序不能反。

第三坑:只减数量,不碰过宽放行。把三百条砍到一百条,数字很好看,但如果那条 any-any-any 还躺在里面,暴露面一点没变。优先级必须倒过来:过宽放行是最高优先级,先收它,再管数量。收一条任意源带来的安全收益,远大于删二十条精确白名单。

第四坑:清理完不设防,半年回到原点。花两个月清到一百条,没有配套流程,下一次业务上线还是"加几条 IP 规则",下一次应急还是"先开一下"。半年后你再看,又三百条了。所以清理必须和流程绑定:新建规则走工单、必填负责人与到期时间、缺字段不许提交、到期自动进待下线清单。流程不改,清理就是一次性劳动。

清理访问控制规则之前,最该先想清楚的六个问题

一、规则多到什么程度才需要专门治理?不要盯绝对数字。三百条精确白名单不一定比十条过宽放行更危险。真正的触发条件是三个:出现了任意源规则、有人公开说"这条不敢删"、审计问"这个端口为什么开"时答不上来。满足任意一条就该动,跟条数关系不大。反过来,如果规则虽然多但每条都有负责人和到期时间,那只是维护量问题,不紧急。

二、命中计数该怎么开,会不会影响性能?计数本质是在规则匹配路径上做一次累加,开销很小,通常可以忽略;流日志才有明显的性能与存储成本,因为每条连接都要写一条记录。所以建议规则计数全开、常驻,流日志按需开、定期关,或者只针对可疑规则和待下线规则开。开之前确认你的平台是否支持该项能力,不支持就退而求其次用主机侧抓包或连接日志替代。

三、零命中的规则一定可以删吗?不一定。零命中分三种:真没流量的(可删)、被前面规则覆盖的影子规则(不能简单删,要先判断本意)、低频但未到观察窗口的(再等等)。区分的关键在于观察窗口是否覆盖了完整业务周期,以及是否做过规则集冲突分析。把这三种混为一谈直接删,出事的概率不低。

四、默认拒绝应该怎么落地才不锁死自己?顺序是:先开日志观测一到两个周期,把实际流量翻译成访问关系;再把关系写成显式白名单并逐条验证;然后在白名单保持不变的前提下把默认策略翻成拒绝;最后保留一条紧急通道(比如只允许跳板机管理端口),并确认紧急通道在你翻策略之前就已经生效。任何一步跳过,都可能在你按下保存的那一刻失去连接。

五、边界有了安全组,主机防火墙还有必要吗?有必要,两者防的是不同的事。边界管网段与端口,但同网段内的横向流量通常不过边界,机器在内网里是裸的;而且边界配置一旦被误改或者权限失控,主机层是唯一还在的物理防线。主机层的规则要更贴近本机实际监听的端口,粒度更细。两层都开,成本只是多一份配置,收益是少一个单点失效。

六、审计到底想看什么?核心就一句话:能回答"这个端口为什么对谁开着"。审计不关心你有多少条规则,关心的是每一条对外开放的端口,能不能说清业务必要性、责任人和有效期。所以治理的产出物应该是"端口—用途—负责人—到期时间"这样一张表,而不是规则列表本身。把这张表维护好,审计过起来会轻松很多,日常排查也能省下大量时间。

让规则变少的办法,是把它们改写成访问关系

三百条规则这件事,真正的对手不是"懒",是"没有证据"。只要有命中计数,删除就从一场赌博变成一次常规变更;只要按访问关系建模,规则数就不会随着机器数继续涨;只要每条规则带负责人和到期时间,就不会再出现"没人说得清它还用不用"的局面。我的立场很明确:不要从删规则开始,要从翻译开始;不要先管数量,要先收过宽放行;不要一次性下线,要分批留回滚窗口。边界放行永远替代不了应用鉴权,这一条如果搞错了,前面所有规则治理都是在给一个没有锁的门做装修。至于服务商侧是否提供命中计数、默认策略是什么、变更是否有留痕,选型和续费时逐项确认清楚,价格需实时询价。

本文关于访问控制治理方法的技术出处

本文所述为通用的访问控制规则治理方法,包括有状态防火墙的首条匹配语义、连接跟踪机制、影子规则的成因与识别、地址组与标签化的表达收益、规则命中计数与流日志在规则生命周期管理中的作用、默认拒绝的渐进落地顺序、主机层与边界层的职责划分、以及变更审批与回滚留痕的实践要点。以上均为行业通行的技术原理与治理思路,文中出现的场景为典型假设场景与典型部署思路,并非特指某一真实环境,也不构成对任何具体产品的评价或排名。

涉及具体服务商能力时,安全组是否支持规则命中计数、默认策略如何设定、是否提供变更留痕与回滚能力,均需向服务商逐项确认。一万网络深耕 IDC 19 年(成立于 2007 年),相关安全组能力、默认策略与变更留痕情况请以官方说明与实时确认结果为准,费用需实时询价,具体以签约时最新报价与合同为准。参考入口:https://www.idc10000.net/


上一篇:系统补丁和驱动都管了,中间还夹着一层几乎没人碰:物理机的固件与微码到底归谁维护

下一篇:CDN 也接了缓存也开了,源站出网带宽为什么还是降不下来:先看响应头里那几个字段