AJPv13 拡張提案

はじめに

このドキュメントは、ajp13 としても知られる現在の Apache JServ プロトコルバージョン 1.3 の進化に関する提案です。ここではプロトコル全体ではなく、ajp13 からの追加機能のみを扱います。このn回目のパスには、tomcat-dev メーリングリストからのコメントと、開発中に発見された見落としが含まれています。

AJP13 で不足している機能

ajp13 は、Tomcat のようなサーブレットエンジンを Apache のようなWebサーバーにリンクするための優れたプロトコルです。

  • 各リクエストでの再接続時間を避けるために永続接続を使用する
  • ストリームサイズを削減するために多くのHTTPコマンドをエンコードする
  • Webサーバーからサーブレットエンジンに多くの情報(SSL証明書など)を送信する

しかし、ajp13 は以下のサポートが不足しています。

  • Webサーバーとサーブレットエンジン間のセキュリティ。誰でも ajp13 ポートに接続でき(ログインメカニズムは使用されない)、例えば telnet で接続し、データを送信しないことでリモートスレッドを維持できます(接続にタイムアウトがない)。
  • サーブレットエンジンからWebサーバーに渡されるコンテキスト情報。WebサーバーコネクタであるJKの設定の一部は、どのURIを処理するかをWebサーバーに指示することです。mod_jk の JkMount ディレクティブは、どのURIをサーブレットエンジンに転送すべきかをWebサーバーに伝えます。サーブレットエンジンはすでにどのURIを処理するかを知っており、TC 3.3 は利用可能なコンテキストのリストからJKの構成ファイルを生成できます。
  • サーブレットエンジンからWebサーバーへのコンテキストのステータス更新。ISP や仮想ホスティングサービスプロバイダーのような、大規模な Tomcat ファームを持つサイトでは、管理目的でコンテキストを停止する必要がある場合があります。その場合、フロントのWebサーバーはコンテキストが現在ダウンしていることを認識し、最終的にリクエストを別のTomcatに中継する必要があります。
  • リクエスト送信前の接続状態の検証。実際には、JK はリクエストをサーブレットエンジンに送信し、次にその応答を待ちます。しかし、ソケットAPIの利点の一つは、クローズされた接続に write() してもエラー報告がないことですが、クローズされた接続から read() するとエラーコードが返されることです。

AJP13 への提案される追加機能

ここでは、AJP13 に追加できる機能とアドオンについて説明します。このドキュメントは提案であるため、最初はある程度の混乱が予想されます。tomcat メーリングリストでの議論が、点を明確にし、機能を追加するのに役立つことは間違いありませんが、現在のリストは「最低限の必須項目」であると思われます。

  • 接続時の高度なログイン機能
  • Webサーバーとサーブレットエンジンに共通のシークレットキーが存在する、基本的な認証システム。
  • 将来的にAJP13に機能が追加されたとしても、現在の実装が引き続き動作することを確実にするための基本的なプロトコルネゴシエーション。
  • 「不明なパケット」の適切な処理
  • Webサーバーからサーブレットエンジンに渡される拡張環境変数。
  • Servlet 2.3 API で必要とされる追加の SSL 情報 (SSL_KEY_SIZE など) を追加

高度なログイン

  1. WEB-SERVER は LOGIN INIT CMD + NEGOCIATION DATA + WEB SERVER INFO を送信します
  2. TOMCAT は LOGIN SEED CMD + RANDOM DATA で応答します
  3. WEB-SERVER は RANDOM DATA + SECRET DATA の MD5 を計算します
  4. WEB-SERVER は LOGIN COMP CMD + MD5 (SECRET DATA + RANDOM DATA) を送信します
  5. TOMCAT は LOGIN STATUS CMD + NEGOCIED DATA + SERVLET ENGINE INFO で応答します
