NginxのHTTP/3対応で気付いたバーチャルホスト非対応の罠

Nginxを「QUIC + HTTP/3」に対応する方法を調べてみました。

DockerにUbuntuのコンテナを作ってハンズオン。

docker run --name ubuntutest --cap-add=NET_ADMIN  -h www.superusers.jp -it -p 80:80 -p 443:443 -p 443:443/udp ubuntu:22.04

コンテナ作成時に「443/tcpと443/udp」を有効にすることがポイントです。

dockerで意図した動きを確認できたので実機にも導入。
このサイトも「HTTP/3.0」で動かすはずだった...

ネームベースバーチャルホスト非対応

2023年2月時点で、

nginx-quicはネームベースのバーチャルホストに対応していません。IPベースや単一ドメインであればHTTP/3の通信はできます。

後に触れますが、nginx-quicをインストールしてHTTP/3に対応するためにはnginxの設定ファイルに以下の内容が必要です。

listen 443 http3 reuseport;
ssl_protocols TLSv1.3;
add_header Alt-Svc 'h3=":443"; ma=86400';
add_header QUIC-Status $http3;

このサーバーは「www.superusers.jp」もネームバーチャルとして設定しています。

duplicate listen options

「listen 443 http3 reuseport;」を複数のバーチャルホストに設定すると「duplicate listen options」と表示されエラーとなります。

nginx: [emerg] duplicate listen options for 0.0.0.0:443 in /etc/nginx/conf.d/www.superusers.jp.conf:15
nginx: configuration file /etc/nginx/nginx.conf test failed

具体的には「www.superusers.jp」と「www.superusers.jp」の2つのバーチャルホストをHTTP/3に対応しようとした時に分かりました。

「listen 443 http3 reuseport;」はIPアドレスとポート番号をペアリングするので、複数の設定ファイルに同じ記述があるとドメインを区別できないのだろう。

TLS SNI support enabled

ちなみにビルドしたnginx-quicはSNI(Server Name Indication)が有効になっているので、もしかすると設定ミスによるものかもしれません。

ネームバーチャルできるよって人がいたらコメント欲しいです。

NginxのHTTP/3対応の概要

さて、前置きが長くなりましたが、NginxをHTTP/3で通信させるためにはどうすればいいのか調べると、パッチを当ててリビルドする訳ではなく、

Nginxのブランチで開発されているnginx-quicのソースコードをダウンロードしてインストールするということでした。

QUICをサポートするライブラリ

nginx-quicをビルドするためには、QUICをサポートするSSL/TLSライブラリが必要です。

QUICをサポートするSSL/TLSライブラリは以下の3つがあります。

  • BoringSSL
  • LibreSSL
  • QuicTLS

今回はBoringSSLを使ってnginx-quicをインストールします。

nginx-quicのインストール手順

構築環境はDockerに「Ubuntu 20.04.5 LTS」と「Ubuntu 22.04.1 LTS」のコンテナを作って試しています。

Ubuntu 20.04ではBoringSSLのビルドに失敗すると思いますが、その内容についても書いていますので安心してください。

nginx-quicのインストール手順は以下の3つです。

  1. ビルドに必要なパッケージをaptでインストール
  2. BoringSSLのビルド
  3. nginx-quicのインストール

nginxの設定サンプル

少し気が早いのですが、nginx-quicをインストールした後は、既存のNginxの設定ファイルに以下の内容をマージします。

server {
    listen 443 http3 reuseport;					# UDP listener for QUIC+HTTP/3

    ssl_protocols       TLSv1.3;				# QUIC requires TLS 1.3

    location / {
        add_header Alt-Svc 'h3=":443"; ma=86400';	# Advertise that QUIC is available
        add_header QUIC-Status $http3;				# Sent when QUIC was used
    }
}

nginxを再起動してアクセスすると「HTTP/3.0」でリクエストを受けていることが分かります。

