证书 47 天就过期,还在手动续?聊聊我在 AWS 上的自动化方案

证书 47 天就过期,还在手动续?聊聊我在 AWS 上的自动化方案

上周帮朋友排查一个线上故障,折腾两小时最后发现------证书过期了。就这么简单又离谱的原因。客户打不开页面,浏览器直接甩一个 NET::ERR_CERT_DATE_INVALID,朋友还以为是 DNS 出了问题,查了半天。

更离谱的是,这种事以后会越来越频繁。CA/B Forum 去年通过了 Ballot SC-081,证书有效期会一路缩:398 天 → 200 天(2026.3)→ 100 天(2027.3)→ 47 天(2029.3)。到 2029 年,一个半月不续就挂。你管 5 台机器可能还能记住,管 20 台呢?靠人肉置巡?不现实。

今天分享一下我在亮马逊云科技上搞证书自动化的实践,踩过的坑一并说了。

先选路线:你的证书该谁管

在亚马逊云科技上搞 TLS,第一步是想清楚你属于哪种架构:

架构 方案 操心程度
ALB/CloudFront/API Gateway ACM 托管 完全不操心
EC2 上跑 Nginx/Apache Certbot + ACME 需要配一次
EKS Ingress cert-manager K8s 声明式

如果你的流量经过 ALB 或 CloudFront,直接用 AWS Certificate Manager(ACM),证书免费、自动续期、零配置。申请的时候加个 DNS 验证的 CNAME 记录,后面再也不用管了:

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

但现实是很多团队的服务直接跑在 EC2 上,TLS 终止在 Nginx。ACM 的证书没法导出私钥,装不到 Nginx 里。这种情况就得靠 ACME 协议 + Certbot 了。

Certbot 实战:三种模式怎么选

Nginx 插件模式(日常推荐)

Certbot 的 Nginx 插件会自动在你的 server block 里插入验证 location,验证完删掉,整个过程不中断服务:

bash 复制代码
# 安装
sudo snap install certbot --classic

# 一条命令搞定申请
sudo certbot certonly --nginx \
  -d example.com \
  -d www.example.com \
  --email admin@example.com \
  --agree-tos

装完看一眼证书状态:

bash 复制代码
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

Webroot 模式(不碠 Nginx 配置)

有些同事的 Nginx 配置比较复杂(比如用了大量 include 和自定义 map),不想让 Certbot 自动改配置文件。这时候用 Webroot 模式,自己加一段 location 就行:

nginx 复制代码
server {
    listen 80;
    server_name example.com;
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
}

然后跑:

bash 复制代码
sudo certbot certonly --webroot \
  -w /var/www/certbot \
  -d example.com \
  -d www.example.com \
  --email admin@example.com \
  --agree-tos

Standalone 模式(临时用用)

Certbot 自己起一个 Web 服务器来验证,但它得占 80 端口,意味着你得先停掉 Nginx。生产环境别用这个做续期,适合新机器初次部署还没装 Web 服务器的场景。

bash 复制代码
sudo systemctl stop nginx
sudo certbot certonly --standalone \
  -d example.com \
  --email admin@example.com \
  --agree-tos
sudo systemctl start nginx

自动续期的正确姿势

证书拿到了,怎么自动续?Certbot 的 renew 命令会扫描所有本地证书,距离过期 30 天内的自动续。

bash 复制代码
# 先跑一遍 dry-run,确认流程没问题
sudo certbot renew --dry-run

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

踩坑重点--deploy-hook--post-hook 的区别。

  • --deploy-hook:只在续期成功时执行
  • --post-hook:不管有没有续期都执行

我之前用的 --post-hook,结果每天凌晨 cron 触发都会 reload Nginx,日志里全是无意义的 reload 记录。多张证书的时候更烦,一次 renew 跑完会 reload 好几次。改成 --deploy-hook 后世界清净了。

DigiCert ACME:企业级 OV/EV 也能自动化

