zeroserve: ユーザースペース eBPF でスクリプト可能なゼロコンフィグ Web サーバー

zeroserve は、高性能でゼロコンフィグの HTTPS サーバーで、従来の宣言的設定ファイルを実行可能な eBPF プログラムに置き換えることを目的としています。プログラムを設定として扱うことで、zeroserve は開発者がルーティング、認証、レートリミットのロジックを単一の線形スクリプトで定義でき、分散した設定ブロックやオプションプラグインに分割する必要がなくなります。

コアアーキテクチャ: Tarball ベースのデプロイと io_uring

zeroserve は、単一の tarball ファイルからウェブサイト全体をディスクに展開せずに提供します。ロード時にサーバーはパスからバイト範囲へのマップを構築し、tarball に対して直接バイト範囲読み取りを行います。このアプローチによりデプロイは原子的になり、tarball を置き換えて SIGHUP シグナルを送るだけで、サイトコンテンツ、スクリプト、TLS 資料を接続を切断せずに入れ替えることができます。

I/O パフォーマンスを最適化するため、zeroserve は io_uring を使用して monoio ランタイム経由でネットワークとディスクのすべての操作を行います。サーバーはプロセスごとにシングルスレッドのイベントループとして動作し、CPU コア間で複数プロセスを実行することでスケールします。

ユーザースペース eBPF スクリプティング

設定ファイルの代わりに、zeroserve は .zeroserve/scripts/ に配置された eBPF プログラム(C 言語で記述)を使用します。これらのスクリプトは async‑ebpfuBPF をベンダー化)を用いて JIT コンパイルされ、ネイティブ x86-64 マシンコードに変換され、完全にユーザースペースで実行されます。この設計によりカーネルレベルの CAP_BPF 権限が不要になります。

セキュリティと実行

  • ポインタケージング: カーネルの検証器に代わるものとして、zeroserve はポインタケージングを使用し、スクリプト自身のアリーナへのすべてのメモリアクセスをマスクして、権限のないメモリ読み書きを防止します。
  • プリエンプション: ランタイムは完全にプリエンプティブです。タイマーが JIT コンパイルされたコードを割り込み、単一の遅いスクリプトがイベントループを停止させるのを防ぎます。
  • 実行フロー: スクリプトはファイル名順にソートされて実行され、リクエストごとのメタデータマップを共有します。スクリプトが zs_respond または zs_reverse_proxy を呼び出すと、チェーンは途中で終了します。

スクリプト機能

スクリプトは以下のような幅広いヘルパーにアクセスできます:

  • リクエスト変換: URI、メソッド、ヘッダーの読み取りと書き換え。
  • JSON 処理: リクエストボディのパースと zs_json_respond を使用した JSON 応答の生成。
  • セキュリティ: SHA-256、HMAC‑SHA256、そしてシールドされた XChaCha20‑Poly1305 クッキーを用いたステートレスセッション管理による完全な OIDC ログインフロー(認可コード + PKCE)。
  • インフラ: トークンバケットによるレートリミットと、S3 統合のための AWS SigV4 署名。

パフォーマンスベンチマーク

8 コア Ryzen 7 3700X 上で nginx 1.26 と Caddy 2.11(すべて単一コアに固定)と比較したベンチマークにおいて、zeroserve はいくつかの重要な領域で優れたパフォーマンスを示しました。

静的ファイル配信 (HTTPS)

小さな静的ファイル(174 B)に対して、zeroserve は 36,681 req/s を達成し、nginx(31,226 req/s)と Caddy(12,830 req/s)を上回りました。大きなファイル(100 KB)では、zeroserve と nginx は同程度の性能で、zeroserve は約 780 MB/s に達しました。

スクリプトスループット

nginx + LuaJIT と比較した場合、プリエンプションタイマーを 10ms に調整したとき、zeroserve の eBPF スクリプトは大幅な性能向上を示しました:

  • ヘッダーインジェクション: zeroserve eBPF は 43,709 req/s を記録し、nginx Lua の 28,653 req/s を上回りました。
  • 動的 JSON: zeroserve eBPF は 46,945 req/s を記録し、nginx Lua の 41,231 req/s を上回りました。

リバースプロキシ

小さなレスポンス(174 B)に対して、zeroserve のプールされた io_uring プロキシは 26,486 req/s を達成し、nginx(21,761 req/s)と Caddy(7,683 req/s)を上回りました。ただし、大きなプロキシ対象ボディ(100 KB)では、nginx が最も効率的で、5,882 req/s を達成し、zeroserve の 3,631 req/s を上回ります。

モダン TLS 実装

zeroserve は BoringSSL によって終了する包括的なトランスポートセキュリティスタックを含み、以下の機能を備えています:

  • TLS 1.3 のみ: 最新標準への厳格な準拠。
  • 暗号化クライアントハロー (ECH): SNI が平文で現れるのを防止します。
  • JA4 フィンガープリント: クライアントフィンガープリントが eBPF スクリプトに直接公開されます。
  • ECH リレー モード: 復号できないハンドシェイクを上流サーバーに転送し、保護された名前を隠蔽します。

コミュニティの視点と批評

技術ユーザー間の議論は、プロジェクトのアプローチに対する可能性と懐疑の両方を浮き彫りにしています:

"私はこの賭けは的外れだと思います。人々はコードより設定を好み、長い間そうしてきました。組み込み機能だけで多くの人のニーズを十分に満たしており、C 言語でコードを書く必要はありません。"

他の批評家は、発表が「AI 生成」されたものであることや、Nginx のような業界標準と比べて確立されたコミュニティの信頼が欠如していることを指摘しました。コミュニティからの技術的提案としては、.rs ファイルによる eBPF スクリプト向けの Rust サポートの追加や、ハンドシェイク後にユーザースペース SSL をポンピングしないようにする kTLS の実装が挙げられました。

Sources