跳到主要内容

出站连接器

出站连接器让检测规则能够作用于外部世界。规则触发时,其动作可以调用 webhook,或向消息代理/云队列发布消息。DeviceChain 由此将处理后的事件分发到现有系统,例如事件处理工具、数据流水线或其他应用的消息总线。检测只是自动化的一半,出站动作是另一半。

专用的 outbound-connectors 服务负责投递,有意与检测引擎分开。缓慢或异常的外部端点可以拖慢自身投递,但绝不会拖慢规则评估。

此服务需要显式启用

outbound-connectors 随 full 部署配置档提供,不包含在 default 中,因此未指定配置档创建的实例不会运行它。控制台仍显示**连接器(Connectors)**区域;页面会说明实例未运行该功能区域,而不是让人以为功能不存在。

本页所有内容都适用于该区域已部署的情况。要添加它,请使用 full 配置档启动实例。

状态

目前可用:httpCall webhook 动作,以及通过租户作用域、支持版本管理的连接器向 MQTT、Apache Kafka、AWS SNS 和 AWS SQS 投递的 publish 动作;连接器凭据保存在加密密钥存储中。两者都可以在自动化画布上配置为动作节点。gcp_pubsub 是可识别的类型,但目前无法创建(见下文)。

**计划中:**更多 publish 目标(RabbitMQ、Azure、NATS、Redis、Slack、Splunk),采用同一模型。本仓库是判断当前哪些功能能够构建的权威依据。

两种出站动作​

两者都是在自动化画布上编写的动作,与触发告警和发送命令并列。每个动作都可以根据触发结果的条件设置守卫条件。

表单构建器的动作选择器只提供触发告警和发送命令。如果规则已有出站动作,表单会以只读方式显示并保留它,因此在画布与表单之间切换不会丢失该动作。

httpCall:调用 webhook​

httpCall 直接向指定端点发送 HTTP 请求。你通过针对触发结果的 CEL 表达式组织请求体,只发送接收方需要的字段。URL、方法、标头和请求体模板都保存在动作上,因此单次 webhook 无需单独设置。

认证可选,使用密钥句柄。令牌保存在密钥存储中,发送时作为 Authorization: Bearer <token> 标头提供。标头名称和认证方案不可配置,因此这种方式不能用于要求自定义 API 密钥标头的接收方。如果动作指定的句柄下没有密钥,调用绝不会在缺少凭据的情况下发出:它会以 invalid 结果写入一次死信,不再重试。没有机制重放它,因此该次触发的调用不会执行;请修正动作的密钥句柄,让后续触发正常认证。

webhook 投递通过以下方式加固:

  • 拒绝跟随重定向,外部端点不能通过 3xx 将请求转到其他位置。
  • 只允许 http/https 目标,拒绝 URL 内嵌凭据。
  • 移除保留标头和平台标头,租户提供的标头不能伪造认证标头或内部服务身份。
  • 按传输格式验证所有标头名称和值,禁止标头注入所需的 CR/LF。
  • 附带密钥时,不将响应体回显到日志,防止恶意端点把凭据反射到日志中。

publish:发送到连接器​

对于消息代理和云队列,目标是可复用的连接器(见下文),而不是内联配置。你选择已注册连接器,并在 CEL 中组织消息载荷。连接器携带目标和加密封装的凭据。只需配置一次,即可在任意多个规则中复用,凭据绝不出现在规则中。

一个通用 publish 动作覆盖所有消息代理和队列类型;连接器类型选择传输方式。目前支持 mqtt、kafka、aws_sns 和 aws_sqs。

gcp_pubsub 是可识别的类型,但本版本没有投递客户端,因此创建、更新为该类型或发布该类型的连接器都会被拒绝,错误代码为 UNSUPPORTED。控制台不提供该类型。早期版本已存储的该类型连接器,如果有 publish 指向它,仍会因不受支持而进入死信,不会被无声丢弃。

连接器是支持版本管理的资源​

连接器是租户作用域资源,生命周期与设备配置文件或仪表板相同。你编辑草稿、发布不可变版本;变更出现问题时,可回滚到旧版本。连接器保存:

  • 类型(mqtt、kafka、aws_sns、aws_sqs);
  • 对应类型的目标配置:消息代理地址、主题、队列或 ARN,以及 QoS、TLS 等选项;
  • 可选凭据,通过句柄引用。值写入密钥存储,绝不返回明文,与通知渠道密钥完全相同。

由于连接器属于租户,一个租户绝不会看到或使用另一个租户的连接器发送。

投递如何工作​

受守卫条件控制的 publish 或 httpCall 动作触发时,检测引擎不自行执行出站调用,而是通过内部消息总线,将派发请求(已解析动作和幂等键)交给 outbound-connectors 服务,然后继续检测。派发请求是持久化的:连接器服务重启后,请求仍然存在,并在恢复后投递。