DOS攻撃を防ぐため、サーブレットエンジンは LOGIN CMD を 15/30 秒間のみ待ち、タイムアウト例外を管理者調査のために報告します。ログインコマンドには、圧縮能力、暗号化、コンテキスト情報(起動時)、実行時コンテキスト更新(アップ/ダウン)、SSL環境変数のレベル、サポートされるAJPプロトコルレベル(レベル1/レベル2/レベル3...)などの基本的なプロトコルネゴシエーション情報が含まれます。Webサーバー情報には、Webサーバー情報とコネクタ名(例:Apache 1.3.26 + mod_ssl 2.8.8 + mod_jk 1.2.41 + mod_perl 1.25)が含まれます。サーブレットエンジンは、自身のマスク(実行可能なこと)でネゴシエーションマスクを適用し、ログインが受け入れられたときにそれを返します。これにより、Webサーバー上の基本的なAJP13実装(レベル1)が、サーブレットエンジン側のより高度なプロトコルハンドラと連携したり、その逆の場合でも動作するのに役立ちます。AJP13は小さく高速に設計されているため、Webサーバーに存在する多くのSSL情報はサーブレットエンジンに転送されません。ここでは、クライアントSSLデータ(証明書)、サーバーSSLデータ、使用される暗号、その他のデータ(タイムアウトなど)に関する詳細情報を提供するために、4つのネゴシエーションフラグを追加します。

メッセージストリーム

+----------------+------------------+-----------------+
| LOGIN INIT CMD | NEGOCIATION DATA | WEB SERVER INFO |
+----------------+------------------+-----------------+

+----------------+----------------+
| LOGIN SEED CMD | MD5 of entropy |
+----------------+----------------+

+----------------+----------------------------+
| LOGIN COMP CMD | MD5 of RANDOM + SECRET KEY |
+----------------+----------------------------+

+-----------+---------------+---------------------+
| LOGOK CMD | NEGOCIED DATA | SERVLET ENGINE INFO |
+-----------+---------------+---------------------+

+------------+--------------+
| LOGNOK CMD | FAILURE CODE |
+------------+--------------+
  • LOGIN INIT CMD、LOGIN SEED CMD、LOGIN COMP CMD、LOGOK CMD、LOGNOK CMD は 1 バイト長です。
  • MD5、RANDOM + SECRET KEY の MD5 は 32 文字長です。
  • NEGOCIATION DATA、NEGOCIED DATA、FAILURE CODE は 32 ビット長です。
  • WEB SERVER INFO、SERVLET ENGINE INFO は CString です。
シークレットキーは workers.properties 内の新しいプロパティ: secretkey によって設定されます。
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.secretkey=myverysecretkey

シャットダウン機能

AJP13 は AJP12 の機能であるシャットダウンコマンドを欠いています。ログアウトはサーブレットエンジンに自身をシャットダウンするよう伝えます。

+--------------+----------------------------+
| SHUTDOWN CMD | MD5 of RANDOM + SECRET KEY |
+--------------+----------------------------+

+------------+
| SHUTOK CMD |
+------------+

+-------------+--------------+
| SHUTNOK CMD | FAILURE CODE |
+-------------+--------------+
  • SHUTDOWN CMD, SHUTOK CMD, SHUTNOK CMD は 1 バイト長です。
  • RANDOM + SECRET KEY の MD5 は 32 文字長です。
  • FAILURE CODE は 32 ビット長です。

拡張環境変数機能

注記: JK の AJP13 に取り組んでいる際に、私は「JkEnvVar」を実際に発見しました。以下の「拡張環境変数機能」の説明は、元の実装ですでに利用可能であるため、拡張 AJP13 では実装されない可能性があります。説明: 多くのユーザーは、Webサーバーの環境変数の一部がサーブレットエンジンに渡されることを望むでしょう。ネットワークトラフィックを削減するために、Webサーブレットは外部変数をより簡潔な方法で記述するテーブルを送信します。AJP13 ですでに存在する機能である属性リストを使用します。AJP13 では、

AJP13_FORWARD_REQUEST :=
    prefix_code      2
    method           (byte)
    protocol         (string)
    req_uri          (string)
    remote_addr      (string)
    remote_host      (string)
    server_name      (string)
    server_port      (integer)
    is_ssl           (boolean)
    num_headers      (integer)
    request_headers *(req_header_name req_header_value)

    ?context       (byte string)
    ?servlet_path  (byte string)
    ?remote_user   (byte string)
    ?auth_type     (byte string)
    ?query_string  (byte string)
    ?route         (byte string)
    ?ssl_cert      (byte string)
    ?ssl_cipher    (byte string)
    ?ssl_session   (byte string)

    ?attributes   *(attribute_name attribute_value)
    request_terminator (byte)
