站点开始支持TLS1.3

0x00 引言

本站开始支持TLSv1.3 版本。

(2018.11.07更新Final版本,可翻到文章末尾查看)

本文记录了相关的一系列过程,希望对后来的伙伴们有所帮助。

阅读本文可以获取到以下内容:

  1. 什么是TLS1.3
  2. 如何在站点上支持TLS1.3

0x01 什么是TLS1.3

TLS1.3 是SSL加密的最新标准规范,截止目前写作的时间(2018年3月1日),TLS1.3的正式RFC还并没有正式定稿,但其draft已经前行到了ver.24。有兴趣的同学可以前往TLS1.3 Draft24自行阅读。

那么,问题来了,为什么要支持TLS1.3;TLS1.3又给我们带来什么样的好处?

这个问题是个比较有意思的技术范畴,这里只简单介绍一下。

我们都知道,传统的SSL建立连接,都需要4次握手,即2个RTT时间进行密钥交换后再开始正式的数据交互。这时候,2个RTT时间在整个网络链路上是非常消耗时间的。于是大佬科学家们就在思考,是否能减少RTT时间从而提升网络上的性能。

TLS1.3便承担了这样一个重任,从其RFC draft 文稿上我们可以看到,其可以将RTT时间从2-RTT减少到1-RTT,甚至有限的0-RTT。并且,由于算法上的提升,进行大数计算的性能效率也有所提升,从真正意义上提升了整体的SSL性能。

篇幅所限,这里不展开了,后面会专门来撰写一篇文章来做TLS1.3的一些细节研究

0x02 如何在站点支持TLS1.3

我个人的站点是CentOS 7 + nginx,因此这里主要针对这两块来进行介绍。

1 确认系统信息

# cat /etc/centos-release
CentOS Linux release 7.4.1708 (Core)

2 安装nginx 并查看相关参数

# yum install nginx -y
# nginx -v
nginx version: nginx/1.13.8
# nginx -V
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_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-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

这里,建议先将 configure arguments保存起来,后面会需要用到。

3 下载并准备编译nginx

3.1 安装、编译可能用到的依赖如下

yum install -y git gcc gcc-c clang automake make autoconf libtool zlib-devel libatomic_ops-devel pcre-devel openssl-devel libxml2-devel libxslt-devel gd-devel GeoIP-devel gperftools-devel  perl-devel perl-ExtUtils-Embed

3.2 下载 nginx

# cd /tmp
# wget https://github.com/nginx/nginx/archive/release-1.13.9.tar.gz
# unzip release-1.13.9.tar.gz

注意,这里我们从github上下载的nginx release node。使用了最新的1.13.9版本源代码。

其他的Release版本可参考:
Nginx Release

3.3 下载 OpenSSL

OpenSSL 是个非常有意思的准备项,由于TLS1.3并未完全正式定稿,因此OpenSSL本身对于TLS1.3的是实现也有需要争议。这里,我们可以查看一下OpenSSL的Github地址:
OpenSSL

其中,Branch上有分支tls1.3-draft-18, tls1.3-draft-19;该两个分支的TLS1.3实现并不兼容。而现在绝大多数浏览器的实验产品有对draft-18进行支持,可以做实验来对draft-18进行TLS1.3支持。这时候,可以这样下载OpenSSL:

Draft18
# cd /tmp
# wget https://github.com/openssl/openssl/archive/tls1.3-draft-18.zip
# unzip tls1.3-draft-18.zip
# mv openssl-tls1.3-draft-18 openssl
Draft23

那么,对于想尝鲜的你,当然也可以和我一样来尝试最新的draft23。(截止我写文章时,是这样的。当然draft24已经有了,不过OpenSSL暂未支持)

那么就这样来做:

# cd /tmp
# wget https://github.com/openssl/openssl/archive/master.zip
# unzip master.zip
# mv openssl-master openssl

大概版本是: OpenSSL 1.1.1-pre3-dev

编译参数

对于编译的参数,我们需要加入如下内容来启用TLS1.3支持:

--with-openssl=/tmp/openssl --with-openssl-opt='enable-tls1_3'

其中第一个参数指定你下载的openssl的源代码地址,第二个参数是打开tls1.3支持。

于是整个参数运行看起来会是这样:

# auto/configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-openssl=/tmp/openssl --with-openssl-opt='enable-tls1_3' --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-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' 

