背景:
这事起源于需要一个反向代理,请求的后端是某开放云的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配置节
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_n