nginx 配置 HTTPS 服務

nginx 配置 HTTPS 服務
========================

編譯自:
http://nginx.org/en/docs/http/configuring_https_servers.htmlhtml


目錄:
    簡介
    HTTPS 服務器優化
    SSL 證書鏈
    一個 HTTP/HTTPS 服務器
    Name-based HTTPS 服務器
        多個 server 共享一個 SSL 證書
        Server Name Indication
    兼容性
nginx

 

簡介
-----

配置 HTTPS 服務,必須爲 listen 指令加上 ssl 參數,並指定服務器的證書和私鑰:web

    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」,它們分別指定了默認的協議版本和加密算法,因此這些算法不須要顯式地指定。要注意的是,這兩個指令的默認值已經屢次發生改變(詳見 「兼容性」 小節)。bash


server 指令
http://nginx.org/en/docs/http/ngx_http_core_module.html#server服務器

server certificate 指令
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificatesession

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


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

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

 

HTTPS 服務器優化
----------------------

處理 SSL 鏈接會消耗額外的 CPU 資源。在多處理器系統上,應設置對應CPU核心個數的 worker 進程。
(參考:http://nginx.org/en/docs/ngx_core_module.html#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
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache

ssl_session_timeout
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 服務器
---------------------------

創建一個 web 服務,可同時處理 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;
        ...
    }

    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.com 和 www.example.org。缺點是 SubjectAltName 字段的長度是有限制的。

另外一種方法是在證書中設置「通配主機名」,好比 *.example.org,但它只能匹配一個
名字區域的主機名,好比,它不能匹配 example.org 和 www.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

相關文章
相關標籤/搜索