دو پرسشی که مهار آنها را در هم میآمیزد
مهار به پرسشی محدود و مکانیکی پاسخ میدهد: این فرایند به چه چیزی میتواند دست بزند؟ مجوزدهی به پرسشی متفاوت پاسخ میدهد: آیا این اقدام در چارچوب اختیار تفویضشده بود؟ اینها یک پرسش نیستند، و یک مجری سطح سیستمعامل مجهز به پاسخ دادن به دومی نیست.
یک primitive سطح میزبان مانند Windows Execution Containers، یا ماشینآلات قدیمیتر Job Object و AppContainer که بر آن بنا شده، بر syscallها، handleها، tokenها و SIDها عمل میکند. یک فرایند را میبیند که درخواست یک file handle، یک کلید registry، یک network socket، یا یک تخصیص حافظه دارد. قصد، دامنهٔ task، رضایت کاربر، یا تفاوت میان یک agent که بهطور مشروع سندی را درون workspace تخصیصیافتهٔ خود ویرایش میکند و همان agent که آن سند را به دلیل منحرف شدن با prompt injection بیرون میکشد، نمیبیند. kernel هیچ بازنماییای از «این اقدام در چارچوب اختیار بود» ندارد. تنها بازنماییای از «این handle اعطا یا رد شد» دارد.
این تمایز اهمیت دارد زیرا تیمهایی که تحت فشار تحویل هستند مکرراً آن را فرو میکاهند. یک sandbox پیکربندی میشود، sandbox نوشتن خارج از یک پوشه را مسدود میکند، و تیم نتیجه میگیرد که حاکمیت انجام شده است. آنچه در واقع ساخته شده یک مرز جداسازی است. رد حسابرسی برای مجوزدهی — چه کسی چه چیزی را تأیید کرد، تحت کدام task، با کدام data classها — اکنون در پیکربندی sandbox زندگی میکند یا، بدتر، در لایهٔ هیچکس نیست. وقتی بازبینی یک حادثه میپرسد چرا یک agent سابقهٔ مشتری را خوانده که هیچ کاری با آن نداشته، پاسخ در log مهار نیست. log مهار تنها نشان میدهد که یک read موفق شده است.
مرز واقعاً کجاست

این دو لایه نقاط قوت متضاد دارند. primitiveهای سطح kernel در جداسازی قوی و در معناشناسی ضعیف هستند. یک container میتواند بهطور قابلاعتماد جلوگیری کند از اینکه یک فرایند در C:\\Windows\\System32 بنویسد، یک raw socket باز کند، یا فرزندی را spawn کند که token آن را به ارث میبرد. نمیتواند به شما بگوید که آیا قرار بود فرایند این کارها را انجام دهد. موتورهای سیاست معکوس هستند: قوی در معناشناسی، ضعیف در اجرا. یک موتور سیاست میتواند ارزیابی کند که «این agent مجوزی برای task invoice-reconcile-4417 دارد که به data class مالی محدود است و در پانزده دقیقه منقضی میشود» و تصمیم بگیرد که خواندن یک دفتر فروشنده در دامنه است. نمیتواند، بهتنهایی، یک syscall را متوقف کند. اگر موتور سیاست دور زده شود، یا اگر پس از واقعه مشورت شود، نوشتن قبلاً انجام شده است.
بنابراین رابط میان آنها سطح طراحیای است که اهمیت دارد. یک allowlist ایستا — مجموعهای ثابت از مسیرها و میزبانهایی که sandbox اجازه میدهد — شکل اشتباه است، زیرا هیچ تصوری از task، هیچ انقضا، و هیچ راهی برای بیان اینکه همان agent باید حقوق متفاوتی برای کارهای متفاوت داشته باشد ندارد. شکل درست یک capability grant امضاشده است: یک token ساختاریافته که توسط لایهٔ سیاست صادر میشود، به لایهٔ اجرا ارائه میشود، و در نقطهٔ استفاده اعتبارسنجی میشود. یک grant حداقل شامل یک شناسهٔ task، یک دامنه (کدام data classها، کدام مسیرها، کدام مقاصد شبکه)، یک انقضا، و یک حالت پایانی است. لایهٔ اجرا امضا و دامنه را بررسی میکند؛ اختیار را بازتفسیر نمیکند.
این نزدیک به مدل capability است که سیستمعاملها دههها از آن استفاده کردهاند، اعمالشده بر بارهای کاری agent. لایهٔ سیاست صادرکنندهٔ capabilityها میشود؛ لایهٔ سیستمعامل reference monitor میشود. هیچکدام بهتنهایی کافی نیست.
قواعد طراحی برای مهار لایهای
هر grant دامنهٔ task، data classها و یک حالت پایانی حمل میکند. یک grant که میگوید «این agent میتواند D:\\data را بخواند» یک grant نیست؛ یک حفرهٔ دائمی است. یک grant که میگوید «task invoice-reconcile-4417 میتواند data class finance-vendor-ledger را تا ۱۴:۳۲ UTC بخواند، پس از آن capability لغو و فرایند خاتمه مییابد» قابل حسابرسی است. حالت پایانی بهاندازهٔ دامنه اهمیت دارد: agentی که task خود را تمام میکند نباید یک capability زنده را حفظ کند، زیرا یک agent بهخطرافتاده ی