短い「Webサーバー属性名」を使用することで、ネットワークトラフィックが削減されます。
+-------------------+---------------------------+-------------------------------+----+
| EXTENDED VARS CMD | WEB SERVER ATTRIBUTE NAME | SERVLET ENGINE ATTRIBUTE NAME | ES |
+-------------------+---------------------------+-------------------------------+----+
JkExtVars S1 SSL_CLIENT_V_START javax.servlet.request.ssl_start_cert_date
JkExtVars S2 SSL_CLIENT_V_END   javax.servlet.request.ssl_end_cert_date
JkExtVars S3 SSL_SESSION_ID     javax.servlet.request.ssl_session_id


+-------------------+----+-------------------------------------------+
| EXTENDED VARS CMD | S1 | javax.servlet.request.ssl_start_cert_date |
+-------------------+----+-------------------------------------------+
+----+-----------------------------------------+
| S2 | javax.servlet.request.ssl_end_cert_date |
+----+-----------------------------------------+
+----+-----------------------------------------+
| S3 | javax.servlet.request.ssl_end_cert_date |
+----+-----------------------------------------+
拡張AJP13での送信中、S1、S2、S3を含む属性名と、2001/01/03、2002/01/03、0123AFE56 の属性値が表示されます。この例は拡張SSL変数の使用を示していますが、カスタム認証変数などの任意の「個人的な」Webサーバー変数をサーブレットエンジンで再利用できます。コストはAJPトラフィックでわずかなバイトが増えるだけです。
  • EXTENDED VARS CMD は 1 バイト長です。
  • WEB SERVER ATTRIBUTE NAME, SERVLET ENGINE ATTRIBUTE NAME は CString です。
  • ES は空の CString です。

サーブレットエンジンからWebサーバーへのコンテキスト情報の転送

ログオンフェーズの直後、Webサーバーはサーブレットエンジンが処理するコンテキストとURL/URIのリストを要求します。これにより、多くのサイトでのインストールが容易になり、tomcat-user メーリングリストでの設定に関する質問が減り、Servlet API 2.3 に対応できるようになります。このモードは、新しいディレクティブ JkAutoMount によってアクティブ化されます。例: JkAutoMount examples myworker1 /examples/ サーブレットエンジンによって処理されるすべてのコンテキストを取得したい場合は、ワイルドカードを使用できます。例: JkAutoMount * myworker1 * サーブレットエンジンは、/examples、/admin、/test など、多くのコンテキストを持つことができます。特定のワーカーに対して一部のコンテキストのみを使用したい場合があります。これは以前、例えば Apache HTTP Server では、Apache の各 [virtual] エリアでJkMountを手動で設定することによって行われていました。Webサーバーが仮想ホスティングをサポートしている場合、その情報もサーブレットエンジンに転送され、サーブレットエンジンはその仮想ホストのコンテキストのみを返します。その場合、サーブレットエンジンはこれらの特定の仮想サーバー(server.xml で定義)に一致するURL/URIのみを返します。この機能は、ロードバランシング構成で大規模なTomcatファームを共有するISPや大規模サイトに役立ちます。

+-----------------+-------------------+----------+----------+----+
| CONTEXT QRY CMD | VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
+-----------------+-------------------+----------+----------+----+

+------------------+-------------------+----------+-------------------+----------+---------------+----+
| CONTEXT INFO CMD | VIRTUAL HOST NAME | CONTEXTA | URL1 URL2 URL3 ES | CONTEXTB | URL1 URL2 ... | ES |
+------------------+-------------------+----------+-------------------+----------+---------------+----+
コンテキストクエリを通じて、リモートサーブレットエンジンが処理するURL/MIMESのリストを、コンテキストのリストに対して発見します。ワイルドカードモードでは、CONTEXTA は単に「*」を含みます。
  • CONTEXT QRY CMD と CONTEXT INFO CMD は 1 バイト長です。
  • VIRTUAL HOST NAME は CString、つまりヌルバイト (/0) で終端される文字配列です。
  • 空の文字列は単なるヌルバイト (/0) です。
  • ES は空の CString です。URI/URL の終わり、または CONTEXT の終わりを示します。

