05-browser —— 浏览器与桌面自动化

← 返回主报告:Kimi Agent(云端沙箱)技术报告GitHub 原文

合规与风险声明:本报告为安全研究与互操作学习目的的逆向分析,全程只读采集,未对目标系统做任何修改;文中所有密钥、token、账号级标识(chat_id / project_id / tenant_id 等)均已脱敏;沙箱内发现的默认弱口令(VNC/SSH)属平台侧配置,仅作安全发现披露,请勿用于访问任何不属于自己的系统。docx/pdf/xlsx/kimi-slides 等引擎二进制为 Moonshot 专有许可(禁再分发/逆向),本文仅作行为级描述。报告基于单次分析窗口(约 50 分钟生命周期),软件版本与技能库存随镜像更新可能变化。

采集时间:2026-08-18。目标:讲清 Kimi 沙箱里 agent 如何”看”和”操作”浏览器/桌面。 全部结论来自只读访问(SSH kimi@127.0.0.1:12222 + HTTP 兜底 http://127.0.0.1:18080)。

1. 总体架构一句话

沙箱里跑着一套有头(headed)Chromium + Xvnc 虚拟桌面的组合:Chromium 渲染到虚拟 X 显示 :99(KasmVNC 提供,1920×1080,websocket :6080 对外提供 Web 桌面),browser_guard.py 作为守护进程负责拉起 Chromium、保证”永远有标签页且窗口最大化”;agent 侧通过 CDP(Chrome DevTools Protocol,:9222) 操作浏览器,通过 PyAutoGUI/Xlib 点击 X 桌面做焦点等 GUI 级操作;/opt/moonbox-project-template/bin/project-cdp-proxy.py 把 9222 包装成对外可用的 9223(改写 Host 头和 webSocketDebuggerUrl),供沙箱外/项目模板场景连接。

2. Chromium 实例实况

  • 版本:Chrome/151.0.7922.71curl http://127.0.0.1:9222/json/version 实测,Debian 12 自带 chromium,/usr/lib/chromium/chromium)。
  • 进程属主:kimi,主进程 PID 342(采样时点),由 browser_guard.py(PID 84)以 subprocess.Popen 拉起。
  • 当前 CDP /json/list 只有 1 个 pagechrome://newtab/)+ 1 个 NTP 内嵌 iframe,即”守卫维持的兜底标签页”。

实测启动命令行(ps 摘录,与 BrowserCDPGuard.start() 完全吻合):

/usr/lib/chromium/chromium --show-component-extension-options --enable-gpu-rasterization
  --no-default-browser-check --disable-pings --media-router=0 --enable-remote-extensions
  --load-extension= chrome://newtab/ --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0
  --window-position=0,0 --window-size=1920,1080 --no-first-run --start-maximized
  --no-sandbox --disable-dbus --disable-gpu --disable-software-rasterizer
  --enable-logging=file --log-file=/tmp/chromium_detailed.log --disable-infobars
  --disable-blink-features=AutomationControlled --user-data-dir=/tmp/chromium_user_data
  --allow-file-access-from-files --load-extension=/app/pdf-viewer --js-flags="--max_old_space_size=512"

--show-component-extension-options --enable-gpu-rasterization --disable-pings --media-router=0 --enable-remote-extensions --load-extension= 这几个是 Debian chromium 包装脚本默认注入的,非 guard 显式传入。)

关键参数含义

