Backend sandbox
Tổng quan backend
Phần tiêu đề “Tổng quan backend”| Backend | Giá trị | Nền tảng | Khả dụng |
|---|---|---|---|
| VM khép kín | microvm |
macOS, Linux, Windows (hypervisor microVM) | Được đặt cổng theo bước dò trên runtime microVM; áp dụng cho terminal tương tác — shell chạy bên trong một rig, không phải trên máy chủ |
| Gốc | native |
macOS (Seatbelt), Linux và WSL2 (bubblewrap) | Được dò lúc khởi động; Linux từ chối một lượt chạy cần các lệnh từ chối ghi theo mẫu tên tệp mà bubblewrap không thể thực thi |
| Không cô lập | none |
Tất cả | Giá trị mặc định khi phân tích, mặc định của terminal và phương án dự phòng khi không có backend nào khả dụng hoặc --sandbox off được đặt |
SandboxBackend.fromName trả về none khi giá trị được lưu là null hoặc không nhận ra. Giá trị "docker" được lưu sẽ ánh xạ thành native.
microvm là backend duy nhất có ranh giới kernel — khối lượng công việc
thực sự không chạy trên máy chủ. Nó không bao giờ là phương án dự phòng âm thầm
theo cả hai hướng: yêu cầu nó một cách tường minh trên một máy chủ không thể khởi
động VM sẽ thất bại ồn ào (“tôi xin một VM mà lại nhận được laptop của mình” là
kiểu suy giảm duy nhất mà môi trường khép kín không thể cho phép), và chỉ một
mặc định không được chỉ định mới hạ xuống native, kèm một dòng log nói rõ
điều đó. Backend thực tế của phiên được báo về máy khách và gắn nhãn, nên một
terminal không bao giờ có thể tự nhận là khép kín trong khi đang chạy trên máy
chủ. Vòng đời, image và egress cho chính VM được trình bày trong tham chiếu
rigs.
Windows không có sandbox gốc. NativeSandboxAdapter.probe trả về available: false trên mọi máy chủ không phải macOS hay Linux/WSL2. Do đó việc điều phối agent trên Windows dùng none. Backend microvm là riêng biệt: nó được đặt cổng theo bước dò trên runtime microVM và áp dụng cho terminal tương tác cùng rig, không áp dụng cho việc bọc bash trên máy chủ.
Kết quả dò
Phần tiêu đề “Kết quả dò”Bước dò chính là thứ đặt cổng cho việc thực thi. Nó chạy một lần, lúc máy chủ khởi động.
| Máy chủ | available |
Ghi chú / gợi ý |
|---|---|---|
| macOS | true |
sandbox-exec (Seatbelt) — cô lập namespace, không có ranh giới kernel |
Linux hoặc WSL2 có bwrap và socat |
true |
bubblewrap — cô lập namespace, không có ranh giới kernel |
| Linux hoặc WSL2 thiếu một trong hai công cụ | false |
requiresInstall: true, kèm một gợi ý cài đặt nêu tên các gói còn thiếu |
| Mọi nền tảng khác | false |
Sandbox gốc không được cung cấp trên nền tảng này |
Máy chủ ghi kết quả vào một dòng khởi động duy nhất: agent sandbox ON kèm ghi chú, no OS-native agent sandbox on this host kèm gợi ý, hoặc agent sandbox DISABLED by --sandbox off. GET /healthz không mang trường sandbox nào.
--sandbox / CC_SERVER_SANDBOX chấp nhận on (mặc định) hoặc off. Đây là cơ chế chủ động tắt, không phải bật: on nghĩa là “bọc khi máy chủ cung cấp một backend”, không bao giờ là “bắt buộc phải có một backend”.
Backend gốc
Phần tiêu đề “Backend gốc”macOS: Seatbelt
Phần tiêu đề “macOS: Seatbelt”Dùng cơ chế sandbox Seatbelt của Apple (cùng hệ thống mà macOS App Sandbox dùng).
Profile được sinh ra mặc định là dễ dãi ((allow default)) và khoét ra các lệnh từ chối, bởi một profile từ chối-hoàn-toàn-theo-mặc-định sẽ khiến một shell tương tác không dùng được — có quá nhiều lệnh gọi dyld, XPC và Mach để liệt kê hết. Hệ quả thực tế là agent vẫn có thể đọc phần lớn máy chủ; những bản checkout đã đăng ký của bạn được bảo vệ bằng các lệnh từ chối ghi và rename tường minh, chứ không phải bằng sự vô hình.
- Đọc hệ thống tệp: bị từ chối với các đường dẫn bí mật (
~/.ssh,~/.aws,~/.gnupg,~/.config/gh,~/Library/Keychainsvà phần còn lại của nhóm từ chối đọc) - Ghi hệ thống tệp: được đặt lại thành từ chối, rồi các lệnh cho phép tường minh cho từng đường dẫn con, với những đường dẫn bắt buộc từ chối (tệp rc của shell,
.git/hooks, cấu hình Claude, các bản checkout được bảo vệ) bị chặn ngay cả bên trong một gốc có thể ghi.file-write-unlinkbị từ chối trên mọi đường dẫn bị từ chối và các tổ tiên của nó, nên một lệnh rename không thể lách qua một lệnh từ chối ghi - Tiến trình:
process-exectừ chối các tệp nhị phân luôn nguy hiểm, cộng thêm việc chặn thực thi bất cứ thứ gì từ một thư mục có thể ghi - Mạng: bị giới hạn ở các cổng proxy in-process cùng DNS — loopback không được cho phép toàn bộ và socket Unix không được phép. Các proxy thực thi danh sách cho phép theo tên miền của từng phiên (xem Egress mạng)
- Cấu hình: profile được sinh theo từng lần khởi chạy từ
SandboxSpec
Yêu cầu:
sandbox-exec(/usr/bin/sandbox-exec), có trong mọi bản cài macOS — không cần công cụ bổ sung
Linux: bubblewrap
Phần tiêu đề “Linux: bubblewrap”Dùng bubblewrap (bwrap) để tạo các namespace mount và PID.
- Hệ thống tệp: tạo một namespace mount chỉ nhìn thấy những đường dẫn đã được gắn
- Mạng: chạy trong một namespace mạng không chia sẻ; lưu lượng tới được các proxy của máy chủ qua những cầu socket Unix bằng
socat - Tiến trình: cô lập namespace PID
Yêu cầu:
- Đã cài
bubblewrapvàsocat(sudo apt-get install bubblewrap socattrên Debian/Ubuntu) - Kernel hỗ trợ namespace (tiêu chuẩn trên Linux hiện đại)
Bubblewrap có thể che khuất một đường dẫn đã tồn tại nhưng không thể cấm một tệp
.env, *.key hay *.pem mới nằm trong một bind có thể ghi. Chính sách điều
phối tiêu chuẩn yêu cầu những lệnh từ chối theo mẫu tên tệp này, nên việc điều
phối agent bằng backend gốc hiện đóng-khi-lỗi trên Linux/WSL2 ngay cả khi bước
dò tìm thấy bubblewrap. Nó không chạy với một chính sách âm thầm yếu hơn. Cần
một backend có quy tắc tên tệp tại thời điểm tạo để khôi phục việc điều phối gốc
trên Linux. Tắt sandboxing là một lựa chọn chủ động từ bỏ mọi ràng buộc, không
phải một cách lách tương đương.
Backend không cô lập
Phần tiêu đề “Backend không cô lập”Không có sandbox. Tiến trình chạy trực tiếp trên máy chủ với quyền truy cập hệ thống tệp, mạng và tiến trình của máy chủ. ACP và công cụ bash của harness vẫn được làm sạch môi trường và áp chính sách lệnh; Claude Code qua NoSandboxAdapter kế thừa môi trường của máy chủ. Đây là thứ việc điều phối agent quay về trên một máy chủ không có backend khả dụng hoặc có --sandbox off, và cũng là mặc định của terminal khi máy khách không nêu backend nào.
Terminal do máy chủ cung cấp
Phần tiêu đề “Terminal do máy chủ cung cấp”Terminal được sandbox theo những quy tắc riêng của nó, không theo bước dò của đường điều phối. defaultBackend của TerminalSessionService là none và cc_server không ghi đè nó, nên một phiên terminal chạy shell của máy chủ trừ khi máy khách yêu cầu native một cách tường minh — còn bảng terminal của ứng dụng desktop thì không.
Khi native được yêu cầu, shell được bọc qua SandboxManager dưới một profile terminal cố định: $HOME và thư mục làm việc của phiên có thể ghi, mạng mở và ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh cùng ~/Library/Keychains bị từ chối đọc. Token forge chỉ đọc và cổng push không áp dụng ở đây — đó là chuyện của đường điều phối. Nếu việc bọc không được hỗ trợ, phiên sẽ thất bại thay vì hiển thị một nhãn native trên một PTY là shell của máy chủ.
Egress mạng
Phần tiêu đề “Egress mạng”Khi một sandbox được khởi chạy, quyền truy cập mạng ra ngoài được thực thi bởi các proxy in-process, không phải bởi các quy tắc tường lửa của kernel. Trên một máy chủ không có backend khả dụng, không điều nào ở đây áp dụng và một tiến trình được spawn tiếp cận mạng đúng như người dùng máy chủ sẽ làm.
- Runtime sandbox chạy một proxy HTTP/HTTPS và một proxy SOCKS5 trên
127.0.0.1bên trong tiến trìnhcc_server, một cặp cho mỗi phiên bị giới hạn (http_proxy.dart,socks_proxy.dart). Các lượt chạy đồng thời không dùng chung một cặp. - Các tiến trình được sandbox nhận các biến môi trường proxy (
HTTP_PROXY/HTTPS_PROXY→ proxy HTTP,ALL_PROXY→ proxy SOCKS;GIT_SSH_COMMANDđịnh tuyến git-qua-SSH qua proxy SOCKS). Trên Linux, namespace mạng không được chia sẻ, nên các proxy là lối ra duy nhất; trên macOS, sandbox dùng chung loopback của máy chủ và nói chuyện trực tiếp với chúng. - Máy chủ đích của mỗi kết nối được đối chiếu với danh sách cho phép tên miền của phiên; mọi thứ khác đều bị từ chối. Có hai dạng ký tự đại diện, mỗi dạng có đúng một dấu
*: dạng đứng đầu*.example.comkhớp với mọi tên miền con (kể cảa.b.example.com) nhưng không khớp với chính tên miền gốc; dạng ở nhãn giữabedrock.*.amazonaws.comkhớp với đúng một nhãn (bedrock.us-east-1.amazonaws.com, không khớpbedrock.evil.attacker.amazonaws.com). - Danh sách cho phép được phân giải theo từng phiên từ đường cơ sở được tuyển chọn (
kBaselineAllowedDomains: các nhà cung cấp LLM, nơi lưu trữ Git, các registry gói), cộng với các tên miền GitHub (kGithubDomains), cộng vớiSandboxSpec.egressAllowlist. Đường điều phối không bao giờ truyền các mụcegressAllowlistbổ sung, nên trên thực tế mọi lượt chạy đều nhận đường cơ sở cộng với GitHub. - Một danh sách từ chối cố định (
kBaselineDeniedDomains: các endpoint metadata của cloud, các điểm thu thập telemetry) có quyền ưu tiên hơn mọi lệnh cho phép. networkEnabled: falsevẫn khởi động các proxy theo phiên, nhưng danh sách cho phép rỗng nên mọi kết nối ra ngoài đều bị từ chối. Đường điều phối đặt giá trị này khi lượt chạy bắt đầu, dựa trên quy tắc Truy cập mạng: từ chối sẽ tắt mạng, cho phép và hỏi trước giữ mạng bật.
SandboxSpec
Phần tiêu đề “SandboxSpec”Đặc tả được truyền cho SandboxPort.launch:
| Trường | Kiểu | Mô tả |
|---|---|---|
sessionId |
String |
Mã định danh phiên ổn định (thường là id của space) |
workspaceId |
String |
Id của không gian làm việc sở hữu |
agentId |
String? |
Agent hiện đang được gắn với sandbox, nếu có |
bindMounts |
List<SandboxBindMount> |
Các mount máy chủ→khách; sandbox chỉ nhìn thấy những đường dẫn máy chủ được liệt kê ở đây |
networkEnabled |
bool (mặc định true) |
Được đặt lúc điều phối từ quy tắc egress mạng (chỉ tắt khi quy tắc phân giải thành từ chối). Khi tắt, các proxy theo phiên vẫn khởi động nhưng danh sách cho phép rỗng nên mọi kết nối ra ngoài đều bị từ chối |
egressAllowlist |
List<String> (mặc định []) |
Các tên miền egress được phép bổ sung; hỗ trợ ký tự đại diện (*.example.com, cùng dạng một nhãn ở giữa). Đường điều phối không bao giờ điền vào trường này, nên một lượt chạy được bọc sẽ nhận đường cơ sở được tuyển chọn cộng với các tên miền GitHub |
guestWorkdir |
String? |
Thư mục làm việc mặc định bên trong sandbox; từng lệnh gọi exec riêng lẻ có thể ghi đè nó |
mode |
Mode (mặc định chat) |
Khoét ra các quy tắc ghi vào hệ thống tệp: chat giữ các bind mount ở chế độ có thể ghi; review, plan và orchestrate chỉ đọc |
protectedPaths |
List<String> (mặc định []) |
Các đường dẫn máy chủ không bao giờ được phép ghi bên trong sandbox, ở bất kỳ chế độ nào |
runnerStateDirs |
List<String> (mặc định []) |
Các thư mục máy chủ mà runner lưu trạng thái riêng của nó, có thể ghi ở mọi chế độ (hiện tại là một CLAUDE_CONFIG_DIR do Control Center quản lý) |
execGrantRoots |
List<String> (mặc định []) |
Các cây thư mục được người vận hành phê duyệt, mở lại lệnh chặn exec trong thư mục có thể ghi trên macOS. Linux bỏ qua chúng — bwrap không có lệnh chặn exec $HOME nào để mở lại |
protectedPaths chứa các bản checkout repo đã đăng ký gốc, được phân giải theo từng lần điều phối từ các repo của không gian làm việc. Agent chỉ làm việc trong các cây làm việc copy-on-write theo từng cuộc hội thoại, nên các bản gốc không bao giờ được phép ghi từ bên trong sandbox — nếu không, trên macOS, lệnh cho phép ghi $HOME toàn diện của Seatbelt sẽ để chúng có thể ghi được qua bash. Chúng gia nhập nhóm từ chối ghi các thông tin bí mật ở mọi chế độ, kể cả chat.
Mọi đường dẫn từ chối đọc và từ chối ghi đều được phát ra theo cả cách viết nguyên văn lẫn cách viết đã phân giải symlink. Kernel so khớp một quy tắc từ chối với đường dẫn đã phân giải, nên một quy tắc viết theo một đường dẫn có symlink sẽ không bao giờ kích hoạt — và trên macOS đó là trường hợp phổ biến chứ không phải ngoại lệ, vì /tmp phân giải thành /private/tmp và /var/folders/… thành /private/var/folders/…. Các mục chứa * hoặc ? được coi là glob và để nguyên; một đường dẫn không phân giải được thì được giữ nguyên như đã viết.
SandboxBindMount
Phần tiêu đề “SandboxBindMount”| Trường | Kiểu | Mô tả |
|---|---|---|
hostPath |
String |
Đường dẫn tuyệt đối trên máy chủ |
guestPath |
String |
Đường dẫn bên trong sandbox (với sandbox gốc thì bằng hostPath; được giữ lại để các adapter đối xứng nhau) |
readOnly |
bool (mặc định false) |
Gắn ở chế độ chỉ đọc khi là true |
SandboxHandle
Phần tiêu đề “SandboxHandle”Được trả về bởi SandboxPort.launch:
| Trường | Kiểu | Mô tả |
|---|---|---|
sessionId |
String |
Mã định danh phiên |
backend |
SandboxBackend |
Backend nào đang chạy |
state |
SandboxState |
Trạng thái vòng đời |
error |
Object? |
Được đặt khi state là error |
details |
Map<String, Object?> |
Thông tin ghi chép riêng của adapter (thư mục làm việc, PID, đường dẫn profile, v.v.) |
SandboxState
Phần tiêu đề “SandboxState”| Trạng thái | Mô tả |
|---|---|
created |
Đặc tả đã được tạo nhưng VM chưa khởi động |
warm |
Đang chạy và sẵn sàng thực thi lệnh |
active |
Đang thực thi một lệnh |
suspended |
Tạm dừng / được checkpoint xuống đĩa để tiết kiệm RAM |
destroyed |
Đã bị hủy; handle không còn hợp lệ |
error |
Khởi chạy thất bại — xem SandboxHandle.error |
Sự kiện sandbox
Phần tiêu đề “Sự kiện sandbox”| Loại sự kiện | Mô tả |
|---|---|
stdout |
Đầu ra chuẩn từ sandbox |
stderr |
Đầu ra lỗi chuẩn từ sandbox |
exit |
Tiến trình sandbox đã thoát (mang theo exitCode) |
starting |
Sandbox đang khởi động |
ready |
Sandbox sẵn sàng nhận các lệnh gọi exec |
killed |
Tiến trình bị máy chủ kill (OOM, hết thời gian chờ, dừng thủ công) |
violation |
Một chính sách sandbox đã bị vi phạm (mang theo một SandboxViolation) |
SandboxViolation
Phần tiêu đề “SandboxViolation”| Trường | Kiểu | Mô tả |
|---|---|---|
action |
String |
Động từ bị từ chối (ví dụ file-read*, network-outbound) |
target |
String |
Đường dẫn hoặc máy chủ là đích của thao tác bị từ chối |
raw |
String? |
Dòng log thô, hữu ích khi gỡ lỗi |
Xem thêm
Phần tiêu đề “Xem thêm”- Sandbox và bảo mật
- Chính sách sandbox
- Rào chắn
- CLI cc_server: cờ
--sandbox