VirtualMode を使用しない場合、VIRTUAL HOST NAME は「*」です。その場合、サーブレットエンジンは処理するすべてのコンテキストを送信します。

サーブレットエンジンからWebサーバーへのコンテキスト情報の更新

コンテキストの更新は、コンテキストが非アクティブ化/再アクティブ化されるたびにサーブレットエンジンから送信されるメッセージです。更新は JkUpdateMount ディレクティブが使用されるときに有効になります。このディレクティブは AJP13_CONTEXT_UPDATE_NEG フラグを設定します。例: JkUpdateMount myworker1

+--------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT UPDATE CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+--------------------+-------------------+----------+--------+----------+--------+----+
  • CONTEXT UPDATE CMD, STATUS は 1 バイト長です。
  • VIRTUAL HOST NAME, CONTEXTS は CString です。
  • ES は空の CString です。CONTEXT の終わりを示します。

VirtualMode が使用されていない場合、VIRTUAL HOST NAME は「*」です。STATUS はコンテキストが UP/DOWN/INVALID のいずれかを示す 1 バイトです。

サーブレットエンジンへのコンテキストステータスクエリ

このクエリは、Webサーバーが指定されたコンテキストが UP、DOWN、または INVALID(そして削除されるべき)であるかを判断するために使用されます。

+-------------------+--------------------+----------+----------+----+
| CONTEXT STATE CMD |  VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
+-------------------+--------------------+----------+----------+----+

+-------------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT STATE REPLY CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+-------------------------+-------------------+----------+-------------------+--------+----+
  • CONTEXT STATE CMD, CONTEXT STATE REPLY CMD, STATUS は 1 バイト長です。
  • VIRTUAL HOST NAME, CONTEXTS は CString です
  • ES は空の CString です

VirtualMode が使用されていない場合、VIRTUAL HOST NAME は空の文字列です。

不明なパケットの処理

十分にネゴシエートされたプロトコルであっても、Webサーバーまたはサーブレットエンジンのいずれかの一方が理解できないメッセージを受信する状況になることがあります。その場合、受信側は未処理のメッセージを付加した「UNKNOW PACKET CMD」を送信します。

+--------------------+------------------------+-------------------+
| UNKNOWN PACKET CMD | UNHANDLED MESSAGE SIZE | UNHANDLED MESSAGE |
+--------------------+------------------------+-------------------+
メッセージに応じて、送信側はエラーを報告し、可能であればメッセージを別のエンドポイントに転送しようとします。
  • UNKNOWN PACKET CMD は 1 バイト長です。
  • UNHANDLED MESSAGE SIZE は 16 ビット長です。
  • UNHANDLED MESSAGE はバイト配列です (長さは UNHANDLED MESSAGE SIZE に含まれます)

UNHANDLED MESSAGE SIZE を追加 (開発)

リクエスト送信前の接続の検証

注記: この機能は、リクエストを転送する前にWebサーバー側で追加のI/O(読み取り)を必要とするため、通常のプロセスを遅くする可能性があるため、決して使用されないかもしれません.....ソケットAPIの利点の一つは、半閉じ状態のソケットに書き込めることです。サーブレットエンジンがソケットを閉じると、Webサーバーは次のソケットへの read() の時に初めてそれを発見します。基本的に、AJP13プロトコルでは、WebサーバーがHTTPヘッダーとHTTPボディ(8KBチャンクのPOST)をサーブレットエンジンに送信し、その後応答を受信しようとします。接続が切断されていた場合、Webサーバーは受信時にのみそれを知ることになります。バッファリングスキームを使用することもできますが、8KBを超えるデータでのアップロード操作にサーブレットエンジンを使用した場合、どうなりますか?AJP13プロトコルでのハックは、サービス終了後に読み取るバイトを追加することです。

EXAMPLE OF DISCUSSION BETWEEN WEB SERVER AND SERVLET ENGINE

AJP HTTP-HEADER (+ HTTP-POST)   (WEB->SERVLET)

AJP HTTP-REPLY					(SERVLET->WEB)

AJP END OF DISCUSSION			(SERVLET->WEB)
						
