跳转到内容
搜索 RunEntLinux 文档

仅搜索 RunEntLinux 通用文档,各发行版文档使用各自的搜索入口。

    浏览当前文档目录

    SSL 证书管理

    HTTPS 已经成为现代 Web 服务的基本要求。本文介绍如何在 EL 系发行版上获取、配置和管理 SSL/TLS 证书,包括免费的 Let’s Encrypt 证书和内部使用的自签名证书。

    SSL 证书的信任建立在证书链之上:

    根证书 (Root CA)
    └── 中间证书 (Intermediate CA)
    └── 服务器证书 (Server Certificate)

    浏览器信任根证书颁发机构(CA),根 CA 签发中间证书,中间证书签发你的服务器证书。配置服务器时,通常需要提供服务器证书 + 中间证书的完整链。

    扩展名说明
    .pemBase64 编码的证书/密钥(最常见)
    .crt / .cer证书文件(通常是 PEM 格式)
    .key私钥文件
    .csr证书签名请求
    .p12 / .pfxPKCS#12 格式(含证书+私钥)

    Let’s Encrypt 提供免费的 DV(域名验证)证书,有效期 90 天,支持自动续期。

    Terminal window
    # 安装 certbot 及对应的 Web 服务器插件
    sudo dnf install epel-release -y
    sudo dnf install certbot -y
    # 根据 Web 服务器选择插件
    sudo dnf install python3-certbot-nginx -y # Nginx
    # 或
    sudo dnf install python3-certbot-apache -y # Apache
    Terminal window
    # 自动获取并配置(推荐)
    # certbot 会自动修改 Nginx 配置并重载
    sudo certbot --nginx -d example.com -d www.example.com
    # 交互过程中需要:
    # 1. 输入邮箱(用于到期提醒)
    # 2. 同意服务条款
    # 3. 选择是否将 HTTP 重定向到 HTTPS
    Terminal window
    sudo certbot --apache -d example.com -d www.example.com

    如果你想手动配置 Web 服务器:

    Terminal window
    # 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 # 私钥
    Terminal window
    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 验证:

    Terminal window
    # 使用 DNS 验证获取通配符证书
    sudo certbot certonly \
    --manual \
    --preferred-challenges dns \
    -d "example.com" \
    -d "*.example.com"
    # certbot 会提示你添加一条 _acme-challenge TXT 记录
    # 在 DNS 服务商处添加后等待生效,再继续

    对于自动化场景,可使用 DNS 插件:

    Terminal window
    # 以 Cloudflare 为例
    sudo dnf install python3-certbot-dns-cloudflare -y
    # 创建 API 凭据文件
    sudo mkdir -p /etc/letsencrypt
    sudo tee /etc/letsencrypt/cloudflare.ini > /dev/null <<'EOF'
    dns_cloudflare_api_token = your_cloudflare_api_token
    EOF
    sudo 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 插件:

    Terminal window
    # 阿里云 DNS(第三方插件)
    pip install certbot-dns-aliyun
    # Cloudflare
    dnf install python3-certbot-dns-cloudflare
    # Route53 (AWS)
    dnf install python3-certbot-dns-route53

    Certbot 安装后默认配置了 systemd timer 来自动续期:

    Terminal window
    # 查看续期定时器状态
    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:00
    RandomizedDelaySec=3600
    Persistent=true
    [Install]
    WantedBy=timers.target
    EOF
    sudo tee /etc/systemd/system/certbot-renew.service > /dev/null <<'EOF'
    [Unit]
    Description=Certbot 自动续期
    [Service]
    Type=oneshot
    ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
    EOF
    sudo systemctl daemon-reload
    sudo systemctl enable --now certbot-renew.timer

    --deploy-hook 确保证书更新后自动重载 Web 服务器。

    Terminal window
    # 列出所有证书
    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

    自签名证书适用于内网服务、开发测试等不需要公信力的场景。

    Terminal window
    # 一条命令生成自签名证书(有效期 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.key
    sudo chmod 644 /etc/pki/tls/certs/selfsigned.crt

    对于多台内部服务器,建议创建自己的 CA,签发的证书统一受信:

    Terminal window
    # 1. 创建 CA 私钥和证书
    mkdir -p /etc/pki/internal-ca
    cd /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,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = app.internal.example.com
    DNS.2 = *.internal.example.com
    IP.1 = 192.168.1.10
    EOF
    # 使用 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. 在客户端机器上信任内部 CA
    sudo cp ca.crt /etc/pki/ca-trust/source/anchors/mycompany-ca.crt
    sudo update-ca-trust
    Terminal window
    # 查看证书详情
    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
    Terminal window
    # 验证证书与私钥是否匹配
    openssl x509 -noout -modulus -in cert.pem | openssl md5
    openssl 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"

    问题 1:证书链不完整

    Terminal window
    # 症状:浏览器提示"证书不受信任"但证书本身未过期
    # 检查:
    openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>&1 | \
    grep -i "verify"
    # 解决:确保配置了完整证书链
    # Nginx 中 ssl_certificate 应使用 fullchain.pem 而非 cert.pem
    cat cert.pem intermediate.pem > fullchain.pem

    问题 2:证书过期

    Terminal window
    # 检查证书过期时间
    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-run
    journalctl -u certbot-renew

    问题 3:协议或密码套件不兼容

    Terminal window
    # 测试支持的 TLS 版本
    openssl s_client -connect example.com:443 -tls1_2 < /dev/null
    openssl s_client -connect example.com:443 -tls1_3 < /dev/null
    # 列出服务器支持的密码套件(需要 nmap)
    nmap --script ssl-enum-ciphers -p 443 example.com

    问题 4:Nginx 配置错误

    Terminal window
    # 检查配置语法
    sudo nginx -t
    # 常见错误:
    # 1. 私钥与证书不匹配
    # 2. 文件路径错误
    # 3. 权限问题(私钥文件权限应为 600)
    ls -la /etc/letsencrypt/live/example.com/

    问题 5:SELinux 阻止访问证书文件

    Terminal window
    # 检查 SELinux 审计日志
    ausearch -m avc -ts recent | grep ssl
    # 恢复正确的 SELinux 上下文
    sudo restorecon -Rv /etc/letsencrypt/
    sudo restorecon -Rv /etc/pki/tls/
    /usr/local/bin/check_ssl.sh
    #!/bin/bash
    # 监控 SSL 证书过期时间
    DOMAINS=(
    "example.com"
    "api.example.com"
    "www.example.com"
    )
    WARN_DAYS=30
    MAILTO="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})"
    fi
    done
    Terminal window
    chmod +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 -
    Terminal window
    # 生成 DH 参数(增强前向保密)
    sudo openssl dhparam -out /etc/pki/tls/dhparam.pem 2048
    # Nginx 中引用
    # ssl_dhparam /etc/pki/tls/dhparam.pem;
    • 仅启用 TLS 1.2 和 TLS 1.3(禁用 SSLv3、TLS 1.0、TLS 1.1)
    • 使用强密码套件,禁用弱密码
    • 配置完整的证书链
    • 启用 HSTS 头
    • 启用 OCSP Stapling
    • 私钥文件权限设为 600
    • 配置自动续期并验证其正常工作
    • 定期检查证书过期时间
    • 使用 SSL Labs 在线测试评估配置

    配置完成后,使用以下工具测试你的 SSL 配置质量:

    Terminal window
    # SSL Labs(Web)
    # 访问 https://www.ssllabs.com/ssltest/ 输入域名
    # 命令行测试
    curl -I https://example.com
    curl -vI https://example.com 2>&1 | grep -E "SSL|TLS|subject|expire"

    目标是在 SSL Labs 测试中获得 A 或 A+ 评级。