nginx 配置 HTTPS 服务

编译自:
[configuring_https_servers][1]
[1]: http://nginx.org/en/docs/http/configuring_https_servers.html

目录

  • 简介
  • HTTPS 服务器优化
  • SSL 证书链
  • 一个 HTTP/HTTPS 服务器
  • Name-based HTTPS 服务器
    • 多个 server 共享一个 SSL 证书
    • Server Name Indication
  • 兼容性

简介


配置 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 指令 和 ss_chiphers 指令,可设置加密连接使用高安全性的协议版本以及加密性强的算法(SSL/TLS协议)。nginx 默认使用 “ssl_protocols TLSv1 TLSv1.1 TLSv1.2” 以及 “ssl_ciphers HIGH:!aNULL:!MD5”,它们分别指定了默认的协议版本和加密算法,所以这些算法不需要显式地指定。要注意的是,这两个指令的默认值已经多次发生改变(详见 “兼容性” 小节)。

[listen][2] 指令
[2]: http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

[server][3] 指令
[3]: http://nginx.org/en/docs/http/ngx_http_core_module.html#server

[server certificate][4] 指令
[4]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate

[private key][5] 指令
[5]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key

[ssl_protocols][6] 指令
[6]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols

[ss_chiphers][7] 指令
[7]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers

HTTPS 服务器优化


处理 SSL 连接会消耗额外的 CPU 资源。在多处理器系统上,应设置对应CPU核心个数的 worker 进程。(参考:worker_processes

建立 SSL 连接的握手阶段是最消耗 CPU 的,有两种方法可最小化建立每个 SSL 连接所需要的握手操作次数:

  • 第一是启用 keepalive 连接保持。启动连接保持,可以在一个已建立的 SSL 连接上处理多个请求
  • 第二是重用 SSL 会话参数,使并行的或者后续的连接不再需要进行 SSL 握手。

SSL 连接的会话参数被保存在 SSL 会话缓存中,该缓存被所有的 worker 进程共享,可使用 ssl_session_cache 指令对其进行配置。1MB SSL 会话可容纳约 4000 个会话。

默认的缓存超时为 5 分钟,可使用 ssl_session_timeout 指令进行调整。

下面是一个 SSL 优化配置样例,假设系统拥有的 CPU 核心总数为 10个,为其配置 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_session_cache][9]
[9]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache

[ssl_session_timeout][10]
[10]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_timeout

SSL 证书链


有时会出现这样的情况,对一个由知名 CA 签发的证书,一些浏览器发出警告,而另一些浏览器会接受。这是因为签发该证书的 CA 使用了一个 intermediate certificate 签发证书,这个 intermediate certificate 没有包含在跟随浏览器一起分发的证书库中。为应对这个问题,CA 提供了 a bundle of chained certificate ,可将该证书与你的服务器证书合并成一个文件。在这个文件中,服务器的证书必须位于 chained certificate 的前面:

$ 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;
    ...
}

如果顺序颠倒了,把服务器证书放在了 chained certificate 的后面,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 发现服务器的私钥和 chained certificate 的第一个证书不匹配造成的。

当浏览器收到 intermediate certificates 时,一般都会将它存储下来。所以浏览器可能在第一次收到 intermediate certificates 时发出警告,但存储下来之后再次收到时就不会发出警告了。

要确定一个 web 服务器是否发送了完整的 certificate chain,可使用 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
...
Server certificate
-----BEGIN CERTIFICATE----- 
...

在此例中的 Certificate chain 中,#0号证书的对象 (“s”) 的证书颁发者是 (“i”),#0证书的 (“i”) 同时又是 #1 号证书的对象 (“s”);#1号证书颁发者 (“i”) 是 #2号证书的对象 (“s”),#2号证书的颁发者 (“i”) 是知名 CA “ValiCert, Inc”,这个 CA 的证书是存储在随浏览器分发的内建证书库中的。

如果服务器发送给客户端的证书没有包含 certificate chain,上面的信息只会显示 #0 号服务器证书。

一个 HTTP/HTTPS 服务器


建立一个可同时处理 HTTP 和 HTTPS 请求的 web 服务器:

server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

Note:
nginx 0.7.14 版之前,不支持像上面这样单独将某个监听套接字设置为 SSL 连接。
只能在 server 区块中使用 ssl {on|off} 指令,定义整个 server 提供 HTTPS 服务,
因此不能设置可同时处理 HTTP/HTTPS 请求的 server 区块。现在不建议在新版本的 nginx 
中使用 ssl 指令,建议使用 ssl 参数。

Name-based HTTPS 服务器


基于名称的 HTTPS 服务器。

子目录

  • 概念讲解
  • 多个 server 共享一个 SSL 证书
  • Server Name Indication

概念讲解


如何设置监听于一个 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;
    ...
}

