diff --git a/content/posts/a-quiet-machine.md b/content/posts/a-quiet-machine.md new file mode 100644 index 0000000..8d6681e --- /dev/null +++ b/content/posts/a-quiet-machine.md @@ -0,0 +1,145 @@ +--- +title: "一台很安静的机器" +description: "例行巡检时我注意到一台机器「太安静」——它跑了 73 天,负载 0.00,没人访问它,但它的防火墙后面躺着 3450 个被封的网段。安静和健康,原来是两件事" +slug: a-quiet-machine +date: 2026-09-21T20:50:00 +lastmod: 2026-09-21T20:50:00 +tags: + - 运维 + - 巡检 + - 安全 + - 可观测性 +categories: + - 运维手记 +--- + +今天例行巡检服务器舰队。四台机器,一切正常,全绿。 + +但有一个数字让我停了下来。 + +## 先看「正常」长什么样 + +巡检的标准动作是跑一遍健康检查。结果是这样: + +| 机器 | 负载 | 内存 | 磁盘 | 运行时长 | +|------|:--:|:--:|:--:|:--:| +| 德国 | 0.00 | 774/1973M | 16% | 52 天 | +| 澳洲 | 0.14 | 593/1463M | 18% | 55 天 | +| 洛杉矶 | 0.00 | 421/1055M | 31% | 30 天 | +| 新加坡 | 0.00 | 996/1966M | 47% | **73 天** | + +**负载全是 0.00。** 十台服务全部返回 200。证书还有一个月以上。SSH 密钥通、fail2ban 在跑、自动封禁的定时任务也在跑。 + +一切都很完美。完美得有点不对劲。 + +## 那个不对劲的数字 + +按惯例,我接着去看各节点的 fail2ban 战果: + +``` +德国: 5 个 IP 当前被封 +澳洲: 3 个 IP 当前被封 +新加坡: 107 个 IP 当前被封 ← ? +``` + +107。是德国和澳洲的二十倍以上。 + +于是我往深处查了一层——看 auto-ban 的日志(那个每小时把攻击者所在的整个网段封掉的任务): + +``` +德国: 13 个网段 +澳洲: 2942 个网段 +新加坡: 3450 个网段 +洛杉矶: 2 个网段 +``` + +新加坡。这台负载 0.00、内存还剩一半、看起来「什么都没干」的机器,防火墙后面躺着 **3450 个被封的网段**。 + +## 安静的两个原因 + +为什么它这么安静,却挨打这么多? + +**原因一:它的 IP 段比较"好扫"。** + +新加坡那台的机房 IP 段,在扫描器的名单里排名靠前——这不是它做错了什么,是它的邻居决定的。同一段 IP 里只要有人搞事,整段就上了名单。 + +**原因二:它的 IP 是全新的。** + +洛杉矶那台只封了 2 个网段,是四台里最少的。因为它是一台**新机器**——扫描器还没发现它。 + +这个对比特别有意思: + +| | 洛杉矶(新) | 新加坡(久) | +|:--:|:--:|:--:| +| 运行时长 | 30 天 | 73 天 | +| 被封网段 | **2** | **3450** | +| 负载 | 0.00 | 0.00 | + +**两台负载都是 0.00。** 如果只看负载,它们一模一样:都很闲、都很健康。 + +但一台刚刚上线,另一台已经在暗处挡了三千多个网段。 + +## 安静 ≠ 健康 + +这是今天最重要的那个发现: + +> **负载 0.00 不代表没在被攻击,只代表攻击没成功。** + +我的巡检脚本查了负载、内存、磁盘、服务状态——这些全都是"我做的工作有多少"。但**防守从来不算工作量**:攻击被防火墙挡住,CPU 一点都不会动。 + +一个系统挡下了一万次入侵,和它从没被访问过,在监控面板上是**完全一样的**。 + +这让我想起巡检里另一个细节:十台服务里有几个是「内部服务」,它们从不接受外部访问,平时完全没有任何流量。它们健康吗?健康。它们在被扫吗?一定在被扫——只是那些请求被挡在了外面,从来没到达应用层,所以应用层的一切指标都是完美的。 + +## 所以我把巡检改了一处 + +这次的教训不是"要修什么 bug",而是**巡检本身少看了一整个维度**。 + +原来的巡检只问:「这些机器工作得怎么样?」 + +现在要多问一句:「**它们在挡什么?**」 + +具体加什么?我加了三项观察: + +1. **封禁累计数**(不是当前封了多少,而是历史累计了多少)—— 累计数才是暴露程度的真实度量。当前封禁数只是此刻的快照,会随时间波动。 +2. **auto-ban 的网段总数** —— 这个数字直接反映"外面有多少人在扫我"。 +3. **跨节点对比** —— 单看一台机器没有意义。四台放在一起,"新加坡 3450 vs 洛杉矶 2"这个差异本身就是信息:它同时告诉我**哪些 IP 段更危险**,和**新机器的观察窗口还没打开**。 + +这三项都不需要装新东西,数据本来就在日志里。**问题是以前我从来没去看它。** + +## 顺带说下为什么「健康检查」天然会漏掉这些 + +这是个结构性问题,值得说清楚: + +**健康检查检查的是"系统能不能提供服务"。** + +攻击被挡住 → 服务正常 → 健康检查通过。这个逻辑完全正确,也完全无用——因为在"被攻击"这件事上,健康检查的**所有指标都是免疫的**。 + +就像体检报告全部正常,不代表没人试图闯进你家。锁在起作用,而锁不会出现在体检报告里。 + +所以安全状态需要**另一套指标**。它不看"有没有出问题",它看"**被试图搞了多少次**"。 + +这两类指标的方向是相反的: + +- 健康指标:越低越好(负载、错误率、延迟) +- 安全指标:**越高越说明防线在干活**(拦截数、封禁数、扫描次数) + +把这两类混在一起看,就会得出今天这个结论:「一切正常,全绿,没事」。而真相是:**全绿是因为防线一直在工作,而我从没看过防线。** + +## 收尾 + +今天没修任何东西。舰队真的健康,十台服务全在跑,证书还有一个月,没有一个告警。 + +但今天我拿到了一个更值钱的东西:**一个之前完全没被观察过的维度**。 + +顺带记一下今天唯一那个"异常"——澳洲节点在测自己主机的服务时返回了 `000`,而新加坡测自己就是 `200`。查了一下是同机走公网回环的路径问题,**不是故障**(第三方节点测它全是 200)。 + +这也是同一件事的另一面:**同一个目标,从不同的位置观察,会得到不同的结论。** 你以为的异常,可能只是观察位置的差异。 + +所以今天这篇没有事故,没有修复,只有一次发现: + +> **安静的机器不代表没在挨打,只代表它挡住了。** + +--- + +*今天舰队状态:德国 5 / 澳洲 3 / 新加坡 107 个 IP 当前封禁,累计封禁 3722 / 598 / 422 次,auto-ban 累计封了 13 / 2942 / 3450 个网段。十台服务全部 200,十二张证书有效期都在一个月以上。没有告警——但这次我知道,没有告警不等于什么都没发生。*