خلفيات البيئة المعزولة
نظرة عامة على الخلفيات
قسم بعنوان “نظرة عامة على الخلفيات”| الخلفية | القيمة | المنصة | التوفر |
|---|---|---|---|
| جهاز VM في حاوية معزولة | microvm |
macOS وLinux وWindows (مشغّل أجهزة microVM) | مشروطة بفحص زمن تشغيل microVM؛ تنطبق على الطرفيّات التفاعلية — تعمل الصدفة داخل منصة، لا على المضيف |
| أصلية | native |
macOS (Seatbelt)، وLinux وWSL2 (bubblewrap) | تُفحَص عند الإقلاع؛ يرفض Linux تشغيلًا يشترط رفض الكتابة بأنماط أسماء الملفات، وهو ما لا يستطيع bubblewrap فرضه |
| بلا عزل | none |
الكل | الافتراضي عند التحليل، وافتراضي الطرفية، والرجوع حين لا تتوفر خلفية أو يُضبط --sandbox off |
يعيد SandboxBackend.fromName القيمة none لقيمة مخزّنة فارغة أو غير متعرَّف عليها. القيمة المخزّنة "docker" تُقابَل إلى native.
microvm هي الخلفية الوحيدة ذات حد في النواة — حمل العمل لا يعمل على المضيف فعلًا. ليست تراجعًا صامتًا في أي اتجاه: طلبها صراحة على مضيف لا يستطيع إقلاع جهاز VM يفشل بصوت عالٍ («طلبت جهاز VM فحصلت على حاسوبي» هو التدهور الوحيد الذي لا تحتمله الحاوية المعزولة)، ولا يتراجع إلى native إلا افتراضي غير مسمّى، مع سطر سجل يقول ذلك. تُبلَّغ الخلفية الفعلية للجلسة إلى العميل وتظهر بشارة، فلا تستطيع طرفية أن تدّعي أنها جهاز VM في حاوية معزولة وهي تعمل على المضيف. دورة الحياة والصور والخروج لجهاز VM نفسه يغطيها مرجع المنصات.
ليس لـ Windows بيئة معزولة أصلية. يعيد NativeSandboxAdapter.probe القيمة available: false على كل مضيف ليس macOS أو Linux/WSL2. لذلك يستخدم إرسال الوكيل على Windows القيمة none. خلفية microvm منفصلة: مشروطة بفحص زمن تشغيل microVM، وتنطبق على الطرفيّات التفاعلية والمنصات، لا على تغليف bash على المضيف.
نتائج الفحص
قسم بعنوان “نتائج الفحص”الفحص هو ما يبوّب الفرض. يجري مرة واحدة، عند إقلاع الخادم.
| المضيف | available |
الملاحظة / التلميح |
|---|---|---|
| macOS | true |
sandbox-exec (Seatbelt) — عزل على مستوى فضاء الأسماء، بلا حد في النواة |
Linux أو WSL2 مع bwrap وsocat |
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”يستخدم آلية عزل Seatbelt من Apple (النظام نفسه الذي تستخدمه ميزة macOS App Sandbox).
الملف المولَّد متساهل افتراضيًا ((allow default)) ويقتطع منه الرفض، لأن ملفًا يرفض كل شيء افتراضيًا يجعل الصدفة التفاعلية غير صالحة للاستعمال — تعداد استدعاءات dyld وXPC وMach غير عملي. النتيجة العملية أن الوكيل يظل قادرًا على قراءة معظم المضيف، وتُحمى نسخ العمل المسجّلة برفض صريح للكتابة ولإعادة التسمية، لا بإخفائها.
- قراءة نظام الملفات: مرفوضة للمسارات السرية (
~/.sshو~/.awsو~/.gnupgو~/.config/ghو~/Library/Keychainsوبقية مجموعة رفض القراءة) - كتابة نظام الملفات: تُعاد إلى الرفض، ثم سماح صريح بالمسارات الفرعية، مع المسارات واجبة الرفض (ملفات rc للصدفة، و
.git/hooks، وإعداد Claude، ونسخ العمل المحمية) محظورة حتى داخل جذر قابل للكتابة. يُرفضfile-write-unlinkعلى كل مسار مرفوض وعلى أسلافه، فلا تتجاوز إعادة التسمية رفض الكتابة - العملية: يرفض
process-execالثنائيات الخطرة دائمًا، مع حظر تنفيذ أي شيء من دليل قابل للكتابة - الشبكة: محصورة في منافذ البروكسي داخل العملية إضافة إلى DNS — loopback ليس مسموحًا جملةً، ومقابس Unix غير مسموحة. تفرض البروكسيات قائمة سماح النطاقات لكل جلسة (انظر خروج الشبكة)
- التهيئة: تُولَّد الملفات عند كل إطلاق من
SandboxSpec
المتطلبات:
sandbox-exec(/usr/bin/sandbox-exec)، جزء من كل تثبيت macOS — لا أدوات إضافية
Linux: bubblewrap
قسم بعنوان “Linux: bubblewrap”يستخدم bubblewrap (bwrap) لإنشاء فضاءات أسماء للتركيب والمعرّفات (PID).
- نظام الملفات: ينشئ فضاء أسماء للتركيب لا تظهر فيه إلا المسارات المربوطة
- الشبكة: يعمل في فضاء أسماء شبكة غير مشترك؛ يبلغ المرور بروكسيات المضيف عبر جسور مقابس Unix من
socat - العملية: عزل فضاء أسماء المعرّفات (PID)
المتطلبات:
- تثبيت
bubblewrapوsocat(sudo apt-get install bubblewrap socatعلى Debian/Ubuntu) - دعم فضاءات الأسماء في النواة (قياسي على Linux الحديث)
يستطيع bubblewrap أن يغطي مسارًا قائمًا، ولا يستطيع منع ملف جديد بصيغة .env أو *.key أو *.pem داخل ربط قابل للكتابة. تشترط سياسة الإرسال القياسية رفض الكتابة بهذه الأنماط لأسماء الملفات، لذلك يفشل إرسال الوكيل بالخلفية الأصلية مغلقًا على Linux/WSL2 حتى حين يجد الفحص bubblewrap. لا يعمل بسياسة أضعف في صمت. تلزم خلفية بقواعد لأسماء الملفات وقت الإنشاء كي يعود الإرسال الأصلي على Linux. وإطفاء العزل إلغاء اختيار صريح بلا حصر، لا حلًا مكافئًا.
خلفية بلا عزل
قسم بعنوان “خلفية بلا عزل”بلا عزل. تعمل العملية مباشرة على المضيف، بوصول المضيف إلى نظام الملفات والشبكة والعمليات. يظل ACP وأداة bash في الإطار يحصلان على تعقيم البيئة وسياسة الأوامر، ويرث Claude Code عبر NoSandboxAdapter بيئة المضيف. إلى هذا يرجع إرسال الوكيل على مضيف بلا خلفية متاحة أو مع --sandbox off، وهو افتراضي الطرفية حين يسمّي العميل none.
الطرفية المستضافة على الخادم
قسم بعنوان “الطرفية المستضافة على الخادم”تُعزَل الطرفية بشروطها، لا بفحص الإرسال. defaultBackend في TerminalSessionService هو none ولا يتجاوزه cc_server، فجلسة الطرفية تشغّل صدفة المضيف إلا إذا طلب العميل native صراحة — ولوحة الطرفية في تطبيق الحاسوب لا تطلب ذلك.
حين يُطلب native تُغلَّف الصدفة عبر SandboxManager بملف طرفية ثابت: $HOME ودليل عمل الجلسة قابلان للكتابة، والشبكة مفتوحة، و~/.ssh و~/.aws و~/.gnupg و~/.config/gh و~/Library/Keychains مرفوضة القراءة. رمز القراءة فقط وبوابة الدفع لا ينطبقان هنا، فهو شأن الإرسال. إن كان التغليف غير مدعوم تفشل الجلسة بدل أن تُظهر بشارة أصلية على PTY لصدفة المضيف.
خروج الشبكة
قسم بعنوان “خروج الشبكة”حين تُطلَق بيئة معزولة، يُفرَض الوصول الصادر إلى الشبكة ببروكسيات داخل العملية، لا بقواعد جدار حماية في النواة. على مضيف بلا خلفية متاحة لا ينطبق شيء من هذا، وتبلغ العملية المُطلَقة الشبكة تمامًا كما يبلغها مستخدم الخادم.
- يشغّل زمن تشغيل البيئة المعزولة بروكسي HTTP/HTTPS وبروكسي SOCKS5 على
127.0.0.1داخل عمليةcc_server، زوج واحد لكل جلسة مقيّدة (http_proxy.dart،socks_proxy.dart). لا تشترك التشغيلات المتزامنة في زوج واحد. - تتلقى العمليات المعزولة متغيرات بيئة البروكسي (
HTTP_PROXY/HTTPS_PROXY→ بروكسي HTTP، وALL_PROXY→ بروكسي SOCKS؛ ويوجّهGIT_SSH_COMMANDاتصال git عبر SSH عبر بروكسي SOCKS). على Linux يكون فضاء أسماء الشبكة غير مشترك، فالبروكسيات هي المخرج الوحيد؛ وعلى macOS تشارك البيئة المعزولة loopback المضيف وتخاطبها مباشرة. - يُطابَق مضيف وجهة كل اتصال مع قائمة سماح النطاقات للجلسة، ويُرفض ما عداها. شكلان لحرف البدل، كل منهما بنجمة
*واحدة بالضبط:*.example.comفي البداية يطابق أي نطاق فرعي (بما فيهa.b.example.com) لا نطاق القمة نفسه؛ وتسمية وسطىbedrock.*.amazonaws.comتطابق تسمية واحدة بالضبط (bedrock.us-east-1.amazonaws.com، لاbedrock.evil.attacker.amazonaws.com). - تُحَل قائمة السماح لكل جلسة من خط الأساس المنتقى (
kBaselineAllowedDomains: مزوّدو واجهات نماذج اللغة، واستضافة git، وسجلات الحزم)، إضافة إلى نطاقات GitHub (kGithubDomains)، إضافة إلىSandboxSpec.egressAllowlist. لا يمرّر مسار الإرسال أبدًا إدخالاتegressAllowlistإضافية، فيحصل كل تشغيل عمليًا على خط الأساس مضافًا إليه GitHub. - تغلب قائمة رفض ثابتة (
kBaselineDeniedDomains: نقاط بيانات التعريف السحابية، ووجهات القياس عن بُعد) كل سماح. networkEnabled: falseيظل يبدأ بروكسيات الجلسة، لكن قائمة السماح فارغة فيُرفض كل اتصال صادر. يضبط الإرسال هذه القيمة عند بدء التشغيل من قاعدة الوصول إلى الشبكة: الرفض يطفئ الشبكة، والسماح والسؤال أولًا يبقيانها مشغّلة.
SandboxSpec
قسم بعنوان “SandboxSpec”المواصفة الممرَّرة إلى SandboxPort.launch:
| الحقل | النوع | الوصف |
|---|---|---|
sessionId |
String |
معرّف جلسة ثابت (عادة معرّف المساحة) |
workspaceId |
String |
معرّف مساحة العمل المالكة |
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> (الافتراضي []) |
أدلة المضيف التي يحتفظ فيها مشغّل التشغيل بحالته، قابلة للكتابة في كل وضع (المثال الحالي دليل CLAUDE_CONFIG_DIR الذي يديره Control Center) |
execGrantRoots |
List<String> (الافتراضي []) |
أشجار أدلة أقرّها المشغّل تعيد فتح حظر التنفيذ من الأدلة القابلة للكتابة على macOS. يتجاهلها Linux — ليس لدى bwrap حظر تنفيذ على $HOME ليُعاد فتحه |
يحمل protectedPaths نسخ العمل المسجّلة الأصلية، وتُحَل عند كل إرسال من مستودعات مساحة العمل. يعمل الوكلاء حصريًا في أشجار عمل بالنسخ عند الكتابة، واحدة لكل محادثة، فيجب ألا تكون الأصول قابلة للكتابة من داخل البيئة المعزولة — على macOS كان سماح الكتابة الشامل الذي يمنحه Seatbelt لـ $HOME سيتركها قابلة للكتابة عبر bash. تنضم إلى مجموعة رفض الكتابة للأسرار في كل وضع، بما فيه chat.
يُصدَر كل مسار من مسارات رفض القراءة ورفض الكتابة بصيغتين، الحرفية وبعد حل الوصلة الرمزية. تطابق النواة قاعدة الرفض مع المسار المحلول، فالقاعدة المكتوبة على مسار فيه وصلة رمزية لا تُطلَق — وعلى macOS هذه هي الحالة الشائعة لا النادرة، إذ /tmp هو في الحقيقة /private/tmp، و/var/folders/… هو في الحقيقة /private/var/folders/…. الإدخالات التي تحتوي * أو ? تُعامَل أنماطًا وتُترَك كما هي؛ والمسار الذي لا يُحَل يُبقى كما كُتب.
SandboxBindMount
قسم بعنوان “SandboxBindMount”| الحقل | النوع | الوصف |
|---|---|---|
hostPath |
String |
المسار المطلق على المضيف |
guestPath |
String |
المسار داخل البيئة المعزولة (مع البيئة المعزولة الأصلية يساوي hostPath؛ يُبقى لتماثل المحوّلات) |
readOnly |
bool (الافتراضي false) |
يُركَّب للقراءة فقط حين تكون القيمة صحيحة |
SandboxHandle
قسم بعنوان “SandboxHandle”ما يعيده SandboxPort.launch:
| الحقل | النوع | الوصف |
|---|---|---|
sessionId |
String |
معرّف الجلسة |
backend |
SandboxBackend |
الخلفية التي تعمل |
state |
SandboxState |
حالة دورة الحياة |
error |
Object? |
يُضبط حين تكون state بقيمة error |
details |
Map<String, Object?> |
بيانات خاصة بالمحوّل (دليل العمل، ومعرّف PID، ومسار ملف التعريف، وغير ذلك) |
SandboxState
قسم بعنوان “SandboxState”| الحالة | الوصف |
|---|---|
created |
أُنشئت المواصفة ولم يبدأ جهاز VM بعد |
warm |
يعمل وجاهز لتنفيذ الأوامر |
active |
ينفّذ أمرًا الآن |
suspended |
متوقف مؤقتًا، أو محفوظ نقطة تفتيش على القرص لتوفير الذاكرة |
destroyed |
مُفكَّك، والمقبض لم يعد صالحًا |
error |
فشل الإطلاق — انظر SandboxHandle.error |
أحداث البيئة المعزولة
قسم بعنوان “أحداث البيئة المعزولة”| نوع الحدث | الوصف |
|---|---|
stdout |
المخرج القياسي من البيئة المعزولة |
stderr |
الخطأ القياسي من البيئة المعزولة |
exit |
خرجت عملية البيئة المعزولة (تحمل exitCode) |
starting |
البيئة المعزولة قيد الإقلاع |
ready |
البيئة المعزولة جاهزة لقبول استدعاءات exec |
killed |
قتلت المضيف العملية (نفاد الذاكرة، أو انتهاء المهلة، أو إيقاف يدوي) |
violation |
انتُهكت سياسة بيئة معزولة (تحمل SandboxViolation) |
SandboxViolation
قسم بعنوان “SandboxViolation”| الحقل | النوع | الوصف |
|---|---|---|
action |
String |
الفعل المرفوض (مثل file-read* وnetwork-outbound) |
target |
String |
المسار أو المضيف الذي استهدفته العملية المرفوضة |
raw |
String? |
سطر السجل الخام، مفيد للتشخيص |