两个属性保障安全:

  • 发出后继续处理,载荷精确可控。 出站动作不会阻塞规则等待回复。载荷只能通过 CEL 组织,投递路径没有任意脚本,因此规则能够发送的内容有限且可审查。
  • 每次派发都有幂等键。 派发携带由触发结果计算的、基于内容的幂等键:相同检测和动作在每次重新投递和重放中产生同一键。投递语义是至少一次。检测引擎在大约十分钟内再次提交相同派发时(重试,或重启时仍在处理的检测),消息总线只存一次;连接器服务需要重试的派发,或长时间故障后重放的派发,仍可能发送两次。键会转发给接收方,使其能够识别并丢弃重复项:webhook 使用 X-DC-Idempotency-Key 标头,Kafka 使用 idempotency_key 记录标头,SNS/SQS 使用 idempotency_key 消息属性。MQTT 发布不携带键,因为 MQTT 3.1.1 没有存放它的位置。

连接器可以发送到哪里​

连接器建立每个连接时,都会检查目标实际解析到的地址。无论名称是什么,解析为环回、私有、运营商级 NAT、链路本地或云元数据地址的目标都会被拒绝。webhook、MQTT、Kafka、SNS 和 SQS 使用相同检查:

  • 拒绝是最终结果。 被拒绝派发以 blocked 结果进入死信,不重试,因为等待不会让地址变成公网地址。目标只是停机则不同:那是普通故障,会重试。
  • MQTT 消息代理 URL 必须使用 tcp://、mqtt://、ssl://、tls://、mqtts://、ws:// 或 wss://,显式指定端口,每个条目一个消息代理。其他方案(包括 unix://)在保存连接器时被拒绝;已存储连接器派发时也会再次拒绝。
  • 只要尝试的任何地址被拒绝,派发立即成为 blocked;只有实际尝试时才判断地址。因此,多个消息代理的结果可能取决于客户端先到达哪一个。MQTT 客户端连接到允许的第一个消息代理时会投递,只有该代理不可用时才遇到被拒绝的代理。Kafka 客户端随机选择首个种子节点。请只列出允许的目标。
  • Kafka 地址采用 host:port。集群元数据中通告的每个消息代理都会被检查,而不只是配置的地址。
  • SNS 和 SQS 的端点覆盖值与其他目标一样被检查。没有覆盖值时,连接器访问 AWS 区域端点,该端点也接受检查。
  • 不会读取服务自身环境。 它不使用代理变量(HTTPS_PROXY、ALL_PROXY 等)、AWS_* 变量、AWS 配置文件或 Pod 云身份。连接器只到达其指定目标,使用自身携带的凭据。

要允许连接器访问私有目标,运维人员在 instance.config.infrastructure.egress.allowedDestinations 下,为每个地址添加独立的 /32。私有目标包括集群内消息代理、Amazon MSK、Amazon MQ,以及通过启用私有 DNS 的接口 VPC 端点访问的 SNS/SQS;后者会使默认区域名称也解析为私有地址。接口端点在每个可用区都有一个地址,每个地址都需要自己的条目。允许规则适用于所有租户、所有连接器和 webhook,而不仅是添加它时针对的那个对象。

blocked 结果只向租户说明目标解析到被拒绝地址,与 webhook 拒绝提供的信息相同。被拒绝地址本身记录在死信中,运维人员可读,租户不可读。

治理​

每个出站动作都受每租户治理约束,因为外部调用比进程内调用成本更高,也更容易因自身配置形成洪峰。

  • 传递两端都按租户限速。 检测引擎在派发前削减超预算输出,连接器服务在有限预算内接受目标投递流量。
  • 绝不无限制。 没有配置限制的租户回退到平台默认值,绝不无限制。
  • 按到达时间计量。 检测引擎和连接器服务都按触发遥测到达平台的时间计量出站动作。因此,重启或故障切换后排空检测积压时,不会误认为流量洪峰,也不会按租户上限减慢排空。
  • 削减而非重试。 仍超预算的动作以 shed 原因记录为死信,不重试。超过每租户约每秒一封死信的预算后,削减动作只计数,并汇总为每租户每分钟一封死信。

两端都按各自服务副本执行上限,参见上限按副本计算。

隔离与依赖​

outbound-connectors 服务在独立进程中运行,与 event-processing 分开。这一边界是有意设计的:

  • 云 SDK 或消息代理客户端卡住、崩溃或泄漏内存,只影响连接器投递,绝不影响检测。
  • publish 使用的消息代理和云客户端库只链接到该服务。必须在事件重放时产生相同结果的检测引擎保持精简,依赖范围较小。
  • 服务自行解析连接器凭据,检测引擎绝不持有它们。