مجوز خودِ مشکل است
سختگیرانهتر شدن اپل نسبت به دسترسی کامل به دیسک (FDA) در macOS پاسخی معقول به یک حالت شکست واقعی است: یک چارچوب عامل، یک پوشش ترمینال، یا یک کمککننده پسزمینه، گستردهترین اختیار خواندن ممکن را درخواست میکند چون درخواست محدود سختتر قابل تعیین است. نتیجه یک بولی واحد روی تمام فایلهایی است که کاربر میتواند بخواند. عاملی که به یک دایرکتوری پروژه نیاز دارد، همان اختیاری را دریافت میکند که عاملی که به یک فایل پیکربندی نیاز دارد. صدور مجوز ارزان است و استدلال دربارهاش گران.
این عدم تقارن تمام ماجراست. صدور یک مجوز هزینهاش یک دیالوگ است. استدلال دربارهاش هزینهاش بازبینی هر گردشکاری است که به آن وابسته است، چون لغو مجوز محدود نیست: لغو FDA گردشکارهای نامرتبطی را باطل میکند که تصادفاً همان بولی را به اشتراک داشتند. اپراتورها در پاسخ، مجوز را سر جایش میگذارند و آن را زیرساخت محیطی میپندارند، که دقیقاً همان وضعیتی است که سختگیرانهتر شدن میخواهد از آن جلوگیری کند.
مسئله عمیقتر این است که مدل مجوز پیش از عاملهایی وجود داشته که در هر خواندن بدون حضور انسان در حلقه عمل میکنند. وقتی شخصی فایلی را باز میکند، خودِ شخص منشأ است. وقتی عاملی فایلی را بهنمایندگی از یک وظیفه میخواند، وظیفه منشأ است و پلتفرم جایی برای ثبت آن ندارد. FDA میتواند بگوید چیزی keychain شما را خوانده است. نمیتواند بگوید کدام وظیفه، کدام عامل، یا اینکه آن خواندن مشروع بوده یا نه.
کلاسهای داده بهعنوان عنصر اولیه محدودسازی
راهحل پایدار این است که دیگر مجوز سراسری فایلسیستم را واحد مجوزدهی به عامل ندانیم. در عوض حقوق خواندن را بهازای هر کلاس داده محدود کنیم. کلاس داده مجموعهای از مسیرهاست که یک سیاست خواندن مشترک دارند، مثلاً:
- اعتبارنامهها: keychainها، فایلهای
.env، مواد SSH، پیکربندی ارائهدهنده ابری، مخازن اعتبارنامه مرورگر - درختهای کد منبع: نسخههای کاری تحت کنترل نسخه، checkoutهای monorepo، وابستگیهای vendored
- ارتباطات شخصی: مخازن ایمیل، پایگاهدادههای پیام، مخازن تقویم و مخاطبین
- مصنوعات ساخت: خروجی کامپایلشده، کشها، لایههای کانتینر، مخازن مدیر بسته
- پیکربندی سیستم:
/etc، launch agentها، پروفایلهای شبکه
عاملها یک کلاس درخواست میکنند، نه یک مسیر. پلتفرم در زمان صدور مجوز مسیرها را به کلاسها نگاشت میکند و در زمان خواندن دوباره نگاشت میکند. یک عامل ساخت، source-trees و build-artefacts را درخواست میکند؛ یک عامل استقرار، credentials محدود به یک ارائهدهنده و build-artefacts را؛ یک عامل خلاصهسازی، personal-communications برای یک حساب را. این درخواست برای بازبین انسانی به شکلی خواناست که ~/Projects نیست، چون نیت را بیان میکند نه مکان را.

