nginx 使用 SSL证书配置 HTTPS

生成 D-H 密钥文件

生成 Diffie-Hellman (D-H) 密钥文件:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

配置 nginx

创建 SSL 通用配置

检查 nginx 版本:

sudo nginx -v
nginx version: nginx/1.22.1

检查 OpenSSL 版本:

sudo openssl version
OpenSSL 3.0.15 3 Sep 2024 (Library: OpenSSL 3.0.15 3 Sep 2024)

创建一个用于保存 nginx 通用配置的目录:

sudo mkdir -p /etc/nginx/common

创建一个适合大多数场景的 SSL 通用配置文件:

sudo vim /etc/nginx/common/ssl.intermediate.conf

插入配置:

# ssl.intermediate.conf
# Needs nginx nginx 1.22.1, OpenSSL 3.0.15
# Supports Firefox 27, Android 4.4.2, Chrome 31, Edge, IE 11 on Windows 7, Java 8u31, OpenSSL 1.0.1, Opera 20, Safari 9

# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
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:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;

# see also ssl_session_ticket_key alternative to stateful session cache
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
ssl_session_tickets off;

# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/ssl/certs/dhparam.pem;

# HSTS (ngx_http_headers_module is required)
add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains; preload' always;

创建一个兼容性更好(会降低安全性)的 SSL 通用配置文件:

sudo vim /etc/nginx/common/ssl.old.conf

插入配置:

# ssl.old.conf
# Needs nginx nginx 1.22.1, OpenSSL 3.0.15
# Supports Firefox 1, Android 2.3, Chrome 1, Edge 12, IE8 on Windows XP, Java 6, OpenSSL 0.9.8, Opera 5, Safari 1

# old configuration
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_ciphers @SECLEVEL=0: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:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA;
ssl_prefer_server_ciphers on;

# see also ssl_session_ticket_key alternative to stateful session cache
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
ssl_session_tickets off;

# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/ssl/certs/dhparam.pem;

# HSTS (ngx_http_headers_module is required)
add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains; preload' always;

网站引入 SSL 配置

编辑网站 nginx 配置文件:

sudo vim /etc/nginx/sites-available/avinzheng.com.conf

根据需要引入合适的 SSL 通用配置文件:

# avinzheng.com

server {
  listen 443 ssl http2;
  server_name avinzheng.com;
  root /var/wwwroot/avinzheng.com;
  index index.html;

  # ssl
  include /etc/nginx/common/ssl.intermediate.conf;
  ssl_certificate /etc/ssl/web/fullchain.pem;
  ssl_certificate_key /etc/ssl/web/key.pem;

  # OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /etc/ssl/web/ca.pem;
  resolver 127.0.0.1 valid=300s;
  resolver_timeout 10s;
}

# redirect http to https
server {
  listen 80;
  server_name avinzheng.com;
  return 301 https://$http_host$request_uri;
}
  • ssl_certificate 填写 PEM 格式(文件后缀可能为 .crt / .cer / .pem )的证书文件路径,文件内容一般包含域名证书 + 中间证书 + 根证书(可选)。如果使用的是 acme.sh 生成的证书,需要使用 --fullchain-file 对应的 fullchain.pem 证书文件。
  • ssl_certificate_key 填写 PEM 格式(文件后缀可能为 .key / .pem )的证书私钥的文件路径(如果 SSL 证书只有一个 .pem 文件,则可能同时包含了证书和私钥,使用同一个文件)。
  • ssl_trusted_certificate 填写 PEM 格式(文件后缀可能为 .crt / .cer / .pem )的中间证书路径(文件内容需要包含所有的中间证书),如果 ssl_certificate 配置的证书文件内容中已经包含了所有的中间证书,该配置项可省略。

部分 CA 证书服务商已经不再支持 OCSP(如 Let’s Encrypt),生成的证书内容将不再包含 OCSP URI 信息,配置 OCSP Stapling 将无效,如果需要使用 OCSP 功能,需要更换 CA 证书服务商。

检查证书是否支持 OCSP:

openssl x509 -noout -ocsp_uri -in /etc/ssl/web/fullchain.pem

如果无内容输出则表示证书不包含 OCSP URI 信息,不支持 OCSP(支持 OCSP 的证书会输出 OCSP URL 地址)。

配置 AppArmor

如果配置了 nginx 的 AppArmor 规则,需要修改规则文件:

sudo vim /etc/apparmor.d/usr.sbin.nginx

需要添加相应文件路径:

#include <tunables/global>

/usr/sbin/nginx {
  ...
  #include <abstractions/openssl>

  ...
  /etc/nginx/common/** r,
  /etc/ssl/certs/dhparam.pem r,
  /etc/ssl/web/* r,
  /var/cache/nginx/ocsp/** rw,
}

加载 AppArmor 配置文件:

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

重启 nginx 服务

验证 nginx 配置是否正确:

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

重启 nginx 服务:

sudo systemctl restart nginx

检查 OCSP Stapling

如果证书支持 OCSP 并且配置了 OCSP Stapling,可检查是否生效:

openssl s_client -connect <domain>:443 -status -servername <domain> | grep -A 17 "OCSP response"

<domain> 为可以通过 HTTPS 正常访问的需要检查的域名。

正常输出信息中应包含以下内容:

OCSP Response Status: successful (0x0)

配置防火墙

UFW 防火墙

Debian / Ubuntu 等系统一般使用的是 UFW 防火墙。

UFW 防火墙添加对应的端口号:

sudo ufw allow 443/tcp

重新加载防火墙规则:

sudo ufw reload

查看防火墙状态:

sudo ufw status verbose

VPS 如果自带了网络防火墙服务,需要在防火墙中添加 443/tcp 端口。

FirewallD 防火墙

CentOS 等系统一般使用的是 FirewallD 防火墙。

查看 FirewallD 当前默认区域永久配置:

sudo firewall-cmd --permanent --list-all
public
target: default
icmp-block-inversion: no
interfaces:
sources:
services: http ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:

可见 services 中没有启用 https 服务,ports 中没有添加 443/tcp 端口。

永久启用 https 服务(443/tcp):

sudo firewall-cmd --permanent --add-service=https

查看下次重启(防火墙重新加载、服务器重启或者系统重启)之后永久生效的服务:

sudo firewall-cmd --permanent --list-service
http https ssh

重载防火墙规则:

sudo firewall-cmd --reload

VPS 如果自带了网络防火墙服务,需要在防火墙中添加 443/tcp 端口。

SSL 安全检测

检测地址:https://www.ssllabs.com/ssltest/

输入域名,正常情况下检测结果应为 A+

参考文献