强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Apache HTTP Server 完全指南 / SSL/TLS 配置

SSL/TLS 配置

SSL/TLS 是保护 Web 通信安全的基础技术。本章详细介绍如何在 Apache 中配置和优化 SSL/TLS。

1. SSL/TLS 基础

1.1 协议版本

协议年份安全性建议
SSLv21995不安全禁用
SSLv31996不安全(POODLE)禁用
TLSv1.01999较弱禁用
TLSv1.12006较弱禁用
TLSv1.22008安全推荐
TLSv1.32018最安全强烈推荐

1.2 证书类型

类型说明适用场景
DV(域名验证)验证域名所有权个人网站、博客
OV(组织验证)验证组织身份企业网站
EV(扩展验证)严格身份验证金融、电商
通配符证书保护所有子域名多子域名网站
多域名证书(SAN)保护多个域名多站点

1.3 Let’s Encrypt

Let’s Encrypt 提供免费的 DV 证书,由 ISRG(Internet Security Research Group)运营。

# 安装 Certbot
sudo apt install certbot python3-certbot-apache

# 或使用 snap
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

2. SSL 模块启用

2.1 启用 mod_ssl

# Debian/Ubuntu
sudo a2enmod ssl
sudo systemctl reload apache2

# CentOS/RHEL
# 确保 httpd.conf 中包含:
# LoadModule ssl_module modules/mod_ssl.so

# 检查模块
apachectl -M | grep ssl

2.2 生成自签名证书(测试)

# 生成自签名证书
sudo openssl req -x509 -nodes -days 365 \
    -newkey rsa:2048 \
    -keyout /etc/ssl/private/selfsigned.key \
    -out /etc/ssl/certs/selfsigned.crt \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/CN=localhost"

# 生成 DH 参数(推荐)
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

2.3 Let’s Encrypt 证书申请

# 自动申请并配置
sudo certbot --apache -d example.com -d www.example.com

# 仅申请证书
sudo certbot certonly --apache -d example.com

# 使用 Webroot 方式
sudo certbot certonly --webroot -w /var/www/html -d example.com

# 使用 Standalone 方式(停止 Apache)
sudo systemctl stop apache2
sudo certbot certonly --standalone -d example.com
sudo systemctl start apache2

证书文件位置:

/etc/letsencrypt/live/example.com/
├── cert.pem        # 证书
├── chain.pem       # 证书链
├── fullchain.pem   # 完整证书链
└── privkey.pem     # 私钥

3. 基本 SSL 配置

3.1 虚拟主机配置

# /etc/apache2/sites-available/example-ssl.conf

<VirtualHost *:443>
    ServerName www.example.com
    ServerAlias example.com
    DocumentRoot /var/www/example.com/public_html
    
    # 基本 SSL 配置
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    
    # 日志
    ErrorLog /var/www/example.com/logs/ssl-error.log
    CustomLog /var/www/example.com/logs/ssl-access.log combined
</VirtualHost>

3.2 HTTP 到 HTTPS 重定向

# 方法 1:mod_rewrite(推荐)
<VirtualHost *:80>
    ServerName www.example.com
    RewriteEngine On
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

# 方法 2:Redirect 指令
<VirtualHost *:80>
    ServerName www.example.com
    Redirect permanent / https://www.example.com/
</VirtualHost>

# 方法 3:RedirectMatch
<VirtualHost *:80>
    ServerName www.example.com
    RedirectMatch permanent ^/(.*)$ https://www.example.com/$1
</VirtualHost>

3.3 中间证书链配置

# 方法 1:使用 SSLCertificateChainFile(旧版)
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/chain.pem

# 方法 2:使用 fullchain.pem(推荐)
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

4. 安全强化配置

4.1 协议和密码套件

# /etc/apache2/conf-available/ssl-params.conf

# 仅允许安全协议
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

# 推荐密码套件(Mozilla Intermediate)
SSLCipherSuite 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
SSLHonorCipherOrder off

# DH 参数
SSLOpenSSLConfCmd DHParameters /etc/ssl/certs/dhparam.pem

# SSL 会话配置
SSLSessionCache shm:/var/run/ssl_scache(512000)
SSLSessionCacheTimeout 300