در لایهٔ سیستمعامل بهطور پیشفرض رد کنید؛ در لایهٔ سیاست بهصورت استثنا مجوز دهید. container باید با هیچ دسترسی فایلسیستم، شبکه یا دستگاه شروع شود و capabilityها را تنها از طریق رابط grant دریافت کند. لایهٔ سیاست نیز باید از یک وضعیت رد شروع کند، و grantها را تنها زمانی صادر کند که یک تعریف task، یک برچسب طبقهبندی داده، و یک رکورد رضایت کاربر یا سرویس همراستا شوند. این الگوی رایج را معکوس میکند، جایی که sandbox مجاز است و لایهٔ سیاست یک پساندیشهٔ logging است.
grant و تصمیم اجرا را جداگانه log کنید. اینها دو رویداد متفاوت با دو مالک متفاوت هستند. log grant ثبت میکند که لایهٔ سیاست یک capability برای task X، دامنهٔ Y، انقضای Z، به اتکای رکورد رضایت W صادر کرده است. log اجرا ثبت میکند که لایهٔ سیستمعامل یک درخواست handle مشخص را تحت آن capability اجازه داده یا رد کرده است. وقتی حادثهای رخ میدهد، log grant به «آیا این مجاز بود؟» پاسخ میدهد و log اجرا به «آیا مرز حفظ شد؟» پاسخ میدهد. ادغام آنها در یک جریان، توانایی نسبت دادن یک شکست به لایهٔ سیاست یا لایهٔ اجرا را از بین میبرد، که دقیقاً همان نسبتی است که در بازبینی نیاز دارید.
معاملهٔ صادقانه
مهار لایهای هزینهٔ latency و پیچیدگی دارد. هر بررسی capability یک round trip به لایهٔ سیاست یا یک اعتبارسنجی در برابر یک token امضاشده است، و هر task به یک چرخهٔ عمر grant با صدور، تازهسازی و لغو نیاز دارد. تیمهایی که این را نادیده میگیرند و بر یک sandbox مجاز واحد تکیه میکنند سریعتر تحویل میدهند و، برای مدتی، بهنظر میرسد که کار میکند. حالت شکست یک نقض نمایشی نیست؛ یک انباشت آهسته از مجوزهای دائمی است که هیچکس نمیتواند برشمارد، و پس از آن حادثهای که در آن log مهار کامل و رکورد مجوزدهی خالی است.
بخش ناراحتکننده این است که مجری سیستمعامل هرگز نخواهد توانست مجوزدهی را تصمیم بگیرد، زیرا مجوزدهی بیانیهای دربارهٔ قصد انسانی و اختیار سازمانی است، و kernel به هیچکدام دسترسی ندارد. بهترین کاری که میتواند بکند اجرای وفادارانه و سریع تصمیمی است که جای دیگری گرفته شده. طراحی رابط میان دو لایه — قالب grant، نقطهٔ اعتبارسنجی، مسیر لغو، logهای جداگانه — کار مهندسی واقعی است. برخورد با sandbox بهعنوان کنترل حاکمیت، میانبری است که رد حسابرسی را از لایهٔ سیاست بیرون میکشد و به یک فایل پیکربندی میبرد که هیچکس بازبینی نمیکند.
یک آزمون مفید: هر اقدام agentی که سیستم شما امروز اجازه میدهد را انتخاب کنید و دو پرسش بپرسید. کدام لایه تصمیم گرفت که مجاز است، و کدام لایه آن را ممکن کرد؟ اگر همان لایه به هر دو پاسخ دهد، مرز لایهای نیست، و بازبینی حادثهٔ بعدی باید مجوزدهی را از logهای اجرا بازسازی کند که هرگز برای حمل آن طراحی نشده بودند.
