Go Micro A2A:Card 自动生成、任务owner与push出站不是同一控制面
Go Micro A2A:Card 自动生成、任务owner与push出站不是同一控制面
知识点轴:A2A / MCP Gateway / harness-runtime / sandbox-security / benchmark-evaluation。 固定阅读 micro/go-micro@cefe2ce 的a2a.go、pushsecurity.go、client.go及部分测试,对照官方指南和历史安全issue #4129;选段见raw/articles/go-micro-a2a-gateway-fixed-code-selected-2026-10-14.md。固定提交时间是10-11 UTC,本篇10-14深读,不是当天新漏洞、不是用户已部署的证明。
为什么值得深挖
这是一个可检查的 A2A 网关实践:service registry 自动派生 Agent Card,把外部 JSON-RPC task 映射到内部 Agent.Chat;同时有主动的push SSRF修补、内存任务存储和自建协议客户端。比“支持A2A”的发布新闻多出可验证的控制面/数据面接口;对 Hermes/llm-wiki 的问题是:Card可发现、task仅owner可读、webhook能安全投递、真实委托有效分别需要什么证据?归属 A2A-Agent2Agent-Protocol 的逐操作任务授权与 MCP-Gateway-Runtime 的目录/真实出口交叉主题簇;loom-a2a-card-registry-runtime-boundaries 已拆管理端与运行时,本例再拆 task owner 与回调凭据/出站。
固定代码上的机制链
| 链路 | 读到的证据 | 不能顺推的结论 |
| 目录→Card→任务 | GET /agents及每agent/skill Card 从 registry type=agent 元数据生成,Card声明protocolVersion=0.3.0;POST per-agent JSON-RPC message/send→Agent.Chat;同一dispatcher存 tasks / pushConfigs / watchers(a2a.go 52, 224–244, 475–483, 590–618, 895–900) | 自动Card不代表签名身份、按caller路由授权或全功能A2A v1合规;所谓“统一入口”只是部署拓扑选项,不等于统一policy已生效。 |
| task读/续订/继续 | tasks/get仅按ID查进程内map;resubscribe按ID订阅;继续消息按给定taskId重取旧历史/context(829–842、790–827、979–998);本段未见将已认证caller/owner传入这三处 | 条件性风险:若外层未限制访问且多租户共用dispatcher,task ID可被获知时可能越权读取/续订或携带他人历史继续。UUID难猜不能代替授权;反之,外层mTLS/鉴权若正确绑定caller,不能仅凭内部代码断言生产可跨用户读。 |
| push配置/凭据 | setPushConfig验URL与task存在,把配置存入map并异步投递;getPushConfig按ID返回原配置(850–889);PushNotificationConfig.Token JSON字段存在(365–367);deliverPush用它生成Bearer(1031–1061) | URL可达/SSRF防护不等于配置owner授权或读回脱敏;readback若含token还牵涉凭据暴露面。A2A #1750是仍开放的v1规范提案(不是已生效的v0.3条款),只作横向设计参照,不说Go Micro违反已批准MUST。 |
| 回调出口/投递 | 默认在登记与发送检查URL,dial前拦被禁IP并解包常见v4-in-v6;ProxyFromEnvironment存在。operator自定义AllowPushURL后关闭内建dial-IP guard,改用http.DefaultClient(pushsecurity.go 32–58、172–244,a2a.go 113–120) | 旧#4129已关闭、默认guard是修复证据,不是未修漏洞。代理出口最终目标/redirect每跳、覆盖自定义policy后权限仍须实测;与a2a-push-dial-ip-proxy-final-target-boundaries同一失效类别但不同实现。 |
一阶原理:Registry→Card只绑定能力声明/位置;HTTP入口→caller身份→task owner→每次task操作授权才界定谁能读与继续;push config→receiver凭据→URL/连接IP/代理最终目的地→receiver ACK界定出站信息流。任何一步的成功不能代替下一步。这里的push是最佳努力:官方文档明确无重试队列、丢消息后依赖tasks/get;因此必须同时解决“谁能读”与“读到最新/真正交付”的双重问题。message/stream在底层同步时可只发完成态SSE,不要把协议接口存在当作实时进度证据。
可执行的隔离验收合同(本次未运行上游服务)
- 冻结代码与实际网关/内嵌handler的外层鉴权配置,使用两名合成caller、两任务及模拟callback。A创建任务,B分别发
tasks/get、resubscribe、续写及push set/get;A正例能完成。记HTTP入口实际身份、task owner、返回/事件、实际存储与接收端,不测试真实用户或生产凭据。 - 使用合成token,分别验证push设/读响应、跨caller readback的密文/脱敏与服务器出站Authorization的receiver;对照#1750的提案时注明protocol version,检查端到端字段而不是仅文档。收到合法推送与拒绝未授权读取的零副作用都要验。
- 在隔离网络做无代理直连、私网代理上的合法外部receiver、可控代理最终目的地、启用自定义
AllowPushURL的交叉测试,逐跳记URL/DNS/dialIP/代理最终IP/ACK;返回成功但receiver断线单列失败。按固定源码配置、外层代理及运行记录分层,别把本机未执行的单测写为通过。
边界/未解问题
本次读了固定Go源码、作者测试及文档,但未clone运行Go测试、未部署网关、未触发跨租户请求;多租户身份是否由外层proxy/服务框架绑定未知,代码级条件性风险不等于确认漏洞。单实例内存保留上限1024、无持久推送队列;多副本同一task是否可读/续订需另测。Go Micro自称v0.3.0,不能与 v1 的 Card/operation/redaction 提案混成一套合规测试;生产前应先选目标协议版本和外层身份模型。并非建议自动改用户 Hermes 生产权限。
写入记录
- 2026-10-14 12:00 CST:正文补A2A任务授权与MCP Gateway目录/出口主题簇入口;不改变固定代码结论或E2E边界。
- 2026-10-14 09:03 CST:固定Go源码与官方指南交叉深读,拆Card→caller/task owner→push凭据与最终出口→接收回执;明确历史issue已修、v1提案未并入、实际多租户/E2E未验证。