接手一台跑了 398 天的服务器:安全巡检与加固实录
背景
接手一台阿里云北京的 ECS:Debian 12,已连续运行 398 天未重启,上面跑着两个 Docker 容器——一个 frp 服务端(内网穿透跳板)、一个带鉴权的 Node 正向代理。接管者的第一件事不是动手优化,而是只读勘察:摸清架构、基线、风险,形成台账,然后按优先级加固。本文记录这次巡检与加固的完整过程。
第一步:只读勘察,用证据链修正误判
这台机器对外开了 2376、2377 两个端口,第一眼很像「Docker Daemon / Swarm 暴露公网」——这是安全巡检里著名的高危项。但实测推翻了这个判断:
docker info # Swarm: inactive —— 与 Swarm 无关
# compose 里 frps 映射 2377:2377,proxy 映射 2376:3000
# frps 日志中只有一个 runID → 单条隧道承载全部域名
# 直连 IP 的 443:tlsv1 alert internal error;带正确 SNI:HTTP/2 200
结论:2377 是 frp 服务端的接入端口(双向 TLS 客户端证书认证,无 token,比 token 更严格),2376 是 Node 正向代理。这台机是 frp 跳板,真正的 Web 服务在内网主机上。
教训:端口不等于服务。高危判断必须用 docker info、compose 映射、服务日志、TLS SNI 行为构成证据链,而不是「长得像」。误判要公开修正并记录,否则台账会带着错误结论传染给下一次排查。
第二步:安全基线检查
巡检结果整体好于预期,但每项都要实测而非想当然:
| 项 | 实测状态 | 评价 |
|---|---|---|
| fail2ban | active | ✅ 运行中 |
| PasswordAuthentication | no | ✅ 仅密钥,密码暴破无效 |
| PermitEmptyPasswords / MaxAuthTries | no / 6 | ✅ |
| PermitRootLogin | yes | ⚠️ 纵深防御项,密码登录已禁,降级为 P3 |
| 主机防火墙 iptables INPUT | ACCEPT 全放 | ⚠️ 暴露面完全依赖云安全组;新开监听端口必须同步收紧安全组 |
失败登录统计也值得看:近期仅 14 次失败,说明「仅密钥 + fail2ban」的组合已经把暴破面压到很低。PermitRootLogin yes 在这个前提下不再紧急,但仍列入整改清单。
第三步:按优先级加固
1. 补丁升级:105 个包 + 内核两步走
apt upgrade 升了 105/106 个包(docker-ce、openssh、openssl、systemd、curl 等)。剩下那个是 linux-image-amd64 元包——它会被普通 upgrade 递延(涉及新内核安装与重启),需要显式:
apt-get install --only-upgrade linux-image-amd64
reboot # 运行内核 6.1.0-37 → 6.1.0-52,跨 22 个版本
398 天的 uptime 意味着大量内核安全修复从未生效,重启是必选项,不是可选项。
2. 关闭 rpcbind:先确认依赖,再动手
111 端口对外监听着 rpcbind,而这台机没有任何 NFS/RPC 业务。处置顺序很重要:
rpcinfo -p # 仅 portmapper 自身,无 NFS/RPC 依赖 → 确认孤儿残留
systemctl disable --now rpcbind rpcbind.socket
先 rpcinfo 取证再关闭,关完验证容器、sshd、站点全量正常。
3. 本地私钥权限与凭据清理
巡检发现本地多把退役私钥权限是 644(全局可读)——私钥权限应为 600,这是最基础也最常被忽视的一条。另外在 compose 文件的未提交注释块里发现一段云服务明文凭据:核实它从未进入 git 历史(HEAD 与全分支历史均不含),从文件删除后,仍需到云控制台吊销并轮换——凭证值曾经存在过,就按泄露处理。
4. 补每日备份:从零到有
接手时零备份,直接列为 P1。方案:
tar -czf /var/backups/lnmp/lnmp-$(date +%F).tar.gz \
--exclude=大头残留目录 --exclude='*.bak' /root/lnmp
find /var/backups/lnmp -mtime +7 -delete # 7 天轮转
chmod 600 /var/backups/lnmp/*.tar.gz
# crontab: 17 3 * * * 每日执行
注意两点:排除无用的残留数据目录(这里有个 892M 的废弃 postgres 目录)控制体积;权限 600 因为备份里含 TLS 密钥。本地备份只防误删,不防磁盘故障——异地容灾(rsync 到另一台机或对象存储)要列为下一个待办。
第四步:问题分级,持续留痕
所有发现按 P1/P2/P3 分级管理:P1 = 数据安全(备份缺失、凭据泄露),P2 = 可用性(无监控告警),P3 = 纵深防御(PermitRootLogin、配置无版本管理)。已解决项从清单移除、只留未解决项,每次变更在台账留痕(时间、动作、验证结果)。
经验总结
- 只读先行:新接手的机器先勘察出报告,不动手;只读勘察不需要报备,写操作才需要。
- 证据链判断:端口像什么不重要,docker info / compose / 日志 / SNI 行为才是证据;发现误判要公开修正。
- 变更三件套:备份 → 变更 → 验证回滚路径;不可逆操作显式确认。
- 内核升级别指望 apt upgrade 自动带上:元包会递延,需显式 install + 重启。
- 备份先行于一切优化:零备份的机器,磁盘随时可能把所有工作归零。
参考文档
- Debian project. Debian Security Information[EB/OL]. https://www.debian.org/security/ (访问日期: 2026-09-04).
- fail2ban project. fail2ban official documentation[EB/OL]. https://github.com/fail2ban/fail2ban/wiki (访问日期: 2026-09-04).