NGINX SSL配置
本节介绍如何在NGINX和NGINX Plus上配置HTTPS服务器。
设置HTTPS服务器
要设置HTTPS服务器,请在您的nginx.conf文件中的块中ssl
将该listen指令的参数包括在内server,然后指定服务器证书和私钥文件的位置:
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或NGINX Plus服务器的每个客户端。私钥是一个安全的实体,应存储在访问受限的文件中。但是,NGINX主进程必须能够读取该文件。另外,私钥可以与证书存储在同一文件中:
ssl_certificate www.example.com.cert;
ssl_certificate_key www.example.com.cert;
在这种情况下,限制对文件的访问非常重要。请注意,尽管在这种情况下证书和密钥存储在一个文件中,但是仅证书被发送到客户端。
该ssl_protocols和ssl_ciphers指令可用于要求客户端建立连接时,只使用SSL / TLS的大力版本和密码。
从1.9.1版开始,NGINX使用以下默认值:
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
有时在较旧的密码设计中会发现漏洞,我们建议在现代NGINX配置中将其禁用(不幸的是,由于现有NGINX部署的向后兼容性,默认配置无法轻易更改)。请注意,CBC模式密码可能容易受到多种攻击(尤其是BEAST攻击,如 CVE-2011-3389中所述),并且由于POODLE攻击,我们建议不要使用SSLv3 ,除非您需要支持旧版客户端。
HTTPS服务器优化
SSL操作会消耗额外的CPU资源。最耗CPU的操作是SSL握手。有两种方法可以最大程度地减少每个客户端执行这些操作的次数:
- 启用保持连接以通过一个连接发送多个请求
- 重用SSL会话参数以避免并行和后续连接的SSL握手
会话存储在工作进程之间共享的SSL会话缓存中,并由ssl_session_cache伪指令配置。一兆字节的缓存包含大约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;
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
...
在此示例s
中,www.GoDaddy.com
服务器证书的主题(“ ”)#0
由“i”
本身是证书#1的主题的发行者()签名。证书#1由本身是证书#2主题的发行者签名。但是,此证书由著名的颁发者签名,该证书的证书存储在浏览器中。ValiCert, Inc.
如果尚未添加证书捆绑包,则仅显示服务器证书(#0)。
单个HTTP / HTTPS服务器
通过将一个listen
带ssl
参数的指令和一个不带参数的指令置于同一虚拟服务器中,可以配置一台同时处理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;
#...
}
在NGINX 0.7.13及更早版本中,无法为各个侦听套接字选择性地启用SSL,如上所述。仅可以使用ssl指令为整个服务器启用SSL ,从而无法设置单个HTTP / HTTPS服务器。指令的ssl
参数listen已添加以解决此问题。ssl因此,该指令在版本0.7.14和更高版本中已弃用。
基于名称的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协议本身的行为引起的。在浏览器发送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;
#...
}
请注意,HTTPS上游(proxy_ssl_ciphers,proxy_ssl_protocols和proxy_ssl_session_reuse)还有一些特定的代理设置,可用于在NGINX和上游服务器之间微调SSL。您可以在HTTP代理模块文档中阅读有关这些内容的更多信息。
具有多个名称的SSL证书
还有其他几种方法可以在多个HTTPS服务器之间共享一个IP地址。但是,它们都有缺点。一种方法是使用SubjectAltName
证书字段中具有多个名称的证书,例如www.example.com
和www.example.org
。但是,SubjectAltName
场的长度是有限的。
另一种方法是使用带有通配符名称的证书,例如*.example.org
。通配符证书可保护指定域的所有子域,但只能在一个级别上。此证书匹配www.example.org
,但不匹配example.org
或www.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;
#...
}
服务器名称指示
TLS服务器名称指示 (SNI)扩展(RFC 6066)是在单个IP地址上运行多个HTTPS服务器的更通用的解决方案,它允许浏览器在SSL握手期间传递请求的服务器名称。使用此解决方案,服务器将知道它应使用哪个证书进行连接。但是,SNI的浏览器支持有限。当前从以下浏览器版本开始受支持:
- Opera 8.0
- MSIE 7.0(但仅在Windows Vista或更高版本上)
- Firefox 2.0和其他使用Mozilla Platform rv:1.8.1的浏览器
- Safari 3.2.1(Windows版本在Vista或更高版本上支持SNI)
- Chrome(Windows版本在Vista或更高版本上也支持SNI)
SNI中只能传递域名。但是,如果请求包含文字IP地址,则某些浏览器会将服务器的IP地址作为名称传递。最好不要依赖于此。
为了在NGINX中使用SNI,必须在构建NGINX二进制文件的OpenSSL库以及在运行时动态链接的库中都支持SNI。如果使用配置构建,则OpenSSL从0.9.8f版本开始支持SNI 。从OpenSSL版本0.9.8j开始,默认情况下启用此选项。如果NGINX是使用SNI支持构建的,则在使用该开关运行时,NGINX将显示以下内容:option --enable-tlsext
-V
$ nginx -V
...
启用TLS SNI支持
...
但是,如果启用了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
相容性说明
-V
自版本0.8.21和0.7.62起,交换机已显示SNI支持状态。- 从0.7.14版本开始,支持伪指令的
ssl
参数listen。在0.8.21版本之前,只能与default
参数一起指定。 - 从0.5.23版本开始支持SNI。
- 从0.5.6版开始,支持共享SSL会话缓存。
- 版本1.9.1及更高版本:默认的SSL协议
TLSv1
,TLSv1.1
以及TLSv1.2
(如果OpenSSL库支持)。 - 从0.7.65和0.8.19及以后的版本,默认SSL协议是
SSLv3
,TLSv1
,TLSv1.1
,和TLSv1.2
(如果OpenSSL库支持)。 - 在0.7.64和0.8.18及早期版本,默认SSL协议
SSLv2
,SSLv3
和TLSv1
。 - 在1.0.5版和更高版本中,默认SSL密码为
HIGH:!aNULL:!MD5
。 - 在0.7.65和0.8.20及更高版本中,默认SSL密码为
HIGH:!ADH:!MD5
。 - 从0.8.19版本开始,默认SSL密码为
ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM
。 - 从0.7.64、0.8.18及更低版本开始,默认SSL密码为
ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP
。