问题背景
近几年我们连续踩过几类「看起来不相关、根因都是暴露面没收住」的坑:公网 Elasticsearch 9200 裸奔被 Meow 类脚本删库、云账号 AccessKey 硬编码进公开仓库被 11 分钟捡走挖矿、Web 上传接口只信前端后缀被种 Webshell、临时联调安全组 0.0.0.0/0 放完忘记回收。每一起单独复盘都能写一篇排查文,但复盘越多越发现——真正缺的不是又一个应急手册,而是上线前就能挡住裸奔的收敛清单。
这篇不再还原某一场事故的时间线,而是把过去半年处置经验压成一份可勾选的 服务器暴露面收敛 Checklist。对象覆盖:Linux/Windows 主机、容器节点、云安全组/NSG、数据库与中间件监听面、运维入口(SSH/RDP/堡垒机)、静态密钥与临时凭证。目标很具体:任何一台新机器、任何一个新端口、任何一把新密钥,上线前都能在清单上找到对应勾选项;历史资产也能用同一套清单做季度收敛。
故障现象
暴露面失控通常不会以「安全事件」的名义出现,而是以下列「业务/运维症状」伪装登场:
- 账单异常先于告警:境外地域凭空多出高配实例、GPU 矿机,费用曲线夜间断崖上翘,业务监控却一片绿。
- 数据组件忽然只剩勒索索引:ES / Mongo / Redis / Kafka 管理口对公网开放,脚本化扫描器 24 小时内完成发现→删库→勒索索引三连。
- 主机出现 UID 0 影子账户与异常外联:应用低权限账户却能通过 sudoers 提权,或上传目录同源可执行导致 Webshell 落地后直接出网。
- 临时放行永久生效:联调同学开了
0.0.0.0/0:9200/3306/6379/22,工单关了规则还在,九十天后某次全网扫描直接命中。 - 「内网服务」其实在公网:NLB/CLB 误绑公网 IP、K8s Service type=LoadBalancer 没走内网注解、NAT 网关 SNAT 表把管理口映出去——资产台账里写着内网,扫描器从公网就能打到。
共同特征只有一句:攻击面比资产台账大,而台账又比监控面大。下面用一次模拟演练场景(对照真实处置经验)把 Checklist 跑通。
排查过程
1. 先画「谁在听、谁能连」两张图
不做资产盘点就谈收敛,等于盲人摸象。我们用三层命令把「真实监听面」拉出来,和 CMDB/云厂商资产清单对账:
|
|
对账结果常见三类漂移:
| 漂移类型 | 典型表现 | 风险 |
|---|---|---|
| 台账有、实际无 | CMDB 记着 8080,进程早下线 | 低,但干扰优先级 |
| 实际有、台账无 | 临时 NodePort / 调试端口未登记 | 高,盲区 |
| 规则宽、业务窄 | 安全组放 0-65535,应用只听 443 | 高,横向面过大 |
容器节点还要多查一层:
|
|
2. 端口收敛:默认拒绝,按业务建白名单
收敛顺序建议 从外到内:公网入口 → 跨 VPC/专线 → 同 VPC 子网 → 主机本地防火墙。原则:
- 公网只留 443(及必要的 80 跳转);SSH/RDP/数据库/缓存/消息队列 一律禁止对
0.0.0.0/0。 - 运维入口收敛到 堡垒机 / VPN / 零信任网关 之后,源 IP 写成堡垒机出口段,而不是「公司出口 + 家庭宽带 + 个人 4G」。
- 数据库与中间件监听绑定 内网网卡或 127.0.0.1 + 本地 socket,禁止直接绑
0.0.0.0再靠安全组兜底——安全组被误改时,本地绑定是最后一道闸。 - 容器环境优先用 ClusterIP + Ingress,慎用 NodePort;必须用 LoadBalancer 时强制内网注解(各云字段不同,写入变更模板)。
主机防火墙示例(firewalld,策略与云安全组对齐,双保险):
|
|
Windows 对等动作:Get-NetFirewallRule 清点 + GPO 下发「禁止高危端口入站」,RDP 仅放行堡垒机网段。
3. 身份与凭据:比关端口更难、也更致命
端口收敛解决的是「门开没开」;凭据治理解决的是「钥匙有几把、谁还握着」。对照真实事故,清单里这几项一项不能少:
- 静态长期密钥清零目标:云 AK/SK、对象存储签名密钥、镜像仓库 robot token、CI PAT——默认生命周期 ≤ 90 天,能上 STS/实例角色的全部上。
- 代码与镜像禁硬编码:pre-commit + CI 强制 gitleaks/trufflehog;历史仓库做过一次
git filter-repo的,要默认「仍按已泄露处置」轮换全部命中密钥。 - sudoers / 本地管理员最小化:禁止
NOPASSWD: ALL,禁止给 www-data/nginx 配find/tar/vim/python等 GTFOBins 高危组合;Windows 本地 Administrators 组成员季度审计。 - SSH 密钥与 authorized_keys 基线:禁用密码登录、禁用 root 直登;
authorized_keys纳入 FIM(AIDE/OSSEC/自研);跳板机登录全量审计。 - 应用密钥进密钥管理系统:HashiCorp Vault / 云 KMS / 密封 secrets,禁止
.env进镜像层。
快速体检命令:
|
|
4. 云侧边界与「以为内网」的坑
安全组/NSG 之外,还有三类高发误配:
- 负载均衡误绑公网:内网 CLB 创建时点错「公网」,或后续被变更成公网 IP。
- 对象存储桶策略过宽:
Principal: *+s3:GetObject组合等于公开读;再叠预签名 URL 长期有效就更糟。 - 安全组引用安全组 形成隐式大环:A 放行 B,B 放行 C,C 对公网 22 开放——人眼 ban 单组时看不出链路。
建议每月导出全量安全组做离线分析,用脚本标红:
- 入站源为
0.0.0.0/0或::/0且端口 ∈ {22, 3389, 3306, 5432, 6379, 9200, 27017, 9092, 2379, 6443} - 出站完全放开且实例角色权限过大(可被 SSRF 换成凭证盗用)
5. 持续扫描:收敛不是项目,是节奏
一次性收敛做完,三个月后必然回潮。最低可行节奏:
| 频率 | 动作 | 工具/产出 |
|---|---|---|
| 每日 | 公网高危端口变化 diff | masscan/nmap + 基线对比;或云厂商「暴露面」产品 |
| 每周 | 新增 0.0.0.0/0 规则工单抽检 |
云配置审计 / Config |
| 每月 | 全量监听端口 vs CMDB 对账 | 主机 Agent 采集 + 报表 |
| 每季 | 密钥轮换与权限右转 | RAM/IAM 权限分析 + 密钥年龄报表 |
| 每半年 | 红队/外部扫描复测 | 第三方或自建漏洞扫描 |
告警必须「可行动」:只报「发现 9200 对公网开放」不够,要带 实例 ID、安全组规则 ID、首次发现时间、负责人、工单链接。
解决方案
把上面过程固化成上线门禁与存量治理两份清单。
A. 新资产上线门禁 Checklist(未勾完禁止投产)
- 资产登记:主机/容器集群/中间件实例写入 CMDB,含负责人、环境、是否允许公网
- 监听面最小:应用只绑必要网卡与端口;管理口不绑
0.0.0.0 - 安全组默认拒绝:入站白名单精确到源网段 + 端口;无
0.0.0.0/0高危端口 - 运维入口:SSH/RDP 仅堡垒机网段;已禁用密码登录与 root 直登
- 主机防火墙:与安全组同策略双保险(防安全组被误改)
- 身份最小权限:实例角色 / 进程账户无多余 sudo;无 GTFOBins 高危组合
- 密钥来源:无硬编码 AK/SK;CI/CD 用 OIDC 或短时令牌;镜像无
.env密钥层 - 日志与审计:登录审计、安全组变更审计、高危 API 调用告警已接值班通道
- 备份与恢复点:数据组件有最近一次成功备份,且恢复演练记录未过期
- 扫描通过:上线前外部扫描 + 内部监听对账均无新增高危项
B. 存量资产季度收敛 Checklist
- 导出全网
0.0.0.0/0入站规则,高危端口清零或改堡垒机源 - 全量
ss -lntup/ 云负载均衡监听 与 CMDB 对账,盲区端口下线或补登记 - 数据库/缓存/消息队列管理口确认不可从公网与非业务网段直达
- 长期 AK/SK、robot token、CI PAT 年龄 > 90 天全部轮换
- 公开仓库与历史 commit 再扫一轮密钥(含 fork 与镜像站)
- sudoers、本地管理员组、authorized_keys 抽样 100% + 全量自动化基线
- K8s:清点 NodePort/LoadBalancer/无主 Ingress;NetworkPolicy 是否从 default-allow 误收到不可用(回滚预案必备)
- 对象存储桶策略与 ACL 扫描,公开读/写清零
- 临时联调规则自动过期机制是否生效(TTL 标签或云防火墙定时策略)
- 复测报告归档,遗留项录入风险台账并设到期日
C. 发现「已裸奔」时的 30 分钟止血序
- 先断后查:安全组/NSG/防火墙立即收口,保留只读镜像/快照用于取证。
- 判定是否失陷:看操作审计与主机外联,区分「仅暴露」与「已被利用」。
- 轮换凭据:凡是这台机器/这个组件能摸到的密钥,默认按已泄露轮换。
- 数据完整性:核心库做只读快照,对比勒索索引/异常 drop。
- 复盘进清单:把本次漏网项回写到门禁 Checklist,避免同类第三次出现。
根因分析
暴露面失控很少是「不会配安全组」这么简单,深层通常是四洞叠加:
- 责任缝隙:应用只关心服务起来,网络只关心通,安全只关心扫描器报表——没有人在上线门禁处做最终勾选。
- 临时性没有生命周期:联调规则、调试端口、个人 AK,创建时都有正当理由,却没有 TTL 与自动回收。
- 控制面单一:只信云安全组,主机防火墙空置;或只信主机 iptables,容器 NodePort 从旁路露出。
- 检测滞后于攻击自动化:扫描器与盗钥机器人是分钟级,人工季度巡检是季度级,窗口期天然留给攻击者。
所以 Checklist 的本质不是文档,是 把「默认拒绝 + 最小权限 + 短生命周期 + 持续对账」写进发布流程的强制闸门。
预防措施
- 发布平台强制校验:CI 流水线集成 gitleaks;CD 创建安全组前校验规则模板,拒绝高危
0.0.0.0/0。 - 安全组即代码:Terraform/Pulumi 管理,PR 必经两人审批;控制台手工改动由配置审计告警并自动漂移纠正。
- 密钥默认短时:云上业务优先实例 RAM 角色 / Pod IRSA / OIDC 联邦,静态 AK 走例外审批且 90 天必轮换。
- 暴露面日更大盘:每日公网端口 diff 进值班群,新增项 T+0 认领、T+1 闭环。
- 临时规则 TTL:所有「临时放行」必须带过期时间,到期自动删;无 TTL 的变更单直接打回。
- 双层防御:云安全组 + 主机防火墙 +(容器)NetworkPolicy,任一层被误改另两层仍可兜住。
- 桌面与服务器同一套思想:运维笔记本的 RDP/SMB/WinRM、跳板机、堡垒机会话,一并纳入季度收敛,避免「服务器收了、人的电脑还是跳板」。
- 演练:每半年做一次「故意裸奔再收敛」桌面推演,验证清单是否可执行、告警是否会响、职责是否清晰。
总结
服务器被打穿的叙事里,0day 很少,忘关的端口、忘删的规则、忘轮换的密钥 才是主角。把暴露面收敛写成 Checklist,是为了把分散在 ES 裸奔、AK 挖矿、Webshell、sudo 提权等多篇事故里的共性动作,收成上线前能勾、季度能复用、出事能止血的同一套动作。
落地时不必追求一次完美:先上「新资产门禁」十条,再跑一个季度「存量高危 0.0.0.0/0 清零」,同时把密钥扫描嵌进 CI。等这三件事稳定,再叠加日更扫描大盘与安全组即代码。暴露面不会归零,但可以逼近「攻击者自动化打不进、打进了分钟级能发现、发现了有清单可执行」——这比再写一篇漂亮的事后复盘,值钱得多。