我们正在进入自动化SSL证书管理(ACM)的时代,通过使用工具如certbot、acme.sh等,我们可以实现SSL/TLS证书的自动申请和续期。下面说一下全面的自动化证书管理方案。

一、自动化证书管理工具比较

工具 支持CA 验证方式 支持的通配符 配置难度 特点

certbot Let's Encrypt HTTP-01, DNS-01 支持 中等 官方推荐,功能强大

acme.sh Let's Encrypt, ZeroSSL等 HTTP-01, DNS-01, TLS-ALPN-01 支持 中等 轻量,支持多CA

lego Let's Encrypt HTTP-01, DNS-01, TLS-ALPN-01 支持 中等 Go编写,易于嵌入

Caddy Let's Encrypt HTTP-01, TLS-ALPN-01 支持 简单 自动HTTPS,内置ACM

二、通用自动化部署流程

1. 使用certbot自动化

bash

安装certbot(以Ubuntu为例)

sudo apt update

sudo apt install certbot python3-certbot-nginx

自动获取并配置证书(适用于Nginx)

sudo certbot --nginx -d example.com -d www.example.com

仅获取证书(不修改配置)

sudo certbot certonly --nginx -d example.com -d www.example.com

使用DNS验证(适用于通配符证书)

sudo certbot certonly --manual --preferred-challenges=dns \

  -d example.com -d *.example.com

设置自动续期(默认已安装cron job)

sudo systemctl status certbot.timer

2. 使用acme.sh自动化

bash

安装acme.sh

curl https://get.acme.sh | sh

source ~/.bashrc

使用HTTP验证

acme.sh --issue -d example.com -d www.example.com \

  --webroot /var/www/html

使用DNS验证(以Cloudflare为例)

export CF_Email="admin@example.com"

export CF_Key="your_cloudflare_api_key"

acme.sh --issue --dns dns_cf \

  -d example.com -d *.example.com

安装证书到Nginx

acme.sh --install-cert -d example.com \

  --key-file /etc/nginx/ssl/example.com.key \

  --fullchain-file /etc/nginx/ssl/example.com.crt \

  --reloadcmd "systemctl reload nginx"

三、DNS验证自动化(通配符证书)

1. 配置DNS API凭证

bash

以Cloudflare为例,其他DNS提供商类似

export CF_Email="admin@example.com"

export CF_Key="your_cloudflare_api_key"

使用acme.sh

acme.sh --issue --dns dns_cf \

  -d example.com -d *.example.com

使用certbot(需要插件)

certbot certonly --dns-cloudflare \

  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \

  -d example.com -d *.example.com

2. DNS插件支持列表

Cloudflare、阿里云、腾讯云、AWS Route53、Google Cloud DNS等

具体配置参考各工具文档

四、容器化环境自动证书管理

1. 使用Docker运行certbot

dockerfile

Dockerfile示例

FROM nginx:alpine

COPY nginx.conf /etc/nginx/nginx.conf

RUN apk add --no-cache certbot certbot-nginx

COPY entrypoint.sh /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

bash

!/bin/bash

entrypoint.sh

启动Nginx

nginx

申请证书(如果不存在)

if [ ! -f /etc/letsencrypt/live/example.com/fullchain.pem ]; then

    certbot --nginx -d example.com -d www.example.com --non-interactive --agree-tos --email admin@example.com

fi

定期续期

while :; do

    每隔12小时检查一次

    sleep 12h

    certbot renew --quiet

done

2. 使用Traefik作为反向代理

yaml

docker-compose.yml

version: '3'

services:

  traefik:

    image: traefik:v2.5

    command:

      - "--api.insecure=true"

      - "--providers.docker=true"

      - "--entrypoints.web.address=:80"

      - "--entrypoints.websecure.address=:443"

      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"

      - "--certificatesresolvers.myresolver.acme.email=admin@example.com"

      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"

    ports:

      - "80:80"

      - "443:443"

      - "8080:8080"

    volumes:

      - "/var/run/docker.sock:/var/run/docker.sock:ro"

      - "./letsencrypt:/letsencrypt"

    labels:

      - "traefik.enable=true"

  web:

    image: nginx:alpine

    labels:

      - "traefik.http.routers.web.rule=Host(`example.com`) || Host(`www.example.com`)"

      - "traefik.http.routers.web.entrypoints=websecure"

      - "traefik.http.routers.web.tls.certresolver=myresolver"

五、Kubernetes自动证书管理

1. 使用cert-manager

yaml

安装cert-manager

kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml

