シナプス技術者ブログ

シナプスの技術者公式ブログ。インターネットで、鹿児島の毎日を笑顔にします。

nginxでQUICに対応したwebサーバーを作る

こんにちは、技術部ネットワーク課の上曽山です。
新しいプロトコルとして注目を集めているQUICですが、私はこれまでほとんど触れた事がなかったため、入門編としてnginxでQUICに対応したwebサーバーを構築してみました。

QUICについて

QUICについて現時点で把握している事は以下の点です。語れるほどの知見が無いので詳細はwikipediaでご確認いただけると幸いです。
https://ja.wikipedia.org/wiki/QUIC

  • QUICはUDPベースの通信プロトコル。
  • エンドポイント間の安全で高速な通信を実現するために開発された。
  • 2021年にRFC9000として標準化された。

ちなみにnginxはver1.25からquicに対応しているそうです。

インストール可能なnginxのバージョン

  • バージョン確認

今回使用するサーバーのOSはRocky Linux 9.4です。
まずは標準リポジトリからインストール可能なnginxのバージョンを確認してみます。

# dnf list nginx
Last metadata expiration check: 0:00:05 ago on Thu 26 Sep 2024 10:01:09 PM JST.
Available Packages
nginx.x86_64                                              1:1.20.1-16.el9_4.1                                              appstream

ちょっと古いバージョンが出てきました。
dnfのモジュールを変更してより新しいnginxがインストール出来るか確認してみます。

# dnf module list nginx
Last metadata expiration check: 0:09:40 ago on Thu 26 Sep 2024 10:01:09 PM JST.
Rocky Linux 9 - AppStream
Name                          Stream                        Profiles                          Summary
nginx                         1.22                          common [d]                        nginx webserver
nginx                         1.24                          common [d]                        nginx webserver

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

残念、1本足りなかった。標準リポジトリからは、お目当てのバージョンがインストール出来ないようです。QUIC対応のnginxをインストールするため公式サイトで公開しているリポジトリを設定することにします。

  • パッケージインストール

以下の設定を/etc/yum.repos.d/nginx.repoとして追加します。
どうせなら最新版を使いたいので[nginx-mainline]の「enabled=0」となっている所を「enabled=1」にしておきましょう。

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

リポジトリ設定が終わったらパッケージをインストールします。

# dnf install -y nginx
Last metadata expiration check: 0:29:22 ago on Thu 26 Sep 2024 09:59:41 PM JST.
Dependencies resolved.
====================================================================================================================================
 Package                  Architecture              Version                                 Repository                         Size
====================================================================================================================================
Installing:
 nginx                    x86_64                    1:1.27.1-1.el9.ngx                      nginx-mainline                    996 k

Transaction Summary
====================================================================================================================================
Install  1 Package
 (以下、省略)

ver1.27.1がインストール出来ました。

# nginx -v
nginx version: nginx/1.27.1


QUICに対応したnginxの設定

  • 設定ファイル作成

公式のサンプル設定をアレンジして使用します。
今回使う設定ではserverディレクティブの部分を抜き出して/etc/nginx/conf.d/quic.confとして追加しています。
# テスト用なので最低限の設定しか入っていません。

server {
    # for better compatibility it's recommended
    # to use the same port for quic and https
    listen 8443 quic reuseport;
    listen 8443 ssl;

    ssl_certificate     /etc/pki/tls/certs/fullchain.crt;
    ssl_certificate_key /etc/pki/tls/private/private.key;

    location / {
        # required for browsers to direct them to quic port
        add_header Alt-Svc 'h3=":8443"; ma=86400';
        root /var/www/html;
    }
}


  • index.html作成

アクセステスト用にindex.htmlを作成しておきます。

# echo test > /var/www/html/index.html


  • サービス起動

まず、設定ファイルの内容に間違いがないか構文チェックをしましょう。

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

チェック結果に間違いが無ければサービスを起動します。

# systemctl restart nginx.service


アクセス状況を確認する

  • chromeでデベロッパーツールを起動する

webサーバーにアクセスする前にCtrl+Shift+iでデベロッパーツールを起動して「ネットワーク」タブを開いておきます。


  • パケットキャプチャ準備

パケットをキャプチャしてアクセス状況を確認するため、WiresharkのCLI版をインストールします。

# dnf install -y wireshark-cli
Last metadata expiration check: 0:49:26 ago on Thu 26 Sep 2024 09:59:41 PM JST.
Dependencies resolved.
====================================================================================================================================
 Package                          Architecture              Version                              Repository                    Size