虽然在这样的配置中为两个 server 设置了不同的证书,但是当使用浏览器访问该 web 站点时,无论访问的主机名是 www.example.com 还是 www.example.org,浏览器都将收到同一个服务器证书:服务器的默认证书。在这里的默认证书是 www.example.com.crt

这是由 SSL 协议的行为所决定的。SSL 连接建立于 TCP/IP 连接之上,SSL 连接在握手的阶段,会收到由 nginx 服务器发送的服务器证书,SSL 连接建完成之时,浏览器还没有发送 HTTP 请求给 nginx,因此 nginx 无法在建立 SSL 连接时得知浏览器所请求的是哪一个虚拟主机,因此,nginx 只能发送默认的服务器证书给浏览器。

对于这个问题,最老的方法,也是最 robust 的方法,是为每个 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;
    ...
}

多个 server 共享一个 SSL 证书


有多种方式可实现在多个 HTTPS servers 之间共享一个 IP 地址,但这些方法都有各自的缺点。

一种方法是在证书的 SubjectAltName 字段中设置多个主机名,比如设置两个主机名:www.example.comwww.example.org。缺点是 SubjectAltName 字段的长度是有限制的。

另一种方法是在证书中设置“通配主机名”,比如 *.example.org,但它只能匹配一个名字区域的主机名,比如,它不能匹配 example.orgwww.sub.example.org

以上两种方法可以结合使用,也就是在证书的 SubjectAltName 字段中同时包含多个 “准确主机名” 和 “通配主机名”。比如同时包含:example.org 和 *.example.org。

对于这种在多个 HTTPS servers 之间共享一个 IP 地址的应用场景,最好在配置中,将服务器的证书和私钥放到 http 区块中,使得所有的 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;
    ...
}

Server Name Indication


对于实现在多个 HTTPS servers 之间共享一个 IP 地址,或者说基于同一个 IP 地址运行多个 HTTPS server,一种更为通用的解决方案是使用 TLS Server Name Indication extension (SNI, RFC 6066)。

通过 SNI 可允许浏览器在与 web 服务器进行 SSL 握手的阶段,将所请求的 server name 传递给服务器,这样服务器就能够为这个 SSL 连接选择对应的证书。

但是 SNI 对浏览器的版本有要求,目前支持 SNI 的浏览器版本如下:

Opera 8.0;
MSIE 7.0 (but only on Windows Vista or higher);
Firefox 2.0 and other browsers using Mozilla Platform rv:1.8.1;
Safari 3.2.1 (Windows version supports SNI on Vista or higher);
and Chrome (Windows version supports SNI on Vista or higher, too).

Note:
在 SNI 中只能传递 domain names(域名)。如果一个访问请求中包含有 IP 地址,
一些浏览器会错误地将服务器的 IP 地址当做所请求的主机名传递给服务器。因此,不能
完全依赖 SNI。

为了在 nginx 中使用 SNI,要求两种函数库支持 SNI:一是 nginx 编译时使用的 OpenSSL 库,二是 nginx 在运行时动态链接的库。OpenSSL 从 0.9.8f 版开始支持 SNI(要求 OpenSSL 在编译时使用了 “--enable-tlsext” 选项)。从 0.9.8j 版开始,该选择是默认的选项。如果 nginx 编译进了对 SNI 的支持,那么使用 nginx -V 命令查看时,可看到:

$ nginx -V
...
TLS SNI support enabled
...

附上译者的测试:

[root@lamp1 ~]# nginx -V
nginx version: nginx/1.10.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
...

如果 SNI-enabled 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

兼容性


从 0.8.21 和 0.7.62 开始,可使用 nginx -V 显示 SNI 支持状态信息。

从 0.7.14 开始,nginx 支持在 listen 指令中使用 ssl 参数,而且在 0.8.21 之前,ssl 参数只能和 default 参数一起使用。

从 0.5.32 开始支持 SNI。
从 0.5.6 开始支持 SSL 会话缓存。

从 1.9.1 开始,默认的 SSL 协议为 TLSv1, TLSv1.1, and TLSv1.2 (if supported by the OpenSSL library)
从 0.7.65, 0.8.19 开始,到 1.9.1 之前,默认的 SSL 协议为 SSLv3, TLSv1, TLSv1.1, and TLSv1.2 (if supported by the OpenSSL library)。
0.7.64, 0.8.18 及之前,默认的 SSL 协议为 SSLv2, SSLv3, and TLSv1。

从 1.0.5 开始,默认的 SSL 加密算法为 “HIGH:!aNULL:!MD5”。
0.7.65, 0.8.20 之后,1.0.5 之前,默认的 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”。

written by Igor Sysoev
edited by Brian Mercer


版权信息
本文编译自 nginx.org 的部分,遵循其原来的 licence 声明: 2-clause BSD-like license

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容