配置 HTTPS 服务器

HTTPS server optimization
SSL certificate chains
A single HTTP/HTTPS server
Name-based HTTPS servers
     An SSL certificate with several names
     Server Name Indication
Compatibility

要配置 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 指令配置。1 MB 的缓存包含大约 4000 个会话。默认缓存超时为 5 分钟。可以通过使用 ssl_session_timeout 指令来增加它。以下是针对具有 10 MB 共享会话缓存的多核系统进行优化的示例配置:

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//emailAddress=info@valicert.com
...

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

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

如果尚未添加证书捆绑包,则仅显示服务器证书#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 服务器。添加listenssl指令的参数 来解决这个问题。因此,不鼓励在现代版本中 使用 ssl指令。

基于名称的 HTTPS 服务器

配置两个或多个 HTTPS 服务器侦听单个 IP 地址时会出现一个常见问题:

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 协议行为引起的。SSL 连接是在浏览器发送 HTTP 请求之前建立的,并且 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 库以及运行时动态链接的库都必须支持 SNI。如果 OpenSSL 使用配置选项“--enable-tlsext”构建,则从 0.9.8f 版本开始支持 SNI 。 自 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