ویژگی حیاتی این است که عضویت در کلاس توسط پلتفرم محاسبه میشود، نه توسط عامل اعلام میشود. اگر عامل میتوانست ادعا کند «این مسیر یک مصنوع ساخت است»، تغییر نام یک راز به dist/ تاکسونومی را شکست میداد. نگاشت باید الگوهای مسیر، فراداده فایل، سیگنالهای محتوا و تبار را ترکیب کند: فایلی که توسط فرایندی نوشته شده که اعتبارنامهای در اختیار دارد، باید حتی پس از کپی شدن، طبقهبندی مجاور اعتبارنامه را به ارث ببرد. یک راز تغییرنامیافته محافظتشده میماند چون کلاسش از نحوه بهوجودآمدنش مشتق میشود، نه از جایی که اکنون نشسته است.
منشأ در هر خواندن
محدودسازی بهتنهایی بدون سابقهای از آنچه رخ داده کافی نیست. هر رویداد خواندن باید حداقل سه فیلد داشته باشد:
{
"agent_identity": "build-runner@ci-7",
"task_id": "task-4f9c2a1e",
"resolved_class": "source-trees",
"path": "/Users/dev/work/api/src/main.rs",
"decision": "allow",
"policy_version": "2024-11-03"
}
شناسه وظیفه فیلدی است که بیشتر سیستمها حذف میکنند و همان فیلدی است که سوابق حسابرسی را قابل تفسیر میکند. بدون آن، یک خط لاگ که خواندن ~/.aws/credentials را نشان میدهد از یک گام استقرار مشروع و یک مسیر استخراج داده قابل تفکیک نیست. با آن، اپراتور میتواند پرسشی بپرسد که مهم است: کدام وظایف اعتبارنامهها را خواندند، و آیا هیچکدام در همان بازه ارتباطات شخصی را هم خواندند؟ این پرسش تنها در صورتی پاسخپذیر است که شناسه وظیفه در هر خواندن حاضر باشد و از نقطه ورود عامل از طریق هر tool call و زیرفرایند منتشر شود.
ثبت تصمیم نگاشت بهاندازه ثبت مسیر اهمیت دارد. سابقهای که برای مسیری resolved_class: source-trees میگوید در حالی که سیاست اکنون آن را credentials طبقهبندی میکند، یک سیگنال انحراف است. بدون تصمیم، تغییرات سیاست تا زمانی که چیزی خراب شود نامرئی میمانند. با آن، انحراف بهصورت یک پرسوجو قابل تشخیص است: خواندنهایی را بیاب که کلاس نگاشتشدهشان با طبقهبند فعلی اختلاف دارد.
یک سازوکار نگاشت مشخص
یک پیادهسازی عملی سه مرحله دارد.
طبقهبندی. یک طبقهبند پسزمینه نگاشتی از الگوهای مسیر و سوابق تبار به کلاسها را نگه میدارد. روی ایجاد فایل، تغییر نام و تغ
حاکم را ثبت میکند.

نگاشت. در هر خواندن، runtime عامل کلاس درخواستی را با نمای فعلی طبقهبند نگاشت میکند. اگر کلاس محاسبهشده مسیر در مجموعه اعطاشده به عامل نباشد، خواندن با دلیلی ساختاریافته رد میشود: کلاس محاسبهشده، نسخه سیاست، و مجوزی که لازم میبود.
انتشار منشأ. runtime یک رویداد خواندن با فیلدهای بالا به یک sink فقط-افزودنی منتشر میکند. sink بر اساس وظیفه، عامل و کلاس قابل پرسوجو است و بستر تشخیص انحراف است.
دو جزئیات تعیین میکند که این در عمل کار میکند یا نه. اول، نگاشت باید بهقدری سریع باشد که روی مسیر خواندن بنشیند؛ کشی کلیدشده بر اساس inode و نسخه سیاست معمولاً کافی است، که با تغییر طبقهبندی باطل میشود. دوم، رد باید قابل اقدام باشد. ردی که میگوید computed_class: credentials, required_grant: credentials:aws-prod دقیقاً به اپراتور میگوید چه چیزی را تغییر دهد. ردی که میگوید permission denied به آنها میآموزد که FDA اعطا کنند.
هزینهاش چیست
سه بدهبستان صادقانه وجود دارد.
تأخیر و نگهداری. نگاشت کلاس یک جستوجو روی مسیر خواندن اضافه میکند و تاکسونومی باید نگهداری شود. کلاسهای دامنهمحور با بازسازماندهی مخازن توسط تیمها منحرف میشوند. تاکسونومیای که نگهداری نشود به منبع ردهای نادرست تبدیل میشود، و ردهای نادرست اعتماد را سریعتر از مجوزهای گسترده فرسایش میدهند.
ردهای مشروع. عاملها گاهی از خواندنهایی که باید مجاز میبودند رد میشوند. یک گام ساخت که فایل تولیدشدهای را میخواند که طبقهبند آن را مجاور اعتبارنامه علامت زده، شکست میخورد. طراحی باید آن رد را قابل اقدام کند نه خاموش: کلاس محاسبهشده، نسخه سیاست، و باریکترین مجوزی که وظیفه را باز میکند را آشکار کند. رد خاموش همان رفتار میانبری را تولید میکند که مجوزهای خام.
مقاومت فروشنده. فروشندگان پلتفرم در برابر ارائه تاکسونومیهای کلاس مقاومت میکنند چون دامنهمحورند و پیشفرضگذاریشان سخت است. یک سیستمعامل همهمنظوره نمیتواند بداند که دایرکتوری fixtures/ یک تیم داده مصنوعی دارد و دیگری snapshotهای تولیدی. مسیر واقعبینانه مجموعهای کوچک از کلاسهای پایه تعریفشده توسط پلتفرم بههمراه سازوکار توسعه است، با این درک که توسعه جایی است که بیشترین ارزش و بیشترین بار نگهداری در آن زندگی میکند.
هیچکدام از اینها رایگان نیست، و هیچکدام پاسخ کامل نیست. اما تغییری در واحد مجوزدهی است. FDA میپرسد آیا عامل میتواند همهچیز را بخواند. مدل محدود به کلاس میپرسد این عامل، در این وظیفه، کدام انواع داده را میتواند بخواند، و پاسخ را ثبت میکند. پرسش دوم همانی است که اپراتورها واقعاً باید پاسخ دهند، و همانی است که مدل فعلی نمیتواند بیان کند.