0.000 HIT - xxx.xxx.xxx.xxx - - [01/Feb/2023:22:12:12 +0900] "GET / HTTP/3.0" 200 22607 "https://www.superusers.jp/cookbook/centos_04-01" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15" "-"
0.000 - - xxx.xxx.xxx.xxx - - [01/Feb/2023:22:12:12 +0900] "GET /xyz/uploads/2022/06/su_boot_centos_m_03-02-520x455.jpg HTTP/3.0" 200 24248 "https://www.superusers.jp/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15" "-"
0.000 HIT - xxx.xxx.xxx.xxx - - [01/Feb/2023:22:12:19 +0900] "GET / HTTP/3.0" 200 22607 "https://www.superusers.jp/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15" "-"
0.000 HIT - xxx.xxx.xxx.xxx - - [01/Feb/2023:22:12:41 +0900] "GET /xyz/engineer_work HTTP/3.0" 200 17679 "https://www.superusers.jp/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15" "-"
0.000 - - xxx.xxx.xxx.xxx - - [01/Feb/2023:22:12:41 +0900] "GET /xyz/uploads/2022/06/su_logo_boot.png?0123456789 HTTP/3.0" 200 7982 "https://www.superusers.jp/category/engineer_work" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15" "-"

ビルドに必要なパッケージ

ビルドに必要な開発用パッケージ等をインストールします。

apt install git cmake golang ninja-build mercurial libunwind-dev

ソースコードをダウンロードしてビルドするのに今回は「/opt/local/src」を使います。

mkdir -p /opt/local/src/nginx && cd $_

BoringSSLのビルド

BoringSSLはGoogleがOpenSSLをフォークして開発したオープンソースのSSL/TLSライブラリです。gitでBoringSSLをダウンロードします。

BoringSSLのダウンロード

git clone https://boringssl.googlesource.com/boringssl

ダウンロードしたboringsslディレクトリに移動します。

cd boringssl

buildディレクトリを作って移動。

mkdir build && cd $_

Ninjaを利用してBoringSSLをビルド

一階層上のソースディレクトリを指定してビルドします。

cmake -GNinja ..

cmakeでもいいのですが、Ninjaを使えばビルド時間を短縮できます。

ninja

問題なくビルドが完了したら「nginx-quicのインストール」を実行します。

Ubuntu 20.04.5 LTSのビルドエラー

Ubuntu 20.04.5 LTSの環境では以下のようにfailedしました。

[134/665] Generating crypto_test_data.cc
FAILED: crypto_test_data.cc
cd /opt/local/src/nginx/boringssl && /usr/bin/go run util/embed_test_data.go -file-list /opt/local/src/nginx/boringssl/build/embed_test_data_args.txt > /opt/local/src/nginx/boringssl/build/crypto_test_data.cc
# command-line-arguments
util/embed_test_data.go:81:16: undefined: os.ReadFile
util/embed_test_data.go:131:16: undefined: os.ReadFile
note: module requires Go 1.19
[139/665] Building C object crypto/fipsmodule/CMakeFiles/fipsmodule.dir/bcm.c.o
ninja: build stopped: subcommand failed.

7行目の「note: module requires Go 1.19」がヒントです。

Goのバージョンが古い

aptでインストールしたGoのバージョンを確認してみましょう。

apt info golang

aptでインストールしたGoは「1.13」なのでアップグレードする必要があります。

Package: golang
Version: 2:1.13~1ubuntu2
Priority: optional
Section: devel
Source: golang-defaults
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Go Compiler Team <team+go-compiler@tracker.debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 12.3 kB
Depends: golang-1.13, golang-doc (>= 2:1.13~1ubuntu2), golang-go (>= 2:1.13~1ubuntu2), golang-src (>= 2:1.13~1ubuntu2)
Homepage: https://golang.org
Download-Size: 2900 B
APT-Manual-Installed: yes
APT-Sources: http://ports.ubuntu.com/ubuntu-ports focal/main arm64 Packages
Description: Go programming language compiler - metapackage

