تخطَّ إلى المحتوى

خلفيات البيئة المعزولة

الخلفية القيمة المنصة التوفر
جهاز 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 «غلّف حين يقدّم المضيف خلفية»، ولا تعني أبدًا «اشترط خلفية».

يستخدم آلية عزل 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 — لا أدوات إضافية

يستخدم 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 يظل يبدأ بروكسيات الجلسة، لكن قائمة السماح فارغة فيُرفض كل اتصال صادر. يضبط الإرسال هذه القيمة عند بدء التشغيل من قاعدة الوصول إلى الشبكة: الرفض يطفئ الشبكة، والسماح والسؤال أولًا يبقيانها مشغّلة.

المواصفة الممرَّرة إلى 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/…. الإدخالات التي تحتوي * أو ? تُعامَل أنماطًا وتُترَك كما هي؛ والمسار الذي لا يُحَل يُبقى كما كُتب.

الحقل النوع الوصف
hostPath String المسار المطلق على المضيف
guestPath String المسار داخل البيئة المعزولة (مع البيئة المعزولة الأصلية يساوي hostPath؛ يُبقى لتماثل المحوّلات)
readOnly bool (الافتراضي false) يُركَّب للقراءة فقط حين تكون القيمة صحيحة

ما يعيده SandboxPort.launch:

الحقل النوع الوصف
sessionId String معرّف الجلسة
backend SandboxBackend الخلفية التي تعمل
state SandboxState حالة دورة الحياة
error Object? يُضبط حين تكون state بقيمة error
details Map<String, Object?> بيانات خاصة بالمحوّل (دليل العمل، ومعرّف PID، ومسار ملف التعريف، وغير ذلك)
الحالة الوصف
created أُنشئت المواصفة ولم يبدأ جهاز VM بعد
warm يعمل وجاهز لتنفيذ الأوامر
active ينفّذ أمرًا الآن
suspended متوقف مؤقتًا، أو محفوظ نقطة تفتيش على القرص لتوفير الذاكرة
destroyed مُفكَّك، والمقبض لم يعد صالحًا
error فشل الإطلاق — انظر SandboxHandle.error
نوع الحدث الوصف
stdout المخرج القياسي من البيئة المعزولة
stderr الخطأ القياسي من البيئة المعزولة
exit خرجت عملية البيئة المعزولة (تحمل exitCode)
starting البيئة المعزولة قيد الإقلاع
ready البيئة المعزولة جاهزة لقبول استدعاءات exec
killed قتلت المضيف العملية (نفاد الذاكرة، أو انتهاء المهلة، أو إيقاف يدوي)
violation انتُهكت سياسة بيئة معزولة (تحمل SandboxViolation)
الحقل النوع الوصف
action String الفعل المرفوض (مثل file-read* وnetwork-outbound)
target String المسار أو المضيف الذي استهدفته العملية المرفوضة
raw String? سطر السجل الخام، مفيد للتشخيص