ディスカッション (11件)
FastCGIが登場してはや30年。未だにレガシーだなんて思っていませんか?実はリバースプロキシのプロトコルとして、現代の環境においても非常に強力で、効率的な選択肢であり続けています。なぜこの枯れた技術が今もなお「最強」なのか、その理由を改めて深掘りします。
興味深いですね。リバースプロキシ関連の作業はこれまでずっとシンプルにNginxの標準機能だけで済ませてきたので、もっと手の込んだことをやるためにFastCGIを使うなんて発想は全くありませんでした。10年ほど前に、書いたC++コードをWeb上で動かすためにFastCGIを少し使ったことはありますが、正直それ以来ほとんど触っていません。
この記事は、何が書かれていないかという点でも非常に興味深いです。かつてのFastCGI対SCGI対HTTPの大論争を思い出しますね。当時私はWeb2.0スタートアップの立ち上げに関わっていて、フロントエンドのスタック構築を任されていました。HTTPが勝った理由はシンプルさです。わざわざ「別の」プロトコルをスタックに導入しなくても、ゲートウェイで既に扱っているHTTPを使えばいいだけですから。今となっては、複雑なネットワークトポロジーも簡単に組めます。負荷が足りなければリバースプロキシを多段構成にできるし、認証、セッション管理、SSL終端、DDoSフィルタリングといった横断的な処理を専門にするサーバーを、リクエストチェーン上の位置を意識させることなく配置できます。さらに、開発環境ではHTTPで直接アプリサーバーに繋ぎ、本番環境ではSSLや認証、攻撃検知を担うリバースプロキシ越しに動かすという同じ構成も使えます。当時、Nginxの方がFastCGI/SCGIモジュールより遥かに高速で堅牢だったことも追い風でした。最初はHTTP -> Lighttpd -> FastCGI -> Djangoという構成を試しましたが、Nginx単体で動かすよりずっと遅かったです。HTTPを採用することは、TCP/IPにおける「エンドツーエンド原則」のWeb版のようなものでした。ネットワークやそのプロトコルは通信内容を問わず、アプリ側の論理はデータをフィルタリングしたり転送したりするネットワークの「ノード」に持たせるという考え方です。これは極めて強力な原則であり、簡単に捨てるべきではありません。記事が指摘しているのは「セキュリティを考えるなら、情報を闇雲に流すよりも『最小権限の原則』に従う方が良い場合が多い」ということです。通信を予期した範囲だけに絞ることで、意図せずネットワークのどこかの脆弱性を助長することを防げます。この記事は、この二つの原則の間の緊張関係を暗に示しています。E2Eは柔軟性をもたらしますが、その柔軟性を悪用されるリスクもあります。一方、最小権限の原則はセキュリティを高めますが、システムを柔軟に設計できず、新しい要件への対応が難しくなるというコストを伴います。
Red Hat系で配布されているPHP/Apacheの設定は「FastCGI Process Manager」(FPM)ですね。RHELディストリビューションの他の部分でFastCGIが使われているかは分かりませんが。 $ rpm -qi php-fpm | grep ^Summary Summary : PHP FastCGI Process Manager
記事の内容には同意します。こうした用途ではFastCGIの方がHTTPより優れていますね。ただ、別のプロトコルも紹介させてください。「Web Application Socket (WAS)」というものです。FastCGIでは不十分だと感じて、16年前に仕事で設計しました。メインソケット上でフレーム内にデータを詰め込むのではなく、WASはコントロールソケットと2本のパイプ(生のリクエスト/レスポンスボディ)を使います。WASアプリとWebサーバーの両方がsplice()を使ってパイプ上で処理を行えるため、フレーム化が不要です。リクエストのキャンセルも可能で、3つのファイル記述子はいつでも復旧できます。長年社内の多くのアプリでWASを使ってきましたし、Webホスティング環境向けにPHPのSAPIも書きました。かなりの数のWebサイトが内部でWASで動いています。すべてオープンソースです。ライブラリ: https://github.com/CM4all/libwas ドキュメント: https://libwas.readthedocs.io/en/latest/ ノンブロッキングライブラリ: https://github.com/CM4all/libcommon/tree/master/src/was/async 我々のWebサーバー: https://github.com/CM4all/beng-proxy WebDAV: https://github.com/CM4all/davos WAS SAPI付きPHPフォーク: https://github.com/CM4all/php-src
ユーザーが自分たちのプラットフォームでカスタムページを「なんとなくコーディング(vibe code)」する手段として、古き良きCGIを再発見しました。うちには標準のタスクリストやデータビューアがありますが、ユーザーはもっと細かくカスタマイズしたがるんです。例えばカンバンビューや、フィルタとグラフ付きのカスタムダッシュボードを作りたいとか。環境にはコーディングエージェントがいるので、従来のレポート作成ツールを自作する代わりに、ユーザーが自分で何でも書けるようになっています。Goの標準ライブラリはサーバーサイドでもユーザースペースでもCGIをうまくサポートしています。エージェントがCGIと会話するpage-name/main.goを生成し、サーバーがリクエストをそこに委譲する形です。「個人レベル」のデータやページビューなら、FastCGIで最適化するまでもないですしね。エージェント時代には、古い技術がまた新しいんです。
「レガシー技術を使うことには欠点がある。WebSocketをサポートするアップデートがされなかった」とありますが、WHATWG Streamsがブラウザで広くサポートされている今、長寿命のHTTPリクエスト上で自前のWebSocketを実装するのはかなり簡単です。基本的にバイトストリームを送って、各メッセージの先頭にヘッダー(多くの場合サイズ情報)を付けるだけです。WebSocketに対する利点は以下の通りです。・サーバー層にWebSocketのような特別なパスが不要。・バックプレッシャー(背圧)制御ができる。・HTTP/2/3の改善をそのまま恩恵として受けられる。・フレームのオーバーヘッドが少ない。ただ残念なことに、リクエストボディをストリーミング中にレスポンスを受け取ることはできないはずなので、双方向フルストリーミングを実現するにはリクエストをペアにする必要があります。
FCGIはオーケストレーションシステムでもあります。負荷が上がればサーバータスクを増やし、下がれば終了させ、クラッシュすれば新しいコピーを起動する。単一システム上で動くKubernetesのようなものですね。
(u)WSGIについても触れておくべきでは?
議論の余地は十分あると思いますが、FastCGIはPATH_INFOなどをCGI/1.1に依存しているのがネックです。これはURLデコードされてしまうため、エンコードされたスラッシュ(%2F)を扱えないという欠損があります(一部の実装では//を/に潰すこともありますが、これはHTTPの実装全般の問題でもあります)。アプリによっては重要ではないかもしれませんが、HTTPと比べると表現力は落ちます。私は正確なURL処理ができる方が好みですね。
「True-Client-IPが存在しない場合のみX-Real-IPを使う」という仕様だと、プロキシ側でX-Real-IPを正しく処理していても、攻撃者がTrue-Client-IPヘッダーを送るだけで乗っ取られてしまいますよね。HTTPヘッダーの馬鹿らしさについて少し語りませんか?X-Forwarded-ForにX-Real-IP、各CDN独自のカスタムヘッダー。どれもカンマ区切りのリストで、最後には自分のロードバランサーのIPが役に立たない形で追加されている(理由は分かりますが、無意味です)。これらはすべて悪意あるユーザーエージェントによって挿入される可能性があります。結局、パイプライン内の信頼されたサーバー間で、どうやって重要な情報を伝えるか誰も合意できなかったんでしょうね。User-Agentヘッダーの馬鹿らしさとも通じるものがあります。今やAppleが「プライバシー」の名の下に完全に嘘のOSバージョンを送りつけるなど、形骸化の極みに達していますし。