ClaudeDesktopConnectionRefused故障排查以及本地VMruntime剖析

admin 2026-06-20 05:00:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细分析了ClaudeDesktop在Cowork模式下出现ConnectionRefused错误的排查过程,发现故障源于disclaimer包装器未正确继承TLS根证书导致本地ClaudeCode进程无法验证服务器证书。通过Shell脚本代理注入CA证书成功修复。文章进一步解密了ClaudeDesktop基于macOS虚拟化框架的VM运行时架构,包括Linux虚拟机隔离执行、动态目录挂载机制和gVisor网络沙箱安全边界,揭示了AI助理本地化执行从应用开发向分布式沙箱系统运维的演进趋势。 综合评分: 87 文章分类: 应急响应,安全工具,应用安全,终端安全,云安全


cover_image

Claude Desktop Connection Refused 故障排查以及本地 VM runtime 剖析

CSL CSL

联想全球安全实验室

2026年6月18日 19:00 北京

在小说阅读器读本章

去阅读

点击蓝字 关注我们

Anonymous

事情起因很普通:在日常使用中,Claude Desktop(桌面端)在某次升级后发现 Cowork 模式下发消息突然失败,UI 界面弹出了一条极无信息量的报错:

API Error: Unable to connect to API (ConnectionRefused)

然而,奇怪的是,同一台机器上命令行版的 claude CLI 运行完全正常。既然 CLI 能连通,说明网络本身、账号与云端模型服务都没有问题。差别在于,Desktop 版本在后台多了一套极为复杂的本地执行层。

本文将沿着两条线索展开:先定位并修复这次连接失败的子进程链路,再深入解密 Claude Desktop 背后一整套由 macOS 虚拟化框架、gVisor 用户态网络及动态文件挂载组成的本地虚拟机(VM)运行时。

PART

01

定位与修复ConnectionRefused链路

进程链路诊断

虽然同一台机器的 CLI 正常,但 Claude Desktop 在 Cowork 模式下并非直接调用 Node 脚本,而是通过一个包装程序启动 local Claude Code 进程。

其关键进程链路如下:

Claude Desktop (宿主机 UI)  └── Helpers/disclaimer (Mach-O 包装器)        └── local Claude Code (Node 进程) -> HTTPS/TLS 请求

通过阅读 Desktop 侧的日志,可以抓到 sdkOptions after patch 暴露的环境变量:

executable:&nbsp;'/Applications/Claude.app/Contents/Helpers/disclaimer'executableArgs: ['/Users/<user>/Library/Application Support/.../claude']envKeys: ['ANTHROPIC_BASE_URL',&nbsp;'ANTHROPIC_CUSTOM_HEADERS',&nbsp;'NODE_EXTRA_CA_CERTS']

disclaimer 作为一个 Mach-O 格式的 spawn wrapper,封装了 Desktop 和 local Claude Code 之间的调用。

根证书信任链修复

本次故障的原因与证书链有关:在需要自签名证书或中转网关的公司/测试网络环境下,由disclaimer启动的 local Claude Code 进程没有继承主机的 Node TLS 根证书,导致与 ANTHROPIC_BASE_URL 握手时因为证书无法信任而被拒绝。

由于 disclaimer 是个二进制包装程序,最直接的修复方式是将其重命名,并用一个同名的 Shell 脚本代理它,在 spawn 子进程前将 CA 证书注入环境变量:

#!/bin/shroot_ca="$HOME/.claude-root-ca.crt"if&nbsp;[ -r&nbsp;"$root_ca"&nbsp;];&nbsp;then&nbsp;&nbsp;export&nbsp;NODE_EXTRA_CA_CERTS="$root_ca"fiexec&nbsp;"$@"

替换此入口后,Desktop 的网络请求恢复正常,日志中的 turn cycle 状态重回 healthy。

服务端扣费但本地判失败的“误判点”

调试中还遇到了一个有趣的现象:偏偏本地报 ConnectionRefused错误,但模型服务端已经扣除了 Token 费用。

这是因为 Desktop 触发对话时,发送的不是句简单的 hello,而是一个多路并发请求:

  • 旁路请求(Side Request):使用较轻的模型(如 Sonnet 4),用于生成标题、能力测试或生成 ack。
  • 主请求(Cowork Turn):使用主模型(如 Opus 4-8),向云端大模型发起的推理请求中附带了多达 50 多个本地工具声明 and 长上下文。

在发生证书错误时,旁路轻量请求可能勉强握手成功并产生了 Token 消耗;但最关键的主 turn 请求在接收 SSE(服务器发送事件)数据流时,因为 local Claude Code 自身的 Node TLS 校验不完整而单方面中断了连接(error_status = null,即连接层错误),导致 UI 报错与服务端计费同时成立。

PART

02

解密后台的虚拟机(VM)运行时

一旦解决了网络请求的信任链,我们再把目光投向 Cowork 模式最核心的魔鬼细节:在 macOS 桌面端下,运行模型下达的 shell 命令的那个沙箱,到底长什么样?

VM捆绑包与底层虚拟化

在 macOS 路径下,可以找到 Claude Desktop 的虚拟机包:

~/Library/Application Support/Claude-3p/vm_bundles/claudevm.bundle

