沙箱後端
後端概覽
標題為「後端概覽」的章節| 後端 | 值 | 平台 | 可用性 |
|---|---|---|---|
| 隔離式 VM | microvm |
macOS、Linux、Windows(microVM 虛擬機器監控程式) | 以探測為閘,取決於 microVM 執行階段;套用於互動式終端機 —— shell 在測試機內執行,而非在主機上 |
| 原生 | native |
macOS(Seatbelt)、Linux 與 WSL2(bubblewrap) | 於開機時探測;對於要求以檔名 glob 拒絕寫入的執行,Linux 會拒絕,因為 bubblewrap 無法強制執行 |
| 無 | none |
全部 | 解析時的預設值、終端機的預設值,以及在沒有可用後端或設定了 --sandbox off 時的退回選項 |
對於 null 或無法辨識的已儲存值,SandboxBackend.fromName 會傳回 none。已儲存的 "docker" 會對應至 native。
microvm 是唯一具備核心邊界的後端 —— 工作負載確實不在主機上執行。它從不作為任何一方默默退回的選項:在無法啟動 VM 的主機上明確要求它,會大聲失敗(「我要求的是一部 VM,得到的卻是我的筆電」是這個隔離環境唯一承擔不起的降級),而只有未指明的預設值才會降級至 native,並以一行日誌說明。工作階段的實際後端會回報給用戶端並顯示徽章,因此終端機永遠無法在主機上執行時聲稱自己已受隔離。VM 本身的生命週期、映像與對外流量,見測試機參考。
Windows 沒有原生沙箱。 對於任何不是 macOS 或 Linux/WSL2 的主機,NativeSandboxAdapter.probe 都會傳回 available: false。因此 Windows 上的代理派發採用 none。microvm 後端則是另一回事:它以 microVM 執行階段的探測結果為閘,套用於互動式終端機與測試機,而非包裝主機上的 bash。
探測結果
標題為「探測結果」的章節探測結果是決定是否強制執行的閘門。它只在伺服器開機時執行一次。
| 主機 | available |
備註/提示 |
|---|---|---|
| macOS | true |
sandbox-exec(Seatbelt)—— 命名空間隔離,沒有核心邊界 |
具備 bwrap 與 socat 的 Linux 或 WSL2 |
true |
bubblewrap —— 命名空間隔離,沒有核心邊界 |
| 缺少任一工具的 Linux 或 WSL2 | false |
requiresInstall: true,並附上指出缺少哪些套件的安裝提示 |
| 任何其他平台 | false |
此平台不提供原生沙箱 |
伺服器會以一行啟動日誌記錄結果:agent sandbox ON 加上備註、no OS-native agent sandbox on this host 加上提示,或 agent sandbox DISABLED by --sandbox off。GET /healthz 不含任何沙箱欄位。
--sandbox / CC_SERVER_SANDBOX 接受 on(預設)或 off。它是退出選項,而非啟用選項:on 的意思是「當主機提供後端時就包裝」,永遠不是「要求必須有後端」。
原生後端
標題為「原生後端」的章節macOS:Seatbelt
標題為「macOS:Seatbelt」的章節使用 Apple 的 Seatbelt 沙箱機制(與 macOS App Sandbox 所用的系統相同)。
產生的設定檔預設寬鬆((allow default)),再劃出拒絕項,因為完全預設拒絕的設定檔會令互動式 shell 無法使用 —— 要列舉的 dyld、XPC 與 Mach 呼叫實在太多。實際後果是代理仍可讀取主機的大部分內容;你已註冊的工作副本是靠明確的寫入與重新命名拒絕來保護,而不是靠隱形。
- 檔案系統讀取:機密路徑會被拒絕(
~/.ssh、~/.aws、~/.gnupg、~/.config/gh、~/Library/Keychains,以及拒絕讀取集合中的其餘路徑) - 檔案系統寫入:重設為拒絕,然後加上明確的子路徑允許;強制拒絕的路徑(shell rc 檔案、
.git/hooks、Claude 設定、受保護的工作副本)即使在可寫入的根目錄內也會被封鎖。每一條被拒路徑及其上層路徑都會拒絕file-write-unlink,因此重新命名無法繞過寫入拒絕 - 行程:對永遠危險的二進位檔拒絕
process-exec,並封鎖從可寫入目錄執行任何東西 - 網絡:限制於行內代理伺服器連接埠加上 DNS —— 回送並非一律允許,Unix 通訊端也不允許。代理伺服器會強制執行按每個工作階段的網域允許清單(見網絡對外流量)
- 設定:設定檔按每次啟動,從
SandboxSpec產生
需求:
sandbox-exec(/usr/bin/sandbox-exec),每個 macOS 安裝都內含 —— 無需額外工具
Linux:bubblewrap
標題為「Linux:bubblewrap」的章節使用 bubblewrap(bwrap)建立掛載與 PID 命名空間。
- 檔案系統:建立只看得見被綁定路徑的掛載命名空間
- 網絡:在未共用的網絡命名空間中執行;流量經由
socat的 Unix 通訊端橋接抵達主機代理伺服器 - 行程:PID 命名空間隔離
需求:
- 已安裝
bubblewrap與socat(Debian/Ubuntu 上為sudo apt-get install bubblewrap socat) - 核心命名空間支援(現代 Linux 的標準配備)
Bubblewrap 可以遮蔽既有路徑,但無法禁止可寫入綁定內新增的 .env、*.key 或 *.pem 檔案。標準派發政策要求這些檔名 glob 拒絕,因此原生代理派發目前在 Linux/WSL2 上即使探測找到 bubblewrap 也會失效即拒絕。它不會以悄然放寬的政策執行。要有具備建立時檔名規則的後端,才能恢復 Linux 的原生派發。停用沙箱是明確、不受限制的退出做法,而不是等效的變通方式。
無後端
標題為「無後端」的章節不進行任何沙箱包裝。行程直接在主機上執行,擁有主機的檔案系統、網絡與行程存取權。ACP 與執行框架的 bash 工具仍會取得環境淨化與指令政策;經由 NoSandboxAdapter 的 Claude Code 則繼承主機環境。這是在沒有可用後端或設定了 --sandbox off 的主機上,代理派發所退回的狀態,也是用戶端未指名任何後端時終端機的預設。
由伺服器代管的終端機
標題為「由伺服器代管的終端機」的章節終端機按自己的條件受沙箱包裝,而不是由派發的探測決定。TerminalSessionService 的 defaultBackend 是 none,而 cc_server 不會覆寫它,因此除非用戶端明確要求 native,終端機工作階段會執行主機的 shell —— 桌面版的終端機面板並不會這樣要求。
當要求 native 時,shell 會經由 SandboxManager、在固定的終端機設定檔之下受到包裝:$HOME 與該工作階段的工作目錄可寫入,網絡開放,而 ~/.ssh、~/.aws、~/.gnupg、~/.config/gh 與 ~/Library/Keychains 則被拒絕讀取。唯讀程式碼託管權杖與推送閘道不適用 —— 那是派發層面的事。若無法進行包裝,該工作階段會失敗,而不是在主機 shell 的 PTY 上顯示原生徽章。
網絡對外流量
標題為「網絡對外流量」的章節沙箱啟動後,對外網絡存取是由行內代理伺服器強制執行,而不是靠核心防火牆規則。在沒有可用後端的主機上,這一切都不適用,衍生的行程會以與伺服器使用者完全相同的方式存取網絡。
- 沙箱執行階段會在
cc_server行程內、於127.0.0.1上執行一個 HTTP/HTTPS 代理伺服器與一個 SOCKS5 代理伺服器,每個受限制的工作階段各一對(http_proxy.dart、socks_proxy.dart)。並行執行不會共用同一對。 - 受沙箱包裝的行程會取得代理伺服器環境變數(
HTTP_PROXY/HTTPS_PROXY→ HTTP 代理伺服器,ALL_PROXY→ SOCKS 代理伺服器;GIT_SSH_COMMAND會將 git-over-SSH 導向 SOCKS 代理伺服器)。在 Linux 上,網絡命名空間是未共用的,因此代理伺服器是唯一的出路;在 macOS 上,沙箱共用主機的回送,並直接與它們通訊。 - 每個連線的目標主機會與該工作階段的網域允許清單比對;其他一律拒絕。萬用字元有兩種形式,各含恰好一個
*:前置的*.example.com會匹配任何子網域(包括a.b.example.com),但不匹配頂級網域本身;中間標籤的bedrock.*.amazonaws.com則恰好匹配一個標籤(bedrock.us-east-1.amazonaws.com,而非bedrock.evil.attacker.amazonaws.com)。 - 允許清單會按每個工作階段,從精選的基準清單(
kBaselineAllowedDomains:LLM 供應商、Git 代管、套件註冊表)解析,再加上 GitHub 網域(kGithubDomains),以及SandboxSpec.egressAllowlist。派發路徑從不傳入額外的egressAllowlist項目,因此實際上每次執行得到的,都是基準清單加上 GitHub。 - 一份固定的拒絕清單(
kBaselineDeniedDomains:雲端中繼資料端點、遙測接收端)優先於所有允許項。 networkEnabled: false仍會啟動按每個工作階段的代理伺服器,但允許清單是空的,因此每一個對外連線都會被拒絕。派發會在執行開始時依存取網絡規則設定這個值:拒絕會關閉網絡,允許與先詢問會讓它保持開啟。
SandboxSpec
標題為「SandboxSpec」的章節傳遞給 SandboxPort.launch 的規格:
| 欄位 | 型別 | 說明 |
|---|---|---|
sessionId |
String |
穩定的工作階段識別碼(通常是空間 id) |
workspaceId |
String |
所屬工作區 id |
agentId |
String? |
目前綁定至該沙箱的代理(如有) |
bindMounts |
List<SandboxBindMount> |
主機 → 客體的掛載;沙箱只看得到此處列出的主機路徑 |
networkEnabled |
bool(預設 true) |
派發時依網絡對外連線規則設定(只有解析為拒絕時才關閉)。關閉時,按每個工作階段的代理伺服器仍會啟動,但允許清單是空的,因此每一個對外連線都會被拒絕 |
egressAllowlist |
List<String>(預設 []) |
額外允許的對外流量網域;支援萬用字元(*.example.com,以及一個標籤的中間形式)。派發從不填入此欄位,因此受包裝的執行只會得到精選基準清單,加上 GitHub 網域 |
guestWorkdir |
String? |
沙箱內的預設工作目錄;個別的 exec 呼叫可以覆寫它 |
mode |
Mode(預設 chat) |
劃定檔案系統寫入規則:chat 讓綁定掛載保持可寫入;review、plan 與 orchestrate 則為唯讀 |
protectedPaths |
List<String>(預設 []) |
在任何模式下都絕不可在沙箱內寫入的主機路徑 |
runnerStateDirs |
List<String>(預設 []) |
執行器用來存放自身狀態的主機目錄,在每個模式下都可寫入(目前是由 Control Center 管理的 CLAUDE_CONFIG_DIR) |
execGrantRoots |
List<String>(預設 []) |
由操作員核准的目錄樹,會在 macOS 上重新開啟「可寫入目錄的 exec 封鎖」。Linux 會忽略它們 —— bwrap 沒有 $HOME 的 exec 封鎖可供重新開啟 |
protectedPaths 存放原始的已註冊程式庫工作副本,於每次派發時從工作區的程式庫解析。代理只在按每個對話的寫入時複製 worktree 中工作,因此原始副本絕不可從沙箱內寫入 —— 否則在 macOS 上,Seatbelt 對 $HOME 的一律寫入允許會令它們可經由 bash 寫入。它們在每個模式下(包括 chat)都會加入機密拒絕寫入集合。
每個拒絕讀取與拒絕寫入的路徑,都會以其字面拼法與符號連結解析後的拼法各輸出一份。核心會以解析後的路徑來比對拒絕規則,因此針對符號連結路徑撰寫的規則永遠不會觸發 —— 而在 macOS 上,這是常態而非罕見情況,因為 /tmp 解析為 /private/tmp,/var/folders/… 解析為 /private/var/folders/…。含有 * 或 ? 的項目會視為 glob 而不加處理;無法解析的路徑則保持原樣。
SandboxBindMount
標題為「SandboxBindMount」的章節| 欄位 | 型別 | 說明 |
|---|---|---|
hostPath |
String |
主機上的絕對路徑 |
guestPath |
String |
沙箱內的路徑(使用原生沙箱時這等於 hostPath;保留以維持配接器對稱) |
readOnly |
bool(預設 false) |
為 true 時以唯讀方式掛載 |
SandboxHandle
標題為「SandboxHandle」的章節由 SandboxPort.launch 傳回:
| 欄位 | 型別 | 說明 |
|---|---|---|
sessionId |
String |
工作階段識別碼 |
backend |
SandboxBackend |
正在執行的是哪個後端 |
state |
SandboxState |
生命週期狀態 |
error |
Object? |
當 state 為 error 時設定 |
details |
Map<String, Object?> |
配接器私有的記帳資料(工作目錄、PID、設定檔路徑等) |
SandboxState
標題為「SandboxState」的章節| 狀態 | 說明 |
|---|---|
created |
規格已建立,但 VM 尚未啟動 |
warm |
正在執行且已可接受 exec 指令 |
active |
目前正在執行指令 |
suspended |
已暫停/已檢查點至磁碟以節省 RAM |
destroyed |
已拆除;控制代碼不再有效 |
error |
啟動失敗 —— 見 SandboxHandle.error |
沙箱事件
標題為「沙箱事件」的章節| 事件類型 | 說明 |
|---|---|
stdout |
沙箱的標準輸出 |
stderr |
沙箱的標準錯誤 |
exit |
沙箱行程已結束(帶有 exitCode) |
starting |
沙箱正在啟動 |
ready |
沙箱已可接受 exec 呼叫 |
killed |
行程被主機終止(OOM、逾時、手動停止) |
violation |
沙箱政策遭到違反(帶有一個 SandboxViolation) |
SandboxViolation
標題為「SandboxViolation」的章節| 欄位 | 型別 | 說明 |
|---|---|---|
action |
String |
被拒的動作詞(例如 file-read*、network-outbound) |
target |
String |
被拒操作所針對的路徑或主機 |
raw |
String? |
原始日誌行,可用於除錯 |
另請參閱
標題為「另請參閱」的章節- 沙箱與保安
- 沙箱政策
- 護欄
- cc_server CLI:
--sandbox旗標