AJPv13 扩展提案

简介

本文件是对当前 Apache JServ 协议版本 1.3(也称为 ajp13)演进的提案。我不会在此处介绍完整协议,只会介绍 ajp13 中的附加内容。此第 n 次通过包含了来自 tomcat-dev 列表的评论和在开发过程中发现的错误。

AJP13 中缺少的功能

ajp13 是将 servlet 引擎(如 tomcat)与 Web 服务器(如 Apache)连接起来的一个好协议

  • 使用持久连接以避免每次请求的重新连接时间
  • 对许多 http 命令进行编码以减小流大小
  • 将 Web 服务器的许多信息(如 SSL 证书)发送到 servlet 引擎

但 ajp13 缺少对以下内容的支持

  • Web 服务器和 servlet 引擎之间的安全性。任何人都可以连接到 ajp13 端口(不使用登录机制)。例如,你可以使用 telnet 连接并通过不发送任何数据(连接中没有超时)来保持远程线程处于活动状态
  • 从 servlet 引擎传递到 Web 服务器的上下文信息。JK(Web 服务器连接器)配置的一部分是向 Web 服务器指示要处理哪个 URI。mod_jk JkMount 指令告诉 Web 服务器必须将哪个 URI 转发到 servlet 引擎。servlet 引擎已经知道它处理哪个 URI,TC 3.3 已经能够根据可用上下文的列表为 JK 生成一个配置文件。
  • 从 servlet 引擎到 Web 服务器的上下文状态更新。拥有 Tomcat 集群的大型网站(如 ISP 和虚拟主机)可能需要出于管理目的而停止某个上下文。在这种情况下,前端 Web 服务器必须知道上下文当前已关闭,以便最终将请求中继到另一个 Tomcat
  • 在发送请求之前验证连接状态。实际上,JK 将请求发送到 servlet 引擎,然后等待答案。但套接字 API 的一个好处是,你可以向已关闭的连接写(),而不会出现任何错误报告,但向已关闭的连接读() 会返回错误代码。

AJP13 的建议附加内容

我们在此描述可添加到 AJP13 的功能和附加组件。由于此文档是一份提案,因此一开始可能会出现相当程度的混乱。请务必在 tomcat 列表中讨论以帮助澄清要点、添加功能,但当前列表似乎是“最低限度”

  • 连接时的高级登录功能
  • 基本授权系统,其中 Web 服务器和 Servlet 引擎中存在共享密钥
  • 基本协议协商,只是为了确保将来如果向 AJP13 添加功能,当前实现仍将有效
  • 干净地处理“未知数据包”
  • 从 Web 服务器传递到 Servlet 引擎的扩展环境变量
  • 添加 Servlet 2.3 API 所需的额外 SSL 信息(如 SSL_KEY_SIZE)

高级登录

  1. WEB 服务器发送 LOGIN INIT CMD + NEGOCIATION DATA + WEB SERVER INFO
  2. TOMCAT 响应 LOGIN SEED CMD + RANDOM DATA
  3. WEB 服务器计算 RANDOM DATA+SECRET DATA 的 MD5
  4. WEB 服务器发送 LOGIN COMP CMD + MD5 (SECRET DATA + RANDOM DATA)
  5. TOMCAT 响应 LOGIN STATUS CMD + NEGOCIED DATA + SERVLET ENGINE INFO
为了防止 DOS 攻击,Servlet 引擎将仅等待 15/30 秒的 LOGIN CMD,并向管理员调查报告超时异常。登录命令将包含基本协议协商信息,如压缩能力、加密、上下文信息(在启动时)、运行时上下文更新(上/下)、SSL 环境变量级别、支持的 AJP 协议级别(level1/level2/level3...)。Web 服务器信息将包含 Web 服务器信息和连接器名称(即 Apache 1.3.26 + mod_ssl 2.8.8 + mod_jk 1.2.41 + mod_perl 1.25)。Servlet 引擎将使用它自己的掩码(它可以做什么)对协商掩码进行掩码,并在接受登录时返回它。这将有助于在 Web 服务器上实现基本 AJP13 实现(级别 1),该服务器与 Servlet 引擎侧的更高级协议处理程序一起工作,反之亦然。AJP13 被设计为小巧且快速,因此 Web 服务器中存在的许多 SSL 信息不会转发到 Servlet 引擎。我们在此添加四个协商标志,以提供有关客户端 SSL 数据(证书)、服务器 SSL 数据、使用的加密和杂项数据(超时...)的更多信息。

消息流

+----------------+------------------+-----------------+
| 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 个字符。
  • 协商数据、协商数据、故障代码长度为 32 位。
  • Web 服务器信息、Servlet 引擎信息为 CString。
将通过 workers.properties 中的新属性 secretkey 设置密钥
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.secretkey=myverysecretkey

关闭功能

AJP13 缺少 AJP12 的一项功能,即关闭命令。注销将告知 servlet 引擎关闭自身。

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

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

+-------------+--------------+
| SHUTNOK CMD | FAILURE CODE |
+-------------+--------------+
  • 关闭命令、关闭确认命令、关闭拒绝命令长度为 1 字节。
  • 随机数 + 密钥的 MD5 长度为 32 个字符。
  • 故障代码长度为 32 位。

扩展的环境变量功能