# SSL 压缩(禁用,防止 CRIME 攻击)
SSLCompression off

# OCSP Stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off

4.2 安全头配置

# 启用 mod_headers
# sudo a2enmod headers

# HSTS(HTTP 严格传输安全)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

# 其他安全头
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

# CSP(内容安全策略)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"

# 预加载 HSTS(可选,提交到 hstspreload.org)
# Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

4.3 完整安全配置模板

# /etc/apache2/conf-available/ssl-security.conf

<IfModule mod_ssl.c>
    # 协议
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    
    # 密码套件
    SSLCipherSuite 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
    SSLHonorCipherOrder off
    
    # 会话
    SSLSessionTickets off
    SSLSessionCache shm:/var/run/ssl_scache(512000)
    SSLSessionCacheTimeout 300
    
    # 压缩
    SSLCompression off
    
    # OCSP
    SSLUseStapling on
    SSLStaplingCache shmcb:/var/run/ocsp(128000)
</IfModule>

<IfModule mod_headers.c>
    # HSTS
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    
    # 安全头
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-XSS-Protection "1; mode=block"
</IfModule>

5. 证书管理

5.1 证书自动续期

# 测试续期
sudo certbot renew --dry-run

# 设置自动续期(cron)
sudo crontab -e
# 添加:
# 0 3 * * * certbot renew --quiet --post-hook "systemctl reload apache2"

# 或使用 systemd timer
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

# 检查 timer 状态
sudo systemctl status certbot.timer

5.2 证书续期钩子

# /etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh
#!/bin/bash
systemctl reload apache2

# /etc/letsencrypt/renewal-hooks/deploy/notify.sh
#!/bin/bash
echo "SSL certificate renewed for $(hostname)" | \
    mail -s "SSL Renewal" [email protected]

