TLS/SSL 证书有效期缩短到 47 天:用 ACME 协议在亚马逊云科技上实现证书全自动续期

CA/B Forum 2025 年 4 月投票通过了证书有效期缩减路线图。2026 年 3 月起 200 天,2027 年 3 月起 100 天,2029 年 3 月起只有 47 天。手动续期?别想了,必须自动化。

背景:证书有效期为什么越来越短

我第一次知道这事是去年底,看到 Apple 在 CA/B Forum 上提的 Ballot SC-081 终于通过了。简单说就是:

时间节点证书有效期DCV 有效期
2025.4 之前398 天398 天
2026.3.15200 天200 天
2027.3.15100 天100 天
2029.3.1547 天10 天

说白了,到 2029 年你的证书一个半月就过期。一台服务器还好,十台二十台呢?手动登上去 renew 一遍?周末忘了续期直接 503?

这不是杞人忧天——我去年就被坑过一次。某个边缘节点的证书过期了,凌晨 2 点告警把我叫醒,排查半天发现是 cron 任务写错了路径。从那以后我就开始认真研究自动化方案。 EC2 上的 Nginx/Apache 里。这就引出了第二条路。

路线二:ACME 协议自动申请+续期(EC2 自管场景)

如果你在 EC2 上跑 Nginx、跑 Apache、跑自建 K8s Ingress,就得用 ACME 协议自己管证书了。好消息是 Certbot 和 acme.sh 都很成熟,配合 cron 可以做到全自动。

Certbot 三种验证模式详解

Certbot 支持三种方式获取证书,适用场景不一样:

模式一:Standalone(独占 80 端口)

Certbot 自己起一个临时 Web 服务器来完成验证。适合还没部署 Web 服务、或者可以短暂停服的场景。

# 先停掉 Nginx
sudo systemctl stop nginx

# 申请证书
sudo certbot certonly --standalone \
  -d example.com \
  -d www.example.com \
  --email admin@example.com \
  --agree-tos \
  --no-eff-email

# 重新启动 Nginx
sudo systemctl start nginx

缺点明显:续期的时候也得停服。生产环境别用这个。

模式二:Webroot(不中断服务)

让 Certbot 把验证文件写到 Nginx 的 webroot 目录,CA 来访问验证。服务不中断。

# Nginx 配置里加一段
# server {
#     listen 80;
#     server_name example.com;
#     location /.well-known/acme-challenge/ {
#         root /var/www/certbot;
#     }
# }

sudo certbot certonly --webroot \
  -w /var/www/certbot \
  -d example.com \
  -d www.example.com \
  --email admin@example.com \
  --agree-tos

模式三:Nginx 插件(一步到位)

Certbot 的 Nginx 插件会自动修改 Nginx 配置、完成验证、安装证书。最省事。

sudo certbot certonly --nginx \
  -d example.com \
  -d www.example.com \
  --email admin@example.com \
  --agree-tos

# 或者连安装也一起做(自动改 nginx.conf)
sudo certbot --nginx \
  -d example.com \
  -d www.example.com

装完之后看一眼证书信息:

sudo certbot certificates
# 输出类似:
# Certificate Name: example.com
# Domains: example.com www.example.com
# Expiry Date: 2026-09-15 (VALID: 89 days)
# Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
# Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem

自动续期:一条 cron 搞定

Certbot 续期命令是 certbot renew,它会检查所有证书,距离过期 30 天以内的自动续。

# 测试续期(不会真的续,只是跑一遍流程)
sudo certbot renew --dry-run

# 写进 crontab,每天凌晨 2:30 跑一次
echo "30 2 * * * root certbot renew --quiet --deploy-hook 'systemctl reload nginx'" | sudo tee /etc/cron.d/certbot-renew

--deploy-hook 是关键——续期成功后自动 reload Nginx,让新证书生效。不加这个的话证书续了但 Nginx 还在用旧的,等于白续。

踩坑记录:我之前写成了 --post-hook,这个不管续没续都会跑。如果你有多张证书,每次 cron 触发都会 reload 一次 Nginx,虽然无害但日志会很吵。用 --deploy-hook 只在真正续期成功时才执行。

