轉載自:https://blog.fcci.cn/2016/05/nginx-https-proxy-sni/
作者姚毅,版權所有,署名轉載
背景:
這事起源於需要一個反向代理,請求的后端是某開放雲的CDN,CDN配置為基於HTTPS的虛擬主機(單IP多Host區分的虛擬主機)
注意:所以本文並不是你的nginx提供了https服務,而在於反向代理的后端是否為https服務。
正常nginx配置了SSL是可以通過HTTPS訪問后端的,但是對SNI的支持有點麻煩。
代理服務器是nginx。以下為配置過程:
首先支持SNI協議的openssl版本最低為0.9.8f
Mozilla NSS 3.11.1 client-side only
OpenSSL
0.9.8f (released 11 Oct 2007) – not compiled in by default, can be compiled in with config option ‘–enable-tlsext’
0.9.8j (released 07 Jan 2009) through 1.0.0 (released 29 March 2010) – compiled in by default
GNU TLS
libcurl / cURL since 7.18.1 (released 30 Mar 2008) when compiled against an SSL/TLS toolkit with SNI support
Python 3.2 (ssl, urllib and httplib modules)
Qt 4.8
Oracle Java 7 JSSE
這里選用了最新的openssl 1.0.1t
openssl安裝
1、下載openssl
curl -o openssl.tar.gz https://www.openssl.org/source/openssl-1.0.1t.tar.gz
2、解壓縮
tar -xvf openssl-1.0.1t.tar.gz
openssl這里不用編譯,有源代碼就可以了
nginx安裝
1、下載nginx
nginx從1.7之后開始支持SNI proxy指令,我這里選擇的nginx 1.10
wget http://nginx.org/download/nginx-1.10.0.tar.gz
2、解壓縮
tar -xvf nginx-1.10.0.tar.gz
3、進入nginx目錄,配置
cd nginx-1.10.0
以下是我的配置命令,目錄自行選擇
./configure –prefix=/opt/nginx/ –with-http_ssl_module –with-openssl=~/openssl-1.0.1t
上面參數解釋:
–prefix是nginx安裝目錄
–with-http_ssl_module是啟用ssl支持
–with-openssl是剛剛下載的openssl代碼目錄
4、編譯、安裝
make && make install
編譯之后,進入nginx的sbin/nginx -V 看一下是否支持
nginx version: nginx/1.10.0
built by gcc 3.4.5 20051201 (Red Hat 3.4.5-2)
built with OpenSSL 1.0.1t 3 May 2016
TLS SNI support enabled
如果有TLS SNI support enabled就表示支持SNI
配置nginx
修改nginx安裝后的conf/nginx.conf文件
以下是我的server配置節
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
server {
listen 8443;
access_log logs/proxy_access.log main;
underscores_in_headers on;
ssl_protocols SSLv3 TLSv1.2 TLSv1.1 TLSv1 SSLv2;
error_log logs/error.log info;
location / {
proxy_ssl_server_name on;
proxy_pass https://$http_host$request_uri;
proxy_ssl_verify off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header cookie $http_cookie;
proxy_set_header Proxy-Connection "";
proxy_http_version 1.1;
}
}
|
以上配置酌情修改,注意以下幾點:
1、proxy_pass 后面是https://這說明請求以https協議發出
2、為了安全,proxy_ssl_verify 應該配置為on,這里便於調試配置了off,如果配置了on,請確保你的ca文件中有對方服務器的證書
3、ssl_protocols 表示支持的協議,印象中是sslv3和tls1.0以后才支持的SNI,所以都寫上吧。
4、最關鍵的一句proxy_ssl_server_name on最關鍵的,也就是把主機名字傳遞給后端服務器,讓對方服務器在TLS握手層面就可以收到host,便於打到具體的主機。(nginx 1.7開始支持)
重啟nginx服務器(涉及socket監聽的不要使用reload,因為無法reload信號無法讓nginx重新監聽)
驗證
curl -v -x 127.0.0.1:8443 http://www.symantec.com/
應返回:
* Trying 127.0.0.1…
* Connected to 127.0.0.1 (127.0.0.1) port 8443 (#0)
> HEAD http://www.symantec.com/ HTTP/1.1
> Host: www.symantec.com
> User-Agent: curl/7.47.1
> Accept: */*
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 OK
特別說明一下:http://www.symantec.com/這里雖然寫的是http,但實際上代理走的是https協議,這里不能直接寫https
未配置SNI會出現的問題
目前發現,對於使用SNI做域名識別的HTTPS Web服務器,如果代理服務器不發送SNI,會返回502錯誤。即無法正常和后端通信。
參考資料
SNI(Server Name Indication)https://en.wikipedia.org/wiki/Server_Name_Indication
nginx關於proxy_ssl_server_name的配置:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_server_name