矛を作らば盾も作らん
そんな格言はないですが、矛となるクライアント(HTTP/3 対応 curl)をせっかく作ったので、盾となるサーバも作ります。
……といっても、やっぱりこのページをなぞっているだけですが
https://github.com/cloudflare/quiche/tree/master/extras/nginx
Cloudflare が作った QUIC の実装である quiche を使います。これを nginx で使えるようにしたパッチも Cloudflare が用意しています。
※ Docker 使えます
その前に。
Docker でお手軽に試せます。
https://hub.docker.com/r/keioni/nginx-http3
導入
quiche は Rust でできているので、Rust が入っていないなら rustup を使って入れます。
他にも、各々の環境にあわせて build に必要なものを入れます。私の場合はこんなパッケージを入れました。
yum install git tar gcc gcc-c++ make cmake3 patch pcre-devel zlib-devel
cmake3 以上が必要なのですが、cmake3 パッケージを入れるとバイナリが cmake3
だったので cmake にシンボリックリンクを張ります。
ln -s /usr/bin/cmake3 /usr/bin/cmake
build
nginx + quiche patch
Cloudflare が用意しているパッチは nginx 1.16.x 系のみの対応です。
curl -O https://nginx.org/download/nginx-1.16.1.tar.gz
tar xzvf nginx-1.16.1.tar.gz
quiche を git から clone してきて、nginx 用のパッチを当てます。
git clone --recursive https://github.com/cloudflare/quiche
cd nginx-*
patch -p01 < ../quiche/extras/nginx/nginx-1.16.patch
build
今回も HTTP/3 対応 curl を作ったときと同じように /opt/http3
以下に入れるようにしました。
./configure \
--prefix=/opt/http3/nginx \
--build="quiche-$(git --git-dir=../quiche/.git rev-parse --short HEAD)" \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_v3_module \
--with-openssl=../quiche/deps/boringssl \
--with-quiche=../quiche
make
make install
確認
うまくできているか確認します。
/opt/http3/nginx/sbin/nginx -V
nginx version: nginx/1.16.1 (quiche-918c13b)
built by gcc 7.3.1 20180712 (Red Hat 7.3.1-9) (GCC)
built with OpenSSL 1.1.0 (compatible; BoringSSL) (running with BoringSSL)
TLS SNI support enabled
configure arguments: --prefix=/opt/http3/nginx --build=quiche-918c13b --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-openssl=../quiche/deps/boringssl --with-quiche=../quiche
実行
設定ファイルの準備
証明書と秘密鍵を用意して、以下のように設定ファイルを作ります。
events {
worker_connections 1024;
}
http {
server {
# Enable QUIC and HTTP/3.
listen 443 quic reuseport;
listen [::]:443 quic reuseport;
# Enable HTTP/2 (optional).
listen 443 ssl http2;
location / {
root html;
index index.html index.htm;
}
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
# Enable all TLS versions (TLSv1.3 is required for QUIC).
ssl_protocols TLSv1.2 TLSv1.3;
# Request buffering in not currently supported for HTTP/3.
proxy_request_buffering off;
# Add Alt-Svc header to negotiate HTTP/3.
add_header alt-svc 'h3-29=":443"; ma=86400';
}
}
起動
/opt/http3/nginx/sbin/nginx
動作確認
socket
netstat -an --udp | grep 443
443/udp が表示されているなら、うまくいっています。
udp 0 0 0.0.0.0:443 0.0.0.0:*
udp6 0 0 :::443 :::*
curl
以前の記事で作った HTTP/3 対応の curl
でアクセスしてみます。
./curl -v --http3 https://localhost/
* Trying 127.0.0.1:443...
* Connect socket 5 over QUIC to 127.0.0.1:443
* QUIC handshake is completed
* Connected to localhost () port 443 (#0)
* Using HTTP/3 Stream ID: 0 (easy handle 0x137c9e0)
> GET / HTTP/3
> Host: localhost
> user-agent: curl/7.71.1
> accept: */*
>
* ngh3_stream_recv returns 0 bytes and EAGAIN
< HTTP/3 200
< server: nginx/1.16.1
< date: Tue, 21 Jul 2020 02:40:17 GMT
< content-type: text/html
< content-length: 612
< last-modified: Tue, 21 Jul 2020 02:09:03 GMT
< etag: "5f164e3f-264"
< alt-svc: h3-29=":443"; ma=86400
< accept-ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host localhost left intact
ちゃんと HTTP/3 でアクセスできました。いえい。
正式なWebサーバとの同居
すでに別のWebサーバアプリでサービスを提供している場合も、HTTP/3 (QUIC) が使うプロトコル・ポートは 443/udp なので競合せず、一緒に動作させることができます。
設定
設定ファイルをこんな感じにします。
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
# Enable QUIC and HTTP/3.
listen 443 quic reuseport;
listen [::]:443 quic reuseport;
location / {
root html;
index index.html index.htm;
}
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
# Enable all TLS versions (TLSv1.3 is required for QUIC).
ssl_protocols TLSv1.2 TLSv1.3;
# Request buffering in not currently supported for HTTP/3.
proxy_request_buffering off;
# Add Alt-Svc header to negotiate HTTP/3.
add_header alt-svc 'h3-29=":443"; ma=86400';
# 他のHTTPSサーバと同居させるときに重要
add_header last-modified '';
}
}
元の設定ファイルとの違いは、以下の2つです:
-
listen 443 ssl http2;
を取り除く
これを残したままだと 443/tcp を待ち受けるようになるので、元のWebサーバと競合してしまいます。 -
add_header last-modified '';
を追加
すでにファイルがある場合、キャッシュの問題が発生しがちなのでLast-Modified
ヘッダを付けないようにします。このように設定してもETag
があるので、キャッシュはきちんと行われます。
動作確認
再起動したら netstat で確認します。
sudo netstat -an -p --udp --tcp | grep 443
以下のように tcp
が、元々使っているWebサーバになっていて1、udp
が nginx
になっていれば大丈夫です。
tcp6 0 0 :::443 :::* LISTEN 28911/httpd
udp 0 0 0.0.0.0:443 0.0.0.0:* 9182/nginx: master
udp6 0 0 :::443 :::* 9182/nginx: master
さいごに
HTTP/3 で使うプロトコルの QUIC は、2020年7月27日時点では正式版が出ていません。しかしそう遠くないうちに正式版が出るはずです。
正式版が出る前に、ちょっと試してみるのもいいのでは、と思います。
→ 2021年5月31日に RFC9000 として標準化されました。(2021年8月13日追記)
-
ここでは
httpd
なので分かりやすいですが、nginx
を使っている場合はPIDの違いで判別してください ↩