TLS 1.3 当前(2018.10)支持与部署之现状

今年8月10日,历经三年有余,TLS 1.3 最终版本终于得以发布——RFC 8446. 关于 RFC 的详细介绍可以进一步阅读 A Detailed Look at RFC 8446 (a.k.a. TLS 1.3). TLS 1.3 因为其在握手延迟以及安全性上的改进 (可以参考拙文《TLS1.3/QUIC 是怎样做到 0-RTT 的》),毫不夸张的说,这是一件将深刻而长远影响互联网发展的技术里程碑。那么将 TLS 1.3 尽快平滑应用到线上环境无疑是一件势在必行的事情了。

在我日常的工作中,对于 TLS 1.3 的支持和部署主要关注两个层面: 编程语言(Go, Java)、 API gateway (Nginx) 和浏览器. 下面分别介绍一下这三个层面 TLS 1.3 的支持部署现状。(因为 RFC 8446 已经发布,因此本文中说的支持如无特殊说明,都是指对最终版本 RFC 8446 的支持。)

编程语言对 TLS 1.3 的支持

Go 方面,官方在 TLS 1.3 draft 阶段一直没有跟进。因此,有一个关于对 TLS 1.3 支持的 issue 从 2015 年 open 至今都没有关闭:crypto/tls: add support for TLS 1.3. 其实 Go 发布版本和改进标准库的效率还是挺高的,对于 TLS 1.3 上的“不作为”更多是因为 Go 在兼容性上的承诺导致其并适合在最终版发布前实现互不兼容的 draft 方案。

而 Go 1.11 的发布时间(2018.08.24)与 RFC 8446 的发布时间比较接近,没有足够时间实现并发布该特性。从 golang-dev 小组讨论 Re: crypto/tls and TLS 1.3 看,由于 1.11 没有实现 TLS 1.3 ,那么 1.12 实现 TLS 1.3 基本是板上钉钉的事了:

The key scheduling fact is that the Go 1.11 feature freeze is just a week away, so we decided that it would be too rushed to merge the 1.3 patches for it. I definitely aim to have TLS 1.3 in Go 1.12.

根据惯例, Go 1.12 的发布时间将会是 2019.02~03. 如果期间你实在想用 Go 编程测试 TLS 1.3, 可以尝试使用 CloudFlare 的 tls-tris 库。根据 Go net/http 标准库维护者 Brad Fitzpatrick 的消息,这个库将会被合并到标准库作为 Go 官方 TLS 1.3 的实现。因此,如果你不得不用这个库干一些生成环境的活也大可放心,即使日后升级 Go 1.12, 接口兼容性还是有保证的。

Java 方面,由于 Java 11 出生时间好(2018.09.25), 因此是出生就支持 TLS 1.3 RFC 8446, 具体可以参见 JEP 332: Transport Layer Security (TLS) 1.3. Java 11 是 LTS 版本,因此,如果有条件升级到 11, 推荐使用 Java 11 实现的 TLS 1.3 以及配套的 HttpClient;如果生产环境暂无法升级 Java 版本,推荐使用 OkHttp. 关于 Java Http Client 选型可以参见Java HTTP 组件库选型看这篇就够了

Nginx 对 TLS 1.3 的支持

准确讲应该是 Nginx 所使用 SSL lib 对 TLS 1.3 的支持。在这方面,Boring SSL 跟进速度飞快,在 RFC 发布后第4天实现了对最终版本的支持。OpenSSL 虽然很早就跟进了 draft 的实现,但是对最终版本的支持需要 1.1.1-pre9 及以后的版本:

The OpenSSL git master branch (and the 1.1.1-pre9 beta version) contain our development TLSv1.3 code which is based on the final version of RFC8446 and can be used for testing purposes (i.e. it is not for production use). Earlier beta versions of OpenSSL 1.1.1 implemented draft versions of the standard. Those versions contained the macro TLS1_3_VERSION_DRAFT_TXT in the tls1.h header file which identified the specific draft version that was implemented. This macro has been removed from 1.1.1-pre9 and the current master branch.

TLSv1.3 is enabled by default in the latest development versions (there is no need to explicitly enable it). To disable it at compile time you must use the “no-tls1_3” option to “config” or “Configure”.

Although the latest 1.1.1 versions support the final standard version, other applications that support TLSv1.3 may still be using older draft versions. This is a common source of interoperability problems. If two peers supporting different TLSv1.3 draft versions attempt to communicate then they will fall back to TLSv1.2.