3.4 编译

执行完成后,则再执行:

make

这里,对于用master 分支下来的OpenSSL,可能会碰到pthread_atfork错误。

threads_pthread.c:(.text+0x16): undefined reference to `pthread_atfork'
collect2: error: ld returned 1 exit status
make[1]: *** [objs/nginx] Error 1

那么,修改nginx目录下的objs内的Makefile:

emacs ./objs/Makefile

找到

-Wl,-z,relro -Wl,-z,now -pie -ldl -lpthread -lpthread -lcrypt -lpcre /tmp/openssl-master/.openssl/lib/libssl.a /tmp/openssl-master/.openssl/lib/libcrypto.a -ldl -lz \

修改为:

-Wl,-z,relro -Wl,-z,now -pie -ldl -lcrypt -lpcre /tmp/openssl-master/.openssl/lib/libssl.a /tmp/openssl-master/.openssl/lib/libcrypto.a -ldl -lz -lpthread \

再进行make

3.5查看

# cd objs
# ./nginx -V
nginx version: nginx/1.13.9
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
built with OpenSSL 1.1.1-pre3-dev  xx XXX xxxx
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-openssl=/tmp/openssl-master --with-openssl-opt=enable-tls1_3 --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-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

使用OpenSSL 1.1.1-pre3-dev 编译成功

3.6替换

# cd /usr/sbin
# mv nginx nginx.1.13.8.20180301.official.mainline
# cp /tmp/nginx-release-1.13.9/objs/nginx ./

3.7开启TLS1.3 配置

# emacs /etc/nginx/conf.d/default.conf

注:不同的nginx 默认配置文件的路径可能不同

在配置文件中,加入以下内容:

ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

然后重启nginx

# systemctl restart nginx

3.8测试TLS1.3

站点测试

访问SSLLabs,将自己的站点放上,如我的www.cainwang.com 来进行Test your server

使用testssl工具
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
cd testssl.sh
./testssl.sh --help

然后:

# ./testssl.sh -p cainwang.com

...
 Testing protocols via sockets except NPN+ALPN

 SSLv2      not offered (OK)
 SSLv3      not offered (OK)
 TLS 1      offered
 TLS 1.1    offered
 TLS 1.2    offered (OK)
 TLS 1.3    offered (OK): draft 23
 NPN/SPDY   h2, http/1.1 (advertised)
 ALPN/HTTP2 h2, http/1.1 (offered)

详细情况,用大P

# ./testssl.sh -P cainwang.com

...
 Testing server preferences

 Has server cipher order?     nope (NOT ok)
 Negotiated protocol          TLSv1.3
 Negotiated cipher            TLS13-AES-256-GCM-SHA384, 253 bit ECDH (X25519) (limited sense as client will pick)
 Negotiated cipher per proto  (limited sense as client will pick)
     ECDHE-RSA-AES256-SHA:          TLSv1, TLSv1.1
     ECDHE-RSA-AES256-GCM-SHA384:   TLSv1.2
     TLS13-AES-128-GCM-SHA256:      TLSv1.3
 No further cipher order check has been done as order is determined by the client
使用Chrome Flags

在使用的Chrome栏目框上,输入:

chrome://flags

找到TLS1.3, 选择Enable(Draft)
然后访问站点,打开:

更多工具 -> 开发者工具 -> Security查看 Secure connection是否为TLS1.3。
使用Chrome时,我个人发现,用Draft18能够进行协商,但Draft23不行。据说要用Chrome Canary?

(3月5日更新)
尝试了一下Chrome Canary,Draft23 确认支持。
下载地址: Chrome Canary


(4月6日更新)
nginx 又更新了一下OpenSSL的最新版本,庆祝TLS1.3 draft 28 正式通过了评委会裁定,因此使用了最新的代码来更新TLS1.3.
因此Chrome Canary, SSLLab(draf18) 都无法支持本站的TLS1.3。等待更新吧


(2018.11.07更新)
今天更新了TLS1.3 至Final版本。

其实和上面的过程相似:

  1. 下载 Nginx-1.15.6
  2. 下载Openssl-1.1.1
  3. 按照上面文章的方法进行Nginx编译,将新编译好的Nginx进行替换、重启、完工

总结:

  1. 最新的Nginx 支持TLS1.3
  2. Openssl支持TLS1.3 中的套件

4 参考