Ubuntu 下 nginx 配置多个 HTTPS 站点

说在前面:一般情况下一个 IP 是只能对应一个 HTTPS 站点的,我曾经在配置glype在线代理的时候遇到过这样的问题:在某 VPS 上搭了带 HTTPS 的代理之后,其他指向该 IP 的网站都会跳转到这个代理的域名下,无法正常访问,且浏览器一般会报证书错误(如果使用的 https 证书不是泛域名的,浏览器可能会提示这是一个假冒的网站),其原因是 https 原理决定的:

SSL协议层位于HTTP协议层之下,HTTP协议是被封装在SSL协议中的,所以SSL会话必须在HTTP会话之前建立。因为在建立SSL会话的最初握手阶段,服务器无法知道HTTP请求头的Host字段的内容,也就无法确定究竟使用哪个虚拟主机的配置(例如允许使用哪些加密算法、服务器证书是哪个等等),

也就是说,HTTPS 的握手发生在 HTTP 握手之前,传统意义上的 SSL 握手内容只包含了目标 IP 而不含域名,那么服务端就不知道请求的是哪个站点,至于具体返回的是哪一个取决于服务端配置,但是这一问题在 TLS 标准中进行了改进,Server_name扩展可以让 TLS 握手中包含请求的域名,如果没有其他的请求,Server_name扩展应该允许浏览器访问这个IPV4地址一周左右的时间。根据维基百科TLS 似乎是很早就提出的方案,我很好奇为什么现在没有把 SNI 作为默认的支持

TLS 1.0 was first defined in RFC 2246 in January 1999 as an upgrade of SSL Version 3.0 —— Wiki

在我了解到这个问题之后很久一段时间由于没有解决它的需要,就一直没管如何解决,实际上这个问题是很好处理的,对于服务端而言,开启SNI (Server Name Indication)支持即可,并且如果启用了SSL支持,nginx便会自动识别OpenSSL(OpenSSL 版本>0.9.8)并启用SNI。是否启用SNI支持,是在编译时由当时的 ssl.h 决定的(SSL_CTRL_SET_TLSEXT_HOSTNAME),如果编译时使用的OpenSSL库支持SNI,则目标系统的OpenSSL库只要支持它就可以正常使用SNI了。

查看当前的 Nginx 是否启用了 SNI 支持:

nginx -V    #从软件源安装
/usr/local/nginx/sbin/nginx -V         #编译安装

对于 Ubuntu ,默认使用 apt-get install nginx 安装的版本似乎是不支持 SSL 的,没有启用 SNI 请不要惊讶,建议使用 Debian 系的童鞋选择 apt-get install nginx-extras(喜欢自己编译的请无视我~),这个版本的支持看起来是相当完善的 :

nginx -V                                 [3:19:57]
nginx version: nginx/1.4.6 (Ubuntu)
built by gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1) 
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --with-http_dav_module --with-http_flv_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_mp4_module --with-http_perl_module --with-http_random_index_module --with-http_secure_link_module --with-http_spdy_module --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module --add-module=/build/buildd/nginx-1.4.6/debian/modules/headers-more-nginx-module --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-auth-pam --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-cache-purge --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-dav-ext-module --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-development-kit --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-echo --add-module=/build/buildd/nginx-1.4.6/debian/modules/ngx-fancyindex --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-http-push --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-lua --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-upload-progress --add-module=/build/buildd/nginx-1.4.6/debian/modules/nginx-upstream-fair --add-module=/build/buildd/nginx-1.4.6/debian/modules/ngx_http_substitutions_filter_module
root@StarNetwork-Aliy

嗯,如果看到的是 TLS SNI support enabled ,那么很好,什么其他的事情都不用做了,直接把 https 的站点配置写进去就行,如果是 disabled ,可以安装 nginx-extras 覆盖原有版本,也可以把旧版本删除干净之后重新编译,注意 openssl 的支持:

wget http://www.openssl.org/source/openssl-1.0.0d.tar.gz
tar xvf openssl-1.0.0d.tar.gz

重新编译Nginx,需要在Configuare里多加一行参数,指明openssl的路径并启用 TLS

./configure --prefix=/usr/local/nginx 
-user=www-data --group=www-data \
--with-http_ssl_module \
--with-openssl=./openssl-1.0.0d \
--with-openssl-opt="enable-tlsext"

以上只列出了相关参数,其他参数需要自行取舍。

顺带一提,SNI 要求服务端和客户端都支持 ,近期由于 SSL v3 被认为是不安全的协议,Apache 和 Nginx 的新版本中已经去除了对 SSL v3的支持。也就是说,目前只要不是太旧的 HTTPS 站点,都是能使用 SNI 来支持多个 HTTPS站点。而现在不支持 SNI 的客户端,大多已经被淘汰了。

The client browser must also support SNI. Here are some browsers that do:  以下浏览器支持SNI:

Mozilla Firefox 2.0 or later   火狐2.0或更高
Opera 8.0 or later (with TLS 1.1 enabled)  Opera8.0或更高,需要开启TLS1.1
Internet Explorer 7.0 or later (on Vista, not XP)   IE7或更高(Vista以上,XP不支持)
Google Chrome
Safari 3.2.1 on Mac OS X 10.5.6

关于更多 HTTPS 的建立流程的知识可以参考这篇伯乐在线的文章

关于 nginx 下的 HTTPS 站点配置,可以参考我写过的Nginx 配置安装 SSL 证书与配置在线代理

关于 Apache 配置 SNI,可以参考懒猫同学的这篇博文:单 IPVPS 配置多个 SSL 站点


UPDATE 2017.04.07

现在各个发行版的 nginx 应该是早就默认启用 SNI 了,至少 debian 系的 apt 执行

apt-get install nginx

 已经默认选中 nginx-full 包,务必注意维护服务器的时候【能不靠编译解决问题就不要编译】


本文链接:https://www.starduster.me/2014/12/28/tls-sni-on-ubuntu-nginx/
本站基于 Creactive Commons BY-NC-SA 4.0 License 允许并欢迎您在注明来源和非商业使用前提下自由地对本文进行复制、分享或基于本文进行创作。
请注意:受限于笔者水平,本站内容可能存在主观臆断或事实错误,文中信息也可能因时间推移而不再准确,在此提醒读者结合自身判断谨慎地采纳。

5条评论


  1. [root@centos ~]# /usr/local/nginx/sbin/nginx -V
    nginx version: nginx/1.9.9
    built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
    built with OpenSSL 1.0.1h 5 Jun 2014
    TLS SNI support enabled
    configure arguments: –user=www –group=www –prefix=/usr/local/nginx –with-http_stub_status_module –with-http_ssl_module –with-http_gzip_static_module –with-ipv6 –with-openssl=../openssl-1.0.1h/
    [root@centos ~]# service nginx restart
    Stoping nginx… nginx: [emerg] SSL_CTX_use_PrivateKey_file(“/root/XXX.domain.key”) failed (SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
    failed. Use force-quit
    Starting nginx… nginx (pid 29963 29962) already running.
    我原来有一个域名已经上了SSL,现在想弄第二个,重启nginx就报错了

    回复

    1. 没明白你的意思,什么叫想弄第二个,建议贴 nginx 配置

      回复

回复 Freedom 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据