配置 HTTPS 服务器

HTTPS 服务器优化
SSL 证书链
单个 HTTP/HTTPS 服务器
基于名称的 HTTPS 服务器
     具有多个名称的 SSL 证书
     服务器名称指示
兼容性

要配置 HTTPS 服务器,必须在 侦听套接字 上启用 ssl 参数,在 服务器 块中,并且应指定 服务器证书私钥 文件的位置

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ...
}

服务器证书是公共实体。它将发送给连接到服务器的每个客户端。私钥是安全实体,应存储在具有受限访问权限的文件中,但是,nginx 的主进程必须可读。私钥也可以存储在与证书相同的文件中

    ssl_certificate     www.example.com.cert;
    ssl_certificate_key www.example.com.cert;

在这种情况下,文件访问权限也应受到限制。尽管证书和密钥存储在一个文件中,但只有证书才会发送给客户端。

指令 ssl_protocolsssl_ciphers 可用于限制连接,仅包括 SSL/TLS 的强版本和密码。默认情况下,nginx 使用 “ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3” 和 “ssl_ciphers HIGH:!aNULL:!MD5”,因此通常不需要显式配置它们。请注意,这些指令的默认值已 更改 多次。

HTTPS 服务器优化

SSL 操作消耗额外的 CPU 资源。在多处理器系统上,应该运行多个工作进程,不少于可用 CPU 内核的数量。最消耗 CPU 的操作是 SSL 握手。有两种方法可以最大程度地减少每个客户端的这些操作数量:第一种是通过启用保持活动连接,以通过一个连接发送多个请求,第二种是重用 SSL 会话参数,以避免并行和后续连接的 SSL 握手。会话存储在工作进程之间共享的 SSL 会话缓存中,并由ssl_session_cache指令配置。一兆字节的缓存包含大约 4000 个会话。默认缓存超时时间为 5 分钟。可以使用ssl_session_timeout指令增加该时间。以下是一个针对具有 10 兆字节共享会话缓存的多核系统优化的示例配置

worker_processes auto;

