Article
一次惊心动魄的服务器安全危机:从怀疑被黑到发现环境变量错误的排查之旅
记录一次服务器突然卡顿、SSH连接失效的惊险经历。从怀疑被黑客攻击,到排查发现22端口全开放的安全隐患,再到最终发现是环境变量配置错误导致的容器无限重启循环。一次完整的安全意识和故障排查学习过程。
一次云服务器“被黑”惊魂:从 SSH 失联到 Zod 环境变量报错:
服务器突然"失联"
那是一个平凡的下午,我正在愉快地vibe coding,突然发现部署在云服务器上的应用响应变得异常缓慢。起初以为是网络波动,没太在意。但几分钟后,当我尝试通过 SSH 连接到服务器时,终端无情地显示:
ssh: connect to host xxx.xxx.xxx.xxx port 22: Connection timed out
心跳瞬间加速——服务器失联了!
第一反应:我被黑了吗?
我的第一反应是:"完了,服务器被黑了!"
脑海中闪过各种可能性:
- 是不是有黑客通过漏洞入侵了?
- 是不是被挖矿了?
- 是不是被 DDoS 攻击了?
我立即登录云服务商的控制台,通过 VNC 连接到服务器。还好,控制台还能用,但响应速度慢得让人抓狂。
与 GPT 的问答:发现第一个安全隐患
在焦虑中,我开始向 GPT 求助:"服务器突然卡顿,SSH 连接不上,可能是什么原因?"
GPT 给出了几个建议:
- 检查系统负载:
top或htop - 检查网络连接:
netstat -tulpn - 检查安全组/防火墙设置
我按照建议检查了安全组配置,结果发现了一个让我冷汗直流的错误:我对所有 IP 开放了 22 端口!
# 错误的安全组规则
Port: 22
Protocol: TCP
Source: 0.0.0.0/0 # 对所有人开放!
这是一个极其危险的安全隐患!任何知道服务器 IP 的人都可以尝试暴力破解 SSH 密码。
紧急修复:修改密码 + 收紧安全组
我立即采取行动:
- 修改 root 密码:使用云控制台的密码重置功能
- 收紧安全组:只允许我的办公 IP 和家庭 IP 访问 22 端口
- 启用密钥认证:禁用密码登录,只允许 SSH 密钥登录
# 正确的安全组规则
Port: 22
Protocol: TCP
Source: 123.123.123.123/32 # 只允许特定 IP
修复完成后,我满怀期待地重新尝试 SSH 连接...
失望:问题依旧
然而,现实给了我一记重击——问题并没有解决!服务器依然卡顿,SSH 连接依然时好时坏。
但我观察到一个重要现象:每次重启服务器后,SSH 可以正常连接一段时间,然后逐渐变卡,最终完全失联。
深入排查:发现真正的元凶
既然不是外部攻击,那问题一定在服务器内部。我使用 top -o %CPU 命令查看系统资源使用情况:
top -o %CPU
结果让我大吃一惊:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 mysql 20 0 3.2g 1.8g 12345 S 45.6 23.4 10:23.45 mysqld
5678 lobster 20 0 2.1g 1.2g 67890 S 38.9 15.6 08:12.34 lobster
龙虾(openclaw)和 MySQL 占用了大量的内存!
果断行动:删除可疑进程
看到 "lobster" 这个陌生的进程名,我第一反应是:"这肯定是恶意软件!" 果断执行:
# 找到 lobster 进程
ps aux | grep lobster
# 杀死进程
kill -9 5678
# 删除相关文件(如果存在)
find / -name "*lobster*" -type f -delete
删除后,服务器确实稍微流畅了一些,但问题依然存在,只是从"完全卡死"变成了"极度缓慢"。
关键发现:Docker Compose 的循环重启
在继续排查时,我突然意识到一个规律:每次我启动 docker-compose up 后,服务器就会开始变卡。
于是我开始逐个排查 Docker 容器:
# 查看容器状态
docker ps -a
# 查看容器日志
docker logs [container_id]
当我检查后端容器时,发现了惊人的真相:
docker logs backend-container
# 输出显示:
Starting backend service...
Error: Invalid environment variables
at validateEnv (file:///app/src/env.ts:45:11)
at file:///app/src/index.ts:12:1
Container exited with code 1
Restarting container...
Starting backend service...
Error: Invalid environment variables
at validateEnv (file:///app/src/env.ts:45:11)
at file:///app/src/index.ts:12:1
Container exited with code 1
Restarting container...
容器在无限重启循环中!
真相大白:一个环境变量的疏忽
进一步查看日志,发现了具体的错误信息:
# 更详细的错误信息
ZodError: [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["DATABASE_URL"],
"message": "Required"
}
]
原来,我在 .env 文件中新增了一个 DATABASE_URL 字段,但忘记同步到服务端的 .env 文件了!
问题链条:完整的因果循环
让我来梳理一下整个问题的发生链条:
- 环境变量缺失:服务端缺少
DATABASE_URL环境变量 - Zod 验证失败:启动时 Zod 检查环境变量,发现缺失字段
- 容器启动失败:服务启动失败,容器退出
- Docker 重启策略:Docker 配置了
restart: always,容器不断重启 - 数据库同步循环:每次重启都会执行 Drizzle 的
push操作,同步数据库结构 - 资源耗尽:MySQL 不断执行表结构修改,占用大量资源
- 系统卡顿:系统资源被耗尽,SSH 连接超时
解决方案:一步修复所有问题
修复方法其实很简单:
# 1. 在服务端 .env 文件中添加缺失的环境变量
echo "DATABASE_URL=postgresql://user:password@localhost:5432/dbname" >> .env
# 2. 停止所有容器
docker-compose down
# 3. 重新启动
docker-compose up -d
# 4. 验证服务状态
docker ps
docker logs backend-container
经验教训与安全建议
这次经历让我学到了很多宝贵的经验:
安全方面:
- 永远不要对所有人开放 22 端口:使用 IP 白名单或 VPN 访问
- 使用 SSH 密钥认证:禁用密码登录
- 定期检查安全组规则:确保没有不必要的开放端口
- 监控异常进程:定期检查
top和ps aux输出
运维方面:
- 环境变量管理:使用版本控制或配置管理工具管理环境变量
- 容器重启策略:谨慎使用
restart: always,考虑使用restart: unless-stopped - 日志监控:设置日志监控和告警
- 资源限制:为容器设置 CPU 和内存限制
故障排查流程:
- 从外到内:先检查网络、安全组等外部因素
- 观察规律:注意问题出现的规律和条件
- 分层排查:从系统层到应用层逐步排查
- 善用工具:
top,docker logs,journalctl等工具是排查利器
结语
这次"惊魂记"虽然让我紧张了好几个小时,但最终收获颇丰。不仅修复了一个安全隐患,还学会了更系统的故障排查方法。
在云原生时代,一个小小的环境变量错误就可能引发连锁反应,导致整个系统瘫痪。作为开发者,我们需要时刻保持警惕,既要关注代码质量,也要重视运维安全。
记住:安全无小事,配置需谨慎!