参数作用 / 为什么
--remote-debugging-port=9222 --remote-debugging-address=0.0.0.0开 CDP 并绑 0.0.0.0 —— 沙箱内任何进程(envd、guard、proxy)都能连;0.0.0.0 是配合 cdp-proxy/外部访问
--disable-blink-features=AutomationControlled反检测核心:去掉 navigator.webdriver=true 等自动化指纹,让页面不把 agent 当机器人
--user-data-dir=/tmp/chromium_user_dataprofile 放 /tmp —— 容器每次启动都是干净浏览器(无登录态、无 cookie 残留)
--no-sandbox --disable-dbus容器内没有 userns/dbus,必须关沙箱;安全性靠容器隔离兜底
--disable-gpu --disable-software-rasterizer无真实 GPU,渲染交给 Xvnc 虚拟帧缓冲
--allow-file-access-from-files允许 file:// 页面互相访问(代码注释自承认 “dangerous flag”)——方便 agent 本地预览生成的 HTML
--load-extension=/app/pdf-viewer注入 PDF.js 扩展(见 §5)
--js-flags="--max_old_space_size=512"限制 V8 堆 512MB,防渲染进程吃爆内存
--enable-logging=file --log-file=/tmp/chromium_detailed.log详细日志落盘便于排障
--window-size=1920,1080 --start-maximized与 Xvnc 桌面分辨率对齐

3. /app/browser_guard.py(783 行)精读

路径 /app/browser_guard.py,28KB,由 s6 服务 browser-guard 以 kimi 身份常驻运行(/etc/s6-overlay/s6-rc.d/browser-guard/run):

/command/s6-svwait -u /run/service/kasmvnc   # 先等桌面就绪
export DISPLAY=:99 XAUTHORITY=/home/kimi/.Xauthority HOME=/home/kimi
exec s6-setuidgid kimi python3 /app/browser_guard.py --wait-display --display :99 --timeout 60 --monitor

文件里有两套实现,由环境变量 USE_CDP 选择:

  • BrowserGuard(Playwright 模式)playwright.chromium.launch_persistent_context(executable_path="/usr/bin/chromium", user_data_dir="/app/data/chrome_data", headless=False, ...),自带 UA 伪造(按 chromium 真实版本号拼 UA + Sec-CH-UA 头)、locale=CHROME_LOCALE(默认 zh-CN)、timezone=TZ(默认 Asia/Shanghai)。
  • BrowserCDPGuard(裸 CDP 模式,当前实际生效):直接 subprocess.Popen 拉 chromium(命令行即 §2 那段),之后纯用 requests + websockets 走 CDP HTTP/WS 端点管理。判断依据:ps 实测命令行与 BrowserCDPGuard.start() 的参数逐一对应(--user-data-dir=/tmp/...--js-flags=512),而非 Playwright 模式的 /app/data/chrome_data

BrowserCDPGuard 的监控循环(_monitor_loop,默认 1s 一轮)做三件事:

  1. GET /json/list 过滤 type=="page"一个没有就 PUT /json/new 开新标签页(保证 agent 永远有可用 tab)。
  2. 对第一个标签页:WS 连接后 Browser.getWindowForTargetBrowser.getWindowBounds,若 minimized 则恢复 normal,否则强制 maximized窗口被用户/agent 最小化或还原时自动拉回最大化,配合 VNC 截图得到稳定画面)。
  3. 任何异常 → 清 WS 连接、kill 浏览器进程、重新 start()(最多重试 6 次,指数退避)。即”浏览器看门狗”:崩溃自愈。

其它细节:

  • 启动后用 PyAutoGUI 做一次焦点点击:pyautogui.click(width-25, 115) 然后移回原位 —— 点一下窗口标题栏附近让 Chromium 获得 X 焦点(get_screensize() 来自 /app/utils.py,用 xrandr 解析)。
  • wait_for_display()python-xlib 轮询连 :99,最多 60s,保证 Xvnc 先于浏览器就绪。
  • set_screen_resolution() 通过 CDP Browser.setWindowBounds 改窗口大小(呼应 SCREEN_RESOLUTION 环境变量,默认 1920×1080)。
  • 代码里 BrowserGuard.start() 原有一段无 try 包裹的 pyautogui 点击,/opt/moonbox-project-template/bin/patch-browser-guard.py 会在项目模板容器启动时把它打上 try/except 补丁(说明 moonbox 模板镜像复用同一份 browser_guard.py 再热补丁)。

