27/ZAP
ZeroMQ 认证协议
- 状态:稳定
- 编辑:Pieter Hintjens ph@imatix.com
本文档规定了 ZAP,即 ZeroMQ 认证协议。ZAP 的用例是一组需要对远程客户端进行认证的服务器,这些服务器与一个检查客户端凭据的处理器进行通信。ZAP 定义了服务器如何连接到处理器以及它们之间交换的消息。
另请参见:https://rfc.zeromq.cn/spec:23/ZMTP, https://rfc.zeromq.cn/spec:24/ZMTP-NULL, https://rfc.zeromq.cn/spec:24/ZMTP-PLAIN, https://rfc.zeromq.cn/spec:25/ZMTP-CURVE, https://rfc.zeromq.cn/spec:26/CURVEZMQ。
前言
版权所有 (c) 2013 iMatix Corporation。
本规范是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证(第三版或您选择的任何更高版本)的条款重新分发和/或修改它。发布本规范是希望它有用,但**不提供任何担保**;甚至不包括适销性或特定用途适用性的默示担保。更多详情请参见 GNU 通用公共许可证。您应该已经收到本程序附带的 GNU 通用公共许可证副本;如果没有,请参见 https://gnu.ac.cn/licenses。
本规范是一个 自由开放的标准,受数字标准组织 共识导向规范系统 的管辖。
本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按 RFC 2119 中的描述解释。
总体设计
ZAP 解决了哪些问题?
当服务器需要对客户端进行认证时,它必须在自己进行认证或调用外部方来完成这项工作之间做出选择。虽然内置认证易于理解和构建,但它在许多方面受到限制:
- 无法轻易扩展到任意安全机制。
- 无法与外部认证系统集成。
- 倾向于定制设计,导致每个产品都有自己的方法。
对于安全管理员来说,理想的设计是所有应用程序都调用同一个中央凭据存储库。问题就变成了,“我们如何标准化这种交互?”
与所有分布式设计一样,ZeroMQ 通过定义一个可在任何语言和任何平台使用的通用基于消息的传输层,已经提供了大部分答案。答案的第二部分是构建在 ZeroMQ 之上的简单协议。
ZAP 就是这样一个协议,专门用于认证。
减少组件数量
构建分布式应用程序的一个不良副作用是组件数量增加。这使得构建易于安装和使用的产品变得更加困难。如果我们天真地将 ZAP 设计为使用 TCP 传输和外部处理器,即使对于最简单的应用程序,我们最终也将不得不安装和运行额外的服务。
因此,ZAP 使用了*进程内桥接*设计。也就是说,ZAP 本身要求处理器作为线程与服务器在同一个进程内运行。当应用程序使用多个进程时,每个进程都将包含一个 ZAP 处理器。
如果架构需要,ZAP 处理器可以进一步与更多组件通信。ZAP 没有规定这种对话,但我们假定 ZAP 消息可以按原样转发,并为回复添加路由信息。
ZAP 是一种远程过程调用
ZAP 使用 ZeroMQ 的请求-回复模式。具体来说,处理器绑定一个 REP socket,服务器连接一个 REQ socket。实际上,服务器可以使用 DEALER socket 以一次发送多个请求,处理器也可以出于同样的原因使用 ROUTER socket。此外,ZAP 消息包含请求-回复地址信封,以便请求可以通过多个中介发送,并且回复将返回到正确的服务器。
可扩展的安全性
ZAP 对我们使用的安全机制及其工作方式不做任何假设。认证请求以机制名称(一个 ASCII 字符串)开始,后跟凭据(一个不透明的二进制数据块,其意义仅由安全机制知晓)。此外,服务器指定一个*域*(一个 ASCII 字符串),处理器可以使用它来实现访问控制。认证器回复 OK/NOT OK 响应,并提供一个服务器可以用来向内部应用程序代码标识用户的用户 ID。
ZMTP 3.0 兼容性
ZAP 旨在支持 https://rfc.zeromq.cn/spec:23/ZMTP 的实现(即,提供一种实现 ZMTP 3.0 认证的方式),尽管它不仅限于此用例。本文档规定了 ZMTP NULL、PLAIN 和 CURVE 机制如何通过 ZAP 实现。
消息格式和版本检测
ZAP 对其消息使用一种简单的多帧格式,其中版本号位于第一帧。这旨在使其在任意语言中实现起来最容易。
正式规范
架构
ZAP 定义了一个基于消息的对话,介于发起认证请求的*服务器*和响应请求的*处理器*之间。服务器和处理器**必须**使用以下 socket 类型和传输方式进行通信:
- 服务器**必须**使用 REQ 或 DEALER socket。
- 处理器**必须**使用 REP 或 ROUTER socket。
- 处理器**必须**绑定到端点 “inproc://zeromq.zap.01”。
- 服务器**必须**连接到此端点。
- 每个进程中**必须**有一个处理器。
- 每个进程中**可以**有任意数量的服务器。
- 处理器**必须**在任何服务器启动之前启动。
消息格式
认证对话**必须**包括服务器向处理器发送的一个请求以及处理器向服务器返回的一个回复。
请求消息**必须**包括以下消息帧:
- 一个地址分隔符帧,其长度**必须**为零。
- 版本帧,其**必须**包含三个字节 “1.0”。
- _请求 ID_,其中**可以**包含一个不透明的二进制数据块。
- _域_,其**必须**包含一个(非空)字符串。
- _地址_,源网络 IP 地址。
- _身份_,连接身份(如果存在)。
- _机制_,其**必须**包含一个字符串。
- _凭据_,其**必须**是零个或多个不透明帧。
回复消息**必须**包括以下消息帧:
- 一个地址分隔符帧,其长度**必须**为零。
- 版本帧,其**必须**包含三个字节 “1.0”。
- _请求 ID_,其中**可以**包含一个不透明的二进制数据块。
- _状态码_,其**必须**包含一个字符串。
- _状态文本_,其中**可以**包含一个字符串。
- _用户 ID_,其**必须**包含一个字符串。
- _元数据_,其中**可以**包含一个数据块(blob)。
各字段的含义如下:
- request id: 其含义仅由发送服务器定义。回复**必须**原样回显 request id,不得修改。
- domain: 这请求在某个域内进行认证。域的意义是一个应用程序问题,与 ZAP 无关。
- address: 这提供了客户端的 IP 地址,以便进行基于地址的过滤。它**必须**是一个 IPv4 点分字符串,或一个 IPv6 标准字符串表示形式。
- identity: 这提供了连接提供的 Identity 元数据属性(如果存在)。它**必须**是一个二进制字符串,长度不超过 255 字节。
- mechanism: 这指定了用于认证的安全机制。该机制**不得**为空。
- credentials: 这些提供了用于认证的用户凭据。所需帧的数量由各机制定义。
- status code: 这**应**为 “200” 表示成功,“300” 表示临时错误,“400” 表示认证失败,“500” 表示内部错误(系统故障)。
- status text: 这**应**是一个进一步解释状态的文本,并且**可以**为空。
- user id: 在状态为 200 时,这**可以**提供用户身份供应用程序使用。对于其他状态,它**必须**为空。
- metadata: 在状态为 200 时,这提供要返回给客户端的元数据。元数据**必须**使用 ZMTP 3.0 格式:
metadata = *property
property = name value
name = OCTET 1*name-char
name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
value = 4OCTET *OCTET ; Size in network byte order
所有字符串字段最多 255 个字符,并且仅限 ASCII。
Socket 类型
ZeroMQ 的 REQ 和 REP socket 自动管理地址信封,而 DEALER 和 ROUTER socket 将这些暴露给调用应用程序,应用程序必须显式管理它们。
- 使用 REQ socket 时,服务器**不得**发送或接收地址分隔符。
- 使用 REP socket 时,处理器**不得**发送或接收地址分隔符。
- 使用 ROUTER socket 时,处理器**必须**接收一个额外的帧,指示发送请求的对等方的路由 ID。处理器**必须**在其回复消息的开头发送此路由 ID 帧。
代理处理器
处理器**可以**充当其他处理器的代理。这就是我们使用 ZAP 连接外部认证处理器的方式。在这种情况下,处理器将连接或绑定到一个tcp://端点,而外部处理器将绑定或连接到该端点。
在代理架构中,处理器**应该**转发 ZAP 消息,除了以下情况外不做任何修改:
- 处理器**必须**使用 ROUTER socket 与服务器通信。
- 处理器**必须**将整个接收到的消息(包括第一个路由 ID 帧)发送给外部处理器。
更一般地说,处理自身与服务器之间未知数量中介的终端(非代理)处理器**必须**接受一个地址信封,该信封由 N 个路由 ID 帧后跟一个空地址分隔符帧组成。它在处理请求之前**必须**保存此信封,并且**必须**在回复时将相同的信封发送回去。这就是 REP socket 的作用,因此通常终端处理器将使用 REP socket。
NULL 机制
NULL 机制不提供安全凭据,但允许服务器根据 IP 地址过滤恶意客户端。
要对 NULL 机制执行过滤:
- 机制**必须**是 4 个字符 “NULL”。
- **不得**有凭据帧。
PLAIN 机制
PLAIN 机制为 ZMTP v3.0 定义了一个简单的用户名/密码机制,允许服务器认证客户端。PLAIN 不尝试提供安全或机密性。它适用于安全要求较低的内部网络。
要对 PLAIN 机制执行认证:
- 机制**必须**是 5 个字符 “PLAIN”。
- 凭据**必须**包括两个字符串帧:一个用户名和一个密码。
CURVE 机制
CURVE 机制为 ZMTP v3.0 提供了安全的认证和机密性。此机制实现了 https://rfc.zeromq.cn/spec:26/CURVEZMQ 安全机制。它适用于安全要求较高的公共网络。
要对 CURVE 机制执行认证:
- 机制**必须**是 5 个字符 “CURVE”。
- 凭据**必须**包括被认证对等方的 32 字节长期公钥。
发现
ZAP 没有定义如何在网络上发现处理器。一些可行选项包括硬编码的 TCP 端点或信标发现(由 https://rfc.zeromq.cn/spec:20/ZRE) 实现)。
示例交互
这些示例显示了*在线路上发送*的消息。应用程序接收或发送的消息(特别是对于 REQ、REP 或 ROUTER socket)开头将有不同的地址信封。我们不显示帧大小或标志,仅显示帧内容。
NULL 认证示例
这显示了服务器向处理器发送的认证请求,用于使用 NULL 机制的客户端:
+-+
| | Empty delimiter frame
+-+---+
| 1.0 | Version number
+-----++
| 0001 | Request ID, for example "0001"
+------+
| test | Domain
+------+-------+
| 192.168.55.1 | Address
+------+-------+
| BOB | Identity property
+------+
| NULL | Mechanism
+------+
如果服务器应用程序使用 REQ socket,它**不得**发送分隔符帧。如果它使用 DEALER socket,它**必须**发送分隔符帧。
这显示了处理器向服务器发送的回复示例:
+-+
| | Empty delimiter frame
+-+---+
| 1.0 | Version number
+-----++
| 0001 | Request ID echoed from request
+-----++
| 200 | Status code
+----++
| OK | Status text
+----++
| joe | User id
+-+---+
| | Metadata, empty
+-+
如果服务器应用程序使用 REP socket,它**不得**接收或发送分隔符帧。如果它使用 ROUTER socket,它**必须**接收和发送分隔符帧,并且**还应**在分隔符帧之前接收和发送一个路由 ID 帧。
PLAIN 认证示例
这显示了服务器向处理器发送的认证请求,用于使用 PLAIN 机制的客户端:
+-+
| | Empty delimiter frame
+-+---+
| 1.0 | Version number
+-----++
| 0001 | Request ID, for example "0001"
+------+
| test | Domain
+------+-------+
| 192.168.55.1 | Address
+-------+------+
| BOB | Identity property
+-------+
| PLAIN | Mechanism
+-------+
| admin | Username
+-------++
| secret | Password
+--------+
这显示了处理器向服务器发送的回复示例:
+-+
| | Empty delimiter frame
+-+---+
| 1.0 | Version number
+-----++
| 0001 | Request ID echoed from request
+-----++
| 200 | Status code
+----++
| OK | Status text
+----++
| joe | User id
+-+---+
| | Metadata, empty
+-+
参考实现
在 RFC 存储库中提供了最小的空参考实现,地址为 https://github.com/zeromq/rfc/blob/master/src/spec_27.c。此实现展示了服务器通过inproc://与代理处理器通信,并通过 TCP 与外部终端处理器通信。终端处理器实现了 PLAIN 认证机制。