nginx配置https服务器

本文介绍如何将nginx配置成一个https服务器。

配置https服务器,需要在listen指令后面添加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指令限制了使用的加密算法和版本,只能用比较健壮的加密算法。默认情况下,nginx使用“ssl_protocols TLSv1 TLSv1.1 TLSv1.2”和“ssl_ciphers HIGH:!aNULL:!MD5”,因此一般不需要特意指定。注意,这个默认值已经改变了多次,详见文章底部的兼容性。
https服务器优化
SSL操作会增加CPU的消耗。在多核CPU的操作系统中应使用多个工作进程,并且数量不少于可用CPU的核心数。CPU最密集的操作是SSL握手阶段。有两种方法使这种操作降到最低:第一是开启keep_alive,让多个请求复用一个连接;第二是复用SSL session以避免并行的连接和并发的连接进行SSL握手。通过ssl_session_cache指令配置SSL session,这些session存储在缓存中供工作进程共享使用。1M的缓存里有4000个会话信息。默认的缓存超时时间是5分钟,可以通过ssl_session_timeout指令增加。下面是一个运行在多核操作系统使用了10M缓存的简单配置方法:

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证书链
一些浏览器会要求证书是可信的CA颁布,而另一些浏览器却不理会。这种情况的产生是由于使用的证书是中间人进行颁发的,而这个颁发机构并没有加入到浏览器的信任根上,这时应该将信任根添加到证书中:
$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt
最终的配置文件如下:

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)

浏览器总是保存中间的可信证书,所以很多浏览器不会理会没有证书链的问题。
配置一个包含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指令完整的配置,无法实现一个server上运行HTTPS和HTTP。现在增加了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;
    ...
}

上面的配置使用默认的证书,即www.example.com,并不理会服务器名称,这是SSL的行为。SSL在建立连接时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;
    ...
}

一个证书多个名称
有其他的办法共享同一个IP地址,建立多个HTTPS服务器,但所有的方法都有些弊端。一个方法是将多个名称加到证书的SubjectAltName域中,如www.example.com和www.example.org,但长度有限制。
另一种方法是使用通配符名称,如*.example.org。这种证书可以认证所有的子域名,但只能有一级。它能匹配www.example.org但不能匹配example.org和www.sub.example.org。这两种方法可以混合使用。如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;
    ...
}

兼容性

  • The SNI support status has been shown by the “-V” switch since 0.8.21 and 0.7.62.
  • listen指令的ssl参数从0.7.14被支持,0.8.21版本之前它只能与default参数一同定义
  • SNI has been supported since 0.5.32.
  • 共享SSL会话缓存功能从0.5.6版本开始支持
  • 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”

nginx负载均衡服务器配置

多个应用实例之间的负载均衡是一种优化资源常用的技术。是一种使流量最大化,降低延迟、容错的配置。
nginx可以作为一个高效的HTTP负载均衡服务器,用于将多个应用服务器的流量进行分配以提高性能扩展性及可靠性。

负载均衡的方法
下面的负载均衡机制(或方式)被nginx所支持:

  • 轮询:请求以轮询的形式传给应用服务器
  • 最少连接:下一个请求会分配给活跃连接数最少的服务器
  • ip哈希表:使用哈希函数决定下一个请求由哪个服务器处理(基于客户端的IP)

默认负载均衡服务器配置
最简单的负载均衡服务器的配置如下:

http {
    upstream myapp1 {
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

在上面的例子中,有三个同样的应用实例在srv1-srv3上。当负载均衡的方法没有指定,默认是用轮询的方法。所有请求都会代理到服务器组myapp1上,nginx应用HTTP负载均衡分配请求。
nginx实现的反向代理包括HTTP、HTTPS、FastCGI、uwsgi、SCGI和memcached.
配置HTTPS的负载均衡,用https作为协议。
配置FastCGI、uwsgi、SCGI或memcached,分别使用fastcgi_pass, uwsgi_pass, scgi_pass, 和memcached_pass指令。
最少连接负载均衡
另一种负载均衡的规则是最少连接。最少连接规则允许使用更公平的方式控制负载,特别对于一些耗时较长的请求。
当least_conn指令用在服务器组配置中时,nginx会启用最少连接负载均衡:

upstream myapp1 {
    least_conn;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

会话持久化
请注意,轮询法和最小连接法负载均衡,所有随后的客户端请求可能会被分配到不同的服务器上。不能保证每个客户端的请求总是转发到同一个服务器。
如果有需要将一个客户端与特定的应用服务器进行绑定,换句话说,使客户端会话固定或持久化,可以使用ip-hash机制负载均衡。
使用ip-hash机制,客户端IP用作哈希关键词来决定使用哪个服务器组处理请求。这个方式确保了相同的客户端的请求总是转发到相同的服务器除非服务器不可用。
配置ip-hash负载均衡,只需要加上ip_hash指令到服务器组:

upstream myapp1 {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

带权重的负载均衡
可以通过服务器权重来进一步的影响nginx的负载均衡算法。
上面的例子中,没有配置服务器的权重,这意味着所有服务器的权重都是相同的。
在轮询方式中,权重意味着更多或更少的请求分配到服务器上。
当weight参数在一个server后定义了,权重会作为负载均衡的考量因素。

upstream myapp1 {
    server srv1.example.com weight=3;
    server srv2.example.com;
    server srv3.example.com;
}

在这个配置下,每5个新的请求会在应用实例中如下分配:3个请求转发到srv1,一个请求转发到srv2,另一个转发到srv3.
在最近的nginx版本中可以用类似的方法配置最少连接及ip-hash负载均衡的权重。
健康监测
nginx实现的反向代理包括同频带信号传输(或被动)服务器健康监测。如果一个服务器返回了错误,nginx将会标记这个服务器为失败。然后会在一段时间内避免向这个服务器转发请求。
max_fails指令设置了在fail_timeout期间与服务器之间进行的不成功的通信次数。默认情况下,max_fails为1。当设为0时,此服务器的健康检查被关闭。fail_timeout参数定义了多长时间服务器会被标记为失败的。在fail_timeout间隔结束后,nginx会开始优雅的通过客户端请求探测服务器。如果探测成功,服务器会被标记为可用。