4. CDP :9222 的使用方

监听确认(/proc/net/tcp):9222(chromium)、9223(project-cdp-proxy,PID 64,root)、6080(Xvnc websocket)、8888(kernel-server)、18080/18082、22。

  • browser_guard.py:如上,HTTP 端点 + 每标签页一条持久 WS。
  • envd(/usr/local/bin/envd,Go 二进制,带 debug_info 未 strip,-isnotfc 参数,PID 77,root):agent 运行时本体。采样时 9222 上有 1 条 ESTABLISHED 长连接(本地端口 39258 对)。strings envd | grep -i '9222\|devtools\|cdp' 无直接命中(端点地址应为配置/运行时拼接),但结合架构(envd 是 agent 与沙箱资源之间的网关)可判定浏览器工具调用最终走 CDP。隧道一度中断,socket 属主精确归因(uid 列)未取到,属遗留小尾巴。
  • project-cdp-proxy.py(9223):见 §6。
  • skills 层/app/.agents/skills/rust-browser-pilot/(fast-browser-use 的汉化版)源码里硬编码 ws://localhost:9222src/browser/session.rs:434 等),即 skill 可直接驱动这个有头浏览器。

5. /app/pdf-viewer 扩展

就是开源 PDF Viewer(Rob–W/pdf.js 的 Chrome 移植版,v4.6.129,manifest v3,内置 pdf.js 4.x:content/build/pdf.mjs + pdf.worker.mjs ≈ 3.9MB)。结构:根目录是扩展壳(background.js service worker 串起 pdfHandler.js/extension-router.js/preserve-referer.js/telemetry.js),content/ 是完整 pdf.js viewer(含 100+ 语言 locale 和 CJK cmap —— 明显照顾中文 PDF)。

manifest 要点:host_permissions: 、content script 注入所有 http/https/file 页面(document_start, all_frames)、权限含 webRequest/declarativeNetRequestWithHostAccess/tabs/webNavigation

作用:让 headless 环境里 agent 打开 PDF 链接时得到可渲染、可截图、可选中文字的页面,而不是触发下载。对”看”PDF 类任务(截图 + 视觉模型)是关键一环。

6. /opt/moonbox-project-template/bin/project-cdp-proxy.py(295 行)

