本文介绍如何将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”