Nginx SSL/TLS 配置
SSL/TLS Configuration
概述
SSL/TLS是现代Web应用的基础安全协议,Nginx提供了强大的SSL/TLS支持,包括证书管理、协议配置、性能优化等功能。本文将详细介绍Nginx SSL/TLS的配置方法、安全最佳实践和性能优化技巧。
1. SSL/TLS基础概念
1.1 SSL/TLS协议版本
协议版本演进:
├── SSL 1.0 (已废弃)
├── SSL 2.0 (已废弃)
├── SSL 3.0 (已废弃)
├── TLS 1.0 (RFC 2246, 1999) - 建议禁用
├── TLS 1.1 (RFC 4346, 2006) - 建议禁用
├── TLS 1.2 (RFC 5246, 2008) - 推荐
└── TLS 1.3 (RFC 8446, 2018) - 最新推荐
1.2 证书类型
证书类型:
├── 域名验证证书 (DV - Domain Validated)
├── 组织验证证书 (OV - Organization Validated)
├── 扩展验证证书 (EV - Extended Validation)
├── 通配符证书 (Wildcard Certificate)
└── 多域名证书 (SAN Certificate)
2. 基本SSL/TLS配置
2.1 简单HTTPS配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL证书配置
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# 基本SSL参数
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# SSL会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# 网站根目录
root /var/www/example.com;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
# HTTP重定向到HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
2.2 多证书配置
# 主域名证书
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# SSL配置
include /etc/nginx/ssl-params.conf;
location / {
root /var/www/example.com;
try_files $uri $uri/ =404;
}
}
# 子域名证书
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.example.com.crt;
ssl_certificate_key /etc/ssl/private/api.example.com.key;
include /etc/nginx/ssl-params.conf;
location / {
proxy_pass http://api-backend;
proxy_set_header Host $host;
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 https;
}
}
# 通配符证书
server {
listen 443 ssl http2;
server_name *.example.com;
ssl_certificate /etc/ssl/certs/wildcard.example.com.crt;
ssl_certificate_key /etc/ssl/private/wildcard.example.com.key;
include /etc/nginx/ssl-params.conf;
# 基于子域名的路由
set $subdomain "";
if ($host ~* ^([^.]+)\.example\.com$) {
set $subdomain $1;
}
location / {
root /var/www/subdomains/$subdomain;
try_files $uri $uri/ =404;
}
}
3. 高级SSL/TLS配置
3.1 SSL参数优化配置文件
# /etc/nginx/ssl-params.conf
# 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/ssl/certs/dhparam.pem;
# ECDH曲线
ssl_ecdh_curve secp384r1;
# SSL会话设置
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP装订
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-chain.crt;
# 安全头部
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options DENY 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' http: https: data: blob: 'unsafe-inline'" always;
3.2 生成DH参数
# 生成强DH参数(可能需要很长时间)
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
# 或者使用4096位(更安全但更慢)
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
3.3 客户端证书认证
server {
listen 443 ssl http2;
server_name secure.example.com;
# 服务器证书
ssl_certificate /etc/ssl/certs/secure.example.com.crt;
ssl_certificate_key /etc/ssl/private/secure.example.com.key;
# 客户端证书配置
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client on; # 强制客户端证书
ssl_verify_depth 2;
include /etc/nginx/ssl-params.conf;
location / {
# 传递客户端证书信息
proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
proxy_pass http://secure-backend;
}
}
# 可选客户端证书认证
server {
listen 443 ssl http2;
server_name optional-cert.example.com;
ssl_certificate /etc/ssl/certs/optional-cert.example.com.crt;
ssl_certificate_key /etc/ssl/private/optional-cert.example.com.key;
# 可选客户端证书
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client optional;
include /etc/nginx/ssl-params.conf;
location / {
# 基于证书验证结果的访问控制
if ($ssl_client_verify != SUCCESS) {
return 401 "Client certificate required";
}
proxy_pass http://backend;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
}
}
4. Let's Encrypt自动化配置
4.1 Certbot基本配置
# 安装Certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx
# 获取证书并自动配置Nginx
sudo certbot --nginx -d example.com -d www.example.com
# 测试自动续期
sudo certbot renew --dry-run
4.2 手动证书获取和配置
# ACME挑战配置
server {
listen 80;
server_name example.com www.example.com;
# Let's Encrypt验证路径
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
try_files $uri =404;
}
# 其他请求重定向到HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
4.3 自动续期脚本
#!/bin/bash
# /usr/local/bin/certbot-renew.sh
WEBROOT="/var/www/letsencrypt"
DOMAINS="example.com,www.example.com"
EMAIL="admin@example.com"
# 创建webroot目录
mkdir -p $WEBROOT
# 获取或续期证书
certbot certonly \
--webroot \
--webroot-path=$WEBROOT \
--email $EMAIL \
--agree-tos \
--no-eff-email \
--domains $DOMAINS
# 检查续期结果
if [ $? -eq 0 ]; then
echo "Certificate renewed successfully"
# 重新加载Nginx
nginx -t && systemctl reload nginx
# 发送成功通知
echo "SSL certificate renewed for $DOMAINS" | mail -s "SSL Renewal Success" $EMAIL
else
echo "Certificate renewal failed"
# 发送失败通知
echo "SSL certificate renewal failed for $DOMAINS" | mail -s "SSL Renewal Failed" $EMAIL
fi
4.4 Crontab自动续期
# 添加到crontab
0 12 * * * /usr/local/bin/certbot-renew.sh >/dev/null 2>&1
# 或者使用certbot的内置续期
0 12 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
5. SSL/TLS性能优化
5.1 会话复用优化
http {
# SSL会话缓存
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# 禁用会话票据(避免密钥轮换问题)
ssl_session_tickets off;
# 或者启用会话票据(适用于多服务器环境)
# ssl_session_tickets on;
# ssl_session_ticket_key /etc/ssl/private/ticket.key;
server {
listen 443 ssl http2;
server_name performance.example.com;
ssl_certificate /etc/ssl/certs/performance.example.com.crt;
ssl_certificate_key /etc/ssl/private/performance.example.com.key;
# 启用OCSP装订减少客户端验证时间
ssl_stapling on;
ssl_stapling_verify on;
ssl_stapling_responder http://ocsp.example.com;
# 预加载HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
location / {
root /var/www/performance;
# 启用gzip压缩
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
}
}
5.2 HTTP/2优化
server {
listen 443 ssl http2;
server_name http2.example.com;
ssl_certificate /etc/ssl/certs/http2.example.com.crt;
ssl_certificate_key /etc/ssl/private/http2.example.com.key;
include /etc/nginx/ssl-params.conf;
# HTTP/2推送
location / {
root /var/www/http2;
# 推送关键资源
location = /index.html {
http2_push /css/style.css;
http2_push /js/app.js;
try_files $uri =404;
}
# 静态文件缓存
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# 启用Brotli压缩(如果模块可用)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
}
}
5.3 SSL缓冲区优化
server {
listen 443 ssl http2;
server_name buffered.example.com;
ssl_certificate /etc/ssl/certs/buffered.example.com.crt;
ssl_certificate_key /etc/ssl/private/buffered.example.com.key;
# SSL缓冲区大小优化
ssl_buffer_size 4k; # 减少延迟,适用于小文件
# ssl_buffer_size 16k; # 提高吞吐量,适用于大文件
include /etc/nginx/ssl-params.conf;
location / {
root /var/www/buffered;
# 根据文件大小动态调整缓冲区
location ~* \.(jpg|jpeg|png|gif|mp4|avi)$ {
ssl_buffer_size 16k; # 大文件使用大缓冲区
expires 1y;
}
location ~* \.(css|js|html)$ {
ssl_buffer_size 4k; # 小文件使用小缓冲区
expires 1d;
}
}
}
6. SSL/TLS安全强化
6.1 高安全级别配置
server {
listen 443 ssl http2;
server_name secure.example.com;
# 证书配置
ssl_certificate /etc/ssl/certs/secure.example.com.crt;
ssl_certificate_key /etc/ssl/private/secure.example.com.key;
# 只允许TLS 1.3
ssl_protocols TLSv1.3;
# 严格的密码套件
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_prefer_server_ciphers off;
# 强DH参数
ssl_dhparam /etc/ssl/certs/dhparam-4096.pem;
# 严格传输安全
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 内容安全策略
add_header Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline';" always;
# 其他安全头部
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy no-referrer always;
add_header Feature-Policy "geolocation 'none'; microphone 'none'; camera 'none'" always;
location / {
root /var/www/secure;
# 禁用服务器信息泄露
server_tokens off;
# 禁用不安全的HTTP方法
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 405;
}
}
}
6.2 SSL Labs A+评级配置
# 获得SSL Labs A+评级的配置
server {
listen 443 ssl http2;
server_name aplus.example.com;
# 证书链配置
ssl_certificate /etc/ssl/certs/aplus.example.com-fullchain.crt;
ssl_certificate_key /etc/ssl/private/aplus.example.com.key;
# 协议版本
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/ssl/certs/dhparam-2048.pem;
# 会话配置
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP装订
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
location / {
root /var/www/aplus;
try_files $uri $uri/ =404;
}
}
7. SSL证书管理
7.1 证书监控脚本
#!/bin/bash
# ssl-monitor.sh
CERTIFICATES=(
"/etc/ssl/certs/example.com.crt"
"/etc/ssl/certs/api.example.com.crt"
"/etc/ssl/certs/secure.example.com.crt"
)
WARNING_DAYS=30
CRITICAL_DAYS=7
EMAIL="admin@example.com"
check_certificate() {
local cert_file=$1
local domain=$(basename $cert_file .crt)
if [ ! -f "$cert_file" ]; then
echo "ERROR: Certificate file $cert_file not found"
return 1
fi
# 获取证书过期时间
local expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
local expiry_epoch=$(date -d "$expiry_date" +%s)
local current_epoch=$(date +%s)
local days_until_expiry=$(( (expiry_epoch - current_epoch) / 86400 ))
echo "Domain: $domain"
echo "Expires: $expiry_date"
echo "Days until expiry: $days_until_expiry"
if [ $days_until_expiry -le $CRITICAL_DAYS ]; then
echo "CRITICAL: Certificate expires in $days_until_expiry days!" | \
mail -s "CRITICAL: SSL Certificate Expiring - $domain" $EMAIL
elif [ $days_until_expiry -le $WARNING_DAYS ]; then
echo "WARNING: Certificate expires in $days_until_expiry days!" | \
mail -s "WARNING: SSL Certificate Expiring - $domain" $EMAIL
fi
echo "---"
}
echo "=== SSL Certificate Monitoring ==="
echo "Date: $(date)"
echo
for cert in "${CERTIFICATES[@]}"; do
check_certificate "$cert"
done
7.2 证书更新自动化
#!/bin/bash
# ssl-update.sh
CERT_DIR="/etc/ssl/certs"
KEY_DIR="/etc/ssl/private"
BACKUP_DIR="/etc/ssl/backup/$(date +%Y%m%d)"
update_certificate() {
local domain=$1
local new_cert=$2
local new_key=$3
echo "Updating certificate for $domain..."
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份现有证书
if [ -f "$CERT_DIR/$domain.crt" ]; then
cp "$CERT_DIR/$domain.crt" "$BACKUP_DIR/$domain.crt.bak"
fi
if [ -f "$KEY_DIR/$domain.key" ]; then
cp "$KEY_DIR/$domain.key" "$BACKUP_DIR/$domain.key.bak"
fi
# 验证新证书
if ! openssl x509 -in "$new_cert" -noout; then
echo "ERROR: Invalid certificate file"
return 1
fi
if ! openssl rsa -in "$new_key" -check -noout; then
echo "ERROR: Invalid private key file"
return 1
fi
# 检查证书和私钥匹配
cert_modulus=$(openssl x509 -noout -modulus -in "$new_cert" | openssl md5)
key_modulus=$(openssl rsa -noout -modulus -in "$new_key" | openssl md5)
if [ "$cert_modulus" != "$key_modulus" ]; then
echo "ERROR: Certificate and private key do not match"
return 1
fi
# 安装新证书
cp "$new_cert" "$CERT_DIR/$domain.crt"
cp "$new_key" "$KEY_DIR/$domain.key"
# 设置权限
chmod 644 "$CERT_DIR/$domain.crt"
chmod 600 "$KEY_DIR/$domain.key"
# 测试Nginx配置
if nginx -t; then
systemctl reload nginx
echo "Certificate updated and Nginx reloaded successfully"
return 0
else
echo "ERROR: Nginx configuration test failed, rolling back..."
# 回滚
if [ -f "$BACKUP_DIR/$domain.crt.bak" ]; then
cp "$BACKUP_DIR/$domain.crt.bak" "$CERT_DIR/$domain.crt"
fi
if [ -f "$BACKUP_DIR/$domain.key.bak" ]; then
cp "$BACKUP_DIR/$domain.key.bak" "$KEY_DIR/$domain.key"
fi
return 1
fi
}
# 使用示例
# update_certificate "example.com" "/tmp/new_cert.crt" "/tmp/new_key.key"
8. 故障排除
8.1 SSL配置测试
#!/bin/bash
# ssl-test.sh
DOMAIN="example.com"
PORT="443"
echo "=== SSL/TLS Configuration Test ==="
echo "Domain: $DOMAIN"
echo "Port: $PORT"
echo
# 基本连接测试
echo "1. Basic connection test:"
if openssl s_client -connect $DOMAIN:$PORT -servername $DOMAIN </dev/null 2>/dev/null | grep -q "CONNECTED"; then
echo "✓ SSL connection successful"
else
echo "✗ SSL connection failed"
fi
# 证书有效期检查
echo -e "\n2. Certificate expiry check:"
expiry=$(echo | openssl s_client -connect $DOMAIN:$PORT -servername $DOMAIN 2>/dev/null | openssl x509 -noout -dates | grep notAfter)
echo $expiry
# 支持的协议版本
echo -e "\n3. Supported TLS versions:"
for version in ssl2 ssl3 tls1 tls1_1 tls1_2 tls1_3; do
if echo | openssl s_client -connect $DOMAIN:$PORT -$version 2>/dev/null | grep -q "CONNECTED"; then
echo "✓ $version supported"
else
echo "✗ $version not supported"
fi
done
# 密码套件测试
echo -e "\n4. Cipher suite test:"
echo | openssl s_client -connect $DOMAIN:$PORT -cipher HIGH 2>/dev/null | grep "Cipher is"
# OCSP装订检查
echo -e "\n5. OCSP stapling check:"
echo | openssl s_client -connect $DOMAIN:$PORT -status 2>/dev/null | grep -A1 "OCSP response"
# HTTP/2支持检查
echo -e "\n6. HTTP/2 support check:"
if curl -I --http2 -s https://$DOMAIN/ | grep -q "HTTP/2"; then
echo "✓ HTTP/2 supported"
else
echo "✗ HTTP/2 not supported"
fi
8.2 常见SSL问题诊断
#!/bin/bash
# ssl-diagnose.sh
diagnose_ssl_issues() {
local domain=$1
echo "=== SSL Diagnosis for $domain ==="
# 检查DNS解析
echo "1. DNS Resolution:"
if nslookup $domain >/dev/null 2>&1; then
echo "✓ DNS resolution successful"
else
echo "✗ DNS resolution failed"
return 1
fi
# 检查端口连通性
echo -e "\n2. Port connectivity:"
if nc -zv $domain 443 2>&1 | grep -q "succeeded"; then
echo "✓ Port 443 is accessible"
else
echo "✗ Port 443 is not accessible"
return 1
fi
# 检查证书链
echo -e "\n3. Certificate chain:"
cert_chain=$(echo | openssl s_client -connect $domain:443 -showcerts 2>/dev/null)
if echo "$cert_chain" | grep -q "Verify return code: 0"; then
echo "✓ Certificate chain is valid"
else
echo "✗ Certificate chain verification failed"
echo "$cert_chain" | grep "Verify return code"
fi
# 检查SNI支持
echo -e "\n4. SNI support:"
if echo | openssl s_client -connect $domain:443 -servername $domain 2>/dev/null | grep -q "SSL handshake has read"; then
echo "✓ SNI is working"
else
echo "✗ SNI may not be working"
fi
# 检查混合内容
echo -e "\n5. Mixed content check:"
if curl -s https://$domain/ | grep -qi "http://"; then
echo "⚠ Potential mixed content detected"
else
echo "✓ No obvious mixed content issues"
fi
}
# 使用示例
diagnose_ssl_issues "example.com"
小结
通过本文的学习,你应该掌握:
- SSL/TLS协议的基础概念和配置方法
- 多种证书类型的配置和管理
- Let's Encrypt自动化证书获取和续期
- SSL/TLS性能优化技巧
- 高级安全配置和强化方法
- 证书监控和管理自动化
- 常见SSL问题的诊断和解决
下一篇文章将介绍Nginx的安全配置与防护。