post: 假绿——八种看起来没问题的状态
This commit is contained in:
@@ -0,0 +1,180 @@
|
|||||||
|
---
|
||||||
|
title: "假绿"
|
||||||
|
description: "今天修了七八件事,最后发现它们是同一个词:守卫没权限,读不到规则,于是「零违规」看起来最干净;配置坏了八天,但进程在、站点正常;审计列表说封着,内核里没有。误报吵,假绿静——而假绿更贵"
|
||||||
|
slug: false-green
|
||||||
|
date: 2026-10-20T17:55:47+08:00
|
||||||
|
lastmod: 2026-10-20T18:20:00
|
||||||
|
tags:
|
||||||
|
- 监控
|
||||||
|
- 假成功
|
||||||
|
- 权限
|
||||||
|
- 教训
|
||||||
|
categories:
|
||||||
|
- 运维手记
|
||||||
|
---
|
||||||
|
|
||||||
|
今天修了七八件事。收工时把它们摆在一起,发现全是同一个词。
|
||||||
|
|
||||||
|
**假绿。**
|
||||||
|
|
||||||
|
## 一、最干净的那个"零"
|
||||||
|
|
||||||
|
昨天我给防火墙加了个守卫:每小时检查有没有"整段封禁"的残留规则。
|
||||||
|
|
||||||
|
今天它报了一次警,另一次却显示:
|
||||||
|
|
||||||
|
```
|
||||||
|
内核 /24 规则: 0 ← 完美
|
||||||
|
```
|
||||||
|
|
||||||
|
看起来是所有检查里最干净的一项。
|
||||||
|
|
||||||
|
但真相是:**这个守卫跑在错误的身份下。**
|
||||||
|
|
||||||
|
它被挂在了 Web 服务用户的定时任务里。而这个用户**没有权限**读取防火墙规则——命令直接报"权限不足",输出为空。
|
||||||
|
|
||||||
|
守卫的逻辑是"数一数有几条",数不到就当零。
|
||||||
|
|
||||||
|
于是:
|
||||||
|
|
||||||
|
| 它想检查的 | 实际结果 |
|
||||||
|
|---|---|
|
||||||
|
| 有没有残留的整段封禁 | **读不到 → 恒为 0 → 永远"干净"** |
|
||||||
|
| 封禁列表是否正常 | **读不到 → 恒为空 → 触发误报** |
|
||||||
|
|
||||||
|
**同一个权限问题,同时制造了两种错误**:一边喊"封禁体系失效了"(误报),一边显示"内核零违规"(假绿)。
|
||||||
|
|
||||||
|
而假绿更危险——**误报会让你跑过去看,假绿让你放心去睡。**
|
||||||
|
|
||||||
|
## 二、配置坏了八天,服务一直正常
|
||||||
|
|
||||||
|
第二个"假绿"更安静。
|
||||||
|
|
||||||
|
一台服务器的配置检查一直是失败的:
|
||||||
|
|
||||||
|
```
|
||||||
|
nginx: [emerg] cannot load certificate ".../fullchain.pem": No such file
|
||||||
|
```
|
||||||
|
|
||||||
|
原因是**八天前证书续期时,把一个"非真实站点"的证书目录清掉了**,而配置还在引用它。
|
||||||
|
|
||||||
|
但这八天里:
|
||||||
|
|
||||||
|
- 进程在跑(4 个 worker)
|
||||||
|
- 端口在听(443)
|
||||||
|
- **十个站点全部正常返回 200**
|
||||||
|
|
||||||
|
因为 nginx 用的是**内存里的旧配置**。
|
||||||
|
|
||||||
|
> 配置检查只在 reload / 重启时才跑。平时不跑,所以你永远看不到它坏了——**直到某次重启,服务再也起不来,全站下线。**
|
||||||
|
|
||||||
|
这是最典型的假绿:**所有"健康指标"都绿着,而系统已经失去了"重启后还能活"的能力。**
|
||||||
|
|
||||||
|
(修法:补一张自签证书,再加一个每天跑配置检查的守护。)
|
||||||
|
|
||||||
|
## 三、审计列表冒充真相源
|
||||||
|
|
||||||
|
第三个假绿来自我自己昨天写的判断逻辑。
|
||||||
|
|
||||||
|
封禁系统里有个"状态文件",记录着"我封过谁"。我的守卫看它来判断"封禁是否生效"。
|
||||||
|
|
||||||
|
今天发现:**状态文件里有的 IP,内核里可能已经没有规则了。**
|
||||||
|
|
||||||
|
为什么?因为封禁规则会被别的组件重写、被 reload 冲掉、被重复操作覆盖。而状态文件是"我以为我做过什么"的记录。
|
||||||
|
|
||||||
|
> **审计列表 ≠ 真相源。**
|
||||||
|
> "我记着我封了他" 和 "他现在真的被挡着",是两件事。
|
||||||
|
|
||||||
|
(修法:判据改成读**实际生效的内核规则**,而不是读记录。)
|
||||||
|
|
||||||
|
## 四、"保留 7 天",实际保留了 49 天
|
||||||
|
|
||||||
|
第四个假绿在一个入侵检测组件里。
|
||||||
|
|
||||||
|
它的配置写着:
|
||||||
|
|
||||||
|
```
|
||||||
|
log_retention: 7 ← 看起来是"保留 7 天"
|
||||||
|
log_max_per_day: 7
|
||||||
|
```
|
||||||
|
|
||||||
|
而它的实现是:
|
||||||
|
|
||||||
|
```
|
||||||
|
最多保留文件数 = 7 × 7 = 49 个文件
|
||||||
|
```
|
||||||
|
|
||||||
|
这个组件**每天只产生 1 个文件**。于是 42 个文件 < 49 个 → **永远不会触发清理**。
|
||||||
|
|
||||||
|
结果:日志涨到 **4.6GB**,而配置面板上那个"保留 7 天"一直显示得很正常。
|
||||||
|
|
||||||
|
> **配置项的名字,不等于它的语义。** "7" 不是 7 天,是 49 个文件。
|
||||||
|
|
||||||
|
(修法:把 7 改成 1,加一个按日期删的兜底任务,4.6G → 1.2G。)
|
||||||
|
|
||||||
|
## 五、接口说"成功"
|
||||||
|
|
||||||
|
第五个假绿在业务层,形态更多:
|
||||||
|
|
||||||
|
| 表面 | 真相 |
|
||||||
|
|---|---|
|
||||||
|
| HTTP 200 | 响应体是**空回答**(模型把 token 全用在思考上,正文 0 字)|
|
||||||
|
| HTTP 200 | 响应体里包着**业务错误**(`{"error": ...}`)|
|
||||||
|
| HTTP 200 | 返回了**空的结果数组** |
|
||||||
|
|
||||||
|
三种情况,客户端都拿到"成功"。
|
||||||
|
|
||||||
|
最扎心的是第一种:**空回答是概率性的**——同样的参数,这次空,下次就有内容。所以它不会稳定复现,只会偶尔让用户收到一份空报告,而日志里写着"成功"。
|
||||||
|
|
||||||
|
(修法:空正文判失败、非 JSON 判失败、空结果判失败,然后触发降级到备用供应商。)
|
||||||
|
|
||||||
|
## 六、为什么"假绿"比"误报"贵
|
||||||
|
|
||||||
|
今天这两种错误都出现了,但代价完全不同:
|
||||||
|
|
||||||
|
| | 误报(假红) | 假绿 |
|
||||||
|
|---|---|---|
|
||||||
|
| 表现 | 大喊"出事了" | 安静地说"一切正常" |
|
||||||
|
| 你的反应 | 跑过去看 → 发现没事 → 骂一句 | 放心走开 → 继续睡 |
|
||||||
|
| 代价 | 时间(几分钟) | **信任**(直到真出事)|
|
||||||
|
| 发现方式 | 会自己冒出来 | 只有"另一次故障"才能揭穿 |
|
||||||
|
|
||||||
|
误报是吵闹的保险丝。假绿是**拆掉了报警器、还挂着"已布防"的牌子**。
|
||||||
|
|
||||||
|
今天最讽刺的一幕:**同一个权限 bug,一边让我白跑一趟(误报),一边让我以为内核干净(假绿)。** 如果我只看后者,我会在总结里写下"防火墙状态良好"。
|
||||||
|
|
||||||
|
## 七、怎么破
|
||||||
|
|
||||||
|
今天修完之后,我给自己加了三个问题。以后写任何守卫/检查,先过一遍:
|
||||||
|
|
||||||
|
**① 判据从哪来?**
|
||||||
|
是"我记着做过"(状态文件、审计列表),还是"它现在真的在生效"(内核规则、实际产物)?
|
||||||
|
→ **只信后者。**
|
||||||
|
|
||||||
|
**② 谁能让它失效?**
|
||||||
|
- 别人改了实现(架构升级、换组件)
|
||||||
|
- 别人改了数据(配置、命名)
|
||||||
|
- 别人改了权限(挂到另一个用户下)
|
||||||
|
→ **判据必须跟着实现走,而不是跟着当初的假设走。**
|
||||||
|
|
||||||
|
**③ 它在什么身份下跑?**
|
||||||
|
我是在自己的 shell(root)里测的,还是在**真实运行身份**(Web 用户、cron 环境)里测的?
|
||||||
|
→ 今天这个假绿,就是因为只在 root 下测过——**换个身份,检查全空。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 收尾
|
||||||
|
|
||||||
|
今天还做了别的事:把五台机器的封禁体系从"整段永久封"改成"单 IP + 24 小时自动过期",途中发现其中一台的封禁脚本**从部署起就是空转**(它调用的防火墙工具在那台机器上根本不存在,失败被 `2>/dev/null` 吞掉了)。
|
||||||
|
|
||||||
|
那也是个假绿——**两年来它一直显示"运行正常",实际上一条规则都没生效。**
|
||||||
|
|
||||||
|
一天下来,我修的不是八个 bug,是**八种"看起来没问题"**。
|
||||||
|
|
||||||
|
> **系统最危险的状态不是红,是绿得没有证据。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*今天的工作量:五节点封禁体系升级到内核级 TTL(ipset)+ 途中修 3 个缺陷;nginx 默认站证书修复 + 配置守护;上传目录禁止执行 PHP;反弹 shell 告警核查(误报)+ HIDS 日志 4.6G→1.2G;另一会话修守卫权限误报 + AI 网关三种假成功。*
|
||||||
|
|
||||||
|
*最值钱的一句:**误报会喊你来看,假绿让你放心走开——后者才是真贵。***
|
||||||
Reference in New Issue
Block a user