2 ポイント 投稿者 GN⁺ 2024-09-26 | 1件のコメント | WhatsAppで共有

My Blog Technology

このウェブサーバーは、自分のブログをホストするために設計された最小限のウェブサーバーです。最初から公開インターネットに耐えられるよう堅牢に作られています。リバースプロキシは必要ありません。実際に動作している様子は http://playin.coz.is/index.html で確認できます。Redditでハッキングを募って、面白くて悪意のあるリクエストのログをギガバイト単位で収集しました。その一部は attempts.txt に保存してあり、後で暇つぶしにさらに見ていく予定です。

でも……なぜ?

私は自分専用のツールを作るのが好きで、何でも「battle-tested」でなければならないという話を聞くのにうんざりしていました。クラッシュしたらどうするのか? バグは修正できます。

仕様

  • Linux専用
  • HTTP/1.1、パイプライニング、keep-alive接続を実装
  • HTTPS対応(BearSSLを使用し、TLS 1.2まで対応)
  • 依存関係は最小限(HTTPS使用時は libc と BearSSL)
  • 設定可能なタイムアウト
  • アクセスログ、クラッシュログ、ログローテーション、ディスク使用量制限
  • Transfer-Encoding: Chunked なし(411 Length Required で応答し、クライアントに Content-Length 付きで再送させる)
  • 単一コア(より良いVPSを入手したら変更予定)
  • 静的ファイルのキャッシュなし(まだ)

ベンチマーク

このプロジェクトの主眼は堅牢性ですが、決して遅くはありません。nginxとの簡単な比較です(静的エンドポイント、どちらも単一スレッド、1K接続制限)。

  • (blogtech)

    $ wrk -c 500 -d 5s http://127.0.0.1:80/hello
    
    • 平均レイテンシ: 6.66ms
    • リクエスト/秒: 76974.24
    • 転送/秒: 6.09MB
  • (nginx)

    $ wrk -c 500 -d 5s http://127.0.0.1:8080/hello
    
    • 平均レイテンシ: 149.11ms
    • リクエスト/秒: 44227.78
    • 転送/秒: 8.27MB

nginx設定:

worker_processes 1;
events {
  worker_connections 1024;
}
http {
  server {
    listen 8080;
    location /hello {
      add_header Content-Type text/plain;
      return 200 "Hello, world!";
    }
  }
}

ビルドと実行

デフォルトでは、サーバーのビルドはHTTP専用です。

$ make

このコマンドにより、serve(リリースビルド)、serve_cov(カバレッジビルド)、serve_debug(デバッグビルド)の実行ファイルが生成されます。リリースビルドはポート80、デバッグビルドはポート8080で待ち受けます。

HTTPSを有効にするには、BearSSLをクローンしてビルドする必要があります。

$ mkdir 3p
$ cd 3p
$ git clone https://www.bearssl.org/git/BearSSL
$ cd BearSSL
$ make -j
$ cd ../../
$ make -B HTTPS=1

同じ実行ファイルが生成されますが、ポート443(リリース)または8081(デバッグ)でセキュア接続が利用可能になります。cert.pemkey.pem ファイルは実行ファイルと同じディレクトリに配置する必要があります。名前と場所を変更するには、次を修正します。

#define HTTPS_KEY_FILE "key.pem"
#define HTTPS_CERT_FILE "cert.pem"

ローカルでHTTPSをテストするには、自己署名証明書(および秘密鍵)を生成します。

openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:2048
openssl req -new -x509 -key key.pem -out cert.pem -days 365

使い方

サーバーは docroot/ フォルダから静的コンテンツを配信します。これを変更するには、respond 関数を修正します。

typedef struct {
  Method method;
  string path;
  int major;
  int minor;
  int nheaders;
  Header headers[MAX_HEADERS];
  string content;
} Request;