而第一个 OpenSSL 1.1.1 release 是在 2018.09.11, 因此如果你跟我一样是 OpenSSL 的死忠粉,当前阶段 Nginx 支持 TSL 1.3 的最佳方式是 Nginx 1.15.5 + OpenSSL 1.1.1. 而这种脏活、苦活、累活当然是交给 Docker 解决了:从源代码编译 nginx docker 镜像开启 TLS 1.3,项目地址可以参见 docker-nginx.

配置 Nginx 支持 TLS 1.3 需要注意一点:默认情况下 Nginx 因为安全原因,没有开启 TLS 1.3 0-RTT,可以通过添加 ssl_early_data on; 指令开启 0-RTT. 完整配置可以参考 nginx.conf.

浏览器对 TLS 1.3 的支持

当前阶段,Chrome 69 和 Firefox 62 都只支持到 draft 28, 而 draft 28 与最终版本是不兼容的。因此,要测试体验 TLS 1.3 final 需要使用 Chrome Beta 测试版。然后在 chrome://flags/#tls13-variant 开启 TLS 1.3 final:

扩展阅读

Java HTTP 组件库选型看这篇就够了

Java HTTP 组件库选型看这篇就够了

最近项目需要使用 Java 重度调用 HTTP API 接口,于是想着封装一个团队公用的 HTTP client lib. 这个库需要支持以下特性:

  1. 连接池管理,包括连接创建和超时、空闲连接数控制、每个 host 的连接数配置等。基本上,我们想要一个 go HTTP 标准库自带的连接池管理功能。
  2. 域名解析控制。因为调用量会比较大,因此希望在域名解析这一层做一个调用端可控的负载均衡,同时可以对每个服务器 IP 进行失败率统计和健康度检查。
  3. Form/JSON 调用支持良好。
  4. 支持同步和异步调用。

在 Java 生态中,虽然有数不清的 HTTP client lib 组件库,但是大体可以分为这三类:

  1. JDK 自带的 HttpURLConnection 标准库;
  2. Apache HttpComponents HttpClient, 以及基于该库的 wrapper, 如 Unirest.
  3. 非基于 Apache HttpComponents HttpClient, 大量重写应用层代码的 HTTP client 组件库,典型代表是 OkHttp.

HttpURLConnection

使用 HttpURLConnection 发起 HTTP 请求最大的优点是不需要引入额外的依赖,但是使用起来非常繁琐,也缺乏连接池管理、域名机械控制等特性支持。以发起一个 HTTP POST 请求为例:

可以看到,使用 HttpURLConnection 发起 HTTP 请求是比较原始(low level)的,基本上你可以理解为它就是对网络栈传输层(HTTP 一般为 TCP,HTTP over QUIC 是 UDP)进行了一次浅层次的封装,操作原语就是在打开的连接上面写请求 request 与读响应 response. 而且 HttpURLConnection 无法支持 HTTP/2. 显然,官方是知道这些问题的,因此在 Java 9 中,官方在标准库中引入了一个 high level、支持 HTTP/2 的 HttpClient. 这个库的接口封装就非常主流到位了,发起一个简单的 POST 请求:

封装的最大特点是链式调用非常顺滑,支持连接管理等特性。但是这个库只能在 Java 9 及以后的版本使用,Java 9 和 Java 10 并不是 LTS 维护版本,而接下来的 Java 11 LTS 要在2018.09.25发布,应用到线上还需要等待一段时间。因此,虽然挺喜欢这个自带标准库(毕竟可以不引入三方依赖),但当前是无法在生产环境使用的。

Apache HttpComponents HttpClient

Apache HttpComponents HttpClient 的前身是 Apache Commons HttpClient, 但是 Apache Commons HttpClient 已经停止开发,如果你还在使用它,请切换到 Apache HttpComponents HttpClient 上来。

Apache HttpComponents HttpClient 支持的特性非常丰富,完全覆盖我们的需求,使用起来也非常顺手:

对 Client 细致的配置和自定义支持也是非常到位的:

完整示例请参考 ClientConfiguration.

基本上,在 Java 原生标准库不给力的情况下,Apache HttpComponents HttpClient 是最佳的 HTTP Client library 选择。但这个库当前还不支持 HTTP/2,支持 HTTP/2 的版本还处于 beta 阶段(2018.09.23),因此并不适合用于 Android APP 中使用。

OkHttp

由于当前 Apache HttpComponents HttpClient 版本并不支持 HTTP/2, 而 HTTP/2 对于移动客户端而言,无论是从握手延迟、响应延迟,还是资源开销看都有相当吸引力。因此这就给了高层次封装且支持 HTTP/2 的 http client lib 足够的生存空间。其中最典型的要数OkHttp.

OkHttp 接口设计友好,支持 HTTP/2,并且在弱网和无网环境下有自动检测和恢复机制,因此,是当前 Android APP 开发中使用最广泛的 HTTP clilent lib 之一。

