Athena 远程 RPC 与 WebRTC:设备 JWT 不等于逐方法授权

作者:

在

结论:设备身份 JWT 只是建立 Athena 长连接的「门票」,不是本机每个 RPC 方法的授权。本地方法映射没有逐方法角色/权限检查,getMessage() 只要求服务名存在于 SERVICE_LIST;而 WebRTC 本机服务默认监听 127.0.0.1:5001,其 /stream 请求体可自选数据通道名单。因此远程 RPC 与直播应归入独立、非实时的服务域,并把 sendcan 等控制路径排除在通用数据通道之外。

连接、调度与故障路径

manager_thread() 在 DongleId 缺失或为 UnregisteredDevice 时,不启动 manage_athenad/uploader。athenad.main() 用本地私钥签发身份 JWT,作为 jwt cookie 连接 ATHENA_HOST(默认 wss://athena.comma.ai),断开后以带随机抖动的退避重连。网络会话的服务端授权规则不在本地仓库中。

组件 关键函数 边界
接收 ws_recv() 文本/二进制帧进入共享 recv_queue;收到 ping 写 LastAthenaPingTime,超过 70 秒无 ping 触发重连。
RPC jsonrpc_handler()、rpc.handle() 以方法名查全局 dispatcher,异常转 JSON-RPC 错误;本地方法映射没有逐方法角色/权限检查,也不验证 jsonrpc 版本字段。
发送 send_queue_push()、ws_send() 回复高优先级、日志转发低优先级;按 4096 字符分 WebSocket 帧。共享队列在重连时仍是进程级对象,不能当成精确一次的 RPC 事务。
活性 ws_manage() 根据 IsOffroad 调整 TCP keepalive;仅是连接活性参数,不决定 RPC 授权范围。

rpc.handle() 对未识别方法返回 METHOD_NOT_FOUND,参数类型错误返回 INVALID_PARAMS,普通异常返回 SERVER_ERROR;这些错误映射是协议处理,不是权限策略。连接建立后 handle_long_poll() 会启动接收、发送、连接管理、四个上传、日志转发和可配置数量的 RPC worker;worker 将请求原文写事件日志,敏感载荷需另行评估。上传生命周期还有几处状态语义:HTTP 200/201/401/403/412 均结束该队列项,不能把队列移除等同于远端成功存储;cancelUpload() 只标记队列内 ID,正在执行的传输并未由该函数直接中止。此外片段创建在非 PC 平台要求 IsOffroad。

RPC 能力清单

能力 关键入口 源码约束与注意点
设备与网络查询 getVersion()、getPublicKey()、getSimInfo() 查询版本、设备公钥、SIM/网络元数据;公开公钥不同于公开私钥,但返回数据仍须考虑设备隐私。
消息读取 getMessage() 可订阅任一注册服务并等待一条消息。该 RPC 是广范围读能力,不能直接授予通用第三方插件。
日志枚举 listDataDirectory()、scan_dir() 从 Paths.log_root() 递归列路径并按前缀筛选;递归时不跟随目录符号链接。
指定 URL 上传 uploadFilesToUrls()、_do_upload() 队列入口拒绝空、绝对或含 .. 的文件名,检查文件存在,随后向请求中的 URL 发 PUT;没有目的域名 allowlist。
上传生命周期 upload_handler()、cancelUpload() 过期 31 天丢弃、计费网络可推迟/中断、特定网络错误重试至最多 30 次。队列移除不等于远端成功存储。
本地代理 startLocalProxy() 仅允许 LOCAL_PORT_WHITELIST={22}(8022 兼容映射到 22),再桥到 127.0.0.1;白名单约束本地目标端口,不是对 remote_ws_uri 的域名校验。
直播 startStream() 固定宽路摄像头、输出 carState/deviceState;仅在持久 CarParams.notCar 为真时声明入站 testJoystick。

WebRTC 本机入口与数据桥

webrtcd.main() 默认监听 127.0.0.1:5001,HTTP handler 提供 /stream、/schema、/notify,但在 WebrtcdHandler._dispatch_request() 中没有独立鉴权检查。不能据此推断外部网络可直接访问默认监听地址;本机其他进程、端口转发或修改 --host 后的暴露面需要单独控制。

handle_get_stream() 只要求 JSON content type。会话时限很具体:生成 Answer 限时 30 秒,建立连接限时 15 秒,普通会话最长 300 秒;包含 testJoystick 的 body 会话走不限时等待断开的分支。无会话 5 秒后 schedule_teardown() 清除 IsLiveStreaming。

数据桥的关键不对称在于:远端 Athena 的 startStream() 固定了服务名单,但直接调用本机 /stream 的请求体可自选名单;StreamSession.message_handler() 对入站普通消息仅按本会话 bridge_services_in 名单过滤,代码中没有按主题风险等级的统一许可层。所以必须区分「默认 Athena 调用约束」和「本机服务自身约束」。

/schema 仅检验字段属于 log.Event 且非废弃字段,/notify 向当前会话广播提供的 JSON;CerealOutgoingMessageProxy.update() 把订阅主题转 JSON 发数据通道。handle_post_notify() 的假通道探针确认:通知按当前会话列表逐个广播,某通道发送异常被忽略,即使没有会话也返回 HTTP 200 OK——该返回值只代表本机 handler 完成遍历,不是远端客户端已收到通知的回执。

自制硬件与插件化要求

  1. 将远程 RPC 与直播纳入独立、非实时服务域。插件注册清单需声明具体 RPC、日志文件和 cereal 主题读写权限;不能把设备身份 JWT 直接作为所有本机方法的全权令牌。
  2. 本机 /stream、/notify 和桥接写主题应由受信任调用方认证并用固定 allowlist 约束;尤其是 sendcan、carControl 等控制路径不能成为通用数据通道的可写目标。
  3. 上传能力应限制可访问目录的真实路径、目标 URL/域名与重定向策略,并把「服务端接受」「传输完成」「HTTP 拒绝」「重试耗尽」表示为不同状态。
  4. 验证矩阵至少包括:断网/重连时队列隔离,恶意 RPC 参数与超大消息,本机端口被非预期进程访问,symlink/路径边界,计费网络切换,会话竞争与清理,权限拒绝及行车状态切换。

以上均为建议的重构验收,不是当前源码已经提供的保证;现有测试并未证明服务端授权、HTTP 本机访问控制、敏感主题限制、外发 URL 范围或实车会话时序。

常见问题

设备 JWT 等于本机方法的授权吗?

不等于。JWT 只用于建立 Athena 长连接;rpc.handle() 以方法名查全局 dispatcher,本地方法映射没有逐方法角色/权限检查。因此不能把设备身份 JWT 当作所有本机方法的全权令牌。

getMessage() 有按主题分级的权限吗?

没有。它只要求服务名存在于 SERVICE_LIST,没有按主题敏感度进一步分级。这是广范围读能力,不能直接授予通用第三方插件。

WebRTC 本机服务是默认对外开放的吗?

不是。它默认监听 127.0.0.1:5001,且 _dispatch_request() 中没有独立鉴权检查。但本机其他进程、端口转发或修改 --host 后的暴露面需单独控制,不能据此断言对外可访问。

为什么说 /stream 的名单约束与 Athena 的不是一回事?

因为远端 Athena 的 startStream() 固定了服务名单,而直接调用本机 /stream 的请求体可自选名单;message_handler() 仅按本会话 bridge_services_in 过滤,没有按主题风险等级的统一许可层。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/REMOTE_RPC_WEBRTC_BOUNDARIES_DEEP_DIVE.md(取用了 Athena 连接与调度、RPC 能力清单、WebRTC 本机入口与数据桥、插件化要求)。
  • 项目 02 —— openpilot-research/DEVICE_IDENTITY_REGISTRATION_DEEP_DIVE.md(仅用于确认设备 JWT 的来源与注册前提)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注