معرفی
این راهنما بر پایه مستندات رسمی پروژه نوشته شده و هدف آن ساختن یک مدل ذهنی دقیق برای طراحی، عملیات و عیبیابی است.
نکته 1
Linux namespaceها viewهای مستقل برای PID، mount، network، user، IPC، UTS و cgroup میسازند.
نکته 2
namespace بهتنهایی sandbox کامل نیست و kernel مشترک باقی میماند.
نکته 3
ترکیب namespace با capability، seccomp و LSMهایی مثل AppArmor/SELinux مرز قویتری میسازد.
برداشت عملیاتی IROSG
قبل از هر تغییر Production، نسخه، وابستگی، سطح دسترسی، rollback و observability را بررسی کنید. منبع رسمی همین صفحه مرجع اصلی جزئیات است.
جداسازی دید با جداسازی کامل ماشین فرق دارد
Namespace به گروهی از فرایندها دیدی جدا از یک نوع منبع kernel میدهد. نکته کلیدی عبارت «یک نوع منبع» است. داشتن network namespace تازه به تنهایی فایلها، شناسه کاربران یا پردازنده را جدا نمیکند. کانتینر معمولاً ترکیبی از چند namespace و کنترل دیگر است. به همین دلیل، برای توضیح امنیت یا عیبیابی باید مشخص کنید کدام namespaceها مشترک و کدام جدا هستند. واژه کانتینر بدون این جزئیات، مرز واقعی را نشان نمیدهد.
همه این محیطها همچنان از kernel میزبان استفاده میکنند. یک ماشین مجازی با kernel مستقل مدل دیگری دارد. Namespace میتواند دید و دسترسی را محدود کند، ولی سقف مصرف منابع یا فیلتر همه فراخوانیهای سیستمی را خودکار ایجاد نمیکند. cgroup، capability، seccomp و سیاستهای امنیتی نقشهای مکمل دارند. این تمایز برای طراحی مهم است: اگر مسئله شما مصرف بیرویه حافظه است، فقط ساخت namespace تازه راهحل کافی نیست.
PID namespace و چند شناسه برای یک فرایند
در PID namespace، فرایندها مجموعه شناسههای قابل مشاهده متفاوتی دارند. یک فرایند میتواند از دید داخل شناسهای داشته باشد و از دید میزبان با شناسه دیگری دیده شود. بنابراین شماره PID ثبتشده در log کانتینر را بدون زمینه با PID میزبان مقایسه نکنید. هنگام بررسی، شناسه محیط و زمان را نیز نگه دارید. PID قابل استفاده مجدد است و به تنهایی شناسه دائمی یک اجرای برنامه نیست.
فرایند نخست در PID namespace نقش ویژهای در چرخه عمر و رسیدگی به فرایندهای فرزند دارد. برنامهای که برای اجرای معمول نوشته شده ممکن است به عنوان PID نخست رفتار مورد انتظار در دریافت سیگنال یا جمعآوری فرزندهای پایانیافته نداشته باشد. این مسئله میتواند به shutdown کند یا باقی ماندن zombie منجر شود. استفاده از سازوکار init مناسب و آزمون سیگنال توقف، بخشی از طراحی کانتینر است. فقط اجرای موفق برنامه در ابتدا کافی نیست.
Mount namespace و انتشار mount
Mount namespace نمای mountها را جدا میکند. دو فرایند میتوانند یک مسیر مشابه را به منابع متفاوت ببینند، یا یکی mount تازهای داشته باشد که دیگری ندارد. این ویژگی پایه مهمی برای ساخت root filesystem کانتینر است. با این حال، اشتراک یا انتشار mountها قواعد جداگانه دارد. اگر propagation را ندانید، ممکن است انتظار جداسازی داشته باشید ولی تغییر mount از یک محیط به دیگری منتقل شود یا برعکس، mount مورد نیاز دیده نشود.
برای آزمایش، از پوشه و فایلسیستم موقت در محیط غیرحساس استفاده کنید. قبل و بعد از ایجاد namespace، فهرست mountها را مقایسه و یک mount آزمایشی اضافه کنید. سپس از فرایند بیرونی بررسی کنید آیا تغییر دیده میشود. نتیجه را با وضعیت propagation تطبیق دهید. از mount کردن روی مسیرهای حیاتی میزبان برای آموزش خودداری کنید؛ همان مفهوم با یک مسیر آزمایشی روشن میشود و خطر مخفی کردن فایلهای مهم را ندارد.
Network namespace؛ پشته مستقل، نه اتصال خودکار
Network namespace رابطها، routeها، پورتها و بخشهای مربوط به پشته شبکه را جدا میکند. داخل محیط تازه، وجود مسیر اینترنت یا حتی فعال بودن loopback را فرض نکنید. معمولاً باید رابط و route مناسب پیکربندی شود. زوج veth میتواند دو محیط را مانند دو سر یک کابل مجازی متصل کند، اما آدرسدهی و سیاست عبور ترافیک همچنان لازم است. ساخت رابط به تنهایی ارتباط کاربردی ایجاد نمیکند.
یک آزمایش ساده، دو namespace با یک زوج veth و آدرسهای آزمایشی است. ابتدا دسترسی مستقیم میان دو سر را بررسی کنید و سپس route اضافی یا bridge را معرفی کنید. این ترتیب کمک میکند خطای لایه لینک از خطای مسیریابی جدا شود. اگر DNS کار نمیکند ولی ارتباط IP برقرار است، مسئله را در نامگشایی جستوجو کنید، نه اینکه فوراً namespace را حذف کنید. تنظیم resolver ممکن است به فایلهای mountشده و ابزار مدیریت وابسته باشد.
پورت یکسان در محیطهای متفاوت
دو برنامه در دو network namespace میتوانند روی شماره پورت یکسان گوش دهند، زیرا فضای پورت آنها جداست. اما برای دسترسی از بیرون باید مسیر مشخصی مانند forwarding یا proxy وجود داشته باشد. پورت publishشده روی میزبان با پورت داخل محیط یکسان نیست، حتی اگر عددشان برابر باشد. در عیبیابی، سه نقطه را جدا ببینید: listener داخل، مسیر انتقال و listener یا قاعده روی میزبان. موفقیت اتصال داخل کانتینر، دسترسی از شبکه بیرونی را ثابت نمیکند.
وقتی capture میگیرید، محل مشاهده اهمیت دارد. بسته ممکن است در یک سر veth دیده شود و در رابط دیگری به دلیل سیاست فیلتر یا route نرسد. ثبت همزمان در چند نقطه محدود و مرتبط، از گرفتن حجم عظیم ترافیک همه میزبان مفیدتر است. آدرسها ممکن است پس از NAT تغییر کنند؛ برای پیوند رخدادها به زمان و مشخصات جریان توجه کنید. این روش مسیر واقعی را به جای حدس از روی تنظیمات نشان میدهد.
User namespace و نگاشت هویت
User namespace اجازه میدهد شناسههای کاربر و گروه در داخل و بیرون نگاشت متفاوت داشته باشند. ممکن است فرایند داخل خود را root ببیند ولی روی میزبان به یک شناسه غیرممتاز نگاشت شده باشد. این قابلیت برای اجرای بدون root سراسری مهم است، اما به معنی قدرت نامحدود داخل یا بیخطر بودن همه عملیات نیست. capabilityها و مالکیت منابع نسبت به namespace مربوط تفسیر میشوند و برخی عملیات همچنان محدود هستند.
نگاشت UID و GID باید دقیق بررسی شود، بهویژه هنگام دسترسی به فایلهای مشترک میزبان. فایلی که داخل مالک مشخصی دارد ممکن است بیرون با شناسه دیگری دیده شود. تغییر مالکیت گسترده برای رفع سریع خطای permission میتواند داده میزبان را تحت تأثیر قرار دهد. ابتدا نگاشت و مالکیت واقعی را بخوانید و سپس حداقل تغییر لازم را انجام دهید. در ذخیره مشترک، این موضوع باید بخشی از قرارداد استقرار باشد، نه اصلاح دستی پس از هر اجرا.
namespaceهای دیگر چه چیزی را جدا میکنند؟
UTS به نام میزبان و اطلاعات مربوط آن دید جدا میدهد. تغییر hostname داخل یک محیط الزاماً نام میزبان اصلی را تغییر نمیدهد، مشروط به اینکه namespace جدا باشد. IPC منابع ارتباط میانفرایندی مشخصی را جدا میکند. Cgroup namespace نمای مسیرهای cgroup را تغییر میدهد، ولی خود آن به معنی اعمال سقف مصرف نیست. Time namespace نیز درباره بعضی ساعتهاست و نباید با امکان تغییر مستقل همه جنبههای زمان سیستم یکسان فرض شود. هر نوع باید بر اساس منبعی که واقعاً پوشش میدهد فهمیده شود.
این تفکیک در بررسی کانتینرهای دارای اشتراک عمدی مهم است. ممکن است چند فرایند network namespace مشترک داشته باشند ولی mount namespace متفاوت. در چنین حالتی، دسترسی به localhost میان آنها میتواند برقرار باشد، در حالی که فایلهای یکدیگر را نبینند. بنابراین یک ویژگی مشترک را به همه منابع تعمیم ندهید. برای هر رابطه، سؤال دقیق بپرسید: کدام فرایند چه منبعی را با کدام فرایند دیگر شریک است؟
مشاهده و ورود برای عیبیابی
رابطهای proc اطلاعات namespaceهای فرایند را نشان میدهند و ابزارهایی مانند lsns میتوانند نمایی از آنها بدهند. مقایسه شناسه namespace میان دو فرایند راهی برای بررسی اشتراک است. ابزار nsenter امکان ورود به namespace مشخص فرایند هدف را میدهد، ولی باید نوع و هدف را صریح انتخاب کنید. ورود به namespace شبکه به معنی ورود خودکار به همه محیط فایل و کاربر نیست. این دقت جلوی اجرای فرمان در زمینه اشتباه را میگیرد.
پیش از اجرای فرمان تغییردهنده، شناسه فرایند هدف و هویت محیط را دوباره بررسی کنید. فرایند ممکن است پایان یافته و PID دوباره استفاده شده باشد. در رخداد تولید، ابتدا فرمانهای مشاهدهای اجرا کنید و خروجی را ثبت کنید. تغییر route یا mount در namespace اشتباه میتواند به سرویس دیگری اثر بگذارد. داشتن برچسب واضح محیط و فهرست فرمانهای محدود برای بررسی، احتمال این اشتباه را کاهش میدهد.
چرخه عمر و منابع باقیمانده
عمر namespace فقط به یک shell تعاملی وابسته نیست؛ ارجاعها و فرایندهای دیگر میتوانند آن را نگه دارند. اگر محیطی ظاهراً بسته شده ولی منابع باقیاند، باید فرایندها و ارجاعهای نگهدارنده بررسی شوند. حذف نام یک namespace شبکه با پایان یافتن همه استفادهکنندگان آن یکسان نیست. برای آزمایشگاه، روش cleanup مشخص داشته باشید و پس از پایان، رابطها، mountها و فرایندهای باقیمانده را بررسی کنید.
در پایان طراحی، آزمونهای منفی انجام دهید: فرایند نباید PIDهای نامجاز را ببیند، mount خصوصی نباید بیدلیل منتشر شود و مسیر شبکه خارج از سیاست نباید در دسترس باشد. سپس آزمون مصرف منابع و محدودیت syscall را با ابزار مربوط به همان کنترلها انجام دهید. این مجموعه نشان میدهد هر لایه مسئول چه چیزی است. امنیت کانتینر از ترکیب کنترلهای مشخص و آزموده به دست میآید، نه از نام یک فناوری منفرد.
تفاوت namespace با chroot
تغییر ریشه مسیر با chroot نمای مسیر فایل را عوض میکند، اما مجموعه همه جداسازیهای namespace را ایجاد نمیکند. برنامه داخل آن ممکن است همچنان شبکه و PIDهای مشترک داشته باشد و سطح دسترسی آن تعیینکننده است. بنابراین chroot را به تنهایی معادل کانتینر امن معرفی نکنید. در یک آزمایش آموزشی، خروجی مشاهده فرایند و شبکه را قبل و بعد از تغییر ریشه مقایسه کنید تا این محدودیت روشن شود. همچنین دیدن فایلسیستم متفاوت دلیل کافی برای نتیجهگیری درباره user namespace نیست. هر کنترل باید با شاهد خودش تأیید شود؛ ظاهر محیط shell میتواند بسیار شبیه یک سیستم مستقل باشد، در حالی که منابع مهم همچنان مشترک باقی ماندهاند.