另一方面,OkHttp 提供的接口与 Java 9 中 HttpClint 接口比较类似 (严格讲,应该是 Java 9 借鉴了 OkHttp 等开源库的接口设计?),因此,对于喜欢减少依赖,钟情于原生标准库的开发者来说,在 Java 11 中,从 OkHttp 切换到标准库是相对容易的。因此,以 OkHttp 为代表的 http 库以后的使用场景可能会被蚕食一部分。

小结

  • HttpURLConnection 封装层次太低,并且支持特性太少,不建议在项目中使用。除非你的确不想引入第三方 HTTP 依赖(如减少包大小、目标环境不提供三方库支持等)。
  • Java 9 中引入的 HttpClient,封装层次和支持特性都不错。但是因为 Java 版本的原因,应用场景还十分有限,建议观望一段时间再考虑在线上使用。
  • 如果你不需要 HTTP/2特性,Apache HttpComponents HttpClient 是你的最佳选择,比如在服务器之间的 HTTP 调用。否则,请使用 OkHttp, 如 Android 开发。

扩展阅读

IM/XMPP服务器选型

一、IM协议选型


IM协议按照是否公开可以分为私有协议(腾讯QQ)和开放协议(GTalk)。私有IM协议需要从零开始设计和搭建,时间和财力成本极高。而开放协议:1)经过业界的长期研究和验证,在安全性、完备性容、容错性等诸多方面都有保障。2)由于其开放的特性,业界已经有很多优秀的开源IM Server和IM Client,直接基于这些开源组件进行开发,可以在相对短的时间内快速搭建高质量的Chat基础服务。3)开放协议一般具有较高的可扩展性,可以进行协议的扩展和改造,以适应特殊的应用场景和需求。


目前主流的IM协议有XMPP (Extensible Messaging and Presence Protocol), SIMPLE(session initiation protocol for instant messaging and presence leveraging extensions), IMPP (Instant Messaging and Presence Protocol).


IMPP主要定义了必要的协议和数据格式,用来构建一个具有空间接收、发布能力的即时信息系统。到目前为止,这个组织已经出版了三个草案RFC,但主要的有两个:一个是针对站点空间和即时通讯模型的(RFC 2778);另一个是针对即时通讯/空间协议需求条件的(RFC2779)。RFC2778是一个资料性质的草案,定义了所有presence和IM服务的原理。RFC2779定义了IMPP的最小需求条件。另外,这个草案还就presence服务定义了一些条款,如运行的命令、信息的格式,以及presence服务器如何把presence的状态变化通知给客户。


SIMPLE计划利用SIP来发送presence信息。SIP是IETF中为终端制定的协议。SIP一般考虑用在建立语音通话中,一旦连接以后,依靠如实时协议(RTP)来进行实际上的语音发送。但SIP不仅仅能被用在语音中,也可以用于视频。SIMPLE被定义为建立一个IM进程的方法。SIMPLE在2002年夏季得到额外的信任,目前,微软和IBM都致力于在它们的即时通讯系统中实现这个协议。 SIMPLE小组致力于进程模式的操作,这将提升运行效率,使基于SIP的机制能够进行会议和三方电话交谈控制,也考虑到能和未来提供的许多新特性实现兼容并提升表现能力。有了进程模式,SIMPLE使用SIP来建立一次进程,再利用SDP(进程描述协议)来实际传输IM数据。


XMPP是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个服务器进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。


XMPP应用广泛,国内外已有众多大规模的应用案例,参见表1。基本上,除了商业公司私有化的聊天协议,XMPP已经成为事实上的标准IM协议。


表1 XMPP应用一览表

公司

产品

Google

Hangouts (前身是GTalk)

Apple

Apple Push Notification Service

iMessage

WhatsApp Inc

WhatsApp (customized)

腾讯

微信 (参考XMPP+ActiveSync)

小米

米聊

深圳市和讯华谷

极光推送

环信

环信


基于XMPP的方案拥有大量的Server端、Client端以及类库实现,可以快速搭建满足业务需求的应用。


XMPP去中心化的设计具有天生的scale out扩展性,同时支持server-2-server通信,可以与其他XMPP服务器进行连接,实现帐号的跨域聊天。XMPP的这种特性非常适合应用于提供IM基础服务的平台。


综上所诉,XMPP是以上几个协议中最完善、可扩展性最好、应用最广、组件支持最多的一个协议。因此,Chat Service选用XMPP作为Chat Service的IM协议。


二、XMPP Server选型


XMPP Server拥有众多的实现方案,参见表2.


表2 XMPP Server实现方案