BackportsからGo 1.19をダウンロード

「add-apt-repository」を使えるようにするために「software-properties-common」をインストールします。

apt install software-properties-common

golang-backportsのリポジトリを追加します。

add-apt-repository ppa:longsleep/golang-backports

3行目で「続けるためには[ENTER]キーを押す」ように言われるので[ENTER]キーを押します。

 Golang 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18 and 1.19 PPA for Ubuntu
 More info: https://launchpad.net/~longsleep/+archive/ubuntu/golang-backports
Press [ENTER] to continue or Ctrl-c to cancel adding it.

Get:1 http://ppa.launchpad.net/longsleep/golang-backports/ubuntu focal InRelease [17.5 kB]
Hit:2 http://ports.ubuntu.com/ubuntu-ports focal InRelease
Hit:3 http://ports.ubuntu.com/ubuntu-ports focal-updates InRelease
Hit:4 http://ports.ubuntu.com/ubuntu-ports focal-backports InRelease
Get:5 http://ppa.launchpad.net/longsleep/golang-backports/ubuntu focal/main arm64 Packages [4533 B]
Hit:6 http://ports.ubuntu.com/ubuntu-ports focal-security InRelease
Fetched 22.1 kB in 3s (8459 B/s)
Reading package lists... Done

取得できるパッケージを「apt update」で更新します。

apt update

アップグレードできるパッケージを表示させます。

apt list --upgradable

アップグレードできるパッケージリストにgoの1.19が表示されていることを確認。

Listing... Done
golang-doc/focal 2:1.19~1longsleep1 all [upgradable from: 2:1.13~1ubuntu2]
golang-go/focal 2:1.19~1longsleep1 arm64 [upgradable from: 2:1.13~1ubuntu2]
golang-src/focal 2:1.19~1longsleep1 arm64 [upgradable from: 2:1.13~1ubuntu2]
golang/focal 2:1.19~1longsleep1 arm64 [upgradable from: 2:1.13~1ubuntu2]

apt upgradeでgoが1.9にアップグレードされます。

apt upgrade

ninjaを走らせてみましょう!

ninja

このように表示されたらBoringSSLのビルド成功です。

[665/665] Linking CXX executable decrepit/decrepit_test

nginx-quicのインストール

nginx-quicはQUIC HTTP/3をサポートするよう「nginx mainline 1.23.x」をベースに開発され、nginxのlatestが定期的にマージされています。

コードの品質はベータ版レベルということなので実運用で使用するのかの判断は自己責任でお願いします。

cd /opt/local/src/nginx

nginx-quicのダウンロード

Mercurialバージョン管理システムのコマンドラインインターフェース「hg」を使ってnginx-quicをダウンロードします。

hg clone -b quic https://hg.nginx.org/nginx-quic

nginx-quicに移動します。

cd nginx-quic

必要パッケージのインストール

次に紹介するnginx-quicのconfigureオプションで実行するとエラーになるので、必要なパッケージを事前にインストールします。

apt install libpcre2-dev zlib1g-dev libxslt1-dev libgd-dev libgeoip-dev

nginx-quicのconfigureオプション

BoringSSLのライブラリを利用するnginx-quicのconfigureオプションに、通常のオプションをマージしたものがこちらです。

./auto/configure \
--prefix=/usr/local/nginx \
--sbin-path=/usr/local/nginx/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/run/nginx.pid \
--lock-path=/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=www-data \
--group=www-data \
--with-http_ssl_module \
--with-http_v2_module \
--with-debug \
--with-cc-opt="-I../boringssl/include" \
--with-ld-opt="-L../boringssl/build/ssl -L../boringssl/build/crypto" \
--with-http_v3_module \
--with-stream \
--with-stream_ssl_module \
--with-stream_quic_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_geoip_module=dynamic \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_degradation_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-mail \
--with-mail_ssl_module \
--with-file-aio \
--with-pcre \
--with-pcre-jit \
--with-stream=dynamic

