深入 ACME 协议:在亚马逊云科技 EC2 上实现 TLS 证书全生命周期自动管理

引言:证书缩期的技术必然性

CA/B Forum 的 Ballot SC-081 确定了证书有效期缩减路线:398 天 → 200 天(2026.3)→ 100 天(2027.3)→ 47 天(2029.3)。背后的逻辑很清楚——缩短有效期能降低私钥泄露的风险窗口,但代价是运维复杂度指数级上升。

我管理着十几台 EC2 实例上的证书,去年手动续期还能应付,现在已经开始全面切自动化了。这篇文章把我的方案、配置和踩坑经验完整记录下来。

一、方案全景:ACM 托管 vs ACME 自管

ACM 托管:零运维方案

如果你的架构是 客户端 → ALB/CloudFront → EC2,TLS 终止在 ALB 上,那用 ACM 就够了:

aws acm request-certificate \
  --domain-name "*.example.com" \
  --validation-method DNS \
  --region us-east-1

ACM 证书免费、自动续期、跟 ALB/CloudFront 无缝集成。限制是不能导出私钥,没法直接装到 Nginx 上。

ACME 自管:EC2 直接终止 TLS 的场景

当 EC2 直接暴露给用户(没有 LB),或者有特殊的证书需求(OV/EV、自定义 CA),就得用 ACME 协议手动管理了。

ACME(Automatic Certificate Management Environment)是 Let's Encrypt 推动的标准协议(RFC 8555),核心流程:

  1. 客户端向 CA 发起证书申请
  2. CA 下发验证挑战(HTTP-01 或 DNS-01)
  3. 客户端完成挑战,证明域名所有权
  4. CA 签发证书
  5. 到期前重复上述流程完成续期

二、Certbot 三种验证模式的技术对比

Standalone 模式

sudo certbot certonly --standalone \
  -d example.com -d www.example.com \
  --email admin@example.com --agree-tos --no-eff-email

原理:Certbot 在 80 端口起临时 HTTP 服务器。CA 访问 http://example.com/.well-known/acme-challenge/<token> 完成验证。

适用场景:初始部署、还没装 Web 服务器。
致命缺点:续期时也需要停服占端口。

Webroot 模式

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

原理:Certbot 把挑战文件写到指定目录,由已运行的 Nginx 提供访问。需要在 Nginx 配置中加:

server {
    listen 80;
    server_name example.com;
    
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    
    location / {
        return 301 https://$host$request_uri;
    }
}

适用场景:不想让 Certbot 碰 Nginx 配置的洁癖选手。

Nginx 插件模式

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

原理:Certbot 通过 Nginx 插件直接修改 server block,临时插入验证 location,验证完删掉。

适用场景:大多数 EC2 + Nginx 的标准部署。推荐首选。

三种模式对比

维度 Standalone Webroot Nginx 插件
是否中断服务
是否修改 Nginx 配置 需手动加 location 自动处理
续期友好度
生产环境推荐 ✓(首选)

三、自动续期的完整配置

基础 cron 配置

# 验证续期流程
sudo certbot renew --dry-run

# 生产 cron
echo "30 2 * * * root certbot renew --quiet --deploy-hook 'systemctl reload nginx'" \
  | sudo tee /etc/cron.d/certbot-renew
sudo chmod 644 /etc/cron.d/certbot-renew

deploy-hook vs post-hook 的区别

这是一个我踩过的坑:

  • --deploy-hook:只在证书实际被续期时执行
  • --post-hook:不管有没有续期,每次 renew 命令结束都执行
  • --pre-hook:每次 renew 命令开始前执行

多证书场景下,用 --post-hook 会导致每次 cron 触发都 reload Nginx。虽然不会出问题,但日志很吵。

续期失败告警

# /etc/cron.d/certbot-renew
30 2 * * * root certbot renew --quiet --deploy-hook 'systemctl reload nginx' || curl -X POST https://hooks.slack.com/xxx -d '{"text":"证书续期失败,请检查"}'

四、DigiCert ACME:企业级证书自动化

Let's Encrypt 只签发 DV 证书。如果合规要求 OV/EV,可以用 DigiCert ACME:

# 注册账户(EAB 凭据从 DigiCert CertCentral 获取)
sudo certbot register \
  --server https://acme.digicert.com/v2/acme/directory \
  --eab-kid "YOUR_KEY_ID" \
  --eab-hmac-key "YOUR_HMAC_KEY" \
  --agree-tos --email admin@example.com

# 申请证书(后续续期跟 LE 一样走 cron)
sudo certbot certonly --nginx \
  --server https://acme.digicert.com/v2/acme/directory \
  -d example.com

五、多台 EC2 的规模化管理

SSM 批量推送

aws ssm send-command \
  --targets "Key=tag:Role,Values=webserver" \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=[
    "snap install certbot --classic",
    "certbot certonly --nginx -d $(hostname -f) --agree-tos -m admin@example.com -n",
    "echo \"30 2 * * * root certbot renew --quiet --deploy-hook systemctl reload nginx\" > /etc/cron.d/certbot-renew"
  ]'

架构升级:收拢到 ALB

如果团队规模允许,最好的方案还是把 TLS 终止收拢到 ALB + ACM。EC2 只处理 HTTP,彻底不碰证书。

六、监控层:Lambda 证书探测

import ssl, socket, datetime, boto3

def lambda_handler(event, context):
    domains = ['example.com', 'api.example.com']
    for domain in domains:
        ctx = ssl.create_default_context()
        with ctx.wrap_socket(socket.socket(), server_hostname=domain) as s:
            s.settimeout(5)
            s.connect((domain, 443))
            cert = s.getpeercert()
            expiry = datetime.datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
            days_left = (expiry - datetime.datetime.utcnow()).days
            if days_left < 14:
                boto3.client('sns').publish(
                    TopicArn='arn:aws:sns:us-east-1:123456789012:cert-alerts',
                    Message=f'{domain} 证书还剩 {days_left} 天过期'
                )

用 EventBridge 每天触发一次,14 天阈值告警。

总结

场景 推荐方案
ALB/CloudFront 后面 ACM 托管
EC2 自管 DV 证书 Certbot + Nginx 插件 + cron
企业 OV/EV 需求 DigiCert ACME + Certbot
EKS 容器环境 cert-manager + ClusterIssuer
多台 EC2 批量管理 SSM Run Command 或架构升级到 ALB

证书有效期缩短到 47 天不是 if 而是 when。现在花半小时配好自动化,比以后每个月手动续期省心得多。


参考资料:

posted @ 2026-05-04 08:03  亚马逊云开发者  阅读(4)  评论(0)    收藏  举报