====================================================================================================================================
Installing:
 wireshark-cli                    x86_64                    1:3.4.10-6.el9                       appstream                     20 M
Installing dependencies:
 libsmi                           x86_64                    0.4.8-30.el9                         appstream                    2.1 M

Transaction Summary
====================================================================================================================================
Install  2 Packages
 (以下、省略)

インストールが終わったら、port 8443のパケットをキャプチャするため以下のコマンドを実行します。

# tshark -f "port 8443"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'eth0'


  • サイトにアクセスする

結果①: デベロッパーツールの表示

結果②: パケットキャプチャの結果
# IPアドレスは修正してあります。

# tshark -f "port 8443"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'eth0'
    1 0.000000000 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TCP 66 52535 → 8443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
    2 0.000053511 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TCP 66 8443 → 52535 [SYN, ACK] Seq=0 Ack=1 Win=32120 Len=0 MSS=1460 SACK_PERM=1 WS=128
    3 0.000566312 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TCP 60 52535 → 8443 [ACK] Seq=1 Ack=1 Win=2102272 Len=0
    4 0.002508796 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TLSv1 2101 Client Hello
    5 0.002532789 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TCP 54 8443 → 52535 [ACK] Seq=1 Ack=2048 Win=31872 Len=0
    6 0.003183784 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TLSv1.3 298 Server Hello, Change Cipher Spec, Application Data, Application Data
    7 0.004330802 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TCP 66 52536 → 8443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
    8 0.004351746 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TCP 66 8443 → 52536 [SYN, ACK] Seq=0 Ack=1 Win=32120 Len=0 MSS=1460 SACK_PERM=1 WS=128
    9 0.004716759 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TCP 60 52536 → 8443 [ACK] Seq=1 Ack=1 Win=2102272 Len=0
   10 0.004994324 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TLSv1.3 118 Change Cipher Spec, Application Data
   11 0.005343163 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TLSv1.3 341 Application Data
   12 0.006543627 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TLSv1 2069 Client Hello
   13 0.006567335 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TCP 54 8443 → 52536 [ACK] Seq=1 Ack=2016 Win=31872 Len=0
   14 0.007022485 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TLSv1.3 298 Server Hello, Change Cipher Spec, Application Data, Application Data
   15 0.015946974 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TLSv1.3 809 Application Data
   16 0.016176939 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TLSv1.3 346 Application Data
   17 0.016267019 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 TLSv1.3 118 Change Cipher Spec, Application Data
   18 0.016402416 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 TLSv1.3 341 Application Data
   19 0.034615510 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 QUIC 1292 Initial, DCID=8f01bae40f0809e3, PKN: 1, CRYPTO
   20 0.034615648 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 QUIC 1292 Initial, DCID=8f01bae40f0809e3, PKN: 2, CRYPTO, CRYPTO, CRYPTO, CRYPTO, PADDING, CRYPTO, PING, PADDING, CRYPTO
   21 0.035015251 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 QUIC 93 Initial, SCID=00000000000010068ffd45fec76a46058978366e, PKN: 0, ACK
   22 0.035554403 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 QUIC 1242 Handshake, SCID=00000000000010068ffd45fec76a46058978366e
   23 0.036424482 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 QUIC 214 Protected Payload (KP0), DCID=00000000000010068ffd45fec76a46058978366e
   24 0.036578232 xxx.xxx.xxx.100 → xxx.xxx.xxx.1 QUIC 539 Protected Payload (KP0), DCID=00000000000010068ffd45fec76a46058978366e
   25 0.036762955 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 QUIC 397 Protected Payload (KP0)
   26 0.036887297 xxx.xxx.xxx.1 → xxx.xxx.xxx.100 QUIC 694 Protected Payload (KP0)

結果①、結果②、どちらも最初はTCP(http/1.1)でアクセスを行い、途中からQUIC(h3)に変わっている様子が確認できます。
# h3はHTTP/3(HTTP over QUIC)の意
最初にTCPで通信するのは、ブラウザはサーバーがQUICに対応している事を知らないからです。

    location / {
        # required for browsers to direct them to quic port
        add_header Alt-Svc 'h3=":8443"; ma=86400';
        root /var/www/html;
    }

nginxの設定でAlt-Svcヘッダーを付加するようにしているため、1度アクセスしてヘッダー情報を受信すると次からはQUIC(h3)で通信するようになります。

最後に

すごく浅い部分だけですが、QUICで通信している様子を実際に確認する事が出来ました。
引き続き、パケットを解析したりしてQUICの実装について理解を深めていきたいと思います。