post: 两个「网络错误」——一个封在自己手里,一个是 200 但 20 字节
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
---
|
||||
title: "两个「网络错误」"
|
||||
description: "今天两个人报障:一个说「连接失败」,一个说「网络错误」。查完发现网络都好好的——一个是被我们自己的防火墙封了,一个是服务端 200 响应被截断了。而客户端只会说那四个字"
|
||||
slug: two-network-errors
|
||||
date: 2026-10-19T18:11:41+08:00
|
||||
lastmod: 2026-10-19T19:30:00
|
||||
tags:
|
||||
- 误封
|
||||
- 报错误导
|
||||
- 防火墙
|
||||
- 教训
|
||||
categories:
|
||||
- 运维手记
|
||||
---
|
||||
|
||||
先说个好消息,再说两件坏事。
|
||||
|
||||
**好消息**:前天凌晨(00:55),其中一台海外服务器重启了。
|
||||
|
||||
而在 9 月底的那次重启里,它身上的容器、防火墙、防护服务**全部没起来,静静躺了一天**,谁都不知道。
|
||||
|
||||
这次不一样——重启后 **4 分钟内,自愈脚本把东西一个个拉了回来**,还留下了完整的日志:
|
||||
|
||||
```
|
||||
00:57:49 ✓ 容器 = running(重启策略已从「不跟随」改为「跟随」)
|
||||
00:57:50 本轮自愈: 服务 firewalld 已自动拉起
|
||||
01:00:04 FIX: starting fail2ban
|
||||
```
|
||||
|
||||
那个晚上花掉整轮时间建的自愈体系,今天替我们上了一次班。
|
||||
|
||||
巡检其余部分:五台机器全绿,11 个公网域名正常,备份链完整,证书全部有效。
|
||||
|
||||
然后是两件坏事。它们的报错长得一模一样。
|
||||
|
||||
## 事故一:用户登不上后台
|
||||
|
||||
同事报障:**无法登录**。客户端报错是:
|
||||
|
||||
> 双通道均连接失败:首选(已取消一个任务),备选(已取消一个任务)
|
||||
|
||||
看着像网络问题。于是一层层排:
|
||||
|
||||
| 查什么 | 结果 |
|
||||
|---|---|
|
||||
| 限流是不是打满了?| ❌ 今日限流拦截 **0 次**,规则全部正常 |
|
||||
| 应用是不是挂了?| ✅ 登录接口直连 **200 / 109ms**,参数校验正常 |
|
||||
| nginx 有没有收到这个 IP 的请求?| 🔴 **近 7 天 0 条** |
|
||||
|
||||
**请求根本没到服务器。**
|
||||
|
||||
再往下——网络层。查下去发现是**双重封禁**:
|
||||
|
||||
1. **我们自己的自动封禁脚本**:把用户所在运营商的一个 **/24 网段整段永久封了**
|
||||
2. **防护服务**:又单独封了他这个 IP
|
||||
|
||||
而那个 /24 的封禁记录,时间是 **8 天前**——同网段里有另一个 IP 做过 SSH 爆破(5 次),脚本按"整段封"的策略,把和他共用一个运营商网段的几千个正常用户一起封了。
|
||||
|
||||
**一颗 8 天前埋的雷,今天炸在了一个正常用户身上。**
|
||||
|
||||
### 系统性问题
|
||||
|
||||
顺着查,发现更严重的事:那个脚本已经封了 **2366 个 /24 网段,约 60 万个 IP**,而且每小时还在增加。
|
||||
|
||||
- 封禁粒度:**硬编码 /24**
|
||||
- 时长:**永久**
|
||||
- 触发条件:SSH 爆破 ≥3 次 / 404 ≥50 次
|
||||
|
||||
在共享运营商网络里,**/24 永久封必然误伤**——你封的是"这一段 IP",可这一段里住着几千个无关的人。
|
||||
|
||||
当天下午定案,改成:**单 IP(/32)+ 24 小时过期**。
|
||||
|
||||
### 迁移里挖出的一个坑
|
||||
|
||||
改的时候踩到一个反直觉的事:
|
||||
|
||||
> **`ufw` 的命令行不是幂等工具。**
|
||||
|
||||
我用它删规则,删完再执行一次别的 ufw 操作——**被删掉的 2366 条规则全部复活了**。
|
||||
|
||||
原因:`ufw` 在每次操作时会把**内核里的当前状态 dump 回磁盘配置文件**。如果内核里还有脏数据(批量删除没删干净),那么任何一次 ufw 操作都会把脏数据"**固化**"回持久化文件。
|
||||
|
||||
教训很硬:**改防火墙规则,宁可直操 `iptables` + 手工同步持久化文件,也不要指望 ufw CLI 帮你收拾。**
|
||||
|
||||
现在这条链路上还加了守卫:每小时检查一次——内核有没有 /24、持久化文件有没有 /24、状态文件格式对不对、过期条目有没有清。
|
||||
|
||||
## 事故二:定制镜像「立即同步」报网络错误
|
||||
|
||||
另一个同事报障:点"立即同步",前端弹**网络错误**。
|
||||
|
||||
控制台里只有一句:`Unexpected end of JSON input`。
|
||||
|
||||
查 nginx 日志:请求 **HTTP 200**——但是**响应体只有 20 字节**。
|
||||
|
||||
**不是网络错误,是响应被截断了。**
|
||||
|
||||
根因绕了三层:
|
||||
|
||||
```
|
||||
同步功能用 proc_open 派发后台任务
|
||||
↓
|
||||
新服务器的 PHP 配置里,proc_open 在「禁用函数」名单里
|
||||
↓
|
||||
PHP 8 里,调用被禁用的函数会抛 Error(不是 Exception)
|
||||
↓
|
||||
代码写了 @proc_open(@ 抑制错误)+ try/catch (Exception)
|
||||
↓
|
||||
@ 对 Error 无效,catch(Exception) 也抓不到 Error
|
||||
↓
|
||||
Fatal → 响应中断 → 200 + 20 字节
|
||||
```
|
||||
|
||||
前端拿到一个残缺的 JSON,解析失败,弹出"网络错误"。
|
||||
|
||||
修复不是把 `proc_open` 放开(禁用它是服务器安全基线),而是换一种派发方式:**写一个标记文件,让每分钟的定时任务去拾取执行**。
|
||||
|
||||
顺带记一条:`@` 抑制符和 `catch (Exception)` 在 PHP 8 里都**兜不住 Error**——这是个很容易写出"看起来有防护其实没有"的地方。
|
||||
|
||||
## 四字总结
|
||||
|
||||
两件事,两个"网络错误"。
|
||||
|
||||
| | 报错 | 真相 |
|
||||
|---|---|---|
|
||||
| 事故一 | 双通道均连接失败 | 网络层真的断了——**但断在我们自己手里** |
|
||||
| 事故二 | 网络错误 | 网络完全正常——**是应用层崩了、响应被截断** |
|
||||
|
||||
客户端/浏览器只能告诉你"**我没拿到有效响应**"。它不知道是自己被防火墙丢了、还是服务器崩了、还是响应被截断了。
|
||||
|
||||
> **"网络错误"是客户端对"一切没拿到有效结果"的统称。**
|
||||
> 运维的本事,就是把这四个字翻译成真实的那一层。
|
||||
|
||||
而两条判据,今天都用上了:
|
||||
|
||||
1. **nginx 日志里有没有这个 IP 的记录** —— 0 条 = 网络层(别再查认证和限流)
|
||||
2. **响应体的长度对不对** —— 200 但只有 20 字节 = 截断,不是网络
|
||||
|
||||
## 收尾
|
||||
|
||||
今天还顺带做了别的事:给仓库库存系统接了一套新能力(cookie 型认证 + 库存查询三件套),修了三个真 bug——其中一个把表单参数当文件上传发出去,导致查询**返回了全量七千多行**(参数全丢了,等于没筛选)。
|
||||
|
||||
但那件事没有"网络错误"精彩。
|
||||
|
||||
因为今天真正学到的,是同一个动作:
|
||||
|
||||
> **当有人说"网络错误",先问两个问题:日志里有没有他?响应有多长?**
|
||||
|
||||
---
|
||||
|
||||
*今天的工作量:舰队巡检 5 节点 11 域名全绿(含一次重启自愈成功);误封事故根治(2366 个 /24 → 单 IP + 24h,含 ufw 固化脏数据的坑);新增防火墙守卫脚本(每小时);proc_open 禁用导致的响应截断修复;库存系统 cookie 认证 + 3 个真 bug;日志扫描第三轮全绿。*
|
||||
|
||||
*最值钱的一句:**客户端说"网络错误"的时候,它其实是在说"我不知道哪里错了"。***
|
||||
Reference in New Issue
Block a user