Let's Encrypt 发的是 DV(域名验证)证书,免费好用。但有些企业内部合规要求 OV 或 EV 证书,显示公司名。好消息是 DigiCert 现在也支持 ACME 协议了,意味着付费证书也能走同样的自动化流程。

bash 复制代码
# 注册 DigiCert ACME 账户(EAB 凭据从 DigiCert 管理后台获取)
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

# 申请证书
sudo certbot certonly --nginx \
  --server https://acme.digicert.com/v2/acme/directory \
  -d example.com

配好后续期流程跟 Let's Encrypt 完全一样,同一条 cron 通吃,不用额外改任何东西。

多台机器怎么管

单机 Certbot 配一次就好,但如果你有十几台 EC2 呢?一台台 SSH 过去配置不现实。三种思路:

思路一:流量收拢到 ALB。TLS 终止在负载均衡器,后端 EC2 只跑 HTTP。新服务强烈推荐这么干。

思路二:SSM 批量推送。用 AWS Systems Manager Run Command 一次搞定所有机器:

bash 复制代码
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"]'

思路三:EKS 用 cert-manager。K8s 环境下声明一个 ClusterIssuer,证书申请和续期全自动:

yaml 复制代码
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

监控兜底:别光靠自动化

自动化做好了不代表可以完全放手。万一哪天 Let's Encrypt 服务端抽风、DNS 解析出了问题、安全组把 80 端口关了,续期就会失败。加一层监控兜底:

python 复制代码
import ssl, socket, datetime, boto3

def check_cert(domain):
    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')
        return (expiry - datetime.datetime.utcnow()).days

def lambda_handler(event, context):
    for domain in ['example.com', 'api.example.com']:
        days = check_cert(domain)
        if days < 14:
            boto3.client('sns').publish(
                TopicArn='arn:aws:sns:us-east-1:123456789012:cert-alerts',
                Message=f'{domain} 证书还剩 {days} 天,请检查续期是否正常'
            )

用 EventBridge 每天触发一次这个 Lambda 就行。14 天阈值给了你两周的缓冲来人工干预。

我的建议

  1. 能上 ALB + ACM 的优先上,省心到不需要解释
  2. EC2 场景 Certbot + Nginx 插件 + deploy-hook,一次配好长期不管
  3. 企业证书走 DigiCert ACME,同一套自动化流程
  4. 不管哪种方案,都加上监控,14 天阈值告警
  5. 配完立刻跑 --dry-run,别等到真正续期那天才发现配错了

证书有效期缩短已经是定局,趁现在还有 200 天的缓冲期起紧把自动化做了。别等凌晨 2 点被告警叫醒才后悔。


参考: 使用 ACME 协议实现 TLS/SSL 证书自动化 - 亚马逊云科技

相关推荐
威联通网络存储2 天前
基于TS-h3087XU-RP的大型成套空分设备DCS历史趋势数据治理
aws
A小辣椒17 天前
AWS Clould Support Engineer就职面试题
aws
亚林瓜子20 天前
AWS WAF中如何放行某个触发了托管规则的接口
aws·waf
悠悠1213822 天前
AWS DevOps Agent 体验一周后,我决定把 oncall 手机调成静音了
云计算·aws·devops
yyuuuzz22 天前
独立站运营的几个技术层面常见问题
大数据·运维·服务器·网络·数据库·aws
yyuuuzz22 天前
游戏云服务器推荐的技术选择思路
大数据·运维·服务器·游戏·云计算·aws
kernelcraft23 天前
Boto3:Python 操作 AWS 的官方 SDK
开发语言·python·其他·aws
普通网友1 个月前
Serverless 框架:多云函数部署(AWS + 阿里云 + 腾讯云)
阿里云·serverless·aws
TG_yunshuguoji1 个月前
亚马逊云代理商:如何用 CloudWatch+Lambda 打造自动化告警系统
大数据·运维·自动化·云计算·aws
yyuuuzz1 个月前
独立站搭建的几个核心技术问题
运维·服务器·网络·数据库·aws