سازمانهایی که سابقه طولانی دارند معمولاً دو سیستم هویت را به موازات یکدیگر اجرا میکنند. یکی متعلق به ارائهدهنده ابری است: نقشهای آیام (IAM roles)، پروفایلهای نمونه (instance profiles) و نقشهای اجرایی (execution roles). دیگری متعلق به خود شماست و همان سیستمی است که سرویسهای داخلی شما هنگام تصمیمگیری برای پاسخگویی به یک درخواست، آن را بررسی میکنند.
روی زیرساختی که خودتان میسازید، میتوانید هویت خود را به هر روشی که دوست دارید راهاندازی کنید. اما روی رایانش مدیریتشده (managed compute) نمیتوانید این کار را انجام دهید. ارائهدهنده فقط یک هویت ابری به فرآیند شما میدهد و چیز دیگری ارائه نمیدهد.
این مقاله توضیح میدهد که چگونه این فاصله را برای بارهای کاری آپاچی اسپارک (Apache Spark) در حال اجرا روی آمازون ایامآر (Amazon EMR) پر میکنیم. بار کاری که تنها با یک هویت ایدابلیوسی (AWS) شروع میشود، باید در نهایت دارای یک هویت داخلی درجه یک باشد. انجام این تبادل ساده است. قابل اعتماد کردن آن بخشی است که نیاز به کار طراحی داشته است. بسیار کم از مطالبی که در ادامه میآید مختص اسپارک (Spark) یا ایامآر (EMR) است.
دو سیستم هویت
احراز هویت داخلی سرویس به سرویس در نتفلیکس (Netflix) روی یک پیکیآی (PKI) خصوصی به نام متاترون (Metatron) اجرا میشود. هر بار کاری یک گواهی ایکس.۵۰۹ (X.509) با عمر کوتاه دریافت میکند و سرویسها با استفاده از تیالاس متقابل (mutual TLS) یکدیگر را احراز هویت میکنند. یک بار کاری نمیتواند به سادگی درخواست گواهی کند. این گواهی تنها پس از آن صادر میشود که سرویس هویت متقاعد شود درخواستکننده واقعاً همان بار کاری است که ادعا میکند. این مرحله تأیید، گواهیدهی یا اتستیشن (attestation) نامیده میشود. در هر محیطی که پشتیبانی میکنیم (ماشینهای مجازی، کانتینرها، توابع)، اتستیشن بر روی نوعی اثبات خاص محیطی است که پلتفرم میتواند به تنهایی آن را بررسی کند.
مفهوم دوم، پروژه داده (Data Project) است که واحد مالکیت ما برای دادهها محسوب میشود. یک پروژه داده دارای جداول است، مجموعهای از کاربران مجاز دارد و هویت خاص خود را داراست. جابها به عنوان پروژه داده اجرا میشوند نه به عنوان شخصی که آنها را راهاندازی کرده است. این همان چیزی است که کنترل دسترسی در سطح جدول و ممیزی را در استفادههای برنامهریزیشده و تعاملی سازگار نگه میدارد.
بنابراین مسئله به این صورت است. یک جاب اسپارک (Spark) روی رایانش مدیریتشده با یک نقش اجرایی ایدابلیوسی (AWS) و بدون هیچ هویت داخلی شروع به کار میکند. هر چیزی که از داخل شرکت نیاز دارد (خواندن یک ستون رمزگذاریشده، ارزیابی لیستهای کنترل دسترسی جدول، یا نگه داشتن طولانیتر یک نشست تعاملی بیش از عمر یک توکن کوتاهمدت) مستلزم داشتن هویت داخلی است.
یک هویت، یک نقش
طراحی بر روی تصمیمی استوار است که کاملاً خارج از پشته اسپارک (Spark) گرفته شده است. هر هویت پروژه داده (Data Project) به صورت ۱:۱ به یک نقش آیام (IAM) اختصاصی نگاشت میشود و سرویسی که مالک فراداده پروژه داده است، این نگاشت را ثبت میکند.
این نگاشت همان چیزی است که ترجمه را امکانپذیر میکند. این امکان را فراهم میسازد که یک بیانیه در واژگان ارائهدهنده («این فرآیند به عنوان نقش R در حال اجرا است») به یک بیانیه در واژگان ما تبدیل شود («این فرآیند بار کاری W است»). بدون آن، چیزی برای ترجمه وجود ندارد و هیچ مقدار رمزنگاری کمک نمیکند. بخش دشوار پل زدن بین دو سیستم هویت به ندرت پروتکل است؛ بلکه پایبندی به یک نگاشت و سپس معتبر نگه داشتن آن است.
یک اعتراض عملی بلافاصله ظاهر میشود. یک حساب ایدابلیوسی (AWS) واحد نمیتواند دهها هزار نقش آیام (IAM) را در خود جای دهد و ما انتظار داریم پروژههای داده در حد ده هزار مورد باشند. ما این موضوع را با شارد کردن قطعی نقشهای پروژه داده در یک استخر کوچک از حسابهای اختصاصی مدیریت کردیم که بسیار فراتر از تعداد پیشبینیشده پروژهها مقیاسپذیر است و این اثر جانبی مفید را دارد که نقشهای بار کاری را در طرف دیگر مرز حساب از هواپیماهای کنترلی که آنها را راهاندازی میکنند، دور نگه میدارد.
اجزا
پنج جزء در این فرآیند شرکت دارند.
- کنترل پلین (Control plane): تنها سرویسی است که اجازه راهاندازی جابهای اسپارک را دارد. این سرویس نقش آیام (IAM) پروژه داده را حل میکند، یک بار فراداده بار کاری را میسازد و امضا میکند و جاب را با آن نقش به عنوان نقش اجرایی ارسال میکند. این بخش کلید امضایی را که صرفاً برای همین منظور صادر شده، در اختیار دارد.
- سرویس پروژه داده (Data Project service): مرجع نگاشت هویت به نقش است.
- سرویس هویت (Identity service): درخواستهای اتستیشن را دریافت میکند، آنها را تأیید کرده و گواهیها را صادر میکند.
- پلاگین اسپارک (Spark plugin): قلابی (hook) را فراهم میکند. اسپارک ۳.۰ به بعد یک رابط پلاگین با اجزای سمت درایور و سمت اجراکننده اضافه کرد که هنگام راهاندازی این فرآیندها مقداردهی اولیه میشوند.
- ایدابلیوسی استیاس (AWS STS): به عنوان یک دفتر اسناد رسمی عمل میکند که کار معمول آن نیست.
گام اول: کنترل پلین یک ادعای امضاده شده میسازد
هنگامی که یک جاب ارسال میشود، کنترل پلین فراخواننده را اعتبارسنجی میکند، نقش آیام (IAM) پروژه داده را جستجو میکند و بار کوچکی را میسازد که بار کاری را که قصد راهاندازی آن را دارد توصیف میکند. این بار فراداده، هویت برنامه، نقش نگاشتشده، محیط، پشته و جزئیاتی را که اعتبارنامههای حاصل باید به آنها محدود شوند، نامگذاری میکند. کنترل پلین بار را امضا کرده و آن را به همراه امضا به عنوان پیکربندی عادی جاب عبور میدهد.
دو ویژگی از این مرحله اهمیت دارد. اول، تنها یک سرویس میتواند یک امضای معتبر تولید کند، بنابراین مجموعه چیزهایی که میتوانند ادعا کنند «این بار کاری مشروع است» کوچک و قابل ممیزی است. دوم، بار داده شده یک ادعا است تا یک مرجع. به خودی خود هیچ چیز را ثابت نمیکند، زیرا پیکربندی جاب از طریق زیرساختی عبور میکند که ما آن را به طور کامل کنترل نمیکنیم و هر چیزی که بتواند آن را بخواند میتواند آن را دوباره پخش (replay) کند.
گام دوم: بار کاری ثابت میکند که هویت ابری را در اختیار دارد
هنگامی که سرویس مدیریتشده فرآیند درایور را راهاندازی میکند، این کار را تحت یک کاربر سیستمعامل انجام میدهد که اعتبارنامههای نقش اجرایی را در دسترس دارد. پلاگین سمت درایور مقداردهی اولیه میشود و از آن اعتبارنامهها برای امضا کردن (اما نه ارسال) یک درخواست به نقطه پایانی هویت ارائهدهنده (sts:GetCallerIdentity) استفاده میکند. نتیجه یک یوارال از پیش امضاده شده با عمر کوتاه است.
آن یوارال یک اثبات قابل انتقال از مالکیت است. هرکسی میتواند آن را دریافت کند. فقط دارنده اعتبارنامههای نقش میتواند آن را تولید کند. و پاسخ به جای بار کاری از طرف ایدابلیوسی (AWS) میآید و بیان میکند که کدام نقش درخواست را امضا کرده است. بار کاری نمیتواند در مورد پاسخ دروغ بگوید زیرا بار کاری پاسخ را ارائه نمیدهد.
این الگو جدید نیست. این همان ایدهای است که پشت احراز هویت ایدابلیوسی آیام (AWS IAM) در هاشیکورد والت (HashiCorp Vault) و در جریانهای گواهیدهی توابع خود ایدابلیوسی وجود دارد. این الگو به خوبی عمومیت مییابد: هر محیطی که اعتبارنامههای ابری را به یک فرآیند بدهد و چیز دیگری ندهد، باز هم میتواند یک بیانیه قابل تأیید درباره هویت خود تولید کند.
پلاگین یک درخواست اتستیشن حاوی هر دو اثر، یعنی یوارال از پیش امضاده شده و فراداده امضاده شده را ارسال میکند.
گام سوم: اعتبارسنجی متقابل
سرویس هویت چهار کار انجام میدهد:
- یوارال از پیش امضاده شده را در مقابل ایدابلیوسی استیاس (AWS STS) دریافت میکند و نقشی را که آن را امضا کرده است میخواند. این بیانیه ارائهدهنده است.
- امضای فراداده را در مقابل کلید صادر شده برای کنترل پلین تأیید میکند. این بیانیه پلتفرم است.
- این دو را با هم مطابقت میدهد (corroborate). نقشی که ایدابلیوسی گزارش میکند باید همان نقشی باشد که کنترل پلین گفته است برای هویت نامبرده شده در فراداده اعزام کرده است.
- گواهیهایی را برای آن هویت، محدود به محیط، پشته و جزئیات حاصل از فراداده امضاده شده صادر میکند.
گام ۳ هدف کل طراحی است. هیچیک از بیانیهها به خودی خود کافی نیستند و از راههای تکمیلی شکست میخورند. بیانیه ارائهدهنده غیرقابل جعل است اما به اندازه کافی مشخص نشده است: به شما یک نقش میدهد و یک نقش یک بار کاری نیست. این بیانیه هیچچیز درباره اینکه چرا این فرآیند وجود دارد یا مجاز است چه چیزی باشد نمیگوید. بیانیه پلتفرم به خوبی مشخص شده است اما به خودی خود قابل تأیید نیست. وقتی این دو با هم قرار می گیرند، امضا میگوید بار کاری به طور مشروع اعزام شده است و یوارال از پیش امضاده شده میگوید این واقعاً همان است. هیچیک از طرفین مجبور نیستند به حساب خود بار کاری از خودش اعتماد کنند.
در اینجا تنشی وجود دارد که ما به طور عمدی آن را حل کردیم و تیمهای دیگر باید انتظار داشته باشند با آن روبرو شوند. پاسخ ارائهدهنده یک نقش را شناسایی میکند، نه یک برنامه. استخراج نام برنامه از نام نقش یک تطبیق رشته شکننده است و شناسههای نشستی که پلتفرم اختصاص میدهد تحت کنترل ما نیستند. ما تصمیم گرفتیم هویت برنامه را از فراداده امضاده شده بگیریم و پاسخ ارائهدهنده را فقط برای اعتبارسنجی متقابل استفاده کنیم. این کار هزینه یک رفت و برگشت شبکه و یک الزام امضای سختگیرانهتر را دارد و در عوض یک داستان اعتماد بسیار روشنتر را به ارمغان میآورد.
مسئله گسترش (Fan-out)
یک برنامه اسپارک (Spark) شامل یک درایور و حداکثر هزاران اجراکننده (executors) است که در طول عمر یک جاب ایجاد و نابود میشوند. اتستیشنی که پیرامون یک فرآیند در هر میزبان طراحی شده باشد، در اینجا دوام نمیآورد.
دو پاسخ دفاعی وجود دارد.
در روش اول، هر اجراکننده به طور مستقل اتستیشن انجام میدهد. این روش یکنواخت است، هیچ مرز اعتماد جدیدی اضافه نمیکند و هویت هر فرآیند را در همان اثبات تأییدشده توسط ارائهدهنده مستقر میکند. هزینه آن تقویت (amplification) است. یک جاب بزرگ میتواند هزاران فراخوان استیاس (STS) و هزاران درخواست اتستیشن را به صورت انفجاری تولید کند که بسیار شبیه به یک حمله است و وابستگی شدیدی به محدودیتهای نرخ سمت ارائهدهنده ایجاد میکند.
در روش دوم، اجراکنندگان اعتبارنامهها را از درایور به ارث میبرند. درایور یک بار اتستیشن انجام میدهد و اعتبارنامهها را از طریق آرپیسی (RPC) داخلی اسپارک که آن را با احراز هویت و رمزگذاری ایاییاس-جیسیام (AES-GCM) پیکربندی میکنیم توزیع میکند تا فقط فرآیندهای موجود در همان برنامه بتوانند شرکت کنند. بار روی سرویس هویت ثابت میماند. هزینه آن یک مرز اعتماد دوم و درایوری است که اکنون به نقطه توزیع اعتبارنامه تبدیل شده است.
با توجه به مقیاس خود در نتفلیکس (Netflix)، ما رویکرد دوم را انتخاب کردیم. درس کلی این است که تقویت هویت یک مسئله ظرفیت است و بهتر است به جای اینکه در طول یک حادثه به اجبار به آن پاسخ داده شود، از پیش به آن پاسخ داده شود.
چرخه حیات
گواهیها طراحیشدهاند تا عمر کوتاهی داشته باشند، بنابراین راهاندازی فقط نیمی از کار است. روی میزبانهای خودمدیریتشده، یک سرویس سیستمی مدیریت تمدید را بر عهده دارد. در داخل رایانش مدیریتشده هیچ قلاب مشابهی وجود ندارد، بنابراین درایور جاوا (Driver JVM) یک تایمر را اجرا میکند که در یک بازه زمانی ثابت، بسیار پیش از انقضا، مجدداً اتستیشن انجام میدهد. مطالب مربوط به اعتبارنامه در یک فهرست موقت نوشته میشود که فقط توسط مالک فرآیند قابل خواندن است و هنگام خروج فرآیند حذف میشود.
اجراکنندگان بهروزرسانی (refresh) نمیشوند. آنها معمولاً عمر کوتاهی دارند و اجراکنندهای که به نوعی عمرش از اعتبارنامههایش بیشتر شود میتواند خارج شده و جایگزین شود که ارزانتر از این است که هر اجراکننده را به یک کلاینت تمدید تبدیل کنیم.
قلمرویی که ارزش دارد وارد هر سیستم مشابهی شود این است که اتستیشن باید یک عملیات تکرارپذیر باشد تا یک مرحله راهاندازی. هر چیزی که فقط یک بار در شروع فرآیند اتفاق بیفتد، در نهایت دلیلی خواهد شد که یک جاب طولانیمدت پس از نه ساعت متوقف شود.
چرا این روش عمومیت مییابد
اگر سیستم هویت خود را اجرا میکنید و در حال انتقال بارهای کاری به رایانش مدیریتشده هستید، خدمات خاص در اینجا اهمیت کمتری نسبت به شکل راهحل دارند.
- روی یک پریمتیو قابل تبادل لنگر بیندازید. یک نگاشت ۱:۱ معتبر واحد بین هویت شما و هویت ارائهدهنده، چیزی است که ترجمه را امکانپذیر میکند. بقیه لولهکشی است.
- دو ادعای مستقل بخواهید و آنها را اعتبارسنجی متقابل کنید. یکی از ارائهدهنده، غیرقابل جعل و با مشخصات ناقص. دیگری از کنترل پلین شما، با مشخصات کامل و قابل پخش مجدد. به تقاطع اعتماد کنید و هرگز به هیچکدام به تنهایی اعتماد نکنید.
- امضاکننده را کمیاب نگه دارید. اگر دقیقاً یک سرویس بتواند ادعای پلتفرم را مطرح کند، داستان اعتماد شما در یک جمله جا میشود.
- به لایهای که هنوز مالک آن هستید متصل شوید. روی رایانش مدیریتشده شما به ندرت میزبان یا سیستم راهاندازی آن را کنترل میکنید، اما تقریباً همیشه زمان اجرا را کنترل میکنید: یک پلاگین، یک عامل، یک نقطه ورود. اتستیشن به آنجا تعلق دارد.
- سیاست تقویت را عمدی تعیین کنید. موتورهای توزیعشده هر عملیات به ازای هر فرآیند را در موازیسازی خود ضرب میکنند.
- اتستیشن را تکرارپذیر و اعتبارنامهها را کوتاهمدت کنید. تمدید یک نیاز است، نه یک پیگیری.
آنچه نتیجه را قابل اعتماد میکند این است که هیچ شرکتکنندهای به تنهایی هویت صادر نمیکند. نه بار کاری، نه کنترل پلین، نه ارائهدهنده. و بار کاری، که در ضعیفترین موقعیت برای اعتماد قرار دارد، هرگز خواسته نمیشود که به Nفع خودش شهادت دهد.
منبع: netflixtechblog.com
