路漫漫其修远兮
吾将上下而求索

nginx学习:使用nginx-quic支持HTTP/3

本文主要介绍使用nginx-quic和boringssl项目来对服务器进行升级支持HTTP/3协议。

1、背景介绍

nginx官方从1.19版本开始,新建立了一个分支,专门用来对QUIC进行支持,官网的链接点这里。注意该项目还处于早期的alpha版本,非常不建议用于生产环境。

The code is at an early alpha level of quality and should not be used in production.

nginx-quic的安装包可以在下面这里找到,由于还处于开发阶段,项目更新得非常快。

https://hg.nginx.org/nginx-quic/shortlog/quic

从官网的readme文件我们可以了解到,截止到2020年8月21日,nginx-quic项目目前只支持h3-27h3-28h3-29三个版本,再早期的草案版本并不支持,不过目前更新的速度非常快,变动也很大。

Currently we support IETF-QUIC draft-27, draft-28, draft-29.
Earlier drafts are NOT supported as they have incompatible wire format.

考虑到浏览器的兼容性,接下来我们主要基于h3-27版本进行测试。

同时需要注意的是openssl官方版本目前并不支持quic协议,官方于2020年2月给出的原因是目前quic并不稳定,还有很多版本需要迭代确定,它们需要优先把精力放在openssl3.0版本的开发上,等到openssl3.0版本开发完成了再来进行quic的支持。

So in conclusion; QUIC is on our minds, but it will not be included in the OpenSSL 3.0 release. We expect more tangible action to happen after we’ve released OpenSSL 3.0.

所以我们这里需要用到谷歌自己使用的openssl分支版本boringssl,这是谷歌对应的开源的自用的openssl版本,谷歌官方表示boringssl这个项目虽然开源了,但是并不建议大家在生产环境上广泛使用,因为它是根据谷歌自身的需求进行调整的,有些API可能并不稳定。

后续更新:2021年5月份,RFC 9000正式发布了HTTP3-QUIC。

rfc9000 (ietf.org)

2、编译安装nginx-quic

这里我们使用了CentOS7来进行编译安装

# lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch
Description:    CentOS Linux release 7.9.2009 (Core)
Release:        7.9.2009
Codename:       Core

首先我们在系统中使用yum安装基本的编译工具,注意此次使用的很多软件工具和依赖都是需要较新版本的,因此后面会进行大量的编译安装,请确保机器拥有足够的性能(可能需要编译gcc)和良好的网络。主要需要使用到的工具软件的安装步骤如下:

2.1 yum安装依赖

需要注意的是这里安装所需要的软件很多,这里并不能全部列出来,但是除了列出来的软件对新版本有要求外,别的都可以直接使用yum进行安装,当然也可以自己根据需要编译安装新版本。后面根据需求自己安装

2.2 编译安装cmake

cmake的版本必须要在3.0以上,cmake可以到这里下载https://cmake.org/download/

wget https://github.com/Kitware/CMake/releases/download/v3.18.2/cmake-3.18.2.tar.gz
tar -zxvf cmake-3.18.2.tar.gz
./bootstrap
gmake
make
make install

cmake --version

2.3 编译安装perl

perl的安装包可以从这里www.cpan.org/src下载。perl的版本需要尽可能新,否则可能会出现问题,这里使用的是5.32.0的稳定版本(截止2020-08-21)

wget https://www.cpan.org/src/5.0/perl-5.32.0.tar.gz
tar -zxvf perl-5.32.0.tar.gz
cd perl-5.32.0/
./Configure -des -Dprefix=/usr/local/perl
make -j8
make test
make install

# 注意最后检查一下系统默认的perl是否为我们新安装的perl
perl -v

# 如果不是则需要进行修改
# 查看默认的perl
which perl | xargs file

# 替换新安装的perl和原来的perl
mv /usr/bin/perl /usr/bin/perl.5.16.3
# 需要注意新安装的perl目录要根据前面编译的时候指定的目录来确定
ln -s /usr/local/perl/bin/perl /usr/bin/perl
# 再次检查
perl -v

