我们正在进入自动化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服务。