SSL 证书自动化系统架构文档
📋 目录
- [1. 系统概述](#1. 系统概述)
- [2. 架构设计](#2. 架构设计)
- [3. 核心脚本说明](#3. 核心脚本说明)
- [4. 技术实现细节](#4. 技术实现细节)
- [5. 部署流程](#5. 部署流程)
- [6. 配置说明](#6. 配置说明)
- [7. 使用示例](#7. 使用示例)
- [8. 工作流程](#8. 工作流程)
- [9. 故障处理](#9. 故障处理)
- [10. 最佳实践](#10. 最佳实践)
- [11. 安全建议](#11. 安全建议)
- [12. 监控和维护](#12. 监控和维护)
1. 系统概述
1.1 目标
构建一个完整的 SSL 证书自动化管理系统,实现:
- ✅ 自动申请 Let's Encrypt SSL 证书
- ✅ 自动配置 Nginx SSL
- ✅ 自动续签即将过期的证书
- ✅ 支持单域名、多域名和通配符证书
- ✅ 完善的监控和告警机制
- ✅ 详细的日志记录
1.2 技术栈
| 组件 | 技术选型 | 说明 |
|---|---|---|
| 证书颁发机构 | Let's Encrypt | 免费的 SSL/TLS 证书 |
| 证书客户端 | Certbot | 官方推荐的 Let's Encrypt 客户端 |
| Web 服务器 | Nginx | 反向代理和 SSL 终止 |
| Web 服务器 | Apache | 可选的 Web 服务器支持 |
| DNS 提供商 | 阿里云/腾讯云/Cloudflare | 用于通配符证书的 DNS 验证 |
| 定时任务 | Cron | 自动续签的定时任务 |
| 日志系统 | Syslog + 独立日志文件 | 详细的操作日志 |
1.3 支持的场景
| 场景 | 验证方式 | 适用性 |
|---|---|---|
| 单域名证书 | HTTP-01 | ✅ 推荐,最简单 |
| 多域名证书 (SAN) | HTTP-01 | ✅ 推荐,支持多个域名 |
| 通配符证书 | DNS-01 | ✅ 推荐,支持所有子域名 |
| 内网域名 | DNS-01 | ⚠️ 需要外部 DNS |
| 动态 IP | DNS-01 | ✅ 推荐 |
2. 架构设计
2.1 系统架构图
┌─────────────────────────────────────────────────────────────┐
│ SSL 自动化管理系统 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ ssl-apply.sh │──────▶│ Certbot │──────▶│ Let's │ │
│ │ 证书申请 │ │ 证书客户端 │ │ Encrypt │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────┐ │
│ │ │ 证书文件 │ │
│ │ └──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐│
│ │ ssl-renew.sh │◀─────────────────────────│ Nginx SSL ││
│ │ 证书续签 │ │ 配置自动化 ││
│ └──────────────┘ └──────────────┘│
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐│
│ │ssl-wildcard │ │ HTTP/HTTPS ││
│ │通配符证书 │ │ 自动重定向 ││
│ └──────────────┘ └──────────────┘│
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐│
│ │ ssl-info.sh │ │ HTTPS 访问 ││
│ │ 证书信息查询 │ │ (443) ││
│ └──────────────┘ └──────────────┘│
│ │
└─────────────────────────────────────────────────────────────┘
2.2 目录结构
deploy/
├── ssl-apply.sh # SSL 证书申请脚本
├── ssl-renew.sh # SSL 证书续签脚本
├── ssl-wildcard.sh # 通配符证书申请脚本
├── ssl-info.sh # 证书信息查询脚本
├── ssl-config.sh # SSL 配置文件
└── ssl-README.md # SSL 使用文档
/etc/letsencrypt/ # Let's Encrypt 根目录
├── live/ # 活跃证书(符号链接)
│ └── example.com/
│ ├── cert.pem # 服务器证书
│ ├── chain.pem # 中间证书
│ ├── fullchain.pem # 完整证书链
│ └── privkey.pem # 私钥
├── archive/ # 证书归档
└── renewal/ # 续签配置
/etc/nginx/
├── sites-available/ # 可用站点配置
│ └── example.com-ssl.conf
└── sites-enabled/ # 启用站点配置(符号链接)
└── example.com-ssl.conf -> ../sites-available/example.com-ssl.conf
/var/log/
├── ssl-automation/ # SSL 自动化日志
│ ├── apply.log # 申请日志
│ ├── renew.log # 续签日志
│ └── error.log # 错误日志
└── nginx/ # Nginx 日志
├── example.com-access.log
└── example.com-error.log
2.3 数据流程图
用户请求
│
▼
HTTP (80) ────────────────────┐
│ │
▼ │
Nginx │
│ │
├── /.well-known/ ────────▶ Let's Encrypt 验证
│ /acme-challenge/ (HTTP-01)
│
└── 其他请求 ───────────────▶ 重定向到 HTTPS
(301)
│
▼
HTTPS (443)
│
▼
Nginx
│
▼
反向代理
│
▼
应用服务
3. 核心脚本说明
3.1 ssl-apply.sh - 证书申请脚本
功能描述:
- 自动检测和安装 Certbot
- 检查 DNS 解析和端口占用
- 申请 Let's Encrypt SSL 证书
- 自动配置 Nginx SSL
- 配置 HTTP 到 HTTPS 重定向
- 开放防火墙端口
输入参数:
bash
./ssl-apply.sh [domain] [email]
| 参数 | 说明 | 示例 | 必填 |
|---|---|---|---|
domain |
域名(支持多域名,逗号分隔) | example.com,www.example.com |
✅ |
email |
证书续签提醒邮箱 | admin@example.com |
❌ (默认: admin@domain) |
主要流程:
开始
│
├─▶ 检查 root 权限
│
├─▶ 检查参数完整性
│
├─▶ 检查 Certbot 安装状态 ──▶ 未安装则安装
│
├─▶ 检查 DNS 解析
│
├─▶ 检查端口 80/443
│
├─▶ 检查 Nginx 安装状态 ──▶ 未安装则询问是否安装
│
├─▶ 检查防火墙 ──▶ 开放 80/443 端口
│
├─▶ 申请 SSL 证书
│
├─▶ 配置 Nginx SSL
│
├─▶ 测试 Nginx 配置
│
├─▶ 重载 Nginx
│
└─▶ 显示证书信息
支持的验证方式:
- ✅ HTTP-01: 通过 Web 服务器验证(默认)
- ✅ Standalone: 临时启动独立服务器验证
输出示例:
bash
$ sudo ./ssl-apply.sh example.com admin@example.com
╔════════════════════════════════════════════════════════════╗
║ SSL 证书自动申请脚本 ║
║ Let's Encrypt Certificate Automation ║
╚════════════════════════════════════════════════════════════╝
[INFO] 域名: example.com
[INFO] 邮箱: admin@example.com
[SUCCESS] Certbot 已安装
[SUCCESS] DNS 解析正确: example.com -> 1.2.3.4
[SUCCESS] 80 端口正在监听
[INFO] 开始申请 SSL 证书...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
[SUCCESS] SSL 证书申请成功!
[SUCCESS] Nginx SSL 配置文件已创建
[SUCCESS] Nginx 配置测试通过
[SUCCESS] Nginx 已重新加载
3.2 ssl-renew.sh - 证书续签脚本
功能描述:
- 自动检查所有证书的有效期
- 自动续签即将过期的证书
- 续签后自动重载 Nginx
- 续签失败时发送邮件通知
- 支持测试模式(dry-run)
- 设置自动续签的 Cron 任务
输入参数:
bash
./ssl-renew.sh [domain] [--dry-run] [--setup] [--email]
| 参数 | 说明 | 示例 | 必填 |
|---|---|---|---|
domain |
指定域名(不指定则续签所有) | example.com |
❌ |
--dry-run |
测试模式,不实际续签 | --dry-run |
❌ |
--setup |
设置 Cron 定时任务 | --setup |
❌ |
--email |
发送续签通知邮件 | --email admin@example.com |
❌ |
主要流程:
开始
│
├─▶ 检查 root 权限
│
├─▶ 检查 Certbot 安装状态
│
├─▶ 检查是否为测试模式
│ ├── 是 ──▶ 运行 certbot renew --dry-run
│ └── 否 ──▶ 继续执行续签
│
├─▶ 检查证书有效期
│ ├── 所有证书都有效 ──▶ 退出
│ └── 存在即将过期证书 ──▶ 继续
│
├─▶ 执行证书续签
│
├─▶ 检查续签结果
│ ├── 成功 ──▶ 重载 Nginx
│ └── 失败 ──▶ 记录错误 + 发送邮件通知
│
└─▶ 记录续签日志
自动续签策略:
- Certbot 默认在证书有效期少于 30 天时才续签
- 建议设置每日检查一次(凌晨 3 点)
- 续签失败会保留旧证书,不影响服务
Cron 配置示例:
bash
# 每天凌晨 3 点检查续签
0 3 * * * /path/to/deploy/ssl-renew.sh >> /var/log/ssl-automation/renew.log 2>&1
# 或每周一凌晨 2 点检查续签
0 2 * * 1 /path/to/deploy/ssl-renew.sh >> /var/log/ssl-automation/renew.log 2>&1
输出示例:
bash
$ sudo ./ssl-renew.sh
[INFO] 开始检查证书续签...
[INFO] 检查到 2 个证书
[INFO] example.com - 剩余 89 天(无需续签)
[WARN] old-domain.com - 剩余 25 天(即将过期)
[INFO] 开始续签证书...
Processing /etc/letsencrypt/renewal/old-domain.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/old-domain.com/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[SUCCESS] 证书续签成功
[INFO] 重新加载 Nginx...
[SUCCESS] Nginx 已重新加载
3.3 ssl-wildcard.sh - 通配符证书申请脚本
功能描述:
- 申请通配符证书(
*.example.com) - 支持 DNS-01 验证
- 支持多个 DNS 提供商(阿里云、腾讯云、Cloudflare)
- 自动添加和删除 DNS TXT 记录
- 自动配置 Nginx
输入参数:
bash
./ssl-wildcard.sh [domain] [email] [--dns-provider provider] [--api-key key] [--secret-key key]
| 参数 | 说明 | 示例 | 必填 |
|---|---|---|---|
domain |
域名 | example.com |
✅ |
email |
证书续签提醒邮箱 | admin@example.com |
❌ |
--dns-provider |
DNS 提供商 | aliyun, tencent, cloudflare |
❌ (默认: aliyun) |
--api-key |
API 访问密钥 | your-api-key |
✅ (DNS 验证需要) |
--secret-key |
API 密钥(部分服务商需要) | your-secret-key |
❌ |
支持的 DNS 提供商:
| 提供商 | 参数说明 | 获取地址 |
|---|---|---|
| 阿里云 | --api-key AccessKeyID, --secret-key AccessKeySecret |
访问控制台 |
| 腾讯云 | --api-key SecretId, --secret-key SecretKey |
访问控制台 |
| Cloudflare | --api-key API Token |
API Token |
主要流程:
开始
│
├─▶ 检查 root 权限
│
├─▶ 检查参数完整性
│
├─▶ 检查 DNS 提供商 API 凭证
│
├─▶ 生成 DNS 验证用的 TXT 记录
│
├─▶ 调用 DNS API 添加 TXT 记录
│
├─▶ 等待 DNS 生效(最多 60 秒)
│
├─▶ 执行证书申请
│
├─▶ 检查申请结果
│ ├── 成功 ──▶ 继续
│ └── 失败 ──▶ 清理 DNS TXT 记录并退出
│
├─▶ 配置 Nginx SSL
│
├─▶ 删除 DNS TXT 记录
│
└─▶ 显示证书信息
DNS 记录示例:
bash
# Certbot 会生成类似这样的记录
_acme-challenge.example.com. IN TXT "xxxxxxxxxxxxxxxxxxxxx"
# 阿里云 API 添加记录示例
aliyun dns add RecordType=TXT \
DomainName=example.com \
RR=_acme-challenge \
Value=xxxxxxxxxxxxxxxxxxxxx
输出示例:
bash
$ sudo ./ssl-wildcard.sh example.com admin@example.com --dns-provider aliyun --api-key LTAIxxxx --secret-key xxxxxx
[INFO] 域名: example.com
[INFO] 邮箱: admin@example.com
[INFO] DNS 提供商: aliyun
[INFO] 开始申请通配符证书...
[INFO] 生成 DNS 验证记录...
[INFO] DNS 记录: _acme-challenge.example.com -> xxxxxxxxxxxxxxxxxxxxx
[INFO] 添加 DNS TXT 记录...
[SUCCESS] DNS 记录已添加
[INFO] 等待 DNS 生效...
[INFO] DNS 已生效(耗时 5 秒)
[INFO] 申请证书...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
[SUCCESS] 通配符证书申请成功!
[SUCCESS] 清理 DNS TXT 记录...
[SUCCESS] DNS 记录已删除
3.4 ssl-info.sh - 证书信息查询脚本
功能描述:
- 查看所有已安装的证书
- 查看指定域名的证书详情
- 检查证书是否即将过期
- 显示证书的到期时间
- 导出证书信息
输入参数:
bash
./ssl-info.sh [domain] [--check-expiry] [--export format] [--output file]
| 参数 | 说明 | 示例 | 必填 |
|---|---|---|---|
domain |
指定域名 | example.com |
❌ |
--check-expiry |
检查即将过期证书(30天内) | --check-expiry |
❌ |
--export |
导出格式(json, csv) | --export json |
❌ |
--output |
输出文件 | --output /path/to/file.json |
❌ |
主要流程:
开始
│
├─▶ 检查 root 权限
│
├─▶ 检查是否有指定域名
│ ├── 有 ──▶ 显示该域名证书信息
│ └── 无 ──▶ 显示所有证书列表
│
├─▶ 检查是否启用到期检查
│ ├── 是 ──▶ 检查并显示即将过期的证书
│ └── 否 ──▶ 跳过
│
├─▶ 检查是否需要导出
│ ├── 是 ──▶ 导出证书信息到文件
│ └── 否 ──▶ 显示到终端
│
└─▶ 完成
证书信息输出格式:
bash
$ sudo ./ssl-info.sh example.com
╔════════════════════════════════════════════════════════════╗
║ SSL 证书信息 ║
╚════════════════════════════════════════════════════════════╝
域名: example.com
证书状态: 有效
颁发机构: Let's Encrypt
颁发日期: 2025-01-12 00:00:00 UTC
到期日期: 2025-04-12 00:00:00 UTC
剩余天数: 90 天
证书文件:
- 证书文件: /etc/letsencrypt/live/example.com/fullchain.pem
- 私钥文件: /etc/letsencrypt/live/example.com/privkey.pem
- 证书链: /etc/letsencrypt/live/example.com/chain.pem
Nginx 配置:
- 配置文件: /etc/nginx/sites-available/example.com-ssl.conf
导出 JSON 格式示例:
bash
$ sudo ./ssl-info.sh --export json --output certs.json
{
"certificates": [
{
"domain": "example.com",
"status": "valid",
"issuer": "Let's Encrypt",
"issued_date": "2025-01-12T00:00:00Z",
"expiry_date": "2025-04-12T00:00:00Z",
"days_remaining": 90,
"cert_path": "/etc/letsencrypt/live/example.com/fullchain.pem",
"key_path": "/etc/letsencrypt/live/example.com/privkey.pem"
}
]
}
4. 技术实现细节
4.1 Certbot 核心命令
证书申请
HTTP-01 验证(推荐,最简单):
bash
# 使用 Nginx 插件自动配置
certbot certonly --nginx \
--email user@example.com \
--agree-tos \
--no-eff-email \
-d example.com \
-d www.example.com
# 使用 Webroot 模式(手动指定 Web 根目录)
certbot certonly --webroot \
--webroot-path /var/www/html \
--email user@example.com \
--agree-tos \
--no-eff-email \
-d example.com
DNS-01 验证(通配符证书):
bash
# 手动模式(需要手动添加 DNS TXT 记录)
certbot certonly --manual \
--preferred-challenges dns \
--email user@example.com \
--agree-tos \
--no-eff-email \
-d "*.example.com" \
-d example.com
# 使用 DNS 插件(需要安装对应插件)
certbot certonly --dns-aliyun \
--dns-aliyun-credentials /path/to/aliyun.ini \
--email user@example.com \
--agree-tos \
--no-eff-email \
-d "*.example.com" \
-d example.com
Standalone 模式(临时独立服务器):
bash
certbot certonly --standalone \
--email user@example.com \
--agree-tos \
--no-eff-email \
-d example.com
证书续签
基本续签:
bash
# 续签所有即将过期的证书
certbot renew
# 续签指定证书
certbot renew --cert-name example.com
# 强制续签(即使未到期)
certbot renew --force-renewal
续签后操作:
bash
# 续签后重载 Nginx
certbot renew --post-hook "systemctl reload nginx"
# 续签后重启服务
certbot renew --post-hook "systemctl restart nginx"
# 续签后执行自定义脚本
certbot renew --post-hook "/path/to/script.sh"
测试续签:
bash
# 模拟续签(不实际续签)
certbot renew --dry-run
# 显示续签过程详细信息
certbot renew --dry-run --force-renewal
证书管理
查看证书信息:
bash
# 查看所有证书
certbot certificates
# 查看指定证书
certbot certificates --cert-name example.com
# 删除证书
certbot delete --cert-name example.com
# 撤销证书
certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem
4.2 Nginx SSL 配置详解
完整的 SSL 配置示例
nginx
# HTTP - 重定向到 HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Let's Encrypt 验证路径
location /.well-known/acme-challenge/ {
root /var/www/html;
try_files $uri =404;
}
# 其他请求重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS - 主配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL 证书配置
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 协议和加密套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# DH 参数(增强安全性)
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 日志配置
access_log /var/log/nginx/example.com-access.log;
error_log /var/log/nginx/example.com-error.log;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
# 客户端上传大小限制
client_max_body_size 20M;
# 超时配置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 反向代理配置(后端服务)
location /api {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 静态文件
location /static {
alias /var/www/html/static;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 默认代理
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
生成 DH 参数(推荐)
bash
# 生成 2048 位 DH 参数(需要几分钟)
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# 或生成 4096 位 DH 参数(更安全,需要更长时间)
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
4.3 防火墙配置
UFW (Ubuntu/Debian)
bash
# 开放 HTTP 和 HTTPS 端口
ufw allow 80/tcp
ufw allow 443/tcp
# 查看防火墙状态
ufw status
# 重新加载防火墙
ufw reload
firewalld (CentOS/RHEL)
bash
# 开放 HTTP 和 HTTPS 端口
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --reload
# 查看开放的端口
firewall-cmd --list-ports
iptables (通用)
bash
# 开放 HTTP 和 HTTPS 端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 保存规则
iptables-save > /etc/iptables/rules.v4
4.4 日志系统
日志目录结构
bash
/var/log/ssl-automation/
├── apply.log # 证书申请日志
├── renew.log # 证书续签日志
└── error.log # 错误日志
/var/log/nginx/
├── example.com-access.log # Nginx 访问日志
└── example.com-error.log # Nginx 错误日志
日志轮转配置
创建 /etc/logrotate.d/ssl-automation:
bash
/var/log/ssl-automation/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 root adm
sharedscripts
postrotate
# 重启相关服务(如果需要)
endscript
}
4.5 邮件通知配置
安装邮件发送工具
bash
# Ubuntu/Debian
apt install mailutils
# CentOS/RHEL
yum install mailx
配置邮件发送
在脚本中添加邮件通知功能:
bash
# 发送邮件函数
send_email() {
local subject="$1"
local body="$2"
local to="$3"
echo "$body" | mail -s "$subject" "$to"
}
# 续签失败时发送邮件
if [ $renewal_result -ne 0 ]; then
send_email "SSL 证书续签失败" "证书续签失败:$domain" "admin@example.com"
fi
5. 部署流程
5.1 首次部署流程
否
是
开始首次部署
准备服务器环境
安装必要软件
配置域名 DNS
执行 ssl-apply.sh
申请证书成功?
检查错误并重试
配置 Nginx
设置自动续签
测试访问
完成部署
5.2 详细步骤
步骤 1: 准备服务器环境
bash
# 更新系统
apt update && apt upgrade -y # Ubuntu/Debian
# 或
yum update -y # CentOS/RHEL
# 安装基础工具
apt install -y curl wget git vim net-tools # Ubuntu/Debian
# 或
yum install -y curl wget git vim net-tools # CentOS/RHEL
步骤 2: 配置域名 DNS
- 登录域名服务商(阿里云、腾讯云、Cloudflare 等)
- 添加 A 记录,指向服务器公网 IP
- 等待 DNS 生效(通常 10-30 分钟)
bash
# 检查 DNS 解析
dig +short example.com
# 或使用 nslookup
nslookup example.com
# 或使用 host
host example.com
步骤 3: 安装 Nginx(如果未安装)
bash
# Ubuntu/Debian
apt install -y nginx
# CentOS/RHEL
yum install -y nginx
# 启动 Nginx
systemctl start nginx
systemctl enable nginx
# 检查 Nginx 状态
systemctl status nginx
步骤 4: 开放防火墙端口
bash
# UFW (Ubuntu/Debian)
ufw allow 80/tcp
ufw allow 443/tcp
ufw reload
# firewalld (CentOS/RHEL)
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --reload
步骤 5: 申请 SSL 证书
bash
# 进入部署脚本目录
cd /path/to/deploy
# 设置脚本执行权限
chmod +x ssl-*.sh
# 申请证书
sudo ./ssl-apply.sh example.com admin@example.com
步骤 6: 设置自动续签
bash
# 设置 Cron 定时任务
sudo ./ssl-renew.sh --setup
# 或手动添加到 crontab
sudo crontab -e
# 添加以下内容(每天凌晨 3 点检查续签)
0 3 * * * /path/to/deploy/ssl-renew.sh >> /var/log/ssl-automation/renew.log 2>&1
步骤 7: 测试访问
bash
# 检查 HTTP 是否重定向到 HTTPS
curl -I http://example.com
# 检查 HTTPS 是否正常
curl -I https://example.com
# 检查 SSL 证书
openssl s_client -connect example.com:443 -servername example.com
6. 配置说明
6.1 ssl-config.sh 配置文件
所有脚本使用统一的配置文件 ssl-config.sh,方便集中管理。
bash
#!/bin/bash
################################################################################
# SSL 自动化系统配置文件
################################################################################
# ===== 基础配置 =====
# 项目根目录
PROJECT_ROOT="/www/wwwroot/nuxt3-wu-code"
DEPLOY_DIR="${PROJECT_ROOT}/deploy"
# 日志目录
LOG_DIR="/var/log/ssl-automation"
# ===== 邮件配置 =====
# 邮件发送地址
NOTIFICATION_EMAIL="admin@example.com"
# 邮件主题前缀
EMAIL_SUBJECT_PREFIX="[SSL Automation]"
# ===== 证书配置 =====
# Let's Encrypt 环境(生产环境:production,测试环境:staging)
LETSENCRYPT_ENV="production"
# 证书续签提前天数(默认 30 天)
RENEWAL_DAYS_BEFORE=30
# 证书有效期提醒天数(默认 15 天)
EXPIRY_WARNING_DAYS=15
# ===== DNS 配置(用于通配符证书) =====
# 默认 DNS 提供商(aliyun, tencent, cloudflare)
DEFAULT_DNS_PROVIDER="aliyun"
# DNS API 凭证目录
DNS_CREDENTIALS_DIR="/etc/letsencrypt/dns-credentials"
# 阿里云配置
ALIYUN_ACCESS_KEY_ID=""
ALIYUN_ACCESS_KEY_SECRET=""
# 腾讯云配置
TENCENT_SECRET_ID=""
TENCENT_SECRET_KEY=""
# Cloudflare 配置
CLOUDFLARE_API_TOKEN=""
# ===== Nginx 配置 =====
# Nginx 配置目录
NGINX_CONF_DIR="/etc/nginx"
# Nginx 站点可用目录
NGINX_SITES_AVAILABLE="${NGINX_CONF_DIR}/sites-available"
# Nginx 站点启用目录
NGINX_SITES_ENABLED="${NGINX_CONF_DIR}/sites-enabled"
# 如果系统使用 conf.d 而不是 sites-available/sites-enabled
NGINX_CONF_D="${NGINX_CONF_DIR}/conf.d"
# Web 根目录(用于 ACME 验证)
WEB_ROOT="/var/www/html"
# ===== 安全配置 =====
# DH 参数文件路径
DH_PARAM_FILE="/etc/nginx/ssl/dhparam.pem"
# DH 参数位数(2048 或 4096)
DH_PARAM_BITS=2048
# 是否启用 HSTS
ENABLE_HSTS=true
# HSTS max-age(秒)
HSTS_MAX_AGE=31536000
# 是否启用 OCSP Stapling
ENABLE_OCSP_STAPLING=true
# ===== 监控配置 =====
# 是否启用证书过期监控
ENABLE_EXPIRY_MONITOR=true
# 监控检查频率(天)
MONITOR_CHECK_INTERVAL=1
# 过期证书通知方式(email, log, both)
EXPIRY_NOTIFY_METHOD="both"
# ===== 备份配置 =====
# 是否启用证书自动备份
ENABLE_CERT_BACKUP=true
# 备份目录
BACKUP_DIR="${PROJECT_ROOT}/backups/ssl"
# 备份保留天数
BACKUP_RETENTION_DAYS=30
# ===== 日志配置 =====
# 日志级别(DEBUG, INFO, WARN, ERROR)
LOG_LEVEL="INFO"
# 日志文件最大大小(MB)
LOG_MAX_SIZE=10
# 日志文件保留数量
LOG_MAX_FILES=10
6.2 修改配置
编辑 ssl-config.sh 文件:
bash
# 使用 vim 编辑
vim /path/to/deploy/ssl-config.sh
# 或使用 nano 编辑
nano /path/to/deploy/ssl-config.sh
重要配置项说明:
| 配置项 | 说明 | 默认值 | 建议值 |
|---|---|---|---|
NOTIFICATION_EMAIL |
接收通知的邮箱 | admin@example.com |
实际管理员邮箱 |
RENEWAL_DAYS_BEFORE |
提前续签天数 | 30 |
30 |
EXPIRY_WARNING_DAYS |
过期警告天数 | 15 |
15 |
DEFAULT_DNS_PROVIDER |
默认 DNS 提供商 | aliyun |
根据实际情况 |
ENABLE_HSTS |
是否启用 HSTS | true |
true |
DH_PARAM_BITS |
DH 参数位数 | 2048 |
2048 或 4096 |
LOG_LEVEL |
日志级别 | INFO |
INFO |
6.3 配置验证
修改配置后,可以运行验证脚本:
bash
# 创建配置验证脚本
cat > /path/to/deploy/ssl-verify-config.sh << 'EOF'
#!/bin/bash
source /path/to/deploy/ssl-config.sh
echo "验证 SSL 配置..."
echo ""
echo "1. 检查日志目录..."
if [ ! -d "$LOG_DIR" ]; then
echo " [WARN] 日志目录不存在: $LOG_DIR"
mkdir -p "$LOG_DIR"
echo " [INFO] 已创建日志目录"
else
echo " [OK] 日志目录存在"
fi
echo ""
echo "2. 检查 DNS 凭证目录..."
if [ ! -d "$DNS_CREDENTIALS_DIR" ]; then
echo " [WARN] DNS 凭证目录不存在: $DNS_CREDENTIALS_DIR"
mkdir -p "$DNS_CREDENTIALS_DIR"
echo " [INFO] 已创建 DNS 凭证目录"
else
echo " [OK] DNS 凭证目录存在"
fi
echo ""
echo "3. 检查 Nginx 配置目录..."
if [ -d "$NGINX_SITES_AVAILABLE" ]; then
echo " [OK] Nginx sites-available 目录存在"
elif [ -d "$NGINX_CONF_D" ]; then
echo " [OK] Nginx conf.d 目录存在"
else
echo " [WARN] Nginx 配置目录不存在"
fi
echo ""
echo "4. 检查备份目录..."
if [ ! -d "$BACKUP_DIR" ]; then
echo " [WARN] 备份目录不存在: $BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
echo " [INFO] 已创建备份目录"
else
echo " [OK] 备份目录存在"
fi
echo ""
echo "配置验证完成!"
EOF
# 运行验证
chmod +x /path/to/deploy/ssl-verify-config.sh
./ssl-verify-config.sh
7. 使用示例
7.1 场景 1: 申请单域名证书
需求: 为 example.com 申请 SSL 证书
bash
sudo ./ssl-apply.sh example.com admin@example.com
预期输出:
╔════════════════════════════════════════════════════════════╗
║ SSL 证书自动申请脚本 ║
║ Let's Encrypt Certificate Automation ║
╚════════════════════════════════════════════════════════════╝
[INFO] 域名: example.com
[INFO] 邮箱: admin@example.com
[SUCCESS] Certbot 已安装
[SUCCESS] DNS 解析正确: example.com -> 1.2.3.4
[SUCCESS] 80 端口正在监听
[INFO] 开始申请 SSL 证书...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
[SUCCESS] SSL 证书申请成功!
[SUCCESS] Nginx SSL 配置文件已创建
[SUCCESS] Nginx 配置测试通过
[SUCCESS] Nginx 已重新加载
7.2 场景 2: 申请多域名证书
需求: 为 example.com 和 www.example.com 同时申请证书
bash
sudo ./ssl-apply.sh example.com,www.example.com admin@example.com
说明:
- 多个域名使用逗号分隔
- 证书会包含所有列出的域名
- Nginx 会自动配置多个域名
7.3 场景 3: 申请通配符证书
需求: 为 *.example.com 申请通配符证书
前置条件:
- 准备阿里云 API 凭证
- 域名 DNS 解析到阿里云
bash
sudo ./ssl-wildcard.sh example.com admin@example.com \
--dns-provider aliyun \
--api-key LTAIxxxx \
--secret-key xxxxxx
说明:
- 通配符证书会覆盖所有子域名
- 需要使用 DNS-01 验证
- 需要 DNS 提供商的 API 凭证
7.4 场景 4: 手动续签证书
需求: 手动续签所有即将过期的证书
bash
# 查看所有证书状态
sudo ./ssl-info.sh
# 续签所有证书
sudo ./ssl-renew.sh
# 或续签指定证书
sudo ./ssl-renew.sh example.com
7.5 场景 5: 测试续签
需求: 测试续签流程(不实际续签)
bash
sudo ./ssl-renew.sh --dry-run
说明:
--dry-run参数表示测试模式- 不会实际续签证书
- 用于验证续签流程是否正常
7.6 场景 6: 检查证书过期情况
需求: 检查所有证书是否即将过期(30天内)
bash
sudo ./ssl-info.sh --check-expiry
输出示例:
╔════════════════════════════════════════════════════════════╗
║ 证书过期检查 ║
╚════════════════════════════════════════════════════════════╝
检查到 2 个证书
[OK] example.com - 剩余 90 天(未过期)
[WARN] old-domain.com - 剩余 25 天(即将过期,建议续签)
7.7 场景 7: 导出证书信息
需求: 导出所有证书信息到 JSON 文件
bash
sudo ./ssl-info.sh --export json --output /path/to/certs.json
输出文件内容:
json
{
"certificates": [
{
"domain": "example.com",
"status": "valid",
"issuer": "Let's Encrypt",
"issued_date": "2025-01-12T00:00:00Z",
"expiry_date": "2025-04-12T00:00:00Z",
"days_remaining": 90,
"cert_path": "/etc/letsencrypt/live/example.com/fullchain.pem",
"key_path": "/etc/letsencrypt/live/example.com/privkey.pem"
},
{
"domain": "*.example.com",
"status": "valid",
"issuer": "Let's Encrypt",
"issued_date": "2025-01-10T00:00:00Z",
"expiry_date": "2025-04-10T00:00:00Z",
"days_remaining": 88,
"cert_path": "/etc/letsencrypt/live/example.com/fullchain.pem",
"key_path": "/etc/letsencrypt/live/example.com/privkey.pem"
}
],
"total_count": 2,
"expiring_count": 0,
"check_time": "2025-01-12T10:00:00Z"
}
7.8 场景 8: 设置自动续签
需求: 设置每天凌晨 3 点自动检查续签
bash
# 方法 1: 使用脚本自动设置
sudo ./ssl-renew.sh --setup
# 方法 2: 手动添加 Cron 任务
sudo crontab -e
# 添加以下内容
0 3 * * * /path/to/deploy/ssl-renew.sh >> /var/log/ssl-automation/renew.log 2>&1
# 查看 Cron 任务
sudo crontab -l
7.9 场景 9: 删除证书
需求: 删除不再需要的证书
bash
# 查看所有证书
sudo ./ssl-info.sh
# 使用 Certbot 删除证书
sudo certbot delete --cert-name old-domain.com
# 或手动删除(不推荐)
sudo rm -rf /etc/letsencrypt/live/old-domain.com
sudo rm -rf /etc/letsencrypt/archive/old-domain.com
sudo rm -f /etc/letsencrypt/renewal/old-domain.com.conf
7.10 场景 10: 回滚证书
需求: 回滚到之前的证书版本
bash
# 查看证书历史
ls -lh /etc/letsencrypt/archive/example.com/
# 复制旧版本证书到 live 目录
sudo cp /etc/letsencrypt/archive/example.com/cert1.pem \
/etc/letsencrypt/live/example.com/cert.pem
# 重载 Nginx
sudo systemctl reload nginx
8. 工作流程
8.1 证书申请流程
DNS 服务器 Let's Encrypt Certbot Nginx ssl-apply.sh 用户 DNS 服务器 Let's Encrypt Certbot Nginx ssl-apply.sh 用户 alt [HTTP-01 验证] [DNS-01 验证] 执行脚本 检查权限和参数 检查 DNS 解析 检查端口占用 配置 ACME 验证路径 请求证书 发起 HTTP-01 验证 访问 /.well-known/acme-challenge/ 返回验证信息 验证通过 返回证书 请求证书 返回 DNS TXT 记录 添加 TXT 记录 等待 DNS 生效 发起 DNS-01 验证 查询 TXT 记录 返回记录 验证通过 返回证书 删除 TXT 记录 配置 SSL 重载服务 显示证书信息
8.2 证书续签流程
邮件系统 Nginx Let's Encrypt Certbot ssl-renew.sh Cron 定时任务 邮件系统 Nginx Let's Encrypt Certbot ssl-renew.sh Cron 定时任务 alt [续签成功] [续签失败] alt [证书即将过期] [证书未过期] 触发续签检查 检查证书有效期 请求续签 申请新证书 颁发新证书 返回新证书 重载服务 记录日志 发送失败通知 记录错误日志 跳过续签 生成续签报告
8.3 证书监控流程
是
否
是
否
是
否
开始监控
检查证书有效期
是否即将过期?
发送预警通知
继续监控
是否需要续签?
执行自动续签
续签是否成功?
更新证书信息
发送失败通知
8.4 完整生命周期
1. 证书申请
├── 域名 DNS 解析
├── 检查环境(Certbot、Nginx、端口)
├── 申请证书
├── 配置 Nginx
└── 测试访问
2. 证书使用
├── Nginx 反向代理
├── HTTPS 加密传输
├── HSTS 强制 HTTPS
└── OCSP Stapling 优化
3. 证书监控
├── 每日检查有效期
├── 即将过期预警
└── 续签状态监控
4. 证书续签
├── 自动检测(Cron)
├── 申请新证书
├── 更新 Nginx 配置
└── 重载服务
5. 证书失效
├── 证书过期
├── 网站无法访问
└── 需要手动处理
9. 故障处理
9.1 常见问题及解决方案
Q1: DNS 解析失败
错误信息:
[ERROR] 无法解析域名 example.com
可能原因:
- 域名未添加 DNS A 记录
- DNS 记录未生效
- 域名拼写错误
解决方案:
bash
# 1. 检查 DNS 解析
dig +short example.com
nslookup example.com
# 2. 确认域名 A 记录已添加到域名服务商
# - 阿里云: https://dns.console.aliyun.com
# - 腾讯云: https://console.cloud.tencent.com/cns
# - Cloudflare: https://dash.cloudflare.com/
# 3. 等待 DNS 生效(通常 10-30 分钟)
# 可以使用在线工具查询全球 DNS 生效情况:
# https://www.whatsmydns.net/
# 4. 清除本地 DNS 缓存
# Windows
ipconfig /flushdns
# Linux
sudo systemd-resolve --flush-caches
# 或
sudo service dnsmasq restart
Q2: 端口被占用
错误信息:
[ERROR] 80 端口已被占用
可能原因:
- Nginx 未启动
- 其他服务占用 80 端口
- 防火墙未开放端口
解决方案:
bash
# 1. 检查端口占用
netstat -tuln | grep :80
# 或
ss -tuln | grep :80
# 或
lsof -i :80
# 2. 查看占用端口的进程
ps aux | grep nginx
# 3. 停止占用端口的进程
sudo kill -9 <PID>
# 4. 启动 Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
# 5. 检查防火墙
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Q3: 证书申请失败
错误信息:
[ERROR] SSL 证书申请失败
可能原因:
- 域名未解析到服务器
- 80 端口无法访问
- 防火墙阻止 Let's Encrypt 访问
- 达到 Let's Encrypt 速率限制
解决方案:
检查 1: 验证域名解析
bash
# 从外部检查域名解析(使用本地网络)
dig +short example.com
# 从服务器内部检查
curl -s ifconfig.me # 获取服务器 IP
dig +short example.com # 查看域名解析 IP
检查 2: 验证 80 端口可访问
bash
# 从外部访问
curl http://example.com
# 从服务器内部访问
curl http://localhost
检查 3: 检查防火墙
bash
# UFW
sudo ufw status
# firewalld
sudo firewall-cmd --list-all
# 开放端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
检查 4: 使用 Staging 环境测试
bash
# Let's Encrypt 有速率限制(例如每周最多 5 次失败)
# 可以使用 Staging 环境测试配置
sudo certbot certonly --test-cert --nginx -d example.com
Q4: Nginx 配置测试失败
错误信息:
[ERROR] Nginx 配置测试失败
可能原因:
- 证书路径错误
- 证书文件不存在
- 配置文件语法错误
解决方案:
bash
# 1. 测试 Nginx 配置
sudo nginx -t
# 2. 查看详细错误信息
sudo nginx -t 2>&1
# 3. 检查证书文件是否存在
ls -la /etc/letsencrypt/live/example.com/
# 4. 检查证书文件权限
sudo chmod 644 /etc/letsencrypt/live/example.com/fullchain.pem
sudo chmod 600 /etc/letsencrypt/live/example.com/privkey.pem
# 5. 检查 Nginx 配置文件
cat /etc/nginx/sites-available/example.com-ssl.conf
Q5: 证书续签失败
错误信息:
[ERROR] 证书续签失败
可能原因:
- 域名解析变更
- 80/443 端口无法访问
- Nginx 配置错误
- DNS 记录失效(通配符证书)
解决方案:
检查 1: 查看续签日志
bash
# 查看续签日志
sudo cat /var/log/ssl-automation/renew.log
# 查看 Certbot 日志
sudo cat /var/log/letsencrypt/letsencrypt.log
检查 2: 手动测试续签
bash
# 测试续签(不实际续签)
sudo certbot renew --dry-run
# 查看详细输出
sudo certbot renew --dry-run --verbose
检查 3: 检查域名解析
bash
# 检查所有域名的解析
dig +short example.com
dig +short www.example.com
检查 4: 强制续签
bash
# 强制续签所有证书
sudo certbot renew --force-renewal
# 强制续签指定证书
sudo certbot renew --cert-name example.com --force-renewal
Q6: 证书已过期
症状:
- 浏览器显示"连接不安全"
- 访问网站出现 SSL 错误
解决方案:
紧急修复:
bash
# 1. 检查证书状态
sudo certbot certificates
# 2. 强制续签
sudo certbot renew --force-renewal
# 3. 如果续签失败,删除旧证书重新申请
sudo certbot delete --cert-name example.com
sudo ./ssl-apply.sh example.com admin@example.com
临时禁用 SSL(不推荐):
bash
# 修改 Nginx 配置,临时禁用 SSL
sudo vim /etc/nginx/sites-available/example.com-ssl.conf
# 注释掉 SSL 相关配置
# ssl_certificate ...
# ssl_certificate_key ...
# 修改监听端口为 80
# listen 80;
# 重载 Nginx
sudo systemctl reload nginx
Q7: 通配符证书申请失败
错误信息:
[ERROR] DNS TXT 记录验证失败
可能原因:
- DNS API 凭证错误
- DNS 记录未生效
- DNS 提供商配置错误
解决方案:
检查 1: 验证 DNS API 凭证
bash
# 测试阿里云 API
# 安装阿里云 CLI
curl https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz -o aliyun-cli.tgz
tar -xzf aliyun-cli.tgz
sudo mv aliyun /usr/local/bin/
# 配置凭证
aliyun configure
# 测试 DNS 解析
aliyun alidns DescribeDomainRecords --DomainName example.com
检查 2: 手动添加 DNS TXT 记录
bash
# 手动添加 TXT 记录到域名服务商
# 记录名: _acme-challenge.example.com
# 记录值: xxxxxxxxxxxxxxxxxxxxx
# 验证记录是否生效
dig +short txt _acme-challenge.example.com
检查 3: 使用手动 DNS 验证
bash
# 使用 Certbot 的手动模式
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com"
# 按提示手动添加 DNS TXT 记录
9.2 日志分析
查看日志位置
bash
# SSL 自动化日志
ls -lh /var/log/ssl-automation/
# Certbot 日志
ls -lh /var/log/letsencrypt/
# Nginx 日志
ls -lh /var/log/nginx/
# 系统日志
journalctl -xe
实时监控日志
bash
# 实时查看续签日志
tail -f /var/log/ssl-automation/renew.log
# 实时查看 Nginx 错误日志
tail -f /var/log/nginx/example.com-error.log
# 实时查看系统日志
journalctl -f
日志搜索
bash
# 搜索错误日志
grep -i error /var/log/ssl-automation/*.log
# 搜索续签日志
grep -i renew /var/log/ssl-automation/renew.log
# 搜索特定域名的日志
grep "example.com" /var/log/ssl-automation/*.log
9.3 故障排查流程图
开始故障排查
│
├─▶ 检查证书状态
│ ├── 查看证书信息: sudo certbot certificates
│ └─▶ 证书是否存在?
│ ├── 是 ──▶ 继续检查
│ └── 否 ──▶ 申请证书: sudo ./ssl-apply.sh
│
├─▶ 检查域名解析
│ ├── 检查 DNS: dig +short example.com
│ └─▶ 解析是否正确?
│ ├── 是 ──▶ 继续检查
│ └── 否 ──▶ 修正 DNS 记录
│
├─▶ 检查端口占用
│ ├── 检查端口: netstat -tuln | grep :80
│ └─▶ 端口是否开放?
│ ├── 是 ──▶ 继续检查
│ └── 否 ──▶ 开放端口/防火墙
│
├─▶ 检查 Nginx 配置
│ ├── 测试配置: sudo nginx -t
│ └─▶ 配置是否正确?
│ ├── 是 ──▶ 继续检查
│ └── 否 ──▶ 修正配置
│
├─▶ 检查证书续签
│ ├── 测试续签: sudo certbot renew --dry-run
│ └─▶ 续签是否正常?
│ ├── 是 ──▶ 继续检查
│ └── 否 ──▶ 检查续签日志
│
└─▶ 查看详细日志
├── 查看 Certbot 日志: cat /var/log/letsencrypt/letsencrypt.log
├── 查看 Nginx 日志: cat /var/log/nginx/error.log
└── 查看系统日志: journalctl -xe
10. 最佳实践
10.1 证书管理
1. 定期检查证书状态
bash
# 每周检查一次证书状态
sudo ./ssl-info.sh --check-expiry
# 或设置 Cron 任务
0 0 * * 1 /path/to/deploy/ssl-info.sh --check-expiry >> /var/log/ssl-automation/monitor.log 2>&1
2. 备份证书
bash
# 创建证书备份脚本
cat > /path/to/deploy/ssl-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/www/wwwroot/nuxt3-wu-code/backups/ssl"
DATE=$(date +%Y%m%d%H%M%S)
mkdir -p "$BACKUP_DIR"
# 备份所有证书
for cert in /etc/letsencrypt/live/*/; do
domain=$(basename "$cert")
echo "备份证书: $domain"
tar -czf "$BACKUP_DIR/$domain-$DATE.tar.gz" -C /etc/letsencrypt/live "$domain"
done
echo "备份完成: $BACKUP_DIR"
# 清理 30 天前的备份
find "$BACKUP_DIR" -type f -mtime +30 -delete
EOF
chmod +x /path/to/deploy/ssl-backup.sh
# 设置每周备份
0 0 * * 0 /path/to/deploy/ssl-backup.sh >> /var/log/ssl-automation/backup.log 2>&1
3. 使用测试环境
在申请正式证书前,先在 Staging 环境测试:
bash
# Staging 环境测试
sudo certbot certonly --test-cert --nginx -d example.com
# 确认无误后,申请正式证书
sudo ./ssl-apply.sh example.com admin@example.com
10.2 安全配置
1. 启用 HSTS
nginx
# 在 Nginx 配置中添加
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
说明:
max-age=31536000: 强制 HTTPS 有效期 1 年includeSubDomains: 包含所有子域名preload: 允许浏览器预加载(需要注册到 HSTS 预加载列表)
2. 生成 DH 参数
bash
# 生成 DH 参数
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# 在 Nginx 配置中使用
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
3. 配置强加密套件
nginx
# 使用现代加密套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
4. 启用 OCSP Stapling
nginx
# 启用 OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
5. 配置安全头
nginx
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self'" always;
10.3 性能优化
1. 启用 HTTP/2
nginx
# Nginx 1.9.5+ 默认支持 HTTP/2
listen 443 ssl http2;
2. 配置 SSL 会话缓存
nginx
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
3. 启用 Gzip 压缩
nginx
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
4. 启用缓存
nginx
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
10.4 监控和告警
1. 设置证书过期监控
bash
# 创建监控脚本
cat > /path/to/deploy/ssl-monitor.sh << 'EOF'
#!/bin/bash
WARNING_DAYS=15
EMAIL="admin@example.com"
# 检查所有证书
certbot certificates | grep -A 2 "Certificate Name:" | while read -r line; do
if [[ $line =~ "Certificate Name:" ]]; then
domain=$(echo "$line" | awk '{print $3}')
elif [[ $line =~ "Expiry Date:" ]]; then
expiry_date=$(echo "$line" | awk '{print $4}')
expiry_timestamp=$(date -d "$expiry_date" +%s)
current_timestamp=$(date +%s)
days_remaining=$(( (expiry_timestamp - current_timestamp) / 86400 ))
if [ $days_remaining -lt $WARNING_DAYS ]; then
echo "警告: $domain 证书将在 $days_remaining 天后过期"
echo "发送邮件通知..."
echo "$domain 证书将在 $days_remaining 天后过期" | mail -s "SSL 证书过期警告" $EMAIL
fi
fi
done
EOF
chmod +x /path/to/deploy/ssl-monitor.sh
# 设置每日监控
0 8 * * * /path/to/deploy/ssl-monitor.sh >> /var/log/ssl-automation/monitor.log 2>&1
2. 监控 SSL 连接
bash
# 使用 OpenSSL 监控 SSL 连接
openssl s_client -connect example.com:443 -servername example.com </dev/null
# 检查 SSL 证书有效期
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
3. 监控 Nginx SSL 错误
bash
# 监控 Nginx 错误日志
tail -f /var/log/nginx/error.log | grep --line-buffered -i ssl
# 统计 SSL 错误数量
grep -i ssl /var/log/nginx/error.log | wc -l
10.5 自动化建议
1. 使用 Git 管理 Nginx 配置
bash
# 初始化 Git 仓库
cd /etc/nginx
sudo git init
sudo git add .
sudo git commit -m "Initial commit"
# 每次修改后提交
sudo git add sites-available/example.com-ssl.conf
sudo git commit -m "Update SSL config for example.com"
2. 使用配置管理工具
推荐使用以下工具管理 SSL 证书:
- Ansible: 自动化部署和配置
- Terraform: 基础设施即代码
- Docker: 容器化部署
3. 集成到 CI/CD 流程
yaml
# GitHub Actions 示例
name: SSL Certificate Check
on:
schedule:
- cron: '0 0 * * *' # 每天检查
jobs:
check-ssl:
runs-on: ubuntu-latest
steps:
- name: Check SSL expiry
run: |
# 使用 curl 检查证书
curl -I https://example.com
# 检查证书有效期
openssl s_client -connect example.com:443 -servername example.com </dev/null | openssl x509 -noout -dates
11. 安全建议
11.1 证书安全
1. 保护私钥
bash
# 私钥文件权限应该是 600
sudo chmod 600 /etc/letsencrypt/live/*/privkey.pem
# 证书文件权限应该是 644
sudo chmod 644 /etc/letsencrypt/live/*/fullchain.pem
2. 定期更新证书
bash
# 设置自动续签
sudo ./ssl-renew.sh --setup
# 定期检查续签状态
sudo certbot certificates
3. 不要共享私钥
- 私钥文件不应复制到其他服务器
- 不要将私钥上传到公开仓库
- 使用加密工具保护私钥(如 GPG)
11.2 服务器安全
1. 配置防火墙
bash
# 只开放必要的端口
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
2. 禁用弱 SSL/TLS 协议
nginx
# 只启用 TLS 1.2 和 TLS 1.3
ssl_protocols TLSv1.2 TLSv1.3;
3. 使用强加密套件
nginx
# 使用强加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
11.3 系统安全
1. 定期更新系统
bash
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# CentOS/RHEL
sudo yum update -y
2. 配置自动安全更新
bash
# Ubuntu/Debian
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
# CentOS/RHEL
sudo yum install yum-cron
sudo systemctl enable yum-cron
sudo systemctl start yum-cron
3. 配置日志轮转
bash
# 配置 SSL 日志轮转
cat > /etc/logrotate.d/ssl-automation << 'EOF'
/var/log/ssl-automation/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 root adm
sharedscripts
}
EOF
12. 监控和维护
12.1 日常检查清单
每日检查
bash
# 1. 检查证书状态
sudo certbot certificates
# 2. 检查 Nginx 状态
sudo systemctl status nginx
# 3. 检查错误日志
sudo tail -n 50 /var/log/nginx/error.log
# 4. 检查 SSL 连接
curl -I https://example.com
每周检查
bash
# 1. 检查证书过期情况
sudo ./ssl-info.sh --check-expiry
# 2. 测试续签流程
sudo certbot renew --dry-run
# 3. 检查磁盘空间
df -h
# 4. 检查系统更新
sudo apt list --upgradable
每月检查
bash
# 1. 备份所有证书
sudo /path/to/deploy/ssl-backup.sh
# 2. 审查 Nginx 配置
sudo nginx -t
# 3. 更新系统
sudo apt update && sudo apt upgrade -y
# 4. 清理旧日志
find /var/log/ssl-automation/ -type f -mtime +30 -delete
12.2 监控指标
关键指标
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| 证书剩余天数 | 证书有效期 | < 15 天 |
| 续签成功率 | 续签是否成功 | < 100% |
| Nginx 启动时间 | Nginx 启动耗时 | > 5 秒 |
| SSL 错误率 | SSL 连接错误率 | > 1% |
| 磁盘使用率 | 日志目录磁盘使用率 | > 80% |
监控脚本
bash
# 创建综合监控脚本
cat > /path/to/deploy/ssl-health-check.sh << 'EOF'
#!/bin/bash
WARNING_DAYS=15
DISK_THRESHOLD=80
EMAIL="admin@example.com"
REPORT_FILE="/tmp/ssl-health-report.txt"
echo "SSL 证书健康检查报告" > "$REPORT_FILE"
echo "检查时间: $(date)" >> "$REPORT_FILE"
echo "================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# 1. 检查证书状态
echo "1. 证书状态检查:" >> "$REPORT_FILE"
certbot certificates >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# 2. 检查即将过期的证书
echo "2. 即将过期的证书:" >> "$REPORT_FILE"
certbot certificates | grep -A 2 "Certificate Name:" | while read -r line; do
if [[ $line =~ "Certificate Name:" ]]; then
domain=$(echo "$line" | awk '{print $3}')
elif [[ $line =~ "Expiry Date:" ]]; then
expiry_date=$(echo "$line" | awk '{print $4}')
expiry_timestamp=$(date -d "$expiry_date" +%s)
current_timestamp=$(date +%s)
days_remaining=$(( (expiry_timestamp - current_timestamp) / 86400 ))
if [ $days_remaining -lt $WARNING_DAYS ]; then
echo "警告: $domain 证书将在 $days_remaining 天后过期" >> "$REPORT_FILE"
fi
fi
done
echo "" >> "$REPORT_FILE"
# 3. 检查 Nginx 状态
echo "3. Nginx 状态:" >> "$REPORT_FILE"
systemctl status nginx | head -n 5 >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# 4. 检查磁盘使用率
echo "4. 磁盘使用率:" >> "$REPORT_FILE"
df -h | grep -E "^/dev/" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# 5. 检查最近的 SSL 错误
echo "5. 最近的 SSL 错误:" >> "$REPORT_FILE"
tail -n 20 /var/log/nginx/error.log | grep -i ssl >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# 发送报告
cat "$REPORT_FILE" | mail -s "SSL 证书健康检查报告" "$EMAIL"
# 清理临时文件
rm -f "$REPORT_FILE"
EOF
chmod +x /path/to/deploy/ssl-health-check.sh
12.3 维护计划
定期维护任务
| 频率 | 任务 | 脚本/命令 |
|---|---|---|
| 每日 | 检查证书状态 | sudo certbot certificates |
| 每日 | 检查 Nginx 日志 | sudo tail -f /var/log/nginx/error.log |
| 每周 | 测试续签流程 | sudo certbot renew --dry-run |
| 每周 | 检查过期证书 | sudo ./ssl-info.sh --check-expiry |
| 每月 | 备份证书 | sudo ./ssl-backup.sh |
| 每月 | 更新系统 | sudo apt update && sudo apt upgrade -y |
| 每月 | 清理旧日志 | find /var/log/ssl-automation/ -type f -mtime +30 -delete |
| 每季度 | 安全审计 | 检查配置和安全设置 |
| 每年 | 证书策略审查 | 审查证书策略和配置 |
自动化维护
bash
# 创建维护脚本
cat > /path/to/deploy/ssl-maintenance.sh << 'EOF'
#!/bin/bash
echo "开始 SSL 自动化维护..."
# 1. 检查证书状态
echo "1. 检查证书状态..."
sudo certbot certificates
# 2. 测试续签
echo "2. 测试续签流程..."
sudo certbot renew --dry-run
# 3. 备份证书
echo "3. 备份证书..."
sudo /path/to/deploy/ssl-backup.sh
# 4. 清理旧日志
echo "4. 清理旧日志..."
find /var/log/ssl-automation/ -type f -mtime +30 -delete
# 5. 检查磁盘空间
echo "5. 检查磁盘空间..."
df -h | grep -E "^/dev/"
echo "维护完成!"
EOF
chmod +x /path/to/deploy/ssl-maintenance.sh
# 设置每月维护
0 0 1 * * /path/to/deploy/ssl-maintenance.sh >> /var/log/ssl-automation/maintenance.log 2>&1
附录
A. 常用命令速查表
Certbot 命令
| 命令 | 说明 |
|---|---|
certbot --version |
查看 Certbot 版本 |
certbot certificates |
查看所有证书 |
certbot renew |
续签所有证书 |
certbot renew --dry-run |
测试续签 |
certbot delete --cert-name example.com |
删除证书 |
certbot revoke --cert-path /path/to/cert.pem |
撤销证书 |
Nginx 命令
| 命令 | 说明 |
|---|---|
nginx -t |
测试配置 |
systemctl start nginx |
启动 Nginx |
systemctl stop nginx |
停止 Nginx |
systemctl restart nginx |
重启 Nginx |
systemctl reload nginx |
重载 Nginx |
systemctl status nginx |
查看 Nginx 状态 |
SSL 测试命令
| 命令 | 说明 |
|---|---|
curl -I https://example.com |
测试 HTTPS 访问 |
openssl s_client -connect example.com:443 |
检查 SSL 连接 |
| `echo | openssl s_client -connect example.com:443 |
B. 参考资料
官方文档
- Let's Encrypt: https://letsencrypt.org/
- Certbot: https://certbot.eff.org/
- Nginx: https://nginx.org/en/docs/
在线工具
- SSL Labs 测试: https://www.ssllabs.com/ssltest/
- DNS 检查: https://www.whatsmydns.net/
- HSTS 预加载: https://hstspreload.org/
推荐阅读
- Mozilla SSL 配置生成器: https://ssl-config.mozilla.org/
- OWASP SSL 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html