nginx-7 配置HTTPS服务器
想要配置一个HTTPS服务器,必须启用在server块中listen端口的ssl,同时使用server_certificate和private_key指令指定相关文件。
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的master进程是可读取的。私钥可以与证书交替存储在同一个文件中。
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
,所以没必要显示配置他们,但是要注意nginx的不同版本他们对应的默认值可能是不一样的。下面会讲到。
HTTPS服务器性能优化
SSL操作会消耗CPU资源,其中,SSL握手最为突出。因此,要提高HTTPS服务器的性能,就要最小化每个客户端连接SSL操作的次数。nginx对此有两种应对方式。
- 启用keepalive,使得通过一个连接可以发送多个请求,而不是一个连接一个请求
- 重用ssl session,对于并行和后续连接,避免SSL握手
ssl_session_cache
ssl_session_timeout 默认5m
worker_processes auto;
http {
# shared 在worker进程间共享
# SSL
# 10m/10M 10兆内存空间(megabyte)
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
如果服务器证书在链接证书之后,nginx会启动失败,并展示以下错误信息。
SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
(SSL: error:0B080074:x509 certificate routines:
X509_check_private_key:key values mismatch)
生成的文件应作为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;
...
}
一个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;
...
}
基于名称的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
还是www.example.org
,只会收到www.example.com.crt
证书。因为在建立SSL连接的时候,服务器名称是未知的,这种情况下,IP地址一样,nginx会选择一个默认服务器,也就是第一个,来处理请求,所以客户端收到的总是第一个服务器的证书。可以通过配置不同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;
...
}
除此之外,解决方法还有拥有多个域名的证书和SNI(Server Name Indication)
可以匹配多个域名的证书
- 拥有多个全称域名的证书
全称域名的长度是有限制的
# 这种证书,可以提升作用域,不必写在server里
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;
...
}
- 通配符域名证书
与在服务器名称中讲的不一致,这里的通配符只能匹配一个部分。比如*.example.org,只能匹配www.example.org
,而不能匹配example.org
或者www.sub.example.org
。
SNI(Server Name Indication)
SNI允许在SSL握手的时候,客户端传递一个服务器名称给服务器。这种情况下,即使多个HTTPS服务器使用同一个IP,只要服务器名称不同,nginx就知道使用哪个服务器了。这是一种更常用的解决方法。现在大多数浏览器都支持SNI。
注意:SNI只传递域名,不应该是IP
为了在nginx中使用sni,必须在构建nginx二进制文件的openssl库以及在运行时动态链接到的库中都支持它。OpenSSL从0.9.8f版本开始支持SNI,前提OpenSSL使用配置选项 --enable-tlsext
构建。自从0.9.8j这个配置选项是默认的。如果nginx是支持SNI的,使用 nginx -V
如下。
$ nginx -V
...
TLS SNI support enabled
...
但是,如果支持SNI的nginx动态链接的OpenSSL库不支持SNI,将会有如下警告。
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
考虑兼容性
- 0.7.14 开始支持在listen指令中指定ssl参数
- 0.5.23 开始支持SNI
- 0.5.6 开始支持shared SSL session cached
- 1.9.1 开始默认的SSL协议是TLSv1, TLSv1.1, and TLSv1.2(如果由OpenSSL支持)
- 0.7.65,0.8.19以后,默认的SSL协议是 SSLv3, TLSv1, TLSv1.1, and TLSv1.2(如果由OpenSSL支持)
- 0.7.64,0.8.18以前,默认的SSL协议是 SSLv2, SSLv3, and 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