2.4 安装golang

golang的安装配置比较简单,我们从https://golang.org/dl直接下载最新的稳定版本即可。

wget https://golang.org/dl/go1.15.linux-amd64.tar.gz
tar -zxvf go1.15.linux-amd64.tar.gz -C /usr/local
# 修改系统默认的go文件
ln -s /usr/local/go/bin/go /usr/bin/go

接下来的go环境变量同学们可以根据自己的实际需求进行配置。对于我个人而言,我直接在/etc/profile中添加下面的配置然后source生效即可。

export GOROOT=/usr/local/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOBIN
export GOPATH=/home/gopath

2.5 编译安装gcc

CentOS7默认的gcc的版本太旧了,编译boringssl的时候会报错,我们手动编译安装新版本的gcc,这里使用的是10.2.0的版本。gcc的版本可以在这里下载http://ftp.gnu.org/gnu/gcc/

wget http://ftp.gnu.org/gnu/gcc/gcc-10.2.0/gcc-10.2.0.tar.gz
tar -zxvf gcc-10.2.0.tar.gz
cd gcc-10.2.0/
# 解压完成后需要下载四个依赖,我们执行脚本即可直接下载,服务器网络不好的同学也可以手动下载
./contrib/download_prerequisites
# 这里需要对c和c++进行支持,为了节省时间禁用掉了交叉编译
./configure -enable-checking=release -enable-languages=c,c++ -disable-multilib
# 接下来这里需要很久
make -j8
make install

# 安装完成之后我们需要替换原来的cc文件和c++文件,确保它们的版本都是最新的版本
# 一般来说原来系统的cc文件和c++文件都在/usr/bin/目录下,而我们编译安装的cc文件和c++文件在/usr/local/bin/
cd /usr/bin/
mv /usr/bin/cc /usr/bin/cc.4.8.5
mv /usr/bin/c++ /usr/bin/c++.4.8.5
ln -s /usr/local/bin/gcc /usr/bin/cc
ln -s /usr/local/bin/c++ /usr/bin/c++
# 最后检查版本
/usr/bin/cc -v
/usr/bin/c++ -v

gcc下载的依赖内容如下,有需要的同学自取

# ./contrib/download_prerequisites
2020-08-21 21:28:24 URL:http://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2 [2383840/2383840] -> "./gmp-6.1.0.tar.bz2" [1]
2020-08-21 21:28:26 URL:http://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2 [1279284/1279284] -> "./mpfr-3.1.4.tar.bz2" [1]
2020-08-21 21:28:28 URL:http://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz [669925/669925] -> "./mpc-1.0.3.tar.gz" [1]
2020-08-21 21:28:30 URL:http://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 [1658291/1658291] -> "./isl-0.18.tar.bz2" [1]
gmp-6.1.0.tar.bz2: OK
mpfr-3.1.4.tar.bz2: OK
mpc-1.0.3.tar.gz: OK
isl-0.18.tar.bz2: OK
All prerequisites downloaded successfully.

2.6 编译安装boringssl

谷歌官方建议我们使用ninja来编译安装boringssl,因此我们需要先安装一个ninja

wget https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip
unzip ninja-linux.zip
cp -r ninja /usr/bin/
which ninja

# boringssl 需要 libunwind-generic 这个module的支持
yum install libunwind-devel libunwind
# 注意这里的git仓库很大,大概在250MB左右,请确保编译安装服务器的网络良好
git clone https://github.com/google/boringssl.git
cd boringssl/
# 建立一个专门用于编译的文件夹
mkdir build
cd build
cmake -GNinja ..
ninja

注意在执行cmake这一步的时候正常情况下检测到的gcc文件和perl库版本应该是我们之前编译安装好的新版本,如果不对的话需要再次检查

# cmake -GNinja ..
-- The C compiler identification is GNU 10.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- The CXX compiler identification is GNU 10.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Perl: /usr/bin/perl (found version "5.32.0")
-- Checking for module 'libunwind-generic'
-- Checking for module 'libunwind-generic'
--   Found libunwind-generic, version 1.2
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/cc
-- Configuring done
-- Generating done
-- Build files have been written to: /root/boringssl/build