# 设置执行权限
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/*.sh

5.3 证书验证

# 检查证书信息
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout

# 检查证书有效期
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -dates -noout

# 检查远程服务器证书
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -text -noout

# 检查证书链
openssl s_client -connect example.com:443 -servername example.com -showcerts

# 测试 SSL 配置
# 使用 ssllabs.com
# 使用 testssl.sh
git clone https://github.com/drwetter/testssl.sh.git
cd testssl.sh
./testssl.sh https://example.com

5.4 证书吊销

# Let's Encrypt 证书吊销
sudo certbot revoke --cert-name example.com

# 使用 OpenSSL 吊销(自签名/私有 CA)
openssl ca -revoke /etc/ssl/certs/example.com.crt -config /etc/ssl/openssl.cnf

6. 多站点 SSL 配置

6.1 SNI(Server Name Indication)

SNI 允许在一个 IP 地址上托管多个 SSL 站点。

# 站点 A
<VirtualHost *:443>
    ServerName site-a.example.com
    DocumentRoot /var/www/site-a
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/site-a.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/site-a.example.com/privkey.pem
</VirtualHost>

# 站点 B
<VirtualHost *:443>
    ServerName site-b.example.com
    DocumentRoot /var/www/site-b
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/site-b.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/site-b.example.com/privkey.pem
</VirtualHost>

# 通配符证书
<VirtualHost *:443>
    ServerName example.com
    ServerAlias *.example.com
    DocumentRoot /var/www/example.com
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>

6.2 通配符证书申请

# 使用 DNS 验证
sudo certbot certonly --manual --preferred-challenges dns \
    -d example.com -d "*.example.com"

# 使用 Cloudflare DNS 插件
sudo apt install python3-certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
    --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
    -d example.com -d "*.example.com"

# Cloudflare 凭证文件
# /etc/letsencrypt/cloudflare.ini
# dns_cloudflare_email = [email protected]
# dns_cloudflare_api_key = your-api-key

7. 性能优化

7.1 SSL 会话优化

# 会话缓存
SSLSessionCache shm:/var/run/ssl_scache(512000)
SSLSessionCacheTimeout 300

# 禁用会话票证(更安全)
SSLSessionTickets off

# 如果启用会话票证,定期轮换密钥
# SSLSessionTicketKeyFile /etc/apache2/ssl/ticket.key

7.2 OCSP Stapling

# 启用 OCSP Stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off

7.3 HTTP/2 优化

# 启用 HTTP/2
# sudo a2enmod http2

Protocols h2 http/1.1

# 或在虚拟主机中配置
<VirtualHost *:443>
    Protocols h2 http/1.1
    SSLEngine on
    # ...
</VirtualHost>

8. 业务场景

8.1 电商网站 SSL 配置

<VirtualHost *:443>
    ServerName shop.example.com
    DocumentRoot /var/www/shop
    
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/shop.example.com.crt
    SSLCertificateKeyFile /etc/ssl/private/shop.example.com.key
    SSLCertificateChainFile /etc/ssl/certs/chain.pem
    
    # 严格安全协议
    SSLProtocol TLSv1.2 TLSv1.3
    SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder on
    
    # HSTS 预加载
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    
    # CSP
    Header always set Content-Security-Policy "upgrade-insecure-requests"
</VirtualHost>

8.2 API 服务器 SSL

<VirtualHost *:443>
    ServerName api.example.com
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/api.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/api.example.com/privkey.pem
    
    # 客户端证书验证(可选)
    SSLVerifyClient optional
    SSLVerifyDepth 2
    SSLCACertificateFile /etc/ssl/certs/client-ca.crt
    
    # 传递客户端证书信息
    RequestHeader set X-Client-Cert "%{SSL_CLIENT_S_DN}s"
</VirtualHost>

8.3 内部服务 SSL

# 使用自签名证书的内部服务
<VirtualHost *:443>
    ServerName internal.example.local
    
    SSLEngine on
    SSLCertificateFile /etc/ssl/internal/server.crt
    SSLCertificateKeyFile /etc/ssl/internal/server.key
    
    # 仅允许内部访问
    <Directory "/var/www/internal">
        Require ip 10.0.0.0/8
        Require ip 172.16.0.0/12
        Require ip 192.168.0.0/16
    </Directory>
</VirtualHost>

9. 故障排除

9.1 常见错误

# 错误:SSL: error:0906D06C:PEM routines:PEM_read_bio:no start line
# 原因:证书文件格式错误
# 解决:检查证书文件内容
openssl x509 -in cert.pem -text -noout

# 错误:SSL: error:14094418:SSL routines:ssl3_read_bytes:tlv1 alert unknown ca
# 原因:证书链不完整
# 解决:使用 fullchain.pem

# 错误:AH01903: Failed to configure CA certificate
# 原因:CA 证书路径错误
# 解决:检查 SSLCACertificateFile 路径

# 错误:SSLConnectionError
# 原因:端口未监听或防火墙阻止
# 解决:检查端口和防火墙配置

9.2 调试命令

# 检查 SSL 配置
apachectl -t -D DUMP_MODULES | grep ssl

# 测试 SSL 连接
openssl s_client -connect localhost:443 -servername example.com

# 查看证书信息
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -text -noout

# 检查 OCSP
openssl s_client -connect example.com:443 -status

# 检查协议支持
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

9.3 日志分析

# SSL 错误日志
tail -f /var/log/apache2/error.log | grep -i ssl

# 检查 SSL 握手
grep -i "ssl" /var/log/apache2/error.log

# 分析 SSL 协议使用
awk '/SSLProtocol/ {print $NF}' /var/log/apache2/access.log | sort | uniq -c

10. 注意事项

  1. 证书过期:设置自动续期和监控告警
  2. 协议安全:禁用不安全的 SSL/TLS 版本
  3. 密码套件:使用推荐的密码套件配置
  4. HSTS:谨慎启用预加载,一旦启用难以撤回
  5. 备份证书:定期备份证书和私钥文件
  6. 合规要求:根据行业要求(如 PCI DSS)配置 SSL

11. 扩展阅读

12. 总结

SSL/TLS 配置是现代 Web 服务器的必备功能:

  • 证书管理:使用 Let’s Encrypt 免费获取证书
  • 安全配置:禁用不安全协议,使用强密码套件
  • 性能优化:OCSP Stapling、会话缓存
  • 自动化:自动续期,减少人工维护

正确配置 SSL/TLS 可以保护用户数据安全,提升网站信任度。