要配置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;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
服务器证书是公共实体。它被发送到连接到服务器的每个客户端。私钥是一个安全的实体,应该存储在具有受限访问权限的文件中,但是,它必须是nginx的主进程可读的。私钥可以替代地存储在与证书相同的文件中:
ssl_certificate www.example.com.cert;
ssl_certificate_key www.example.com.cert;
在这种情况下,还应限制文件访问权限。虽然证书和密钥存储在一个文件中,但只有证书才会发送到客户端。
指令ssl_protocols和ssl_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;
...
}
如果服务器证书和软件包已按错误顺序连接,则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
...
在该示例中,www.GoDaddy.com服务器证书#0的主题(“s”)由发行者(“i”)签名,发行者本身是证书#1的主题,其由发行者本身签署。是证书#2的主题,由知名发行人ValiCert,Inc。签署,其证书存储在浏览器的内置证书库(位于Jack建造的房屋中)中。
如果尚未添加证书包,则仅显示服务器证书#0。
单个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.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;
...
}
服务器名称指示
在单个IP地址上运行多个HTTPS服务器的更通用的解决方案是TLS服务器名称指示扩展(SNI,RFC 6066),它允许浏览器在SSL握手期间传递请求的服务器名称,因此服务器将知道哪个它应该用于连接的证书。目前大多数现代浏览器都支持SNI,但某些老客户或特殊客户可能无法使用。
为了在nginx中使用SNI,必须在构建nginx二进制文件的OpenSSL库以及在运行时动态链接到的库中支持它。如果使用配置选项“--enable-tlsext”构建,OpenSSL支持自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