该目录下包含如下关键文件:

  • rootfs.img(一个约10GB的GPT格式磁盘镜像,包含 EFI 分区与标准的Linux ext4根文件系统
  • vmIP(静态分配的虚拟机 IP:172.16.10.3)
  • gvisorMacAddress(02:00:00:00:00:01)

通过运行时进程 and 插件分析,Claude 桌面端加载了 Swift 插件 swift_addon.node,该插件动态链接了 macOS 系统的 Virtualization.framework 与 vmnet.framework。

也就是说,Claude Desktop 确实是在 macOS 后台启动了一个真正完整的轻量化 Linux 虚拟机,用作隔离的本地工具执行沙箱。

降权的用户执行环境

当模型在对话里调用Bash 跑脚本时,如果直接用 root 权限运行,一旦指令存在恶意越权行为,后果严重。

阅读 coworkd.log 日志可以看到,系统在启动 VM 时,会通过 useradd 动态创建一个临时用户: useradd -m -d /sessions/ -s /bin/bash

  • 虚拟机内部由高权限 of coworkd 守护进程管理环境
  • 大模型下达并被本地拉起的 mcp__workspace__bash 工具,实际上是在虚拟机里通过 oneshot-bash 降权运行在 UID/GID 为 1004/1004的  普通用户下,且该用户不在 sudo 组中,实现了安全的特权隔离。

PART

03

VM目录共享与网络托管机制

既然命令行工具是在 Linux 虚拟机里执行,那么 VM 是如何读写我们 macOS 本地项目里的代码的?并且它是如何连网的?

动态目录共享与主客机路径映射

本地目录进虚拟机采用的是 Apple Virtualization 的目录共享技术(类似 virtiofs)。

在虚拟机内部,只读挂载了主机的根共享点 /mnt/.virtiofs-root 。为了防止模型随意读取不该读的文件,Desktop 实施了基于单次命令的动态子目录挂载与主客机路径映射(Path Mapping):

  • 当用户向对话授权某个目录(如 ~/Downloads)时,HostLoop 记录该授权;
  • 当模型触发 mcp__workspace__bash 时,coworkd 临时将主机的 Downloads 目录以读写形式挂载到虚拟机内的 /sessions//mnt/Downloads 下;
  • 命令执行完毕后,挂载点被立即卸载。

由于主机与虚拟机路径不同,Desktop 内部包含了一个 pre-tool hook 路径混用拦截机制。在解包 Desktop 的 app.asar(对应构建后的 index.js )后可以发现,代码中包含了一个针对模型工具调用的 pre-tool hook 拦截器。当模型尝试将虚拟机路径( /sessions/… )传给宿主机侧的工具(如 Read / Edit / Write)时,该机制会在工具执行前直接拦截,并返回明确的警告提示,迫使模型区分环境并自行修正路径,从而避免跨系统文件操作失败:

Host 工具必须且只能使用宿主机路径( /Users//Downloads )。

VM Bash 必须且只能使用挂载路径( /sessions//mnt/Downloads )

精确的虚拟机沙箱安全边界

在启动 local Claude Code 时,桌面端会向其传递受管的安全沙箱设置(sandbox 配置)

  • 命令执行边界(allowUnsandboxedCommands: false):

系统强行禁止在宿主机(macOS)直接执行非沙箱命令。这确保了模型下达的所有 shell 操作必须被路由进物理隔离的 Linux 虚拟机中执行,保护了物理机的安全。

  • 网络出网边界(network.allowedDomains & allowManagedDomainsOnly: true):

这套沙箱网络限制作用于虚拟机内部。gVisor 配合 Desktop 宿主程序在网络出口层级,将虚拟机内所有命令(如 curl、git、npm 等)的连接去向严格锁定在 allowedDomains(如 api.anthropic.com 或指定的内部托管网关)之内。这正是作者总结的“这不是一个 VM 随便出网的模型,由 Desktop 控制 allowed domains”。如果在沙箱内部执行命令连往未授权的外网服务器,其网络流量会被在出口处拦截阻断。

PART

04

总结与技术边界的代价

Claude Desktop Cowork 的这套架构展示了极其严密的安全设计边界:

  • Shell 隔离:使用隔离 of Linux 虚拟机限制系统命令对物理机(macOS)的直接危害。
  • 目录隔离:通过单次命令动态挂载与 ro/rw 细粒度控制防本地文件被过度读取。
  • API 与网络隔离:宿主机控制网络出口,通过 gVisor 强行拦截并限制 allowed domains 白名单,把数据外泄的风险降到最低。

这种将“本地执行”和“虚拟机容器”深度融合的思路非常激进,但也带来了显著的排查与维护代价。

在命令行 CLI 时代,遇到连接报错我们只需要排查环境变量 and 本机的代理。而在 Desktop 时代,一旦出现类似 ConnectionRefused 或是 Something went wrong 的错误,开发人员和安全研究者必须依次排查:Electron 进程链 -> Disclaimer 包装器 -> HostLoop 调度 -> Workspace MCP -> 虚拟机运行状态 -> gVisor 网络栈。这也标志着 AI 助理在本地的落地,已经从单纯的“应用开发”演变成了“本地微型分布式沙箱系统”的运维与调试。

<<<  END >>>

往期精彩合集

● 从几类典型案例看攻击溯源与钓鱼风险防范

● Cloudflare Dynamic Workers 与 Workflows: 边缘计算正在从“部署代码”走向“运行时代码”

● AI时代下的DevSecOps:安全开发如何跑出“机器速度”?

● 调研完10+开源AI渗透工具:离真正自动化还差得很远

● 后端安全不是玄学:从编码到上线的8条安全底线


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:联想全球安全实验室 CSL CSL《Claude Desktop Connection Refused 故障排查以及本地 VM runtime 剖析》

评论:0   参与:  0