---> AJP STATUS 				(SERVLET->WEB AJP13)
AJP STATUS は、リクエスト/レスポンス #N の終了時ではなく、次のセッションの開始時にサーブレットエンジンによって読み取られます。さらにその際、WebサーバーはOS依存の関数(またはより良いAPR関数)を使用して、さらに読み取るデータがあるかどうかを判断することもできます。そしてそのデータは CONTEXT Updates である可能性があります。これにより、Webサーバーが非アクティブ化されたコンテキストにリクエストを送信することを回避できます。その場合、ロードバランシングが使用されていると、リクエストを処理するために別のサーブレットエンジンを検索します。そしてこの機能は、Tomcat ファームを持つISPや大規模サイトが、サービス中断なしにサーブレットエンジンを更新するのに役立ちます。
+------------+-------------+
| STATUS CMD | STATUS DATA |
+------------+-------------+
  • STATUS CMD と STATUS DATA は 1 バイト長です。

結論

拡張 AJP13 プロトコルの目標は、元の AJP13 の制限の一部を克服することです。より簡単な設定、大規模なサイトとTomcatファームへのより良いサポート、シンプルな認証システム、そしてプロトコル更新への備えを提供します。JK(ネイティブ)およびサーブレットエンジン(Java)における安定した ajp13 実装を使用することで、よく知られた ajp13 の合理的な進化と言えます。

拡張 AJP13 のコマンドとIDインデックス

AJP13 プロトコルに追加されるコマンドとIDのインデックス

コマンドID

コマンド名コマンド番号
AJP13_LOGINIT_CMD0x10
AJP13_LOGSEED_CMD0x11
AJP13_LOGCOMP_CMD0x12
AJP13_LOGOK_CMD0x13
AJP13_LOGNOK_CMD0x14
AJP13_CONTEXT_QRY_CMD0x15
AJP13_CONTEXT_INFO_CMD0x16
AJP13_CONTEXT_UPDATE_CMD0x17
AJP13_STATUS_CMD0x18
AJP13_SHUTDOWN_CMD0x19
AJP13_SHUTOK_CMD0x1A
AJP13_SHUTNOK_CMD0x1B
AJP13_CONTEXT_STATE_CMD0x1C
AJP13_CONTEXT_STATE_REP_CMD0x1D
AJP13_UNKNOW_PACKET_CMD0x1E

ネゴシエーションフラグ

コマンド名番号説明
AJP13_CONTEXT_INFO_NEG0x80000000Webサーバーはログイン後にコンテキスト情報を要求する
AJP13_CONTEXT_UPDATE_NEG0x40000000Webサーバーはコンテキストの更新を要求する
AJP13_GZIP_STREAM_NEG0x20000000Webサーバーは圧縮ストリームを要求する
AJP13_DES56_STREAM_NEG0x10000000Webサーバーはシークレットキーを使用した暗号化されたDES56ストリームを要求する
AJP13_SSL_VSERVER_NEG0x08000000サーバーSSL変数に関する拡張情報
AJP13_SSL_VCLIENT_NEG0x04000000クライアントSSL変数に関する拡張情報
AJP13_SSL_VCRYPTO_NEG0x02000000暗号化SSL変数に関する拡張情報
AJP13_SSL_VMISC_NEG0x01000000その他SSL変数に関する拡張情報

ネゴシエーションID番号説明
AJP13_PROTO_SUPPORT_AJPXX_NEG0x00FF0000サポートされるプロトコルのマスク
AJP13_PROTO_SUPPORT_AJP13L1_NEG0x00010000通信はAJP13レベル1を使用可能
AJP13_PROTO_SUPPORT_AJP13L2_NEG0x00020000通信はAJP13レベル2を使用可能
AJP13_PROTO_SUPPORT_AJP13L3_NEG0x00040000通信はAJP13レベル3を使用可能

その他のすべてのフラグは、将来のために予約されているため0に設定する必要があります。

失敗ID

失敗ID番号
AJP13_BAD_KEY_ERR0xFFFFFFFF
AJP13_ENGINE_DOWN_ERR0xFFFFFFFE
AJP13_RETRY_LATER_ERR0xFFFFFFFD
AJP13_SHUT_AUTHOR_FAILED_ERR0xFFFFFFFC

ステータス

失敗ID番号
AJP13_CONTEXT_DOWN0x01
AJP13_CONTEXT_UP0x02
AJP13_CONTEXT_OK0x03