利用Nginx-Quic重新编译Nginx支持HTTP3+TLSv1.3

其实我这小博客早在2020年初就开启了HTTP3的支持,当时的Nginx版本是Nginx 1.16.1,是利用的Cloudflare的quiche项目,前情提要:Nginx启用HTTP3。可能是HTTP3的草案有新版本,再加上浏览器的升级,到目前为止已经差不多过了一年,看网站统计数据很多朋友都是通过搜索引擎搜索’Nginx HTTP3’来到我这小博客,再加上Nginx的nginx-quic项目,所以打算更新一下Nginx以支持更高的HTTP3草案版本。

前排提示

这个版本我用了大概两个月之后发现,存在不明原因的bug,其中一个表现就是开启HTTP3协议之后会导致WordPress无法登录,会提示“用户名/密码为空”一直卡在登录界面,建议不要将这个版本部署到正式环境,以免造成损失。

HTTP3协议

HTTP3是HTTP2的升级版,是在Google的QUIC协议衍生而来,到目前为止(2021年1月),HTTP3还没有推出正式版本,只是推出了一系列草案,目前(2021年1月),据我所知的,用的最多的还是HTTP3 Draft 29,包括Google本身一些服务,Chrome浏览器最新的88.0.4324.96正式版在我本地显示的都是h3-29,我本来想查询各个版本的Chrome对HTTP3的支持是怎样的但是换了几个关键字也还是没有找到Google官方相关文档,如果有朋友看到了这里,看到了Google关于Chrome对HTTP3的版本支持文档,欢迎告知我一下。

Nginx-Quic项目

Nginx-Quic项目是Nginx官方推出的,跟上面说的Cloudflare的quiche没有关系,只是在Nginx的主线(mainline)版本的基础上推出的实验性质的项目,根据Nginx官方文档的说法,这项目是quic的分支,目前Nginx的版本为最新的Nginx 1.19.6,以后可能会将HTTP3相关功能合并进主线。正因为是实验性质项目,所以官方也不推荐在正式环境使用,但是我一个小博客,怕啥,不差这一回当小白鼠了,毕竟折腾的过程才是最有意思的。

Nginx-Quic项目地址:

https://quic.nginx.org/

编译环境及相关说明

其实各个系统编译过程都差不多,我个人更喜欢Debian10以及我手上的服务器都是安装的Debian10系统,所以我选择了在纯净的Debian10系统下来编译这个项目,CentOS或者Ubuntu都可以的。

因为涉及到从源代码编译,编译之前需要安装一大堆的依赖以及编译环境程序,我个人是建议使用独立的环境来编译,新建一个虚拟机或者使用一些按量计费的云服务都行,按量计费的Vultr或者国内的腾讯云都可以,因为涉及到BoringSSL及Nginx的编译,建议内存不要少于2G。

安装编译需要的软件

Nginx的HTTP3依赖BoringSSL或者OpenSSL,所以安装编译环境的时候一并把编译BoringSSL所需要的依赖也安装上:

apt install build-essential ca-certificates zlib1g-dev libpcre3 libpcre3-dev tar unzip libssl-dev wget curl git cmake ninja-build golang 

为了方便,我这里选择在~下面放置源代码,所有的代码保存及编译工作都在这个目录下面进行,下面开始是编译过程。

获取并编译BoringSSL

据Nginx的文档,Nginx官方说的是使用BoringSSL,对于BoringSSL我也不熟悉,就不多赘述了,直接上相关命令:

git clone --depth=1 https://github.com/google/boringssl.git
cd boringssl
mkdir build
cd build
cmake -GNinja ..
ninja
cd ../..
mkdir -p boringssl/.openssl/lib
cp boringssl/build/crypto/libcrypto.a boringssl/build/ssl/libssl.a boringssl/.openssl/lib
cd boringssl/.openssl
ln -s ../include .
cd ../..

如果命令执行过程没有报错,说明BoringSSL正确编译通过了。

获取Nginx源代码并编译

为了方便获取Nginx的编译参数以及利用系统命令管理Nginx,最好还是安装一下Nginx,如果对系统熟悉,不安装也可以,命令如下:

apt update
apt upgrade
apt install curl gnupg2 ca-certificates lsb-release
echo "deb http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx"     | tee /etc/apt/sources.list.d/nginx.list
echo "deb-src http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx"     | tee /etc/apt/sources.list.d/nginx.list
curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -
apt install nginx

Nginx-Quic的源代码获取稍微麻烦一点,并不是用的git来获取,用的是另一种源代码版本控制软件,安装hg及获取Nginx源码命令如下:

apt install hgsubversion
hg clone -b quic https://hg.nginx.org/nginx-quic

除了BoringSSL本身,Nginx本身还依赖部分别的组件,为了方便,可以使用系统自带的命令来帮我们安装这些依赖,Nginx编译依赖安装命令如下:

apt-get build-dep nginx

自己确定Nginx的编译参数或者通过如下命令获取Nginx的编译参数:

nginx -V

然后拼接HTTP3相关编译参数:

--with-http_v3_module --with-http_quic_module --with-stream_quic_module --with-cc-opt="-I../boringssl/include" --with-ld-opt="-L../boringssl/build/ssl -L../boringssl/build/crypto" 

上面的编译参数可以根据自己的需求增加或者减少,需要注意的是BoringSSL的路径不能出错,否则后面的编译过程会出错。

这是我完整的Nginx编译命令及编译参数:

# cd nginx-quic
# ./auto/configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/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_gunzip_module --with-http_gzip_static_module --with-http_realip_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt=-I../boringssl/include --with-ld-opt='-L../boringssl/build/ssl -L../boringssl/build/crypto' --with-http_v3_module --with-http_quic_module --with-stream_quic_module
# make

总共三行,我是拼接的Nginx本身自带的编译参数。

如果编译成功的话,在~/nginx-quic/objs/目录下面应该会出现一个名为nginx的文件,这就是我们编译好的支持HTTP3的Nginx,可以当前机器直接执行也可以传到别的服务器执行,我是为了方便,用它将本机安装的Nginx可执行文件给替换了:

cp /usr/sbin/nginx{,.bak}
cp ~/nginx-quic/objs/nginx /usr/sbin/nginx

然后就能通过执行nginx -V查看新的Nginx的相关信息了。

修改Nginx的配置文件开启HTTP3支持

这是我实际测试之后最终修改得到的Nginx支持HTTP3、HTTP2、TLSv1.3功能的部分配置文件,HTTPS及HTTP3都是443端口:

listen 443 http3 quic reuseport;
listen 443 ssl http2;
ssl_certificate /path/to/cloudbool.com.cert;
ssl_certificate_key /path/to/cloudbool.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
add_header Alt-Svc '$http3=":443";ma=86400';

配置好之后需要重启Nginx,并且防火墙或者安全组记得放行对应的HTTP3端口,否则会出现无法启用HTTP3的问题。

HTTP3功能使用体验

说实话,到目前Chrome、Firefox及Safari这三大浏览器都没有默认支持HTTP3,都需要各种方法去开启HTTP3,个人感觉是绝大多数用户都关系不大,更多的还是我们这群爱折腾的人在玩。

而且经过我的实际测试,同一个网站,不同网络可能并不是一定能使用上HTTP3,我的实际测试就是,在电信网络环境下,不管怎么配置,都无法使用HTTP3进行连接,但是同样的软件,在移动宽带网络下却能使用HTTP3进行连接,不知道是不是跟所使用网络的UDP限制情况有关。

最后,上张图,证明能正常使用HTTP3功能:

Nginx HTTP3 h3-29