void respond(Request request, ResponseBuilder *b) {
  if (request.major != 1 || request.minor > 1) {
    status_line(b, 505); // HTTP Version Not Supported
    return;
  }

  if (request.method != M_GET) {
    status_line(b, 405); // Method Not Allowed
    return;
  }

  if (string_match_case_insensitive(request.path, LIT("/hello"))) {
    status_line(b, 200);
    append_content_s(b, LIT("Hello, world!"));
    return;
  }

  if (serve_file_or_dir(b, LIT("/"), LIT("docroot/"), request.path, NULLSTR, false))
    return;

  status_line(b, 404);
  append_content_s(b, LIT("Nothing here :|"));
}

ここで request.path フィールドを切り替えてエンドポイントを追加できます。パスはリクエストバッファのスライスにすぎません。URIはパースされません。

テスト

サーバーは定期的に valgrind と sanitizers(address、undefined)で実行し、wrk を使って負荷をかけています。また、HTTP/1.1仕様への準拠を確認するために tests/test.py に自動テストを追加しています。自分のウェブサイトをホストし、あちこちに投稿して負荷を維持しています。インターネット上で脆弱なウェブサイトをスキャンするあらゆるボットが、優秀なファザーになります。

既知の問題

  • サーバーがHTTP/1.0クライアントにHTTP/1.1で応答する

コントリビュート

私は主に DEV ブランチで作業し、ときどき MAIN にマージしています。プルリクエストを開くときは DEV を対象にするとやりやすいでしょう。

GN⁺の要約

  • このプロジェクトは、最小限の依存関係と堅牢性を目指したウェブサーバーです。
  • HTTP/1.1 と HTTPS をサポートし、さまざまなログ機能と設定可能なタイムアウトを提供します。
  • ベンチマーク結果では nginx より高速な応答時間を示しています。
  • 開発者が自分のツールを作り、バグを修正する過程を楽しめるよう設計されています。
  • 類似機能を持つプロジェクトとしては Nginx や Apache HTTP Server があります。

1件のコメント

 
GN⁺ 2024-09-26
Hacker Newsのコメント
  • リバースプロキシは不要: Jetty を使ってリバースプロキシなしでアプリをインターネットにデプロイしても問題はなかった

    • セキュリティや性能に関する具体的な理由もなく、リバースプロキシを使うべきだという意見が多い
    • 本当にリバースプロキシが必要なのか疑問に思っている
  • 自作の C Web サーバー: 商用 Web サイトを運用していた C Web サーバーを作った

    • 128MB RAM と 1 CPU で大量のトラフィックを処理した
    • 20年前のインターネット環境は今ほど敵対的ではなかったと述べている
    • ボットは優れたファザーとして機能するが、実際のファジングも必要だ
  • サービス構築の満足感: システム API を使って基本的なサービスを構築するのは非常に満足感がある

    • poll() 関数が高い性能を示すことに驚いた
    • 接続ごとの関数と関連構造体、配列が nginx、redis、memcached と似ている
    • 素晴らしい仕事だ
  • 小さなプロジェクトの紹介: 余暇の時間に始めた面白いプロジェクトを紹介している

  • Kore フレームワークの推薦: C アプリを書くときに公開部分を自分で書くのが面倒なら、Kore フレームワークを勧める

    • ACME 証明書管理、Pgsql、curl、WebSocket などの機能が組み込まれている
    • Lua/Python と C を混在させてモジュールをビルドし、実行できる
  • 興味深いリンクの共有: sqlite.org の althttpd インスタンスは 1 日に 50 万件以上の HTTP リクエストを処理している

    • 月額 40 ドルの Linode で 200GB のコンテンツを配信している
    • HTTP リクエストの 19% は CGI 経由で Fossil ソースコードリポジトリにアクセスしている
  • 自作ツールを作る楽しさ: 何もかも「実戦テスト済み」でなければならないという意見にはうんざりしている

    • バグは修正できる
  • Chaos Communication Congress の講演: セキュリティ機能を備えた C 製のブログ / Web サーバーに関する講演を思い出させる

    • 不変ストレージ、権限縮小、TLS 証明書にアクセスできないことなどの機能が含まれている
  • 安定した Web サイト: トップページに表示されてもクラッシュしない Web サイト

  • 基本に立ち返る: 必要なものだけを使って基本に立ち返るアプローチが好きだ

    • ソフトウェアの不要な機能が性能に与える影響に疑問を持っている
    • 開発者に祝意を伝えている