Name

Platform(s)

License

Details

Latest Release

Apache Vysper

Windows / Linux

Apache License Version 2.0

mina.apache.org

2011-02-23

Citadel

Linux

GPL3

citadel.org

2013-08-14

CommuniGate Pro

Linux / Mac OS X / Windows

Commercial

communigate.com

2013-09-10

Coversant SoapBox Server

Windows

Commercial

coversant.com

unknown

djabberd

Linux

GPL3

danga.com

2011-06-13

ejabberd

Linux / Mac OS X / Solaris / Windows

GPL2

process-one.net

2013-06-28

IceWarp

Linux / Windows

Commercial

icewarp.com

2012-12-11

iChat Server

Mac OS X

Commercial

apple.com

2012-07-25

in.jabberd

Linux

GPL2

inetdxtra.sourceforge.net

2013-05-16

Isode M-Link

Linux / Solaris / Windows

Commercial

isode.com

2013-06-24

jabberd 1.x

Linux

GPL2

jabberd.org

2012-06-28

jabberd 2.x

Linux / Solaris / Windows

GPL2

jabberd2.org

2012-08-26

Jabber XCP

Linux / Solaris / Windows

Commercial

cisco.com

2008-10-31

Jerry Messenger

Linux / Windows

Commercial

j-livesupport.com

unknown

Kwickserver

Windows

GPL

kwickserver.info

2010-10-15

Metronome IM

Linux / Mac OS X

ISC/MIT

lightwitch.org/metronome

2013-10-02

MongooseIM

Linux / Mac OS X

GPL2

erlang-solutions.com

2013-05-23

Openfire

Linux / Mac OS X / Solaris / Windows

Apache

igniterealtime.org

2013-05-28

Oracle Communications Instant Messaging Server

Linux / Solaris / Windows

Commercial

oracle.com

2013-05-07

Prosody IM

Linux / Mac OS X / Windows

MIT/X11

prosody.im

2013-09-10

psyced

Linux / Mac OS X / Windows

GPL2

psyced.org

2011-11-22

Siemens OpenScape

Linux

Commercial

siemens-enterprise.com

2011-12-15

Tigase

Linux / Solaris / Mac OS X / Windows

AGPL

tigase.org

2013-04-24

Vines

Linux / Mac OS X

MIT

getvines.com

2013-06-22

Wokkel

Linux / Solaris / Mac OS X

MIT

wokkel.ik.nu

2013-01-12

其中,较为成熟,且使用广泛的是ejabberd, jabberd 2.x, Openfire, Tigase(蓝底标出). 这几个Server的详细比较可以参考这两篇文章:Choosing An XMPP Server, Android Push 开源方案解析. 以上两篇文章的结论如下:

  1. Jabberd 2.x 使用C语言实现,但是,存在着数据库事务的滥用、内存泄露、不一致的非阻塞设计等问题,最重要的是该server已经很长时间没有人维护;因此,chesspark在使用jabberd 2.x三年后,转用ejabberd。无独有偶,Jabber.org也在2010年淘汰Jabberd, 转为使用ejabberd.

  2. Openfire以及Tigase都是基于JAVA的解决方案。但是极光推送团队认为,Openfire单机并发很有限,集群方案不成熟,代码古老而缺乏及时更新,因此不适合应用在生产环境中。因此,极光团队在初期使用Tigase解决方案。但是在使用中发现,Tigase其集群方案实现复杂,单节点容量有限,后期转为自己开发server.


从编程语言角度看,主流的XMPP Server主要是JAVA和Erlang。JAVA语言的优势是类库完备,容易招人。Erlang的优势是hot code swap, live console, 高并发. ejabberd与Openfire/Tigase比较而言,最大的优势是相对优雅的集群方案以及更高的并发性能。在没有语言偏向性的情况下,一般在开发初期都会选用ejabberd, 如WhatsApp, Facebook Chat, 米聊


从各种XMPP Server支持的特性看,ejabberd是对XMPP协议支持最好、实现最为全面的server.


从开源协议看,Tigase采用AGPL开源协议,因此只要有代码修改,就必须对代码进行开源。Openfire采用Apache开源协议,修改代码后可以闭源。ejabberd采用GPL v2开源协议,如果只在服务端提供服务,而不进行代码二次分发,修改代码以后也可以「闭源」,参考


综合以上各因素,Chat Service采用ejabberd作为初期的XMPP Server.

WhatsApp 公开源代码: http://www.whatsapp.com/opensource/

Chat Secure: https://chatsecure.org/blog/

Public XMPP Server列表:https://xmpp.net/directory.php

可以查看这些server的状态及基本信息(实现方式、认证方式等)。