2021年4月6日,Epic Games的一张通配符TLS证书过期,Fortnite、Rocket League、EpicGames Store全线宕机将近五个半小时。这张证书被挂在了数百个后端服务上,过期瞬间,登录、支付、游戏内连接全部同时断裂。更尴尬的是,证书更新后,客户端的重试逻辑又把自己的服务器打了一波DDoS。Epic后来发了公开事故报告,原话是"我们以为覆盖了所有证书使用场景,但实际上没有。"
类似的事不少。2020年8月Spotify的 *.wg.spotify.com 证书过期,全球用户断连约一小时。2017年Equifax遭遇大规模数据泄露,直接原因之一是流量检测设备的SSL证书过期了十个月,安全监控等于瞎了,攻击者往外拖数据拖了76天才被发现。当时Equifax内部一共有324张过期证书。
Keyfactor的调研数据摆在那:88%的企业经历过因证书过期导致的非计划停机,过去两年平均每家发生过3次以上。这些企业的IT团队不菜,流程也有,但证书就是漏了。原因不复杂,证书管理在大多数DevOps体系里是个盲区。代码有版本控制,配置有GitOps,基础设施有Terraform,唯独证书还停留在"有人记得续期"的阶段。
拆开来看,一张证书过期后,流水线里到底会连环断掉哪些环节。
容器镜像拉取:第一个倒下的多米诺骨牌
Kubernetes集群从私有Registry拉镜像走的是HTTPS。Registry证书过期,kubelet直接拒绝连接,Deployment不断重试,Pod卡在ImagePullBackOff,HPA扩容出来的新Pod全部启动失败。业务流量还在涨,容量却上不去,雪球开始滚。
有些团队为了省事配了 insecure-skip-tls-verify绕过校验。证书有效时这个配置毫无存在感,但一旦Registry换了证书,某些固化了指纹的客户端配置反而因为校验逻辑冲突出问题。绕过的代价是你连证书更新这件事都感知不到,直到某天客户端报"unableto verify the first certificate"才反应过来。
服务间mTLS:信任链从根部断裂
微服务架构下服务间通信大量走mTLS。Istio、Linkerd这些Service Mesh自己有证书轮换机制,但很多团队的内部CA证书和根证书并没有纳入Mesh的自动轮换。根证书过期时,所有Sidecar之间的mTLS握手全部失败,服务调用返回503,链路追踪上一片红。错误日志只会告诉你certificatehas expired or is not yet valid,不会告诉你是哪张证书、在哪个节点。
Epic Games的事故就是这个模式。一张通配符证书挂在数百个服务上,过期那一刻所有服务同时开始报错。不是一个个服务依次挂掉,是一整片同时断。这种"爆炸半径"问题在设计通配符证书时就该考虑到,但实际操作中,为了方便,很多团队还是一张通配符证书打天下。
Helm部署中的Secret引用:换了证书却没重启
Helm部署时把TLS Secret作为值传入是常规操作。证书更新后,如果你只更新了Secret但没有同步滚动重启引用它的Pod,旧Pod仍然持有旧证书的内存缓存。表面上证书换了,实际流量还在走旧证书。等旧证书过期那一刻,正在运行的服务突然开始报TLS握手失败,而你不会把原因和"上周换过证书"这件事联系起来。这种问题的排查周期通常以天计,因为所有人的注意力都在"为什么突然挂了"上,没人往回看两周前的变更记录。
CI/CD辅助步骤:静默失败的告警链
Pipeline里调用Slack通知、JIRA建工单、云厂商API的操作,普遍依赖HTTPS。如果目标端证书更换而你本地CABundle没同步,Pipeline在 curl 阶段直接报错退出。这种失败发生在Pipeline的辅助步骤里,不影响主构建,但部署通知和状态回写全部哑火。运维人员收不到告警,问题像滚雪球一样放大。Epic的事故里也有类似情况,证书过期后客户端的重试逻辑反而成了二次灾害的源头。
为什么监控抓不到证书过期
大部分团队的监控覆盖了CPU、内存、磁盘、响应延迟,但证书有效期检查基本靠Zabbix或Prometheus的 ssl_certificate_expiry 指标做被动采集。这个指标只覆盖已知域名,你在黑盒探测里配了哪些URL,它就检查哪些。内部服务间通信用的证书、Registry证书、CA中间证书,统统不在探测范围内。等告警触发时,距离过期往往只剩几个小时甚至已经过期了。
用了cert-manager的团队也别太放心。cert-manager确实能自动签发和轮换证书,但它依赖签发端的可用性。如果你的签发CA是自建的,CA自身证书过期了,cert-manager的Certificate资源会一直卡在Issuing 状态,新证书签不出来,旧证书又在倒计时。这个窗口期一旦撞上业务高峰,就是事故。Epic在事故报告里也承认,他们有证书监控系统,但"没有覆盖到所有使用证书的地方"。这说明监控系统本身也存在盲区,光靠黑盒探测不够。
把证书拉进自动化体系
解决思路就一条:把证书当作基础设施代码的一部分,纳入GitOps管理,让续期变成Pipeline里的一个常规Step。
ACME协议与自动签发
ACME(RFC 8555)协议把证书申请从人工流程变成了API调用。GlobalSign Atlas支持ACME协议,可以在证书到期前自动完成域名验证、签发、部署全流程。集成到CI/CD中的方式很直接:在Pipeline的pre-deploy阶段加一个Step,通过ACMEClient(如certbot、acme.sh)向Atlas申请证书,写入Kubernetes Secret,然后触发滚动重启。整个过程不需要人参与,也不需要邮件提醒。
证书全生命周期管理
ACME解决了签发自动化,但没解决"全景可见性"。一个中大型集群里可能有上百张证书,分布在Ingress、ServiceMesh、内部CA、第三方集成等不同位置。你需要一个统一的证书清单,记录每张证书的指纹、签发者、有效期、绑定服务。GlobalSign的证书生命周期管理(CLM)平台可以对接ACME签发的同时提供仪表盘视图,把全量证书的到期时间线铺开,优先级一目了然。EpicGames在事故后做的第一件事就是审计所有已知证书,找出监控盲区。如果有CLM平台,这件事在事故发生前就该做完了。
Pipeline中的证书健康检查门禁
在CI/CD里加一个证书健康检查门禁,部署前强制验证目标环境的证书状态。证书剩余有效期不足14天,直接阻断部署,强制团队先处理证书。这种硬性门禁比邮件提醒有效得多。邮件可以忽略,Pipeline报红没法绕过。
具体实现上,可以用一个简单的Shell脚本嵌在Pipeline的 before_script 里:
# pre-deploy 证书检查CERT_END_DATE=$(echo | openssl s_client -connectapi.example.com:443 2>/dev/null \
| openssl x509 -noout -enddate2>/dev/null | cut -d= -f2)
DAYS_LEFT=$(( ($(date -d "$CERT_END_DATE" +%s) - $(date +%s)) / 86400))
if [ $DAYS_LEFT -lt 14]; then
echo "证书将在 ${DAYS_LEFT} 天后过期,部署中止"
exit 1
fi
配合定时调度的ACME续期,整个流程就闭环了:定时任务每60天自动续期证书,部署前门禁检查兜底,CLM平台提供全局视图。证书续期从运维黑洞变成了流水线上的一个常规Step。
证书透明度日志:你得先知道有哪些证书
还有个常被忽略的点。证书透明度日志(CT Log)是公开的证书签发记录,你可以通过它监控自己域名下被签发的所有证书。有些团队遇到过内部人员私自签发证书,或者自动化脚本误签发证书的情况,通过CTLog监控可以及时发现异常签发行为。这不算直接防止过期,但它是证书治理的一部分。你得先知道有哪些证书存在,才能保证它们都被纳入了续期计划。Epic的事故也暴露了这个问题,他们自己都没完全清楚那张通配符证书挂在了多少个服务上。
有效期缩短倒计时:手动续期即将彻底不可行
2025年4月,CA/Browser Forum正式通过了SC-081v3提案,TLS证书有效期将分阶段缩短:2026年3月15日起最长200天,2027年3月15日起最长100天,2029年3月15日起最长47天。GlobalSign在投票中投了赞成票。提案里有一句话说得很直白:通过持续缩短有效期,论坛已经表明自动化是证书生命周期管理的必要条件。
47天有效期的证书,意味着你每年要换7到8次。如果还靠人工续期,光是处理证书续期就够一个运维团队忙的,而且出错率会直线上升。2027年缩到100天时手动续期就已经不现实了,更别说47天。现在不把ACME自动化建起来,到2026年3月第一波缩减生效时就会很难受。
SSL证书过期击穿流水线,根因不是证书本身,而是证书管理没有被当作DevOps实践的一环来对待。代码有版本控制、配置有GitOps、基础设施有IaC,证书却还在靠人记。把ACME自动化签发、CLM全景管理、Pipeline门禁检查三件事做到位,证书续期就是流水线上的一个常规Step,不是凌晨三点的PagerDuty告警。证书有效期缩短的趋势已经板上钉钉,越早建好自动化体系,后面越主动。