0x01 前言
因为太久没接触HTTP/2的相关设定,还不知道chrome对于HTTP/2的要求更加严格了,因为系统预设的openssl不支持ALPN使得chorme只能通过HTTP 1.1连接我的服务器。
在上周我将naxsi替换为modsecurity,终于能用上HTTP/2这一技术。但是在测试中发现curl、Firefox和其他软件在访问时都能通过HTTP/2进行连接,而chrome却一直是HTTP 1.1。
0x02 BUG
以下是通过curl命令获取头部信息,可以看到是支持HTTP/2协议的:
MacBook-Air:~ terence$ curl -I https://ngx.hk HTTP/2 403 server: nginx date: Thu, 21 Sep 2017 07:33:42 GMT content-type: text/html; charset=utf-8 strict-transport-security: max-age=31536000; preload; includeSubDomains
以下是chrome的结果:
0x03 DEBUG
其实问题很简单,在Chrome 51中Google移除了对SPDY协议和NPN协商协议的支持。
SPDY协议成为了HTTP/2协议,这个升级比较简单,只需要修改web服务器,也就是nginx即可;而NPN则是基于openssl的,取而代之的是一种新的协商协议:ALPN。
问题就出在ALPN协商协议的支持上:
图片来自Supporting HTTP/2 for Website Visitors
更多详细内容请点击以下链接:
从上图可以看出,目前仅有Debian 9和ubuntu 16.04系统中的openssl支持ALPN,而openssl也只有1.0.2或更新的版本才支持ALPN。
在centos 中默认的openssl版本为1.0.1e,而在我写这篇文章的时候已经可以升级到1.0.2k:
[root@modsecurity ~]# yum info openssl Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Installed Packages Name : openssl Arch : x86_64 Epoch : 1 Version : 1.0.1e Release : 60.el7_3.1 Size : 1.5 M Repo : installed From repo : centos7-updates Summary : Utilities from the general purpose cryptography library with TLS implementation URL : http://www.openssl.org/ License : OpenSSL Description : The OpenSSL toolkit provides support for secure communications between : machines. OpenSSL includes a certificate management tool and shared : libraries which provide various cryptographic algorithms and : protocols. Available Packages Name : openssl Arch : x86_64 Epoch : 1 Version : 1.0.2k Release : 8.el7 Size : 492 k Repo : centos7-base Summary : Utilities from the general purpose cryptography library with TLS implementation URL : http://www.openssl.org/ License : OpenSSL Description : The OpenSSL toolkit provides support for secure communications between : machines. OpenSSL includes a certificate management tool and shared : libraries which provide various cryptographic algorithms and
也就是说,在默认情况下编译nginx,因为openssl不支持ALPN的缘故,chrome不会使用HTTP/2进行连接,而采用HTTP/1.1。
解决办法也很简单,一种是升级系统中的openssl,只需要yum update即可;第二种是在编译nginx的时候指定openssl路径。
0x04 编译
如果系统中的业务对openssl的版本没有要求,那么直接update再编译是最好的选择;当然也可以指定openssl的路径自行编译。
首先准备好nginx源码和openssl:
#新建文件夹并进入相关目录 [root@modsecurity ~]# mkdir /root/codex/ #进入相关目录 [root@modsecurity ~]# cd /root/codex/ #下载openresty [root@modsecurity codex]# wget https://openresty.org/download/openresty-1.11.2.5.tar.gz #下载openssl [root@modsecurity codex]# wget https://www.openssl.org/source/openssl-1.0.2l.tar.gz
下载链接可通过以下链接找到:
在选择openssl版本的时候需要注意,1.0.1所有的版本以及更旧的版本已经停止支持了,而1.0.2系列版本则到2019年12月31日才终止技术支持,目前稳定版本为1.1.0。如果可以,建议选择1.1.0系列版本的openssl。
下载完成后解压:
#解压openresty [root@modsecurity codex]# tar zxvf openresty-1.11.2.5 #解压openssl [root@modsecurity codex]# tar zxvf openssl-1.0.2l.tar.gz
最后是编译:
#进入文件夹 [root@modsecurity codex]# cd openresty-1.11.2.5/ #configure [root@modsecurity openresty-1.11.2.5]# ./configure --with-openssl=/root/codex/openssl-1.0.2l --with-http_v2_module --with-http_ssl_module
configure无错误后可以看到以下内容:
Configuration summary + using OpenSSL library: /root/codex/openssl-1.0.2l
然后编译和安装:
#编译 [root@modsecurity openresty-1.11.2.5]# make #安装 [root@modsecurity openresty-1.11.2.5]# make install
最后查看版本信息:
[root@modsecurity openresty-1.11.2.5]]$ nginx -V nginx version: openresty/1.11.2.5 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) built with OpenSSL 1.0.2l 25 May 2017 TLS SNI support enabled configure arguments: --prefix=/usr/local/nginx/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.10 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.18 --add-module=../redis2-nginx-module-0.14 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt='-Wl,-rpath,/usr/local/nginx/luajit/lib -Wl,-E' --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --lock-path=/var/lock/nginx.lock --with-http_gunzip_module --with-pcre --with-pcre-jit --with-http_perl_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_geoip_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-select_module --with-poll_module --with-file-aio --with-http_degradation_module --with-libatomic --http-client-body-temp-path=/var/tmp/nginx/client_body --http-proxy-temp-path=/var/tmp/nginx/proxy --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --add-module=/root/codex/naxsi/naxsi/naxsi_src --add-dynamic-module=/root/codex/ModSecurity-nginx --with-openssl=/root/codex/openssl/openssl-1.0.2l
0x05 配置
我发现编译完成需要在nginx配置文件中的server字段中配置listen的参数,例如:
server { listen 443 ssl http2; server_name www.enginx.cn; ssl on; ssl_certificate /usr/local/nginx/ssl/enginx.cn.crt; ssl_certificate_key /usr/local/nginx/ssl/enginx.cn.key; ssl_buffer_size 16k; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:DES-CBC3-SHA; ssl_prefer_server_ciphers on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_session_cache builtin:20480 shared:SSL:10m; ssl_session_timeout 3h; ssl_stapling on; ssl_session_tickets on; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=31536000; preload; includeSubDomains" always; access_log /var/log/nginx/access.log main; return 301 https://enginx.cn$request_uri; }
0x06 结语
centos7在编译安装nginx之前建议先升级系统,这样就不再需要那么繁琐的步骤了。