一、简介
Nginx在1.25.0版本将nginx-quic分支正式合入主线,标志着Nginx主线版本也可以支持IETF QUIC和HTTP/3协议。QUIC和HTTP/3协议的关系如下图所示:
(图片来源:https://en.wikipedia.org/wiki/QUIC)
QUIC协议在UDP层之上实现了可靠传输+加密,相当于HTTPS的TCP+TLS层。HTTP/3协议基于QUIC协议,在QUIC层之上实现了HTTP语义。
Nginx在1.25.0新增quic核心模块,用于实现QUIC协议栈、连接管理、事件管理功能。新增ngx_http_v3_module模块,用于实现HTTP/3协议。由于Nginx主要用于HTTP服务器,本文主要测试Nginx HTTP/3协议的功能。
二、编译quictls密码库
Nginx实现QUIC协议需要依赖特定TLS库。目前可使用TLS库主要有BoringSSL, LibreSSL, QuicTLS等库。QuicTLS库对OpenSSL的兼容性最好、编译相对简单。本文使用QuicTLS作为Nginx的TLS库。
2.1 下载quictls源代码
quictls的项目位于https://github.com/quictls/openssl,quictls对OpenSSL官方各主要发行版做了QUIC API的适配。可根据使用的OpenSSL版本选择quictls版本。本文使用OpenSSL_1_1_1u的quictls版本,下载链接如下:
https://github.com/quictls/openssl/releases/tag/OpenSSL_1_1_1u-quic1
2.2 编译和安装
解压quictls代码,然后执行编译安装:
cd openssl-OpenSSL_1_1_1u-quic1/
./config no-shared --prefix=/usr/local/OpenSSL_1_1_1u-quic1
make -j `nproc`
make install_sw
完成后,quictls安装到/usr/local/OpenSSL_1_1_1u-quic1目录下。
三、编译Nginx
3.1 下载Nginx代码
Nginx的项目位于https://github.com/nginx/nginx,Nginx从1.25.0开始正式支持QUIC和HTTP/3。本文使用1.25.1版本,下载链接如下:
https://github.com/nginx/nginx/releases/tag/release-1.25.1
3.2 编译和安装
解压Nginx代码,然后执行编译安装:
cd nginx-release-1.25.1
auto/configure --prefix=/usr/local/nginx-quic --with-debug --with-http_ssl_module --with-http_v3_module --with-cc-opt=-I/usr/local/OpenSSL_1_1_1u-quic1/include --with-ld-opt='-L/usr/local/OpenSSL_1_1_1u-quic1/lib/'
make -j `nproc`
make install
完成后,Nginx安装到/usr/local/nginx-quic目录下。
四、配置和启动nginx
(1)在/usr/local/nginx-quic/conf/目录下,新建http3_test.conf配置文件。listen 8443 quic表示Nginx监听UDP 8443实现QUIC协议;listen 8443 ssl表示Nginx监听TCP 8443端口实现HTTPS协议。
server {
# for better compatibility it's recommended
# to use the same port for quic and https
listen 8443 quic reuseport;
listen 8443 ssl reuseport;
ssl_certificate certs/test.crt;
ssl_certificate_key certs/test.key;
location / {
# required for browsers to direct them to quic port
add_header Alt-Svc 'h3=":8443"; ma=86400';
}
}
(2)修改/usr/local/nginx-quic/conf/nginx.conf文件,在http配置块增加一行配置:
include http3_test.conf;
(3)切换到/usr/local/nginx-quic/sbin目录,启动Nginx
(4)观察Nginx启动进程和监听端口成功
[root@localhost sbin]# ps -aux|grep nginx
root 21743 0.0 0.0 8896 5488 ? Ss 16:04 0:00 nginx: master process ./nginx
nobody 21785 0.0 0.0 9664 6264 ? S 16:06 0:00 nginx: worker process
nobody 21786 0.0 0.0 9760 6476 ? S 16:06 0:00 nginx: worker process
nobody 21787 0.0 0.0 9664 3436 ? S 16:06 0:00 nginx: worker process
[root@localhost sbin]# ss -an|grep 8443
udp UNCONN 0 0 0.0.0.0:8443 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:8443 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:8443 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:8443 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:8443 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:8443 0.0.0.0:*
五、编译测试客户端
本文选择curl作为quic测试客户端。curl的QUIC和HTTP/3实现依赖quictls、nghttp3、ngtcp2库,分别对其进行编译和安装。
5.1 编译nghttp3
(1)下载nghttp3源代码
本文使用nghttp3-0.12.0版本,下载链接:https://github.com/ngtcp2/nghttp3/releases/tag/v0.12.0
(2)编译和安装nghttp3库
cd nghttp3-0.12.0
autoreconf –i
./configure --prefix=/usr/local/nghttp3-0.12.0 --enable-lib-only
make -j `nproc`
make install
完成后,nghttp3安装到/usr/local/nghttp3-0.12.0/目录下。
5.2 编译ngtcp2
(1)下载ngtcp2源代码
本文使用ngtcp2-0.16.0版本,下载链接:https://github.com/ngtcp2/ngtcp2/releases/tag/v0.16.0
(2)编译和安装ngtcp2库
cd ngtcp2-0.16.0
autoreconf –i
./configure PKG_CONFIG_PATH=/usr/local/OpenSSL_1_1_1u-quic1/lib/pkgconfig:/usr/local/nghttp3-0.12.0/lib/pkgconfig LDFLAGS="-Wl,-rpath,/usr/local/OpenSSL_1_1_1u-quic1/lib" --prefix=/usr/local/ngtcp2-0.16.0
make -j `nproc`
make install
完成后,ngtcp2安装到/usr/local/ngtcp2-0.16.0/目录下。
5.3 编译curl
(1)下载curl源代码
本文使用curl master分支的最新代码(8.2.0-dev版本),下载链接:https://github.com/curl/curl/archive/refs/heads/master.zip
(2)编译和安装curl
cd curl-master
autoreconf -fi
LDFLAGS="-Wl,-rpath,/usr/local/OpenSSL_1_1_1u-quic1/lib" ./configure --with-openssl=/usr/local/OpenSSL_1_1_1u-quic1/ --with-nghttp3=/usr/local/nghttp3-0.12.0/ --with-ngtcp2=/usr/local/ngtcp2-0.16.0/ --prefix=/usr/local/curl-8.2.0-dev
make -j `nproc`
make install
完成后,curl安装到/usr/local/curl-8.2.0-dev/目录下。
六、QUIC功能测试
使用curl客户端测试Nginx 8443端口,命令如下:
cd /usr/local/curl-8.2.0-dev/bin/
# 使用HTTPS发起请求,观察到输出HTTP/1.1 200 OK,说明使用HTTPS协议。
./curl https://127.0.0.1:8443 -kv
# 使用HTTP3发起请求,观察到输出HTTP/3 200,说明使用HTTP/3协议。
./curl --http3 https://127.0.0.1:8443 -kv
# 使用alt-svc发起请求,首次请求使用HTTPS协议,再次请求自动切换为HTTP/3协议。
./curl --alt-svc altsvc.cache https://127.0.0.1:8443 -kv
七、抓包分析
使用tcpdump抓包,使用quictls提供的sslkeylog功能导出tls主密钥,使用wireshark观察QUIC报文。
(1)导出tls主密钥
配置SSLKEYLOGFILE环境变量export SSLKEYLOGFILE=quic_keylog_file
再次使用curl客户端访问,会再当前目录生成保存有tls主密钥的quic_keylog_file文件。
(2)使用tcpdump抓包
tcpdump -i lo udp port 8443 -w quic.pcap
(3)将quic.pcap和quic_keylog_file导入wireshark
在wireshark 【Prefences】->【Protocols】->【TLS】->【Master-Secret log filename】导入quic_keylog_file,然后打开quic.pcap。wireshark会自动对流量解密。
八、参考链接
(1)nginx quic介绍 http://nginx.org/en/docs/quic.html
(2)QUIC RFC https://datatracker.ietf.org/doc/html/rfc9000
(3)HTTP/3 RFC https://datatracker.ietf.org/doc/html/rfc9114
(4)curl支持HTTP/3介绍 https://github.com/curl/curl/blob/master/docs/HTTP3.md