2.7 编译nginx

剩下的nginx的编译安装步骤就和正常的nginx编译安装一致,这里不再赘述,模块可以根据自己的需求进行安装,开启HTTP/3模块需要使用--with-http_v3_module --with-cc-opt="-I../boringssl-master/include" --with-ld-opt="-L../boringssl-master/build/ssl -L../boringssl-master/build/crypto"参数。

cd nginx-quic/
./auto/configure \
--prefix=/usr/local/nginx \
--with-http_v3_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 \
--with-file-aio \
--with-http_ssl_module \
--with-http_v2_module \
--with-stream=dynamic \
--with-stream_ssl_module
make
make install

安装完成后我们检测nginx的参数:

#nginx -V
nginx version: andblog.cn/1.21.4
built by gcc 10.2.0 (GCC) 
built with OpenSSL 1.1.1 (compatible; BoringSSL) (running with BoringSSL)
TLS SNI support enabled

3、配置nginx-quic

  • 由于HTTP/3需要使用udp协议端口,请注意开放对应的防火墙

  • http2监听的是443的tcp端口,而http3监听的是udp端口

  • nginx中添加了$http3$quic变量,可以添加到日志中,这样就可以看到是否使用了HTTP/3来进行访问了

  • 如果有多个server_name,在不指定IP的情况下,只需要在任意一个配置了listen 443 http3 quic reuseport;那么其他所有server_name都会开启HTTP3,并且不需要再添加该配置否则会报错(不知道后续会不会把报错去掉),如果需要部分server_name开启HTTP/3,请指定监听IP。

  • http3增加了http3_max_field_sizehttp3_max_table_capacityhttp3_max_blocked_streamshttp3_max_concurrent_pushes、 http3_pushhttp3_push_preload这六个变量来控制http3的性能

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    gzip                on;
    gzip_min_length 1k;
    gzip_comp_level 6;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml;
    gzip_vary on;
    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;
    server_tokens off;

    ssl_certificate      /usr/local/nginx/conf/ssl/fullchain.pem;
    ssl_certificate_key  /usr/local/nginx/conf/ssl/privkey.pem;

    include             /usr/local/nginx/conf/mime.types;
    default_type        application/octet-stream;


    ssl_stapling_verify on;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:1m;
    ssl_verify_depth 10;
    ssl_session_timeout 30m;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ecdh_curve X25519:P-256:P-384;
    ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256;

    # 添加HSTS配置
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    # 添加header告知浏览器支持http3,注意这里不再需要像之前的草稿版本http3那样指定如h3-27 h3-28 h3-29等版本
    add_header Alt-Svc 'h3=":443"; ma=86400';

    # 开启 TLS 1.3 0-RTT
    ssl_early_data on;
    # 添加 Early-Data 头告知后端, 防止重放攻击
    proxy_set_header Early-Data $ssl_early_data;



    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /usr/local/nginx/conf/conf.d/*.conf;
}



#cat api.andblog.cn.conf 
server {
    listen 443 ssl http2;
    server_name  api.andblog.cn;

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:8081;
    }
  
    error_page 403 404 https://andblog.cn;
}

修改配置运行之后应该能看到udp的443端口被nginx进程监听

使用ss -tunl | grep 443来查看是否有端口监听

4、测试

目前的主流浏览器只有Firefox和Chrome支持了HTTP/3协议。并且两者支持程度并不高,Firefox是默认禁用,Chrome是默认开启部分,因此配置起来有点麻烦。

4.1 HTTP/3 CHECK

http3check.net网站提供了网站的HTTP/3支持检测

参考:https://tinychen.com/20200821-nginx-quic-compile-to-implement-http3/#3%E3%80%81%E9%85%8D%E7%BD%AEnginx-quic

未经允许不得转载:江哥架构师笔记 » nginx学习:使用nginx-quic支持HTTP/3

分享到:更多 ()

评论 抢沙发

评论前必须登录!