IronClaw 已经在本地迷你主机上运行一段时间了,之前主要是把 IronClaw 接入到本地或者云端免费的 LLM,通过处理日常琐碎事务来感受各种 LLM 的能力——所有对话都是通过 IronClaw 自带的管理面板完成的。出于安全考虑,我一直没有接入像飞书、Telegram 这样的 IM 即时通讯软件,因为这得将本地服务通过 tunnel 暴露在公网上。

如果你不了解 IronClaw,可以参考我之前写的文章 钢铁版 OpenClaw - IronClaw 安全机制解析。简单地说 IronClaw 就是安全版的 OpenClaw,或者说是安全版的 AI 小助手。

目前 IronClaw 支持两种方式接入 IM:轮询方式和 tunnel 方式。

前者不需要将本地服务暴露在公网上:由 IronClaw 周期性地向 IM 服务器发起请求,一次性拉取所有 DM 私信然后本地处理。这样做实时性很差,因为你通过 IM 发出的消息,不能立即到达 IronClaw,而是需要等待 IronClaw 下一次轮询。如果为了提高实时性,把周期设置的很短,又可能触发服务限速,并且大部分时间你不会跟这个 AI 小助手聊天,因此大部分的轮询都是没必要的。

最理想的接入方式是后者,即通过 tunnel 的方式。

这段时间,我想让自己的 AI 小助手变得方便些,比如人不在电脑前面的时候,让 IronClaw 帮忙查信息,这就不得不考虑接入 IM 了。

Pasted image 20260408092736 以下是将飞书接入 IronClaw 的过程。

1. 创建飞书应用

  1. 访问 飞书开放平台,登录后点击“开发者后台” -> “创建企业自建应用”

  2. 填写应用名称、描述,选择图标,点击“创建” Pasted image 20260408110501

  3. 创建应用后在左侧菜单点击“添加应用能力” -> 找到“机器人” -> 点击“添加” Pasted image 20260408110713

  4. 在“权限管理”页面,点击“批量导入/导出权限”,粘贴以下 JSON 配置:

{
  "scopes": {
    "tenant": [
      "contact:user.base:readonly",
      "im:message",
      "im:message.group_at_msg:readonly",
      "im:message.p2p_msg:readonly",
      "im:message:send_as_bot",
      "im:resource"
    ]
  }
}

Pasted image 20260408110944

  1. 在左侧“凭证与基础信息”中可以找到 App IDApp Secret Pasted image 20260408111332
  2. 在左侧“事件与回调” -> “加密策略” 中可以找到 Verification Token Pasted image 20260408111358

此时,创建的应用尚未发布,飞书聊天界面中也看不到,等后面配置“事件与回调”再发布即可。

2. IronClaw 配置

飞书应用创建后,执行 ironclaw onboard --channels-only 为 IronClaw 配置 channel:

$ ironclaw onboard --channels-only

ℹ Tunnel Configuration (for webhook endpoints):
ℹ A tunnel exposes your local agent to the internet, enabling:
ℹ   - Instant Telegram message delivery (instead of polling)
ℹ   - Slack, Discord, GitHub webhooks

## 1. 是否为 ironclaw 配置自启动 tunnel,
##    这里输入 `N` 即可,
##    即不需要通过 ironclaw 启动 tunnel,
##    原因会在下面设置 tunnel 时说明。
Configure a tunnel? [y/N] N 

Which channels do you want to enable?

  (Use arrow keys to navigate, space to toggle, enter to confirm)

[] CLI/TUI (always enabled)
    [·] HTTP webhook
    [·] Signal
    [·] Discord (will install)
    [] Feishu (installed)
    [·] Slack (will install)
    [·] Telegram (will install)
    [·] Whatsapp (will install)

## 2. 通过方向键上下移动选项,选中 Feishu 后
##    按`空格`选中,按`回车`确认

## 3. 接下来按提示输入:
##    `feishu_app_id`,
##    `feishu_app_secret`,
##    `feishu_verification_token`

我配置的 OpenClaw 版本是 0.24.0,需要在 commit 732c23e06b 基础上打上下面的补丁,才能正常接入飞书:

diff --git a/src/extensions/manager.rs b/src/extensions/manager.rs
index be97971d..6b28ed0f 100644
--- a/src/extensions/manager.rs
+++ b/src/extensions/manager.rs
@@ -4523,11 +4523,16 @@ impl ExtensionManager {
         config_updates.extend(self.load_channel_runtime_config_overrides(name).await);
         let mut should_rerun_on_start = false;

-        // Refresh webhook secret
-        if let Ok(secret) = self
-            .secrets
-            .get_decrypted(user_id, &webhook_secret_name)
-            .await
+        // Refresh webhook secret (only if managed by host)
+        let managed_by_host = capabilities_file
+            .as_ref()
+            .map(|f| f.webhook_secret_managed_by_host())
+            .unwrap_or(true);
+        if managed_by_host
+            && let Ok(secret) = self
+                .secrets
+                .get_decrypted(user_id, &webhook_secret_name)
+                .await
         {
             router
                 .update_secret(name, secret.expose().to_string())

该补丁与 verification token 有关,详细信息可以参考个人 github 仓库分支 fix/feishu-with-0.24.0 分支。如果不这么改,后续为飞书应用配置“事件与回调”时,ironclaw 会对飞书发来的首次验证请求返回 401 Unauthorized,导致配置无法完成。

配置完成后,重新启动 IronClaw,让配置生效。

3. tunnel 设置

IronClaw 支持下面几种自启动 tunnel

  • ngrok
  • cloudflared
  • Tailscale Funnel
  • 本地自定义 tunnel

自启动 tunnel 就是由 IronClaw 来启动/停止的 tunnel。IronClaw 以子进程的方式启动这类 tunnel,因此本地还需要安装相应的 tunnel 命令。

需要注意的是,IronClaw 将 Gateway 和 Webhook Server 作为单独的服务启动,分别绑定在不同端口。先前踩的一个坑是通过命令 ironclaw onboard --channels-only 配置自启动 tunnel,tunnel 启动后没有绑定在接入 IM 对应的 Webhook Server 服务上,而是绑定在 Gateway 服务。这是因为由 IronClaw 启动 tunnel 时,优先绑定 Webhook Server,但是根据配置情况,可能会回退到 Gateway。其中的细微关键,在 IronClaw 文档中没有说明,需要查看项目代码。

为了简化配置,我没有配置自启动 tunnel(输入 N 跳过了),而是在 IronClaw 之外单独启动一个 tunnel,直接绑定在 Webhook Server 上。

ngrok 和 cloudflared 是两种常见的 tunnel。相比自建 tunnel,这两种方式不需要云服务器,相对简便。其中 ngrok 最为直接,cloudflared 额外需要一个域名。出于篇幅考虑,下面先介绍 ngrok 配置方式。

3.1 ngrok tunnel

ngrok 一个轻量级的隧道工具,能在你的本地服务和公网之间建立加密通道。它的核心优势是无需公网 IP、无需配置防火墙、自动处理 HTTPS 证书。ngrok 有免费和付费套餐,对于将 AI Agent 接入 IM 这种场景,免费套餐够用了。

Pasted image 20260408094837

  1. 安装 ngrok - 注册登录后,ngrok 官网会显示各种环境下的安装方法 Pasted image 20260406175947

安装完成后,需要执行 ngrok config add-authtoken ${AUTH_TOKEN}AUTH_TOKEN 添加到用户默认的配置文件 ngrok.yml 中。

AUTH_TOKEN 属于个人重要安全信息,务必妥善保护!

  1. 为 ngrok 添加 AUTH_TOKEN 后,ngrok 就可以直接启动了:
# 将本地 8080 端口暴露到公网
$ ngrok http 8080

启动后,终端会显示相关信息,包括本地服务暴露在公网的临时地址 https://${temporary_subdomain}.ngrok-free.app 以及临时地址所在区域等信息。

暴露在公网的服务地址也需要保密,其他人拿到这个地址之后便可以对本地服务发起请求,对于没有妥善防护的本地服务,这是极其危险的!

也可以将 ngrok 配置为系统服务:

## 在命令中指定配置文件路径
sudo ngrok service install --config=/opt/ngrok/ngrok.yml

启动 ngrok 服务之前,将指定的配置文件准备好:

## /opt/ngrok/ngrok.yml
version: "3"
endpoints:
  - name: ironclaw  ## 用户指定的名字
    url: https://xxxxxxxxxxxxxx.ngrok-free.dev  ## 免费用户不能自定义域名,需使用 dev domain
    upstream:
      url: http://127.0.0.1:8080  ## 待暴露的本地服务,ironclaw webhook 服务默认绑定 8080 端口
agent:
    authtoken: xxxxxxxxxxxxxxxxxx  ## ngrok 提供的 AUTH_TOKEN,妥善保管,泄漏后应立即重置

Pasted image 20260408103610

ngrok 会提供一个免费的固定地址 dev domain,可以在 Domains 查看,同样需要妥善保护!

  1. 测试 ngrok tunnel

启动 ironclaw(已配置 feishu)和 ngrok,通过 curl 命令验证 tunnel 是否正常运行:

curl -X POST 'https://${subdomain}.ngrok-free.dev/webhook/feishu' \
-H "Content-Type: application/json" \
-d '{"event": "user.created", "id": 12345, "name": "张三"}'

## 收到这样的响应说明 tunnel 配置成功
{"error":"Webhook authentication failed"}%

如果收到 {"error":"Webhook authentication failed"} 这样的响应说明 tunnel 正常,因为通过 curl 命令发出的请求并不是合法的飞书 DM 事件。

4. 配置飞书事件订阅

完成前面一系列的设置后,需要为飞书应用配置“事件和回调”: Pasted image 20260408121347

  1. 在“请求地址”这里填入暴露在公网的 Webhook Server 地址:https://${ngrok_free_dev_subdomain}/webhook/feishu

点击“保存”,飞书会向这个地址发送首次验证请求,验证成功后才允许添加事件: Pasted image 20260408122044

  1. 在“事件配置”页面点击“添加事件”,搜索 im.message.receive_v1(接收消息),勾选后确认添加: Pasted image 20260408122354

  2. 然后发布飞书应用: Pasted image 20260408122447 点击页面上方“创建版本”,填入“应用版本号”和“更新说明”,并确认,发布应用。

  3. 应用发布成功后,飞书聊天界面会收到提示: Pasted image 20260408122734 点击“打开应用”,可以进入到和飞书机器人的聊天页面。

  4. 在和飞书机器人聊天之前,需要在 IronClaw 批准配对请求,否则 IronClaw 仅接收机器人的信息,不会进行处理。

## 列出待处理的配对请求
$ ironclaw pairing list feishu

## 批准飞书配对,配置请求开始的部分就是配对码,类似 ABC12345 这样
$ ironclaw pairing approve feishu ${paring_code}

IronClaw 批准配对后,即可通过飞书机器人与 AI 小助手正常聊天。 Pasted image 20260408124246

IronClaw 0.24.0 版本中飞书 channel 的实现依旧简陋,处理飞书机器人首次聊天时,不会主动提醒用户完成配对,只是返回一个 body 为空的响应。相比之下,IronClaw 中 Telegram 的实现会在第一次收到信息时,主动提示用户执行配对命令。这是在接入过程中踩的另一个坑。

但是 IronClaw 项目官方似乎不理睬外部贡献者创建的 issue 或者 PR:我在 0.18.0 版本创建的一个 issue,直到 0.21.0 版本,发现问题解决主动关闭这个 issue 时都没有官方人员的评论回复;类似的还有一个修复性质的 PR,也一直悬而未决,PR 下面没有拒绝,没有评论,也不会批准。

这是很“退烧”的体验,如果不是因为 IronClaw 已经在本地运行一段时间,我可能会尝试 OpenClaw + NemoClaw 的组合。

5. 安全风险

用隧道工具把本地服务暴露到公网确实方便,但“能访问”和“安全”是两回事。ngrok 的情况比较特殊,它不只是开发工具,它们也是黑客工具箱里的常客。由于 ngrok 常被恶意软件用来建立 C2 通信通道,很多杀毒软件已经将 ngrok 的二进制文件标记为“潜在不需要的程序”。

即使把服务托管在 ngrok 后面,也不能假设边缘网络会挡住所有攻击。当你在内网机器上运行 ngrok 时,相当于在外网和内网之间开了一个不受控的入口。tunnel 工具暴露的不仅是那个端口,而是运行 tunnel 的整台机器的网络可达性。一旦隧道入口的服务存在漏洞,整个内网都可能被波及。这是最容易被忽略的架构级风险。

一个额外的主观因素:ngrok 提供的免费域名通常是亚马逊位于日本的节点。飞书的 DM 事件先绕到日本,才能送到本地 IronClaw,所以将飞书接入 IronClaw 后,现在我已经弃用 ngrok。

ngrok 最大的优势是配置简单,便于个人开发临时调试,建议用完即关。相比之下,cloudflared 可以提供更多的安全防护,但是对于 IM 接入来说,它和 ngrok 没有本质上的区别。如果追求更好的安全性,可以考虑自建 tunnel,或者干脆弃用 tunnel 方案,转向其他集成方式。

写在最后

先是分享如何通过 ngrok tunnel 将 IM 接入到本地 IronClaw,然后又说 ngrok 不安全,前后显得自相矛盾了。写下这篇文章主要还是为了分享折腾 IronClaw 过程中踩过的坑;关于 ngrok 的风险也必须说明,算是叠个甲,避免非计算机专业的读者按步照搬后,将本地暴露在不必要的安全风险中。

cloudflared tunnel 的配置方式后面有时间会单独介绍,最近还在考虑非 tunnel 的接入方案,实现后也会公布出来,敬请期待。