纯 asyncio 写的 CDP 反向代理(无第三方依赖),默认 0.0.0.0:9223 → 127.0.0.1:9222(可用 CDP_PROXY_* 环境变量改)。两个核心功能:

  1. 重写 Host 头:新版 Chrome 的 CDP 校验 Host 必须为 localhost/IP,外部用域名连会被 403;proxy 统一改写成 Host: 127.0.0.1:9222
  2. 重写 discovery 响应/json/json/list/json/version):把返回 JSON 里所有指向 loopback 的 webSocketDebuggerUrl 替换成客户端请求用的公网 Host(非 loopback 时升级 ws://wss://),使外部 CDP 客户端拿到的 WS 地址回指 proxy 自身。对非 discovery 请求(含 WS upgrade)直接双向透传(64KB 块)。

即:moonbox”项目沙箱”把内部 Chromium 的调试能力安全地暴露给外部 IDE/预览面板的适配层。同目录还有 patch-browser-guard.pypatch-kernel-server.pywrite-template-version.pystart-moonbox-project-sandbox.sh(写启动时间戳后 exec /init),说明 moonbox 项目模板 = kimiagent 基础镜像 + 这层补丁。

7. KasmVNC 桌面

  • s6 服务 kasmvnc 的 run 脚本就是 exec /bin/bash /root/setup_kasmvnc.sh(root 权限,kimi 读不了,经 18080 HTTP 兜底取得全文)。
  • 脚本内容:从 SCREEN_RESOLUTION(默认 1920×1080)算宽高 → vncpasswd 设置密码(明文写死在脚本里:password,属弱口令但仅容器内 VNC 用)→ 生成 /root/.vnc/kasmvnc.yaml,关键项:
    • network.websocket_port: 6080interface: 0.0.0.0protocol: httprequire_ssl: false(自签 snakeoil 证书备着但非强制)
    • desktop.gpu.hw3d: falsedrinode: /dev/dri/renderD128
    • data_loss_prevention.clipboard 双向开启,mime 限定 chromium/x-web-custom-data, text/html, image/png —— 显然为浏览器剪贴板场景定制
    • 编码:max_frame_rate: 60、动态质量 7-8、视频区阈值 45% —— 网页滚动时走视频编码,兼顾流畅与带宽
  • 实际 Xvnc 命令行(PID 155,root):Xvnc :99 -SecurityTypes None -AlwaysShared ... -rfbport 5901 -websocketPort 6080 -geometry 1920x1080 -httpd /usr/share/kasmvnc/www ...-SecurityTypes None + -AlwaysShared + -DisconnectClients 0:容器内无认证、多人共享、永不踢人 —— 6080 直接是内置 noVNC Web 客户端入口,供产品前端嵌入”实时桌面流”给用户围观 agent 操作。
  • 桌面的”看”有两条路径:① 用户侧 = KasmVNC web 流;② agent 侧 = CDP Page.captureScreenshot / Playwright screenshot / PyAutoGUI(X 级截图)。X 桌面存在的意义主要是”有头浏览器需要一个 DISPLAY”以及向用户直播。

8. 自动化库盘点

pip list 命中(kimi 用户环境):

  • playwright 1.62.0(且 ~/.cache/ms-playwright 里下载了 chromium-1194/1234 与 headless_shell —— 但 guard 实际用系统 /usr/bin/chromium,Playwright 更多是给 agent 任务代码/脚本备用)
  • PyAutoGUI 0.9.54(X 级鼠标键盘,guard 的焦点点击即用它)
  • websockets 15.0.1(guard 的裸 CDP WS 客户端)
  • 另有 python-xlib(wait_for_display 用)、requests、loguru
  • 无 selenium / pyppeteer;node 侧未见 puppeteer 全局安装的证据(skills 里的 browse CLI 是按需 npm install -g @browserbasehq/browse-cli 的模式)

skills 里与浏览器直接相关的:browse(Browserbase browse-cli,本地模式 fallback 到本地 Chrome)、fast-browser-use / rust-browser-pilot(Rust CDP 客户端,直连 ws://localhost:9222)、playwright-scraper-skill

9. 结论:agent 如何”看”和”操作”

  • 看浏览器:CDP(DOM snapshot / 截图 / a11y 树);PDF 由 pdf-viewer 扩展转成可渲染网页;需要整屏视觉时截 X 桌面(PyAutoGUI)或经 VNC。
  • 操作浏览器:主通道 CDP :9222(envd 的浏览器工具 + rust-browser-pilot 等 skill 都指这里);Playwright 为备用/脚本通道。
  • 操作桌面:PyAutoGUI/xdotool 类 X 级输入(证据是 guard 的焦点点击),X 显示由 Xvnc :99 提供。
  • 稳定性保障:browser-guard = 浏览器看门狗(保标签页、保最大化、崩溃重启),s6 保证 guard 本身不死,kasmvnc 先于 guard 启动(s6-svwait 依赖)。
  • 对外暴露:6080 = 桌面直播;9223 = 调试能力出口(proxy 改写 Host/WS URL)。

附:本次未竟事项

  • 采样中沙箱 frp 隧道(12222/18080)在会话后段中断,9222 长连接的属主进程(uid 归因)未取到最终证据;/root/setup_kasmvnc.sh 只取到前 120 行(尾部应是 Xvnc 启动行,但已由 ps 实测命令行补全)。
  • envd 的浏览器工具内部协议(Go 二进制)未逆向,仅确认其为 CDP 消费方之一。