http {
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    server {
        listen              443 ssl;
        server_name         www.example.com;
        keepalive_timeout   70;

        ssl_certificate     www.example.com.crt;
        ssl_certificate_key www.example.com.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        ...

SSL 证书链

一些浏览器可能会抱怨由知名证书颁发机构签名的证书,而其他浏览器可能会毫无问题地接受该证书。出现这种情况是因为颁发机构使用未出现在特定浏览器分发的知名受信任证书颁发机构证书库中的中间证书对服务器证书进行了签名。在这种情况下,颁发机构会提供一组链式证书,这些证书应连接到已签名的服务器证书。服务器证书必须出现在合并文件中的链式证书之前

$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt

应在ssl_certificate指令中使用生成的文件

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.chained.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

如果服务器证书和证书包按错误的顺序连接,nginx 将无法启动,并将显示错误消息

SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
   (SSL: error:0B080074:x509 certificate routines:
    X509_check_private_key:key values mismatch)

因为 nginx 尝试使用私钥与证书包的第一个证书一起使用,而不是与服务器证书一起使用。

浏览器通常会存储它们接收到的并由受信任颁发机构签名的中间证书,因此,积极使用的浏览器可能已经拥有所需的中间证书,并且可能不会抱怨在没有链式证书包的情况下发送的证书。为了确保服务器发送完整的证书链,可以使用openssl命令行实用程序,例如

$ openssl s_client -connect www.godaddy.com:443
...
Certificate chain
 0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
     /1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc
     /OU=MIS Department/CN=www.GoDaddy.com
     /serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
   i:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
 2 s:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
   i:/L=ValiCert Validation Network/O=ValiCert, Inc.
     /OU=ValiCert Class 2 Policy Validation Authority
     /CN=http://www.valicert.com//[email protected]
...

在使用SNI测试配置时,指定-servername选项非常重要,因为openssl默认不使用 SNI。

在此示例中,www.GoDaddy.com服务器证书 #0 的主题(“s”)由颁发机构(“i”)签名,而颁发机构本身是证书 #1 的主题,由颁发机构签名,而颁发机构本身是证书 #2 的主题,由知名颁发机构ValiCert, Inc.签名,其证书存储在浏览器的内置证书库中(位于杰克建造的房子里)。

如果未添加证书包,则只显示服务器证书 #0。

单个 HTTP/HTTPS 服务器

可以配置一个同时处理 HTTP 和 HTTPS 请求的单个服务器

server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

在 0.7.14 之前,无法针对各个侦听套接字选择性启用 SSL,如上所示。只能使用 ssl 指令为整个服务器启用 SSL,这使得无法设置单个 HTTP/HTTPS 服务器。添加 listen 指令的 ssl 参数是为了解决此问题。因此,不建议在现代版本中使用 ssl 指令。

基于名称的 HTTPS 服务器

在单个 IP 地址上侦听两个或更多 HTTPS 服务器时,会出现一个常见问题

server {
    listen          443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

使用此配置,浏览器将收到默认服务器的证书,即 www.example.com,而不管请求的服务器名称如何。这是由 SSL 协议行为引起的。在浏览器发送 HTTP 请求之前,SSL 连接已建立,nginx 不知道所请求服务器的名称。因此,它可能只提供默认服务器的证书。

解决此问题的最古老且最可靠的方法是为每个 HTTPS 服务器分配一个单独的 IP 地址

server {
    listen          192.168.1.1:443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}

server {
    listen          192.168.1.2:443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

具有多个名称的 SSL 证书

还有其他方法可以在多个 HTTPS 服务器之间共享单个 IP 地址。但是,所有这些方法都有其缺点。一种方法是在 SubjectAltName 证书字段中使用具有多个名称的证书,例如 www.example.comwww.example.org。但是,SubjectAltName 字段长度有限。

另一种方法是使用具有通配符名称的证书,例如 *.example.org。通配符证书保护指定域的所有子域,但仅限于一个级别。此证书与 www.example.org 匹配,但与 example.orgwww.sub.example.org 不匹配。这两种方法也可以结合使用。证书可以在 SubjectAltName 字段中包含确切名称和通配符名称,例如 example.org*.example.org

最好在配置的 http 级别放置具有多个名称的证书文件及其私钥文件,以在所有服务器中继承其单个内存副本

ssl_certificate     common.crt;
ssl_certificate_key common.key;

server {
    listen          443 ssl;
    server_name     www.example.com;
    ...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ...
}

服务器名称指示

在单个 IP 地址上运行多个 HTTPS 服务器的更通用的解决方案是 TLS 服务器名称指示扩展 (SNI,RFC 6066),它允许浏览器在 SSL 握手期间传递请求的服务器名称,因此,服务器将知道它应该为连接使用哪个证书。SNI 目前 受支持 大多数现代浏览器,但某些旧或特殊客户端可能不会使用它。

只有域名可以在 SNI 中传递,但是如果请求包含实际 IP 地址,一些浏览器可能会错误地将服务器的 IP 地址作为其名称传递。不应该依赖于此。

为了在 nginx 中使用 SNI,它必须在 nginx 二进制文件构建的 OpenSSL 库和运行时动态链接到的库中都得到支持。如果使用配置选项构建,OpenSSL 自 0.9.8f 版本以来支持 SNI “--enable-tlsext”。 自 OpenSSL 0.9.8j 以来,此选项默认启用。如果 nginx 是使用 SNI 支持构建的,那么 nginx 在使用 “-V” 开关运行时将显示此内容

$ nginx -V
...
TLS SNI support enabled
...

但是,如果启用了 SNI 的 nginx 动态链接到不带 SNI 支持的 OpenSSL 库,nginx 会显示警告

nginx was built with SNI support, however, now it is linked
dynamically to an OpenSSL library which has no tlsext support,
therefore SNI is not available

兼容性

作者:Igor Sysoev
编辑:Brian Mercer