2020 年 2 月,一家全球 CDN 服务商因为一张中间证书过期,导致数千个下游站点在 30 分钟内同时不可访问。事后的根因分析只有一行字:证书有效期到了,没人续。2023 年,某大型云厂商因为内部 mTLS 证书轮转失败,微服务之间的通信全部中断,影响了数百万用户的订单流程。
这些事故不是因为 PKI(Public Key Infrastructure,公钥基础设施)的概念有多复杂,也不是因为工具不够成熟。问题在于:大多数团队把证书当成一次性的运维任务——申请、安装、然后忘记它,直到它过期。
证书管理的核心挑战是生命周期管理:谁负责签发?什么时候轮转?过期了怎么发现?私钥存在哪里?撤销了怎么通知?这些问题中的每一个,如果没有自动化的答案,就是一颗定时炸弹。
本文要回答一个具体的问题:如何建立自动化的证书生命周期管理,彻底消除证书过期导致的宕机?
在上一篇中,我们讨论了加密架构的整体设计。本文聚焦于加密体系中最容易在生产环境出问题的部分——密钥与证书的管理。
一、问题场景
1.1 证书过期事故的典型模式
证书过期事故几乎总是遵循同一个模式:
- 团队在项目上线时手动申请了证书,有效期一到三年。
- 证书信息记录在某个运维文档或者工单系统中,但没有人定期检查。
- 一年或两年后,负责申请证书的人已经离职或转岗。
- 证书过期,TLS 握手失败,用户看到浏览器警告页面,或者服务之间的 mTLS(mutual TLS,双向 TLS)连接直接断开。
- 运维团队紧急手动续期,但因为不熟悉流程,可能更新了错误的证书、遗漏了某些节点、或者忘了重启服务。
这个模式的根因不是”人的疏忽”,而是流程依赖人的记忆。任何依赖人类记忆的运维流程,在足够长的时间线上都会失败。
1.2 证书管理的规模问题
在传统架构中,一个组织可能只有几十张证书——几个域名的 TLS 证书加上一些内部系统的证书。但在微服务架构和零信任网络(Zero Trust Network)中,每个服务实例都需要自己的身份证书,证书数量可能达到数万甚至数十万。
| 架构类型 | 证书数量级 | 有效期 | 管理方式 |
|---|---|---|---|
| 传统单体 | 10 - 100 | 1 - 3 年 | 手动 / 电子表格 |
| 微服务 | 1,000 - 10,000 | 天 - 月 | 半自动 |
| 零信任 / 服务网格 | 10,000 - 100,000+ | 小时 - 天 | 全自动 |
当证书数量达到上千张时,手动管理已经不可能。当有效期缩短到小时级别时,人工干预的窗口根本不存在。自动化不是”有了更好”——它是唯一可行的方案。
1.3 密钥泄露的后果
证书管理的另一面是私钥保护。如果私钥被泄露:
- 攻击者可以冒充你的服务,实施中间人攻击(Man-in-the-Middle,MITM)。
- 如果泄露的是 CA(Certificate Authority,证书颁发机构)的私钥,攻击者可以签发任意证书,整个信任链崩塌。
- 即使事后撤销了证书,在 CRL(Certificate Revocation List,证书撤销列表)和 OCSP(Online Certificate Status Protocol,在线证书状态协议)更新传播到所有客户端之前,攻击窗口仍然存在。
因此,密钥管理和证书管理是一枚硬币的两面,必须一起设计。
二、PKI 基础架构
2.1 信任模型
PKI 的核心是信任链(Chain of Trust)。信任从根证书颁发机构(Root CA)开始,逐级向下传递:
Root CA(根 CA)
└── Intermediate CA(中间 CA)
└── Leaf Certificate(叶子证书 / 终端实体证书)
为什么需要中间 CA?因为根 CA 的私钥极其重要,一旦泄露就意味着整个 PKI 体系崩溃。因此根 CA 通常是离线的(air-gapped,物理隔离),只在签发中间 CA 证书时使用。日常的证书签发由中间 CA 完成。即使中间 CA 被攻破,只需要撤销该中间 CA 的证书,根 CA 仍然安全。
2.2 证书链验证
当客户端(比如浏览器)收到服务器的证书时,验证过程如下:
- 检查叶子证书的签名:用中间 CA 的公钥验证叶子证书的数字签名。
- 检查中间 CA 的签名:用根 CA 的公钥验证中间 CA 证书的签名。
- 检查根 CA 是否在本地信任库中:操作系统和浏览器内置了一组受信任的根 CA 证书。
- 检查证书的有效期、域名匹配、扩展属性等。
如果链中任何一环验证失败,TLS 握手就会被拒绝。
2.3 X.509 证书结构
X.509 是最广泛使用的证书标准。一张证书包含以下关键字段:
Certificate:
Version: v3
Serial Number: 04:a3:b2:...
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=Example Intermediate CA, O=Example Inc
Validity:
Not Before: 2026-01-01 00:00:00 UTC
Not After: 2026-04-01 00:00:00 UTC
Subject: CN=api.example.com
Subject Public Key Info:
Algorithm: id-ecPublicKey (P-256)
Public Key: 04:ab:cd:...
X509v3 Extensions:
Subject Alternative Name:
DNS:api.example.com
DNS:*.api.example.com
Key Usage: Digital Signature
Extended Key Usage: TLS Web Server Authentication
Authority Information Access:
OCSP - URI:http://ocsp.example.com
CA Issuers - URI:http://ca.example.com/intermediate.crt
CRL Distribution Points:
URI:http://crl.example.com/intermediate.crl
几个值得注意的字段:
- Subject Alternative Name(SAN,主体备用名称):现代浏览器已经不再使用 Common Name(CN)来匹配域名,而是使用 SAN。一张证书可以包含多个域名。
- Key Usage 和 Extended Key Usage:限制证书的用途,防止一张服务器证书被滥用为 CA 证书。
- AIA(Authority Information Access):告诉客户端去哪里查询 OCSP 和中间 CA 证书。
三、证书生命周期
证书从生成到废弃,经历五个阶段:
3.1 生成(Generation)
生成证书的流程:
- 生成密钥对(私钥和公钥)。
- 创建证书签名请求(CSR,Certificate Signing Request),包含公钥和主体信息。
- 将 CSR 提交给 CA。
- CA 验证请求者的身份,签发证书。
用 OpenSSL 生成 CSR 的示例:
# 生成 ECDSA P-256 私钥
openssl ecparam -genkey -name prime256v1 -out server.key
# 基于私钥创建 CSR
openssl req -new -key server.key \
-out server.csr \
-subj "/CN=api.example.com/O=Example Inc"
# 查看 CSR 内容
openssl req -in server.csr -text -noout3.2 分发(Distribution)
签发的证书需要部署到目标服务器。分发方式取决于架构:
- 手动部署:通过 SSH 或配置管理工具(Ansible、Puppet)将证书文件复制到服务器。
- 集中式密钥管理服务:服务在启动时从 Vault、AWS Secrets Manager 等系统拉取证书。
- 自动签发与挂载:cert-manager 在 Kubernetes(容器编排平台)中自动签发证书并挂载为 Secret。
3.3 续期(Renewal)
续期是证书生命周期中最容易出错的环节。最佳实践:
- 在证书有效期剩余 1/3 时开始续期。例如 90 天有效期的证书,在第 60 天开始续期。
- 续期流程应该与首次签发流程完全相同,且完全自动化。
- 续期后需要验证新证书是否正确部署,旧证书是否被替换。
3.4 撤销(Revocation)
当私钥泄露或证书信息有误时,需要撤销证书。有三种机制:
CRL(Certificate Revocation List,证书撤销列表)
CA 定期发布一份包含所有已撤销证书序列号的列表。客户端下载这份列表来检查证书是否被撤销。
缺点:CRL 文件可能很大(数 MB),更新频率低(通常每小时到每天),客户端需要缓存和定期下载。
OCSP(Online Certificate Status Protocol,在线证书状态协议)
客户端向 OCSP 响应器(OCSP Responder)发送实时查询,询问某张证书是否被撤销。
缺点:增加了 TLS 握手的延迟(需要额外的网络请求),且暴露了用户正在访问哪些网站(隐私问题)。
OCSP Stapling(OCSP 装订)
服务器预先从 OCSP 响应器获取自己证书的状态响应,并在 TLS 握手时将响应”装订”在证书上一起发给客户端。客户端无需额外请求。
# Nginx 启用 OCSP Stapling 配置
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.example.com.crt;
ssl_certificate_key /etc/ssl/private/api.example.com.key;
# 启用 OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
3.5 废弃(Retirement)
证书过期后,相关的密钥材料应该被安全销毁。但需要注意:如果有使用该证书加密的数据尚未解密,销毁私钥意味着数据永久丢失。因此密钥的保留策略需要与数据保留策略对齐。
四、ACME 协议与自动化
4.1 ACME 协议概述
ACME(Automatic Certificate Management Environment,自动化证书管理环境)是由 ISRG(Internet Security Research Group)设计的协议,最初为 Let’s Encrypt 开发,现已成为 IETF 标准(RFC 8555)。
ACME 的核心思想是:用自动化的域名控制权验证替代传统的人工审核。如果你能证明你控制了某个域名,CA 就自动签发证书。
4.2 ACME 协议流程
以下是 ACME 协议的完整交互流程:
sequenceDiagram
participant Client as ACME 客户端
participant Server as ACME 服务器(CA)
participant DNS as DNS 服务器
participant Web as Web 服务器
Note over Client,Server: 第一步:账户注册
Client->>Server: POST /acme/new-account(公钥,联系方式)
Server-->>Client: 201 Created(账户 URL)
Note over Client,Server: 第二步:创建订单
Client->>Server: POST /acme/new-order(域名列表)
Server-->>Client: 201 Created(订单 URL,授权 URL 列表)
Note over Client,Server: 第三步:获取挑战
Client->>Server: GET 授权 URL
Server-->>Client: 200 OK(挑战列表:HTTP-01 / DNS-01)
Note over Client,Server: 第四步:完成挑战(DNS-01 示例)
Client->>DNS: 创建 TXT 记录 _acme-challenge.example.com
Client->>Server: POST 挑战 URL(通知已就绪)
Server->>DNS: 查询 TXT 记录验证域名控制权
Server-->>Client: 200 OK(挑战状态:valid)
Note over Client,Server: 第五步:提交 CSR
Client->>Server: POST finalize URL(CSR)
Server-->>Client: 200 OK(证书 URL)
Note over Client,Server: 第六步:下载证书
Client->>Server: GET 证书 URL
Server-->>Client: 200 OK(完整证书链)
4.3 HTTP-01 vs DNS-01 挑战
ACME 协议支持多种域名控制权验证方式,最常用的是 HTTP-01 和 DNS-01。
HTTP-01 挑战
CA 给客户端一个令牌(token),客户端需要在
http://<域名>/.well-known/acme-challenge/<token>
路径下放置一个包含特定内容的文件。CA 通过 HTTP
请求验证该文件存在。
优点: - 实现简单,不需要 DNS API 权限。 - 大多数 ACME 客户端默认支持。
缺点: - 需要服务器的 80 端口可达。 - 不支持通配符证书(wildcard certificate)。 - 每个域名需要单独验证。
DNS-01 挑战
客户端在域名的 DNS 中创建一条
_acme-challenge.<域名> 的 TXT
记录,内容是 CA 指定的验证值。CA 通过 DNS 查询验证。
优点: - 支持通配符证书。 - 不需要 Web 服务器或者 80 端口。 - 可以为内部域名签发公开 CA 的证书。
缺点: - 需要 DNS API 的编程访问权限。 - DNS 传播有延迟,验证可能需要等待。
| 对比维度 | HTTP-01 | DNS-01 |
|---|---|---|
| 通配符证书 | 不支持 | 支持 |
| 端口要求 | 80 端口可达 | 无端口要求 |
| DNS API | 不需要 | 需要 |
| 内部域名 | 需要外部可达 | 只需 DNS 可控 |
| 验证速度 | 快(秒级) | 慢(分钟级,DNS 传播) |
| 适用场景 | 公网 Web 服务器 | CDN、通配符、内部服务 |
4.4 cert-manager 在 Kubernetes 中的集成
cert-manager 是 Kubernetes 生态中最广泛使用的证书管理工具。它将证书管理抽象为 Kubernetes 原生资源(Custom Resource),实现了声明式的证书生命周期管理。
安装 cert-manager
# 使用 Helm 安装 cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true配置 Let’s Encrypt 签发器
# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: security@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
- dns01:
cloudDNS:
project: my-gcp-project
selector:
dnsZones:
- "example.com"声明证书资源
# certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: api-tls
namespace: production
spec:
secretName: api-tls-secret
duration: 2160h # 90 天
renewBefore: 720h # 到期前 30 天续期
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- api.example.com
- "*.api.example.com"
privateKey:
algorithm: ECDSA
size: 256通过 Ingress 注解自动签发
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080这个配置实现了完全自动化:cert-manager 监听 Ingress 资源的变化,自动通过 ACME 协议向 Let’s Encrypt 申请证书,存储为 Kubernetes Secret,并在到期前自动续期。工程师只需要声明”我需要这个域名的证书”,剩下的全部由 cert-manager 处理。
五、短期证书策略
5.1 为什么证书应该短期化
传统的 TLS 证书有效期通常是一到三年。从 2020 年起,CA/Browser Forum(CA/浏览器论坛)已经将公共证书的最大有效期缩短到 398 天,并且趋势是继续缩短。Let’s Encrypt 的证书有效期只有 90 天。
短期证书的优势:
- 缩小攻击窗口。即使私钥被泄露,证书也会在短时间内自然过期,攻击者无法长期利用。
- 降低对撤销机制的依赖。CRL 和 OCSP 都有传播延迟和可用性问题。如果证书本身只有几个小时的有效期,撤销的必要性大幅降低。
- 强制自动化。当有效期短到无法手动管理时,团队被迫建立自动化流程。而自动化流程一旦建立,反而比手动流程更可靠。
- 减少证书管理的认知负担。不需要维护电子表格来跟踪证书过期时间,因为系统自动处理了。
5.2 极短期证书:小时级有效期
在服务网格(Service Mesh)和零信任架构中,证书的有效期可以缩短到小时甚至分钟级别。SPIFFE(Secure Production Identity Framework For Everyone,面向所有人的安全生产身份框架)定义了一种标准的工作负载身份格式——SVID(SPIFFE Verifiable Identity Document,SPIFFE 可验证身份文档)。
SPIFFE 的核心概念:
- 每个工作负载(workload)有一个唯一的 SPIFFE ID,格式为
spiffe://<trust-domain>/<workload-path>。 - SPIRE(SPIFFE Runtime Environment,SPIFFE 运行时环境)负责为工作负载签发和轮转 X.509 SVID。
- SVID 的默认有效期可以设置为 1 小时,每 30 分钟轮转一次。
关于零信任架构中工作负载身份的更多讨论,参见零信任架构一文。
5.3 无停机证书轮转
短期证书的前提是轮转过程不能中断服务。实现无停机轮转的关键技术:
双证书缓冲区
服务同时持有当前证书和下一个证书。新证书在当前证书过期之前签发并加载。TLS 库在握手时使用当前有效的证书。
// Go 语言实现证书热轮转
package main
import (
"crypto/tls"
"log"
"sync"
)
type CertReloader struct {
mu sync.RWMutex
cert *tls.Certificate
certPath string
keyPath string
}
func (cr *CertReloader) LoadCertificate() error {
cert, err := tls.LoadX509KeyPair(cr.certPath, cr.keyPath)
if err != nil {
return err
}
cr.mu.Lock()
cr.cert = &cert
cr.mu.Unlock()
log.Println("证书已重新加载")
return nil
}
func (cr *CertReloader) GetCertificate(
hello *tls.ClientHelloInfo,
) (*tls.Certificate, error) {
cr.mu.RLock()
defer cr.mu.RUnlock()
return cr.cert, nil
}
func main() {
reloader := &CertReloader{
certPath: "/etc/certs/tls.crt",
keyPath: "/etc/certs/tls.key",
}
if err := reloader.LoadCertificate(); err != nil {
log.Fatal(err)
}
tlsConfig := &tls.Config{
GetCertificate: reloader.GetCertificate,
}
_ = tlsConfig
// 使用 tlsConfig 创建 TLS 监听器
// 另起一个 goroutine 监听证书文件变化并调用 LoadCertificate
}Envoy 的 SDS(Secret Discovery Service,密钥发现服务)
Envoy 代理通过 SDS API 动态获取证书,无需重启。SPIRE 作为 SDS 服务器,在证书轮转时通过 gRPC 流推送新证书给 Envoy。整个过程对应用完全透明。
六、内部 PKI
6.1 为什么需要内部 PKI
公共 CA(如 Let’s Encrypt、DigiCert)签发的证书适用于面向公网的服务。但内部服务之间的 mTLS 不应该使用公共 CA:
- 域名限制。公共 CA
只为你拥有的公共域名签发证书,而内部服务可能使用
service-a.namespace.svc.cluster.local这样的内部域名。 - 签发速度。公共 CA 的签发需要域名验证,可能需要几秒到几分钟。内部 PKI 可以在毫秒内签发。
- 控制权。使用公共 CA 意味着你的内部服务身份依赖外部组织。如果公共 CA 出现问题,你的内部通信也会受影响。
- 成本。公共 CA 的通配符证书或大量证书可能费用不菲。内部 PKI 的签发成本为零。
6.2 内部 PKI 架构
一个典型的内部 PKI 架构如下:
离线根 CA(存储在 HSM 中,物理隔离)
├── 在线中间 CA - 服务网格(签发 mTLS 证书)
│ ├── SPIRE Server(自动签发工作负载证书)
│ └── cert-manager CA Issuer(签发内部服务证书)
└── 在线中间 CA - 基础设施(签发基础设施证书)
├── 数据库 TLS 证书
├── 消息队列 TLS 证书
└── 管理后台 TLS 证书
6.3 使用 cert-manager 构建内部 CA
cert-manager 内置了 CA 类型的 Issuer,可以直接用 Kubernetes Secret 中存储的 CA 证书和私钥来签发内部证书。
# 创建 CA 密钥对(实际生产中应该用更安全的方式生成)
# internal-ca-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-ca
spec:
ca:
secretName: internal-ca-keypair
---
# 使用内部 CA 签发服务证书
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: order-service-tls
namespace: production
spec:
secretName: order-service-tls
duration: 24h
renewBefore: 8h
issuerRef:
name: internal-ca
kind: ClusterIssuer
dnsNames:
- order-service.production.svc.cluster.local
usages:
- server auth
- client auth
privateKey:
algorithm: ECDSA
size: 2566.4 信任根的分发
内部 PKI 的一个关键问题是:如何让所有服务信任内部 CA 的根证书?
几种常见方案:
- 操作系统信任库:将根 CA
证书安装到每台机器的系统信任库(
/etc/ssl/certs/)。适合 VM 部署。 - 容器镜像内置:在基础镜像中包含内部根 CA 证书。简单但更新不灵活。
- ConfigMap / Secret 挂载:在 Kubernetes 中通过 ConfigMap 分发根 CA 证书,服务启动时加载。
- trust-manager:cert-manager 生态中的信任分发组件,可以自动将 CA 证书分发到指定的命名空间。
# trust-manager Bundle 示例
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: internal-ca-bundle
spec:
sources:
- secret:
name: internal-ca-keypair
key: ca.crt
target:
configMap:
key: ca-certificates.crt
namespaceSelector:
matchLabels:
trust-bundle: "enabled"七、HSM 与密钥保护
7.1 HSM 是什么
HSM(Hardware Security Module,硬件安全模块)是一种专用硬件设备,用于生成、存储和使用加密密钥。HSM 的核心特性是:私钥永远不离开硬件。所有加密操作(签名、解密)都在 HSM 内部执行,软件只能通过 API 提交数据和获取结果,无法导出私钥。
7.2 FIPS 140 安全等级
FIPS 140(Federal Information Processing Standard 140,联邦信息处理标准 140)是美国国家标准与技术研究所(NIST)制定的密码模块安全标准。最新版本是 FIPS 140-3。
| 安全等级 | 要求 | 典型实现 | 适用场景 |
|---|---|---|---|
| Level 1 | 至少使用一个经过认证的加密算法 | 软件加密库(如 OpenSSL) | 开发和测试环境 |
| Level 2 | 需要物理篡改证据(tamper-evident) | 低端 HSM,加封条的服务器 | 一般商业应用 |
| Level 3 | 需要物理防篡改(tamper-resistant),身份认证 | 专用 HSM 设备 | 金融、支付、CA 根密钥 |
| Level 4 | 检测到物理攻击时主动销毁密钥 | 高端军用 HSM | 政府、国防 |
大多数企业的 CA 根密钥需要 FIPS 140-2/3 Level 3 认证的 HSM。
7.3 云 HSM 服务
主流云厂商都提供了托管的 HSM 服务:
AWS CloudHSM
- 提供 FIPS 140-2 Level 3 认证的专用 HSM 集群。
- 每个 HSM 实例独占,不与其他客户共享。
- 通过 PKCS#11、JCE、OpenSSL 等标准接口访问。
- 按小时计费,通常每小时 1-2 美元。
AWS KMS(Key Management Service,密钥管理服务)
- 多租户的托管密钥管理,底层使用 HSM 但对用户透明。
- FIPS 140-2 Level 2 认证(部分区域支持 Level 3)。
- 按 API 调用次数计费,成本远低于 CloudHSM。
- 不支持 PKCS#11 等标准接口,使用 AWS 专有 API。
Azure Dedicated HSM
- 提供 Thales Luna 7 HSM,FIPS 140-2 Level 3 认证。
- 专用硬件,不与其他租户共享。
Azure Key Vault Managed HSM
- 托管的 HSM 池,FIPS 140-2 Level 3 认证。
- 支持 BYOK(Bring Your Own Key,自带密钥)。
7.4 软件密钥管理
不是所有场景都需要 HSM。对于非根 CA 的密钥,软件密钥管理可能是更实际的选择:
HashiCorp Vault
Vault 是最流行的开源密钥管理工具。它的 PKI 密钥引擎可以作为内部 CA:
# 启用 PKI 密钥引擎
vault secrets enable pki
# 配置最大证书有效期
vault secrets tune -max-lease-ttl=87600h pki
# 生成根 CA 证书
vault write pki/root/generate/internal \
common_name="Internal Root CA" \
ttl=87600h
# 启用中间 CA
vault secrets enable -path=pki_int pki
# 生成中间 CA 的 CSR
vault write pki_int/intermediate/generate/internal \
common_name="Internal Intermediate CA"
# 用根 CA 签发中间 CA 证书(省略了 CSR 传递步骤)
# 配置角色(定义可以签发哪些域名的证书)
vault write pki_int/roles/internal-service \
allowed_domains="svc.cluster.local" \
allow_subdomains=true \
max_ttl=72h应用通过 Vault API 或 Vault Agent 自动获取和续期证书。
7.5 密钥仪式(Key Ceremony)
对于根 CA 密钥的生成,需要执行严格的密钥仪式。这是一个高度正式的流程,通常包括:
- 多人在场:至少需要两名以上授权人员同时在场(双人控制原则)。
- 物理安全:在受控的安全房间中进行,全程录像。
- 离线操作:使用 air-gapped(物理隔离)的计算机,不连接任何网络。
- HSM 初始化:在 HSM 中生成密钥对,密钥永远不离开 HSM。
- 密钥分片:使用 Shamir 秘密共享(Shamir’s Secret Sharing)将 HSM 的访问凭证分成 N 片,至少需要 M 片才能重建(M-of-N 阈值方案)。
- 分片分发:将每一片交给不同的受托人(Key Custodian),受托人将分片存储在独立的保险箱中。
- 文档记录:记录整个过程的每一步,包括参与人员、使用的设备序列号、生成的证书指纹等。
Let’s Encrypt 的根密钥仪式文档是公开的,可以作为参考。
八、证书监控与告警
8.1 监控什么
即使证书管理已经自动化,监控仍然是必不可少的安全网。需要监控的指标:
- 证书过期时间:最关键的指标。当证书剩余有效期低于阈值时告警。
- 证书续期状态:cert-manager 的续期是否成功?ACME 请求是否失败?
- 证书链完整性:服务器是否正确配置了完整的证书链?
- 私钥与证书匹配:部署时是否错误地使用了不匹配的私钥?
- CT 日志(Certificate Transparency Log,证书透明日志):是否有未授权的证书被签发?
8.2 Prometheus 监控 cert-manager
cert-manager 原生暴露 Prometheus 指标。关键指标:
# Prometheus 告警规则
# cert-manager-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cert-manager-alerts
namespace: monitoring
spec:
groups:
- name: cert-manager
rules:
- alert: CertificateExpiringSoon
expr: |
certmanager_certificate_expiration_timestamp_seconds
- time() < 7 * 24 * 3600
for: 1h
labels:
severity: warning
annotations:
summary: "证书即将过期"
description: >
证书 {{ $labels.name }}(命名空间 {{ $labels.namespace }})
将在 {{ $value | humanizeDuration }} 后过期。
- alert: CertificateNotReady
expr: |
certmanager_certificate_ready_status{condition="True"} == 0
for: 15m
labels:
severity: critical
annotations:
summary: "证书未就绪"
description: >
证书 {{ $labels.name }}(命名空间 {{ $labels.namespace }})
已经 15 分钟未处于 Ready 状态。
- alert: CertificateRenewalFailure
expr: |
increase(
certmanager_controller_sync_call_count{
controller="certificates-issuing",
status="error"
}[1h]
) > 0
for: 5m
labels:
severity: critical
annotations:
summary: "证书续期失败"
description: "cert-manager 在过去 1 小时内有证书签发失败。"8.3 外部证书探测
对于非 Kubernetes 环境的证书,可以使用 Blackbox Exporter 或自定义脚本进行外部探测。
#!/bin/bash
# check_cert_expiry.sh
# 检查证书过期时间并输出 Prometheus 格式的指标
DOMAINS="api.example.com cdn.example.com admin.example.com"
for domain in $DOMAINS; do
expiry_date=$(echo | \
openssl s_client -servername "$domain" \
-connect "$domain:443" 2>/dev/null | \
openssl x509 -noout -enddate 2>/dev/null | \
cut -d= -f2)
if [ -n "$expiry_date" ]; then
expiry_epoch=$(date -d "$expiry_date" +%s)
now_epoch=$(date +%s)
remaining_seconds=$((expiry_epoch - now_epoch))
echo "cert_expiry_seconds{domain=\"$domain\"} $remaining_seconds"
else
echo "cert_probe_success{domain=\"$domain\"} 0"
fi
done8.4 CT 日志监控
证书透明(Certificate Transparency,CT)日志是公共的、只能追加的日志,记录了所有公共 CA 签发的证书。通过监控 CT 日志,可以发现是否有人为你的域名签发了未授权的证书。
工具如 CertSpotter 或 Facebook 的 ct-monitor 可以订阅 CT 日志,当发现你的域名出现新证书时发送告警。
告警策略建议:
- 证书剩余有效期 < 30 天 → 信息级(Info)
- 证书剩余有效期 < 14 天 → 警告级(Warning)
- 证书剩余有效期 < 7 天 → 严重级(Critical)
- 证书续期失败 → 严重级(Critical)
- CT 日志发现未知证书 → 严重级(Critical)
九、工程案例
9.1 背景
某电商平台运营着 200 多个微服务,部署在 3 个 Kubernetes 集群上,分布在两个云区域。在实施自动化证书管理之前,团队经历了两次因证书过期导致的严重事故:
- 第一次:支付网关的 TLS 证书过期,支付接口中断 47 分钟,直接经济损失约 120 万元。
- 第二次:内部服务之间的 mTLS 证书过期,订单服务无法调用库存服务,导致用户无法下单,持续 23 分钟。
两次事故的根因相同:证书由运维团队手动管理,续期信息记录在内部 Wiki 中,但没有自动化的过期提醒。
9.2 改造方案
团队用 12 周时间完成了证书管理体系的全面改造。
第一阶段(第 1-3 周):证书清单盘点
首先做的事情是搞清楚”我们到底有多少证书”。团队编写了扫描脚本,遍历所有 Kubernetes 集群的 Secret、所有负载均衡器的证书配置、所有 CDN 的证书绑定,建立了一份完整的证书清单。
结果:共发现 347 张活跃证书,其中 89 张将在 60 天内过期,12 张已经过期但因为客户端没有强制校验而未被发现。
第二阶段(第 4-8 周):部署 cert-manager 和自动化
- 在三个 Kubernetes 集群中部署 cert-manager。
- 为公网域名配置 Let’s Encrypt ClusterIssuer(使用 DNS-01 挑战)。
- 为内部服务配置内部 CA ClusterIssuer。
- 将所有手动管理的 Kubernetes TLS Secret 迁移为 cert-manager Certificate 资源。
- 证书有效期设置为 90 天(公网)和 24 小时(内部 mTLS)。
第三阶段(第 9-10 周):监控和告警
- 部署 Prometheus 告警规则监控 cert-manager 指标。
- 为非 Kubernetes 环境的证书(CDN、负载均衡器)部署外部探测脚本。
- 配置告警通知到 Slack 频道和 PagerDuty 轮值。
第四阶段(第 11-12 周):根 CA 密钥保护
- 将内部根 CA 的私钥从 Kubernetes Secret 迁移到 AWS CloudHSM。
- 执行密钥仪式,生成新的根 CA 密钥对。
- 使用 Shamir 秘密共享将 HSM 的管理员凭证分为 5 片,阈值为 3。
- 分发信任根证书到所有集群。
9.3 改造成果
改造完成后的一年内:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 证书过期导致的事故 | 2 次 / 年 | 0 次 |
| 证书续期方式 | 手动 | 全自动 |
| 平均续期响应时间 | 2-4 小时(人工处理) | 自动完成(无人工介入) |
| 证书清单准确率 | 约 60%(多处遗漏) | 100%(cert-manager 管理) |
| 内部 mTLS 证书有效期 | 1 年 | 24 小时 |
| 根 CA 私钥存储 | Kubernetes Secret | AWS CloudHSM(FIPS 140-2 L3) |
最重要的变化不是技术层面的,而是心理层面的:团队不再担心证书过期。证书管理从一个需要人记住的运维任务,变成了一个自我运行的自动化系统。
9.4 遇到的问题与教训
改造过程并非一帆风顺:
DNS-01 挑战的 API 限流。在初始迁移时,cert-manager 同时为几百个域名申请证书,触发了 DNS 服务商的 API 限流。解决方案:配置 cert-manager 的并发限制,分批迁移。
旧客户端不信任新的内部根 CA。部分遗留系统没有更新信任库,在切换到新内部 CA 后无法连接。解决方案:设置过渡期,新旧 CA 并存,旧 CA 签发的证书在过期前继续使用。
CloudHSM 的延迟。将 CA 签名操作迁移到 CloudHSM 后,签发延迟从亚毫秒增加到 5-10 毫秒。对于日常签发量来说不是问题,但在大规模滚动更新时可能成为瓶颈。解决方案:使用中间 CA 缓存策略,只有中间 CA 的密钥在 HSM 中,叶子证书由在线的中间 CA 快速签发。
十、选型对比
10.1 HSM vs 软件密钥管理
| 对比维度 | HSM(硬件安全模块) | 软件密钥管理(如 Vault) |
|---|---|---|
| 安全等级 | FIPS 140-2/3 Level 3 | FIPS 140-2 Level 1-2 |
| 私钥可导出性 | 不可导出(物理隔离) | 可导出(依赖访问控制) |
| 签名性能 | 硬件加速,RSA 2048 约 1000 次/秒 | 受 CPU 限制,性能更高但安全性更低 |
| 延迟 | 5-10 毫秒(含网络) | 亚毫秒(本地)到低毫秒(远程) |
| 成本 | 高(专用 HSM 每小时 1-2 美元,年费约 1-2 万美元) | 低(开源免费,企业版按节点计费) |
| 运维复杂度 | 高(需要密钥仪式,物理安全,供应商锁定) | 中(需要加固部署,密钥备份策略) |
| 合规要求 | 满足 PCI DSS、金融监管等强制要求 | 可能不满足某些行业的强制要求 |
| 适用场景 | 根 CA 密钥、支付签名密钥、最高等级密钥 | 应用层加密密钥、中间 CA 密钥、API 密钥 |
| 灾备方案 | HSM 集群跨区域同步 | Vault 集群跨区域复制 |
选型建议:
- 根 CA 密钥、支付签名密钥:使用 HSM,这是合规的硬性要求,也是安全的底线。
- 中间 CA 密钥:推荐 HSM,但如果成本受限,可以使用加固的 Vault 部署。
- 应用层加密密钥:软件密钥管理即可,HSM 在这个层面成本效益比不高。
- 开发和测试环境:不需要 HSM,使用 Vault 的开发模式或 cert-manager 的自签名 Issuer。
10.2 长期证书 vs 短期证书
| 对比维度 | 长期证书(月-年) | 短期证书(小时-天) |
|---|---|---|
| 管理方式 | 可以手动管理 | 必须自动化 |
| 攻击窗口 | 大(泄露后长期可用) | 小(自然过期) |
| 撤销依赖 | 强依赖 CRL / OCSP | 弱依赖(自然过期即失效) |
| 基础设施要求 | 低(手动即可) | 高(需要自动化签发基础设施) |
| 故障模式 | 遗忘导致过期 | 自动化故障导致无法签发 |
| 运维认知负担 | 高(需要跟踪过期时间) | 低(系统自动处理) |
| 适合场景 | 遗留系统、合作方系统 | 云原生、微服务、服务网格 |
选型建议:
- 面向公网的 TLS 证书:90 天有效期(Let’s Encrypt 默认),通过 cert-manager 或 ACME 客户端自动续期。
- 内部 mTLS 证书:24 小时或更短,通过 SPIRE 或 cert-manager 自动轮转。
- 代码签名证书、客户端证书:根据业务需求,通常 1-3 年,但必须有到期提醒。
10.3 证书管理工具对比
| 工具 | 类型 | 适用环境 | 签发方式 | 特点 |
|---|---|---|---|---|
| cert-manager | Kubernetes 原生 | Kubernetes | ACME、内部 CA、Vault | 声明式,Kubernetes 深度集成 |
| SPIRE | 工作负载身份 | 任意(Kubernetes / VM) | 内部 CA | SPIFFE 标准,极短期证书 |
| Vault PKI | 通用密钥管理 | 任意 | 内部 CA | 灵活的策略引擎,审计日志 |
| Certbot | ACME 客户端 | Linux 服务器 | ACME | 轻量,适合单机部署 |
| acme.sh | ACME 客户端 | Linux / Docker | ACME | 纯 Shell 脚本,无依赖 |
| step-ca | 小型内部 CA | 任意 | 内部 CA、ACME | 轻量级,支持 ACME 协议 |
十一、总结
证书管理的本质是用自动化替代人的记忆。整个体系的设计可以归结为几条原则:
自动化优先。证书的签发、续期、分发、轮转都应该是自动化的。手动操作只出现在密钥仪式等极少数场景。
缩短有效期。证书有效期越短,攻击窗口越小,对撤销机制的依赖越低。但短期证书的前提是可靠的自动化基础设施。
分层保护密钥。根 CA 密钥用 HSM 保护并离线存储;中间 CA 密钥用 HSM 或加固的软件保护;叶子证书的私钥用标准的 Secret 管理。
监控是安全网。即使自动化流程运行正常,也需要监控来发现自动化本身的故障。证书过期告警是最后一道防线。
内外分离。公网服务使用公共 CA,内部服务使用独立的内部 PKI。两者的信任根、管理策略、有效期可以完全独立。
回到本文开头的问题:如何建立自动化的证书生命周期管理?答案不是某一个工具或某一个流程,而是一套完整的体系——从 CA 架构设计到自动化签发,从密钥保护到监控告警,每个环节都不能有缺口。
在下一篇中,我们将讨论日志架构——另一个看似简单但在生产环境中极其容易出问题的基础设施。
参考资料
- R. Barnes, J. Hoffman-Andrews, D. McCarney, J. Kasten. “Automatic Certificate Management Environment (ACME).” RFC 8555, IETF, 2019.
- D. Cooper, S. Santesson, S. Farrell, S. Boeyen, R. Housley, W. Polk. “Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile.” RFC 5280, IETF, 2008.
- NIST. “Security Requirements for Cryptographic Modules.” FIPS 140-3, 2019.
- SPIFFE Project. “Secure Production Identity Framework for Everyone.” https://spiffe.io/
- cert-manager Project. “Cloud Native Certificate Management.” https://cert-manager.io/docs/
- HashiCorp. “Vault PKI Secrets Engine.” https://developer.hashicorp.com/vault/docs/secrets/pki
- Let’s Encrypt. “How It Works.” https://letsencrypt.org/how-it-works/
- E. Rescorla. “The Transport Layer Security (TLS) Protocol Version 1.3.” RFC 8446, IETF, 2018.
- S. Santesson, M. Myers, R. Ankney, A. Malpani, S. Galperin, C. Adams. “X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP.” RFC 6960, IETF, 2013.
- B. Laurie, A. Langley, E. Kasper. “Certificate Transparency.” RFC 6962, IETF, 2013.
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【密码学百科】PKI 与数字证书:信任链的构建与崩塌
PKI 是互联网信任的基础设施——本文从 X.509 证书结构深入到信任链验证,剖析 CA 失败案例、证书透明度机制,以及 Let's Encrypt 如何改变了证书生态
【系统架构设计百科】架构质量属性:不只是"高可用高性能"
需求评审时写下的'高可用、高性能、高并发',到了架构设计阶段几乎无法落地——因为它们不是可执行的需求。本文从 SEI/CMU 的质量属性理论出发,用 stimulus-response 场景模型把模糊需求变成可量化、可验证的架构约束,并拆解属性之间的冲突与联动关系。
【系统架构设计百科】告警策略:如何避免"狼来了"
大多数团队的告警系统都在制造噪声而不是传递信号。阈值告警看似直观,实则产生大量误报和漏报,值班工程师在凌晨三点被叫醒,却发现只是一次无害的毛刺。本文从告警疲劳的工业数据出发,拆解基于 SLO 的多窗口燃烧率告警算法,深入 Alertmanager 的路由、抑制与分组机制,结合 PagerDuty 的告警疲劳研究和真实工程案例,给出一套可落地的告警策略设计方法。
【系统架构设计百科】复杂性管理:架构的核心战场
系统复杂性是架构腐化的根源——本文从 Brooks 的本质复杂性与偶然复杂性划分出发,结合认知负荷理论与 Parnas 的信息隐藏原则,系统阐述复杂性的来源、度量与控制手段,并给出可操作的架构策略