Nginx 配置 HTTPS 服务器

本贴最后更新于 920 天前,其中的信息可能已经渤澥桑田

配置HTTPS服务器

要配置HTTPS服务器,必须在server块的侦听套接字上启用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;
    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”和“ ssl_ciphers HIGH:!aNULL:!MD5”,因此通常不需要显式配置它们。请注意,这些指令的默认值被更改了几次。

HTTPS服务器优化

SSL操作会消耗额外的CPU资源。在多处理器系统上,应该运行几个工作进程,不小于CPU可用核数。cpu最密集的操作是SSL握手。有两种方法可以最小化每个客户机的这些操作的数量:第一种方法是允许keepalive连接通过一个连接发送多个请求,第二个是重用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;
        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;
    ...
}

如果服务端证书和bundle以错误的顺序连接,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尝试将私钥与bundle的第一个证书一起使用,而不是服务端证书。

浏览器通常会存储它们收到的并由受信任的权威机构签署的中间证书,因此活跃使用的浏览器可能已经拥有所需的中间证书,并且可能不会抱怨在没有链接包的情况下发送的证书。为了确保服务器发送完整的证书链,可以使用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。

在这个例子中,www.godaddy.com服务器0号证书的主题(" s ")是由颁发者(" i ")签名的,而它本身就是1号证书的主题,它又由它的颁发者("i")签名,而它的颁发者又是2号证书的主题("s"),它又由著名的颁发机构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服务器

当配置两个或多个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;
    ...
}

使用此配置,浏览器将接收默认服务器的(设置了default参数的server块,或者第一个server块)证书,即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,它必须同时得到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

翻译:xinlan

回帖
请输入回帖内容 ...