SSL 证书管理
HTTPS 已经成为现代 Web 服务的基本要求。本文介绍如何在 EL 系发行版上获取、配置和管理 SSL/TLS 证书,包括免费的 Let’s Encrypt 证书和内部使用的自签名证书。
SSL/TLS 基础知识
Section titled “SSL/TLS 基础知识”SSL 证书的信任建立在证书链之上:
根证书 (Root CA) └── 中间证书 (Intermediate CA) └── 服务器证书 (Server Certificate)浏览器信任根证书颁发机构(CA),根 CA 签发中间证书,中间证书签发你的服务器证书。配置服务器时,通常需要提供服务器证书 + 中间证书的完整链。
证书文件类型
Section titled “证书文件类型”| 扩展名 | 说明 |
|---|---|
.pem | Base64 编码的证书/密钥(最常见) |
.crt / .cer | 证书文件(通常是 PEM 格式) |
.key | 私钥文件 |
.csr | 证书签名请求 |
.p12 / .pfx | PKCS#12 格式(含证书+私钥) |
Let’s Encrypt + Certbot
Section titled “Let’s Encrypt + Certbot”Let’s Encrypt 提供免费的 DV(域名验证)证书,有效期 90 天,支持自动续期。
安装 Certbot
Section titled “安装 Certbot”# 安装 certbot 及对应的 Web 服务器插件sudo dnf install epel-release -ysudo dnf install certbot -y
# 根据 Web 服务器选择插件sudo dnf install python3-certbot-nginx -y # Nginx# 或sudo dnf install python3-certbot-apache -y # Apache获取证书(Nginx)
Section titled “获取证书(Nginx)”# 自动获取并配置(推荐)# certbot 会自动修改 Nginx 配置并重载sudo certbot --nginx -d example.com -d www.example.com
# 交互过程中需要:# 1. 输入邮箱(用于到期提醒)# 2. 同意服务条款# 3. 选择是否将 HTTP 重定向到 HTTPS获取证书(Apache)
Section titled “获取证书(Apache)”sudo certbot --apache -d example.com -d www.example.com仅获取证书(不修改配置)
Section titled “仅获取证书(不修改配置)”如果你想手动配置 Web 服务器:
# Standalone 模式(需要临时占用 80 端口)sudo certbot certonly --standalone -d example.com -d www.example.com
# Webroot 模式(Web 服务器保持运行)sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com证书文件默认保存在 /etc/letsencrypt/live/example.com/ 下:
/etc/letsencrypt/live/example.com/├── cert.pem # 服务器证书├── chain.pem # 中间证书链├── fullchain.pem # 完整证书链(cert + chain)└── privkey.pem # 私钥手动配置 Nginx
Section titled “手动配置 Nginx”sudo tee /etc/nginx/conf.d/example.com.conf > /dev/null <<'EOF'server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; server_name example.com www.example.com;
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; ssl_prefer_server_ciphers off;
# HSTS(启用后浏览器将强制使用 HTTPS) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" 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;
# 会话缓存 ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off;
root /var/www/example.com; index index.html;
location / { try_files $uri $uri/ =404; }}EOF
sudo nginx -t && sudo systemctl reload nginx通配符证书(*.example.com)需要 DNS 验证:
# 使用 DNS 验证获取通配符证书sudo certbot certonly \ --manual \ --preferred-challenges dns \ -d "example.com" \ -d "*.example.com"
# certbot 会提示你添加一条 _acme-challenge TXT 记录# 在 DNS 服务商处添加后等待生效,再继续对于自动化场景,可使用 DNS 插件:
# 以 Cloudflare 为例sudo dnf install python3-certbot-dns-cloudflare -y
# 创建 API 凭据文件sudo mkdir -p /etc/letsencryptsudo tee /etc/letsencrypt/cloudflare.ini > /dev/null <<'EOF'dns_cloudflare_api_token = your_cloudflare_api_tokenEOFsudo chmod 600 /etc/letsencrypt/cloudflare.ini
# 自动获取通配符证书sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ -d "example.com" \ -d "*.example.com"常用 DNS 插件:
# 阿里云 DNS(第三方插件)pip install certbot-dns-aliyun
# Cloudflarednf install python3-certbot-dns-cloudflare
# Route53 (AWS)dnf install python3-certbot-dns-route53Certbot 安装后默认配置了 systemd timer 来自动续期:
# 查看续期定时器状态systemctl list-timers certbot*
# 手动测试续期(不实际执行)sudo certbot renew --dry-run
# 如果定时器不存在,手动创建sudo tee /etc/systemd/system/certbot-renew.timer > /dev/null <<'EOF'[Unit]Description=Certbot 自动续期定时器
[Timer]OnCalendar=*-*-* 00,12:00:00RandomizedDelaySec=3600Persistent=true
[Install]WantedBy=timers.targetEOF
sudo tee /etc/systemd/system/certbot-renew.service > /dev/null <<'EOF'[Unit]Description=Certbot 自动续期
[Service]Type=oneshotExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"EOF
sudo systemctl daemon-reloadsudo systemctl enable --now certbot-renew.timer--deploy-hook 确保证书更新后自动重载 Web 服务器。
管理已有证书
Section titled “管理已有证书”# 列出所有证书sudo certbot certificates
# 删除证书sudo certbot delete --cert-name example.com
# 扩展证书域名sudo certbot certonly --nginx \ --cert-name example.com \ -d example.com \ -d www.example.com \ -d api.example.com自签名证书(内部使用)
Section titled “自签名证书(内部使用)”自签名证书适用于内网服务、开发测试等不需要公信力的场景。
# 一条命令生成自签名证书(有效期 3650 天)openssl req -x509 -nodes -days 3650 \ -newkey rsa:2048 \ -keyout /etc/pki/tls/private/selfsigned.key \ -out /etc/pki/tls/certs/selfsigned.crt \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=internal.example.com"
# 设置权限sudo chmod 600 /etc/pki/tls/private/selfsigned.keysudo chmod 644 /etc/pki/tls/certs/selfsigned.crt创建内部 CA
Section titled “创建内部 CA”对于多台内部服务器,建议创建自己的 CA,签发的证书统一受信:
# 1. 创建 CA 私钥和证书mkdir -p /etc/pki/internal-cacd /etc/pki/internal-ca
# 生成 CA 私钥openssl genrsa -aes256 -out ca.key 4096
# 生成 CA 证书(有效期 10 年)openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -out ca.crt \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=MyCompany Internal CA"
# 2. 为服务器生成证书# 生成服务器私钥openssl genrsa -out server.key 2048
# 创建证书签名请求 (CSR)openssl req -new -key server.key \ -out server.csr \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=app.internal.example.com"
# 创建扩展配置(支持 SAN)tee server-ext.cnf > /dev/null <<'EOF'authorityKeyIdentifier=keyid,issuerbasicConstraints=CA:FALSEkeyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEnciphermentsubjectAltName = @alt_names
[alt_names]DNS.1 = app.internal.example.comDNS.2 = *.internal.example.comIP.1 = 192.168.1.10EOF
# 使用 CA 签发服务器证书openssl x509 -req -in server.csr \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -out server.crt -days 825 -sha256 \ -extfile server-ext.cnf
# 3. 在客户端机器上信任内部 CAsudo cp ca.crt /etc/pki/ca-trust/source/anchors/mycompany-ca.crtsudo update-ca-trust证书查看与验证
Section titled “证书查看与验证”查看证书信息
Section titled “查看证书信息”# 查看证书详情openssl x509 -in /etc/pki/tls/certs/selfsigned.crt -text -noout
# 只看关键信息openssl x509 -in cert.pem -noout -subject -issuer -dates
# 查看远程服务器的证书openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | \ openssl x509 -text -noout
# 只看过期时间echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | \ openssl x509 -noout -dates# 验证证书与私钥是否匹配openssl x509 -noout -modulus -in cert.pem | openssl md5openssl rsa -noout -modulus -in key.pem | openssl md5# 两个 MD5 值应相同
# 验证证书链完整性openssl verify -CAfile ca.crt -untrusted intermediate.crt server.crt
# 测试 HTTPS 连接curl -vI https://example.com 2>&1 | grep -A 5 "SSL connection"排查 SSL 问题
Section titled “排查 SSL 问题”常见问题及解决方法
Section titled “常见问题及解决方法”问题 1:证书链不完整
# 症状:浏览器提示"证书不受信任"但证书本身未过期# 检查:openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>&1 | \ grep -i "verify"
# 解决:确保配置了完整证书链# Nginx 中 ssl_certificate 应使用 fullchain.pem 而非 cert.pemcat cert.pem intermediate.pem > fullchain.pem问题 2:证书过期
# 检查证书过期时间echo | openssl s_client -connect example.com:443 2>/dev/null | \ openssl x509 -noout -enddate
# 批量检查多个域名for domain in example.com api.example.com www.example.com; do expiry=$(echo | openssl s_client -connect ${domain}:443 -servername ${domain} 2>/dev/null | \ openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) echo "${domain}: ${expiry}"done
# 检查 certbot 续期是否正常sudo certbot renew --dry-runjournalctl -u certbot-renew问题 3:协议或密码套件不兼容
# 测试支持的 TLS 版本openssl s_client -connect example.com:443 -tls1_2 < /dev/nullopenssl s_client -connect example.com:443 -tls1_3 < /dev/null
# 列出服务器支持的密码套件(需要 nmap)nmap --script ssl-enum-ciphers -p 443 example.com问题 4:Nginx 配置错误
# 检查配置语法sudo nginx -t
# 常见错误:# 1. 私钥与证书不匹配# 2. 文件路径错误# 3. 权限问题(私钥文件权限应为 600)
ls -la /etc/letsencrypt/live/example.com/问题 5:SELinux 阻止访问证书文件
# 检查 SELinux 审计日志ausearch -m avc -ts recent | grep ssl
# 恢复正确的 SELinux 上下文sudo restorecon -Rv /etc/letsencrypt/sudo restorecon -Rv /etc/pki/tls/证书过期监控脚本
Section titled “证书过期监控脚本”#!/bin/bash# 监控 SSL 证书过期时间
DOMAINS=( "example.com" "api.example.com" "www.example.com")WARN_DAYS=30MAILTO="admin@example.com"
for domain in "${DOMAINS[@]}"; do expiry_date=$(echo | openssl s_client -connect "${domain}:443" \ -servername "$domain" 2>/dev/null | \ openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$expiry_date" ]; then echo "[错误] 无法获取 ${domain} 的证书信息" continue fi
expiry_epoch=$(date -d "$expiry_date" +%s) now_epoch=$(date +%s) days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -le 0 ]; then MSG="[严重] ${domain} 证书已过期!" echo "$MSG" echo "$MSG" | mail -s "SSL 证书过期告警" "$MAILTO" elif [ "$days_left" -le "$WARN_DAYS" ]; then MSG="[警告] ${domain} 证书将在 ${days_left} 天后过期 (${expiry_date})" echo "$MSG" echo "$MSG" | mail -s "SSL 证书即将过期" "$MAILTO" else echo "[正常] ${domain} 证书剩余 ${days_left} 天 (${expiry_date})" fidonechmod +x /usr/local/bin/check_ssl.sh
# 加入 cron,每天检查一次echo '0 9 * * * /usr/local/bin/check_ssl.sh >> /var/log/ssl_check.log 2>&1' | sudo crontab -安全最佳实践
Section titled “安全最佳实践”推荐的 SSL 配置
Section titled “推荐的 SSL 配置”# 生成 DH 参数(增强前向保密)sudo openssl dhparam -out /etc/pki/tls/dhparam.pem 2048
# Nginx 中引用# ssl_dhparam /etc/pki/tls/dhparam.pem;安全检查清单
Section titled “安全检查清单”- 仅启用 TLS 1.2 和 TLS 1.3(禁用 SSLv3、TLS 1.0、TLS 1.1)
- 使用强密码套件,禁用弱密码
- 配置完整的证书链
- 启用 HSTS 头
- 启用 OCSP Stapling
- 私钥文件权限设为 600
- 配置自动续期并验证其正常工作
- 定期检查证书过期时间
- 使用 SSL Labs 在线测试评估配置
配置完成后,使用以下工具测试你的 SSL 配置质量:
# SSL Labs(Web)# 访问 https://www.ssllabs.com/ssltest/ 输入域名
# 命令行测试curl -I https://example.comcurl -vI https://example.com 2>&1 | grep -E "SSL|TLS|subject|expire"目标是在 SSL Labs 测试中获得 A 或 A+ 评级。