进阶:DigiCert ACME 支持(企业级 OV/EV 证书)

Let’s Encrypt 发的是 DV 证书,不少企业内部要求 OV 或 EV。好消息是 DigiCert 现在也支持 ACME 协议了,意味着你可以用同样的自动化流程来管理付费证书。

# DigiCert ACME 目录 URL
DIGICERT_ACME_URL="https://acme.digicert.com/v2/acme/directory"

# 用 certbot 注册 DigiCert 账户
sudo certbot register \
  --server $DIGICERT_ACME_URL \
  --email admin@example.com \
  --agree-tos \
  --eab-kid "YOUR_KEY_ID" \
  --eab-hmac-key "YOUR_HMAC_KEY"

# 申请证书
sudo certbot certonly --nginx \
  --server $DIGICERT_ACME_URL \
  -d example.com

EAB(External Account Binding)的 Key ID 和 HMAC Key 从 DigiCert 管理后台获取。配置好之后续期流程和 Let’s Encrypt 一模一样。

多台 EC2 的证书管理方案

如果你有十几台 EC2 都需要证书,一台台 SSH 上去配 Certbot 显然不现实。几个方案:

方案一:集中式——用 ELB 卸载 TLS

把所有 EC2 放到 ALB 后面,TLS 终止在 ALB,证书用 ACM 管理。EC2 只处理 HTTP 流量。这是最推荐的架构。

方案二:自动化——Ansible/SSM 批量配置

用 AWS Systems Manager Run Command 批量执行 Certbot 安装和配置:

# 通过 SSM 在所有带 tag 的实例上安装 certbot
aws ssm send-command \
  --targets "Key=tag:Role,Values=webserver" \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=["sudo snap install certbot --classic","sudo certbot certonly --nginx -d $(hostname -f) --agree-tos --email admin@example.com -n"]'

方案三:容器化——cert-manager

如果跑 EKS,用 cert-manager 配合 Let’s Encrypt 自动管理 Ingress 证书:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx

监控证书过期:别等告警了才发现

自动化做好之后,还需要兜底监控。推荐用 CloudWatch + Lambda 做证书过期检查:

import ssl
import socket
import datetime
import boto3

def check_cert_expiry(domain, port=443):
    ctx = ssl.create_default_context()
    with ctx.wrap_socket(socket.socket(), server_hostname=domain) as s:
        s.settimeout(5)
        s.connect((domain, port))
        cert = s.getpeercert()
        expiry = datetime.datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
        days_left = (expiry - datetime.datetime.utcnow()).days
        return days_left

def lambda_handler(event, context):
    domains = ['example.com', 'api.example.com', 'admin.example.com']
    sns = boto3.client('sns')
    
    for domain in domains:
        days = check_cert_expiry(domain)
        if days < 14:
            sns.publish(
                TopicArn='arn:aws:sns:us-east-1:123456789012:cert-alerts',
                Subject=f'证书即将过期: {domain}',
                Message=f'{domain} 的证书还有 {days} 天过期,请检查自动续期是否正常。'
            )

决策树:我该选哪个方案

场景推荐方案理由
业务在 ALB/CloudFront 后面ACM零运维,自动续期
EC2 直接暴露,DV 证书够用Certbot + cron免费,成熟
企业要求 OV/EV 证书DigiCert ACME + Certbot自动化管理付费证书
K8s/EKS 环境cert-manager云原生方式
大量 EC2 需要统一管理ALB 集中卸载,或 SSM 批量降低运维复杂度

总结

证书有效期缩短是大势所趋,从 398 天到 47 天这条路已经定了。与其到 2029 年手忙脚乱,不如现在就把自动化搭好:

  1. 能用 ACM 就用 ACM,零成本零运维
  2. EC2 自管场景用 Certbot,--deploy-hook 别忘了
  3. cron 是续期的核心,写完跑一遍 --dry-run
  4. 加上监控兜底,14 天阈值告警

别等证书过期那天凌晨被叫醒才后悔。


参考资料:

更多推荐