创建ClusterIssuer(Let's Encrypt生产环境)

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

    solvers:

    - http01:

        ingress:

          class: nginx

2. 为Ingress自动申请证书

yaml

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

  name: web-ingress

  annotations:

    cert-manager.io/cluster-issuer: "letsencrypt-prod"

spec:

  tls:

  - hosts:

    - example.com

    - www.example.com

    secretName: example-com-tls

  rules:

  - host: example.com

    http:

      paths:

      - path: /

        pathType: Prefix

        backend:

          service:

            name: web-service

            port:

              number: 80

3. 通配符证书申请(DNS验证)

yaml

apiVersion: cert-manager.io/v1

kind: ClusterIssuer

metadata:

  name: letsencrypt-dns

spec:

  acme:

    server: https://acme-v02.api.letsencrypt.org/directory

    email: admin@example.com

    privateKeySecretRef:

      name: letsencrypt-dns

    solvers:

    - dns01:

        cloudflare:

          email: admin@example.com

          apiKeySecretRef:

            name: cloudflare-api-key

            key: api-key

六、多服务器证书同步

1. 使用rsync同步证书

bash

!/bin/bash

sync-certs.sh

SOURCE_SERVER="primary.example.com"

CERT_PATH="/etc/letsencrypt/live/example.com"

KEY_PATH="/etc/letsencrypt/archive/example.com"

同步到其他服务器

SERVERS=("server1.example.com" "server2.example.com")

for server in "${SERVERS[@]}"; do

    rsync -avz -e ssh \

      $CERT_PATH/fullchain.pem \

      $server:$CERT_PATH/

      rsync -avz -e ssh \

      $KEY_PATH/privkey.pem \

      $server:$KEY_PATH/

    

    重启Web服务器

    ssh $server "systemctl reload nginx"

done

2. 使用分布式配置中心

yaml

使用Consul + consul-template

consul_kv_path: "ssl/example.com"


template {

  source = "/etc/consul-template/cert.crt.tmpl"

  destination = "/etc/nginx/ssl/example.com.crt"

  command = "systemctl reload nginx"

}

3. 使用AWS Secrets Manager

python

import boto3

import os

def upload_cert_to_aws(cert_path, key_path):

    """上传证书到AWS Secrets Manager"""

    client = boto3.client('secretsmanager')

    with open(cert_path, 'r') as f:

        cert = f.read()

    with open(key_path, 'r') as f:

        key = f.read()

     secret_value = {

        'certificate': cert,

        'private_key': key

    }

    response = client.update_secret(

        SecretId='production/ssl/example.com',

        SecretString=json.dumps(secret_value)

    )

    return response

七、监控与告警

1. 证书过期监控

bash

!/bin/bash

check-certs.sh

DOMAINS=("example.com" "www.example.com" "api.example.com")

for domain in "${DOMAINS[@]}"; do

    expiry_date=$(openssl s_client -servername $domain -connect $domain:443 2>/dev/null | \

                  openssl x509 -noout -enddate | cut -d= -f2)

    expiry_epoch=$(date -d "$expiry_date" +%s)

    current_epoch=$(date +%s)

    days_left=$(( ($expiry_epoch - $current_epoch) / 86400 ))

    if [ $days_left -lt 7 ]; then

        echo "警告: $domain 证书将在 $days_left 天后过期"

        发送告警邮件或Slack消息

    fi

done

2. Prometheus监控

yaml

ssl_exporter配置

scrape_configs:

  - job_name: 'ssl'

    static_configs:

      - targets:

        - example.com:443

        - www.example.com:443

    relabel_configs:

      - source_labels: [__address__]

        target_label: __param_target

      - source_labels: [__param_target]

        target_label: instance

      - target_label: __address__

        replacement: ssl-exporter:9219

告警规则

groups:

- name: ssl

  rules:

  - alert: SSLCertExpiringSoon

    expr: probe_ssl_earliest_cert_expiry{job="ssl"} - time() < 86400 * 7

    for: 5m

    labels:

      severity: warning

    annotations:

      description: '证书将在7天内过期 (instance {{ $labels.instance }})'

八、备份与恢复

1. 备份证书和密钥

bash

!/bin/bash

backup-certs.sh

BACKUP_DIR="/backup/letsencrypt"

DATE=$(date +%Y%m%d)

备份整个letsencrypt目录

tar -czf $BACKUP_DIR/letsencrypt-$DATE.tar.gz /etc/letsencrypt

备份Nginx配置

tar -czf $BACKUP_DIR/nginx-ssl-$DATE.tar.gz /etc/nginx/conf.d

上传到云存储

aws s3 cp $BACKUP_DIR/letsencrypt-$DATE.tar.gz s3://backup-bucket/ssl/

2. 恢复证书

bash

!/bin/bash

restore-certs.sh

BACKUP_FILE="letsencrypt-20230101.tar.gz"

停止Web服务器

systemctl stop nginx

恢复备份

tar -xzf /backup/$BACKUP_FILE -C /

修复权限

chown -R root:root /etc/letsencrypt

chmod -R 700 /etc/letsencrypt/archive

启动Web服务器

systemctl start nginx

九、最佳实践

使用DNS验证:适用于通配符证书和隐藏服务器

分离验证和部署:先获取证书,再部署到服务器

设置合理的续期时间:提前30天续期

监控证书状态:设置过期告警

备份证书:定期备份证书和私钥

使用生产环境CA:测试使用staging,生产使用production

限制证书权限:私钥文件权限设置为600

十、故障排除

1. 常见错误

bash

错误:Too many certificates already issued

解决:Let's Encrypt有速率限制,等待或使用已有证书

错误:Challenge failed

解决:检查DNS解析或HTTP服务是否可访问

错误:私钥权限问题

解决:chmod 600 private.key

2. 调试命令

bash

查看证书详情

openssl x509 -in certificate.crt -text -noout

测试连接

openssl s_client -connect example.com:443 -servername example.com

检查证书链

openssl verify -untrusted intermediate.crt certificate.crt

用户通过以上自动化方案,可以彻底告别手动续期SSL证书的繁琐工作,确保网站持续提供安全的HTTPS服务。