备注:在 JK 中处理 AJP13 时,我确实发现了“JkEnvVar”。以下“扩展的环境变量功能”说明可能不会在扩展的 AJP13 中实现,因为原始实现中已提供。说明:许多用户希望将一些 Web 服务器环境变量传递给他们的 servlet 引擎。为了减少网络流量,Web Servlet 将发送一个表,以较短的方式描述外部变量。我们将在其中使用 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 服务器变量(如自定义身份验证变量)都可以在 servlet 引擎中重复使用。代价只是 AJP 流量中增加一些字节。
  • 扩展变量命令长度为 1 字节。
  • Web 服务器属性名称、Servlet 引擎属性名称为 CString。
  • ES 是一个空的 CString。

将上下文信息转发给 Web 服务器的 Servlet 引擎

在登录阶段之后,Web 服务器将请求 servlet 引擎处理的上下文和 URL/URI 列表。这将简化许多网站的安装,减少对 tomcat 用户列表中配置的疑问,并为 servlet API 2.3 做好准备。此模式将通过一个新的指令 JkAutoMount 激活,即:JkAutoMount examples myworker1 /examples/ 如果我们想要获取 servlet 引擎处理的所有上下文,可以使用通配符:即:JkAutoMount * myworker1 * 一个 servlet 引擎可以有许多上下文,如 /examples、/admin、/test。我们可能只想对给定的工作程序使用一些上下文。以前在 Apache HTTP Server 中通过在 Apache 的每个[虚拟]区域中手动设置 JkMount 来完成此操作。如果您的 Web 服务器支持虚拟主机,我们还将把该信息转发给 servlet 引擎,它将仅返回该虚拟主机的上下文。在这种情况下,servlet 引擎将仅返回与特定虚拟服务器(在 server.xml 中定义)匹配的 URL/URI。此功能将帮助 ISP 和大型网站在负载平衡配置中相互利用大量的 Tomcat 服务器场。

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

+------------------+-------------------+----------+-------------------+----------+---------------+----+
| CONTEXT INFO CMD | VIRTUAL HOST NAME | CONTEXTA | URL1 URL2 URL3 ES | CONTEXTB | URL1 URL2 ... | ES |
+------------------+-------------------+----------+-------------------+----------+---------------+----+
我们将通过上下文查询了解到远程 servlet 引擎处理的 URL/MIME 列表,以获取上下文列表。在通配符模式中,CONTEXTA 将仅包含“*”。
  • 上下文查询命令和上下文信息命令长度为 1 字节。
  • 虚拟主机名称为 CString,即以空字节(/0)结尾的字符数组。
  • 空字符串只是一个空字节 (/0)。
  • ES 是一个空 CString。表示 URI/URL 的结尾或 CONTEXT 的结尾。
注意
当不使用 VirtualMode 时,虚拟主机名是“*”。在这种情况下,servlet 引擎将发送处理的所有上下文。

从 Servlet 引擎到 Web 服务器的上下文信息更新

上下文更新是每次上下文停用/重新激活时从 servlet 引擎发出的消息。当指令 JkUpdateMount 使用时,将使用更新。此指令将设置 AJP13_CONTEXT_UPDATE_NEG 标志。例如:JkUpdateMount myworker1

+--------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT UPDATE CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+--------------------+-------------------+----------+--------+----------+--------+----+
  • CONTEXT UPDATE CMD、STATUS 的长度为 1 个字节。
  • 虚拟主机名、CONTEXT 是 CString。
  • ES 是一个空 CString。表示 CONTEXT 的结尾。
注意
当不使用 VirtualMode 时,虚拟主机名是“*”。STATUS 是一个字节,表示上下文是 UP/DOWN/INVALID

对 Servlet 引擎的上下文状态查询

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 个字节。
  • 虚拟主机名、CONTEXT 是 CString
  • ES 是一个空 CString
注意
当不使用 VirtualMode 时,虚拟主机名是一个空字符串。

处理未知数据包

有时,即使使用协商良好的协议,我们也可能遇到一端(Web 服务器或 servlet 引擎)收到无法理解的消息的情况。在这种情况下,接收方将发送“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 服务器端进行额外的 IO(读取)......套接字 API 的一个优点是,您可以在半关闭的套接字上写入。当 servlet 引擎关闭套接字时,Web 服务器只会在下一次对套接字进行 read() 时发现它。基本上,在 AJP13 协议中,Web 服务器将 HTTP 标头和 HTTP 正文(按 8K 块 POST)发送到 servlet 引擎,然后尝试接收答复。如果连接断开,Web 服务器只会收到时间才了解到。我们可以使用缓冲方案,但是当您使用 servlet 引擎进行超过 8ko 数据的上传操作时会发生什么?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)
请求/响应 #N 结束后,servlet 引擎不会读取 AJP 状态,而是在下一个会话开始时读取。此时,web 服务器还可以使用 OS 相关函数(或更好的 APR 函数)来确定是否还有更多数据要读取。这些数据可能是上下文更新。这将避免 web 服务器向已停用的上下文发送请求。在这种情况下,如果使用负载平衡,它将搜索另一个 servlet 引擎来处理请求。此功能将帮助 ISP 和拥有 tomcat 集群的大型站点在不中断服务的情况下更新其 servlet 引擎。
+------------+-------------+
| STATUS CMD | STATUS DATA |
+------------+-------------+
  • STATUS CMD 和 STATUS DATA 的长度为一个字节。

结论

扩展 AJP13 协议的目标是克服原始 AJP13 的一些限制。更简单的配置、对大型站点和 Tomcat 集群的更好支持、简单的身份验证系统以及协议更新的准备。使用 JK(本机)和 servlet 引擎(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