HTTP3之编译nginx
Tags: http3, quic, nginx
Published: 2024-05-29 07:23:38

## 关于
nginx目前最新版本提供了HTTP3服务，为了测试环境，本文记录从源码编译nginx的过程，其中包括依赖的编译。

## 环境
```bash
lsb_release -a
# Distributor ID: Ubuntu
# Description:    Ubuntu 22.04.4 LTS
# Release:        22.04
# Codename:       jammy

gcc --version
# gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
```

## 编译Nginx
### 编译或安装依赖
#### SSL Library
Nginx实现HTTP3底层依赖SSL库，可以选择BoringSSL, LibreSSL, QuicTLS，[如果选择OpenSSL兼容层将不会提供`early data`的功能](https://nginx.org/en/docs/quic.html)。本次我们选择QuicTLS，她也是基于OpenSSL修改的版本。
```bash
git clone --depth 1 -b openssl-3.1.5+quic https://github.com/quictls/openssl
cd openssl
./config enable-tls1_3
make
make install
```
**注意**
1. 如果Linux机器上已经安装了`libssl`和`libssl-dev`，会有冲突和报错，本质问题及解决方法参考另外一篇[文章](https://juejin.cn/post/7337491274457841727)， 如果那边整不明白，可以查看`man ldconfig`，或者直接添加相关库的ld config文件，相信走到这里的，动态库这些个问题应该都差不多了:)。

2. 编译默认动态库位于`/usr/local/lib64`，`include`文件位于`/usr/local/include`

#### 其他依赖
```bash
apt install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libxml2 libxml2-dev libxslt1-dev
```

### 编译Nginx
```bash
./configure \
--prefix=/home/michael/nginx \
--with-debug \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_v3_module \
--with-cc-opt="-I/usr/local/include" \
--with-ld-opt="-L/usr/local/lib64" \
--with-cc-opt="-DNGX_QUIC_DEBUG_PACKETS -DNGX_QUIC_DEBUG_FRAMES -DNGX_QUIC_DEBUG_ALLOC -DNGX_QUIC_DEBUG_CRYPTO" \
--add-dynamic-module="$HOME/download/njs-0.8.4/nginx" # 添加njs模块
make
make install
```
默认情况下，nginx会安装到`/root/nginx`，进入文件夹后，默认会有如下文件夹
```bash
conf 里面有默认的配置文件nginx.conf，可以按照自己要求修改
html 里面对应的默认的index.html页面，可以按照自己要求修改
logs 里面对应nginx的access和error日志
sbin 包括nginx等命令
moudles 如果有添加模块编译比如njs，会有这个目录，包括模块的动态链接库
```
建议将nginx命令加入到path中
```bash
echo 'export PATH="${PATH}:/root/nginx/sbin"' >> ~/.bashrc
```

## 测试Nginx
使用如下命令启动nginx，再访问试下，看是否正常
```bash
nginx -V #查看详细信息
nginx -t -v #测试配置是否正常
nginx -s start # 启动nginx
curl localhost
```

## 配置HTTP3
### 自签证书
```bash
#! /usr/bin/env bash

# Generate self signed ca and server cert for localhost test

set -eou pipefail

CA="ca.pem"
CA_KEY="ca_key.pem"
SERVER_CERT="server_cert.pem"
SERVER_KEY="server_key.pem"
HOST="localhost"
IP="127.0.0.1"

# NOTICE quictls
export LD_LIBRARY_PATH=/usr/local/lib64
openssl version

# clean
rm -f $CA $CA_KEY $SERVER_CERT $SERVER_KEY

# 1. Generate self-signed certificate and private key
openssl req -x509 \
    -newkey rsa:4096 \
    -days 365 \
    -keyout "${CA_KEY}" \
    -out "${CA}" \
    -subj "/C=CN/ST=Hubei/L=Wuhan/O=QUIC/OU=QUICUNIT/CN=localhost/emailAddress=ca@example.com" \
    -noenc > /dev/null 2>&1

echo "CA's self-signed certificate DONE"
# openssl x509 -in "${CA}" -noout -text

# 2. Generate server cert and private key
openssl req -x509\
    -newkey rsa:4096 \
    -keyout "${SERVER_KEY}" \
    -out "${SERVER_CERT}" \
    -subj "/C=CN/ST=Hubei/L=Wuhan/O=QUIC/OU=QUICUNIT/CN=localhost/emailAddress=server@example.com" \
    -addext "subjectAltName=DNS:${HOST},IP:${IP}" \
    -CA "${CA}" \
    -CAkey "${CA_KEY}" \
    -copy_extensions copyall \
    -days 365 \
    -noenc

echo "Server's certificate DONE"
# openssl x509 -in "${SERVER_CERT}" -noout -text

# 6. Verify server certificate
openssl verify \
    -verbose \
    -show_chain \
    -trusted ${CA} \
    "${SERVER_CERT}"
```
**注意**脚本里面的`IP`和`HOST`，将生成的`server_cert.pem`和`server_key.pem`放到前面nginx的安装目录`/root/nginx/certs`，并且将`ca_cert.pem`添加到信任列表(浏览器可以直接导入)。

### Nginx配置文件
修改/root/nginx/conf/nginx.conf中的server块添加如下内容
```nginx
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
server_name  localhost;

ssl_protocols TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384;
TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256;
ssl_prefer_server_ciphers off;

ssl_certificate     /root/nginx/certs/server_cert.pem;
ssl_certificate_key /root/nginx/certs/server_key.pem;
```
重新启动Nginx, 测试HTTP3服务。这里`ssl_cihpers`和`ssl_conf_command Ciphersuites`跟`man openssl-cihpers`一致。

### 测试
### 浏览器
firefox可以直接使用http3服务, 关于浏览器导入自签证书，后面整个专门文章介绍。

### curl
curl需要自编译添加http3服务，是另外一个话题了，curl官网关于编译写的很清晰
```bash
curl --http3 --cacert ca_cert.pem -v https://localhost
```