这是一篇发表已超过三年的旧文,文中的信息可能已经有所发展或是发生改变。

nghttpx 本身并不是一个代理,它只是一个翻译器,因此如果我们需要一个支持 HTTP/2 的正向 HTTPS 代理,可以用一个 HTTP/1.1 的正向代理(如 Squid)和 nghttpx 接在一起实现。使用这样一个 HTTPS 代理,既可以享受 HTTP/2 对多连接的优化(提高客户端和代理服务器之间的连接流畅度),又可以享受外层 TLS 带来的加密和安全。且由于流量特征是 HTTPS,不仅额外开销小,而且在一些封锁严重的 ISP 里也能应用自如。(如封锁了 DTLS 流量的情况下,OpenConnect / AnyConnect 只能 TCP over TCP,效率很低)

需求有两种,一种是客户端原生支持 HTTP/2 的,以下以 Chrome 为例:

另一种是客户端不支持 TLS 的,以下以 Pidgin 为例:

如果你是 Arch Linux 用户,可以直接使用我维护的 aur/nghttp2 包,直接 yaourt -S nghttp2 即可,吃豆人会帮你照料好剩下的一切。

如果你是 CentOS 用户,祝您今天有个好心情。

无论是哪种需求,服务器上都需要 nghttpx 和 Squid。

nghttpx

服务器上 nghttpx,前端接受的是来自客户端的 HTTP/2 请求,后端是 Squid,最小配置是这样:

frontend=0.0.0.0443;tls

其中私钥和证书必须是客户端认可的。你可以选择:

去 NameCheap 之类的网站上买一个商业证书,低至 $9 一年;

自己用 OpenSSL / GnuTLS 等工具签一个,然后在你的客户端里强制设置为信任;

如果你不愿意花钱也不愿意折腾 OpenSSL,那你可以尝试去找家免费的 CA 给你签一个。

需要说明的是,GFW 曾被报道会区分商业证书和野证书并对后者做定点清除。试图使用野证书的同学请将此因素考虑在内。

以上只是最小配置,我个人使用的配置还加上了以下内容,是我在 nghttpx 的文档中挑出来觉得比较有用的选项:

这一选项是调节优先使用哪种协议的。Chrome 46 之后对 HTTP/2 代理的支持已经正常了,这个选项可以不用调了。

Squid 是一个久经考验的正向代理。在我们的用例中,它是 nghttpx 的后端,只需监听 localhost 即可。我用的最小配置如下:

只按照最小配置来做的话,配置出来的 HTTP/2 代理是没有任何鉴权的,任何人都可以把这个地址填进 Chrome 里当代理用,也就是说,这是一个开放代理。但实践证明:

如果你在公网上搭一个不带 TLS 的 HTTP/1.1 开放代理的话,分分钟各种爬虫就会把你的地址撸走,教你做人;

如果你在公网上搭一个带 TLS 的 HTTP/1.1 开放代理的话,来光顾你的爬虫就非常非常少了,几个月也遇不到几只;

所以,如果不想弄鉴权的话,问题也不大,因为目前根本没有 TLS 1.2 + HTTP/2 的爬虫,除非你主动把地址告诉别人,否则不会有人来用你的代理。不过,这样毕竟只是迷宫,而不是门锁,所以为了安全还是可以配置一下鉴权。

在 TLS 层面做鉴权的话,就是用上文所述的 --verify-client 了。你需要自己维护一个 CA,然后把 CA 的根证书放到服务器上,持有该 CA 根证书的私钥签出的证书对应的私钥的用户可以使用该代理,否则根本完成不了 TLS 握手,直接被拒绝。CA 的搭建和管理又是一个巨大的话题了,在此不多做叙述,只是推荐一下两个软件:适用于 GNU/Linux 用户的 XCA,和适用于 OS X 用户的 Keychain。这两个都是能够管理中小型 CA 的 GUI 程序。我个人使用的则是 EasyRSA。当然如果你足够硬核,也可以直接使用命令行的 OpenSSL 去管理 CA。再次强调,这个 CA 只是客户端认证所用的,和你买证书的那种商业 CA 没有也不应该有联系。

推荐用 TLS 鉴权,你会爱上它的。而且 TLS 鉴权的话,Chrome 能用 AutoSelectCertificateForUrls 策略自动选证书,不用每次开 Chrome 的时候点一下。

令人郁闷的是,无论是 Cr 还是 Fx,都不能方便地通过 GUI 配置 HTTPS 代理,只能通过命令行或插件的方式来使用 HTTPS 代理。当然,pac 文件可以写得非常复杂,也可以使用浏览器插件进行更灵活的代理配置。Cr 用户推荐使用 SwitchyOmega。

backend=proxy.example.org443;proto=h2;tls

同样地,这只是最小配置,我个人使用的配置中还有以下选项:

是的,手机。(什么,您是 iOS 用户?您还是用您的 APN 代理,也就是 HTTP 明文代理吧……)

另一种是用 Dockerfile.android 文件,在 Docker 容器里装上乱七八糟的编译环境和依赖,最后产出珍贵的 nghttpx 文件并复制到容器外面来。我竟然已经整整一年没有写博客了。一年里可以写的东西其实不少,但是由于各种原因的确没怎么写。今天因某人提醒我一年没更新了,又正好手头的事情告一段落,于是便这么写了一篇。也算是能造福一些人吧。

欢迎留下评论。评论前,请先阅读《隐私声明》。