nginxのconfigureオプションにnginx-quic用のオプションを18-24行目に追加しています。

Configuration summary
  + using system PCRE library
  + using system OpenSSL library
  + using system zlib library

  nginx path prefix: "/usr/local/nginx"
  nginx binary file: "/usr/local/nginx/sbin/nginx"
  nginx modules path: "/usr/local/nginx/modules"
  nginx configuration prefix: "/etc/nginx"
  nginx configuration file: "/etc/nginx/nginx.conf"
  nginx pid file: "/run/nginx.pid"
  nginx error log file: "/var/log/nginx/error.log"
  nginx http access log file: "/var/log/nginx/access.log"
  nginx http client request body temporary files: "/var/cache/nginx/client_temp"
  nginx http proxy temporary files: "/var/cache/nginx/proxy_temp"
  nginx http fastcgi temporary files: "/var/cache/nginx/fastcgi_temp"
  nginx http uwsgi temporary files: "/var/cache/nginx/uwsgi_temp"
  nginx http scgi temporary files: "/var/cache/nginx/scgi_temp"

このように表示されたらmakeします。

make

エラー表示されることなくmakeが完了したらインストールします。

make install

nginxがインストールされているサーバーであれば、cacheディレクトリは存在していると思うのでディレクトリを作成する必要はありません。

mkdir /var/cache/nginx

nginxのSyntaxチェックをする時などフルパス指定するのが煩わしいので「/usr/sbin/nginx」にシンボリックリンクを作成します。

ln -s /usr/local/nginx/sbin/nginx /usr/sbin/nginx

nginxのバージョンやbuilt情報を表示させてみましょう。

nginx -V

「built with OpenSSL 1.1.1 (compatible; BoringSSL) (running with BoringSSL)」と表示されているか確認してください。

Ubuntu 22.04のnginx-quic

nginx version: nginx/1.23.4
built by gcc 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
built with OpenSSL 1.1.1 (compatible; BoringSSL) (running with BoringSSL)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/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=www-data --group=www-data --with-debug --with-cc-opt=-I../boringssl/include --with-ld-opt='-L../boringssl/build/ssl -L../boringssl/build/crypto' --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-stream --with-stream_ssl_module --with-stream_quic_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module --with-http_auth_request_module --with-mail --with-mail_ssl_module --with-file-aio --with-pcre --with-pcre-jit --with-stream=dynamic --add-module=../naxsi/naxsi_src

TLS SNI support enabledなのにネームバーチャルできないなんて...

Ubuntu 20.04.1のnginx-quic

nginx version: nginx/1.23.4
built by gcc 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
built with OpenSSL 1.1.1 (compatible; BoringSSL) (running with BoringSSL)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/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=www-data --group=www-data --with-debug --with-cc-opt=-I../boringssl/include --with-ld-opt='-L../boringssl/build/ssl -L../boringssl/build/crypto' --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-stream --with-stream_ssl_module --with-stream_quic_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module --with-http_auth_request_module --with-mail --with-mail_ssl_module --with-file-aio --with-pcre --with-pcre-jit --with-stream=dynamic --add-module=../naxsi/naxsi_src

ここまでできたらNginxの設定を行います。

既存サイトのnginxの設定ファイルやSSL/TLS 証明書をdockerのコンテナに設置してHTTP/3を有効にするための設定を追加します。

詳しくはダウンロードした「~/nginx-quic」の中の「README」を参照してください。

関連記事

コメント

この記事へのコメントはありません。

おすすめ記事

  1. 【多段SSH】公開鍵認証で中継サーバー経由のログインが簡単!

  2. インターネットに繋いだサーバーで最初にやらなければならない設定

  3. Swapアウトしているプロセスを特定する方法

  4. Linuxを極めるならコマンドよりも〇〇が先だ!!

  5. お名前ドットコムのDNS設定方法