شکست chroot در OpenVPN: قاعده AppArmor یا توانایی؟
افزودن یک مسیر به نمایه AppArmor هرگز CAP_SYS_CHROOT را نمیدهد، و رد شدن chroot میتواند از سه لایه جداگانه بیاید. مشخص کنید کدام را تغییر دهید.
سرویسدهنده خطای chroot را ثبت میکند و نمایه از پیش سخاوتمند به نظر میرسد، پس راهحل وسوسهانگیز گستردن یک قاعده مسیر و راهاندازی دوباره است. این کار معمولاً چیزی را عوض نمیکند، چون chroot را امتیازی میبندد که نمایه هرگز نمیتوانست واگذار کند. تصمیمگیری درباره اینکه کدام لایه را لمس کنید سه بررسی ارزان میخواهد: آنچه فرایند مجاز است نگه دارد، آنچه سیاست اجازه میدهد، و آنچه راهانداز پیش از اهمیت یافتن آن دو حذف کرده است.
دو دروازه جدا، و فقط یکی مال AppArmor است
chroot به CAP_SYS_CHROOT در فضای نام کاربریای که فرایند در آن اجرا میشود نیاز دارد. این امتیاز متعارف لینوکس است که هسته پیش از آنکه فراخوان با مسیرها کاری کند بررسی میکند. جدا از آن، نمایه اجرایی AppArmor میتواند توانایی را حتی وقتی فرایند آن را دارد رد کند، چون سیاست اجباری در کنار اعتبارنامهها و نه به جای آنها بررسی میشود. این تفاوت در عمل مهم است: دو لایه در فایلهای متفاوت زندگی میکنند و معمولاً افراد متفاوتی آنها را نگه میدارند.
آن دو دروازه با یک errno شکست میخورند. هر دو EPERM تولید میکنند و هر دو در گزارش سرویسدهنده به شکل شکایت عمومی دسترسی ظاهر میشوند، و همین است که حدس زدن درباره محتوای نمایه اینهمه وقت تلف میکند. قاعده مسیری که میخواستید اضافه کنید دسترسی به فایل را پس از موفقیت chroot کنترل میکند؛ هیچ نقشی در مجاز بودن خود عملیات ندارد.
دروازه سوم بالادست هر دو قرار دارد. اگر مدیر سرویس یا یک پوشش، فرایند را بدون توانایی در مجموعه مرزیاش آغاز کرده باشد، هیچ تغییر AppArmor نمیتواند آن را برگرداند، و افزودن تواناییها به نمایه فقط نیتی را مستند میکند که هسته به آن عمل نخواهد کرد.
پیش از ویرایش سیاست، وضعیت امتیاز را بخوانید
از فرایند در حال اجرا شروع کنید، نه از پیکربندیای که فکر میکنید فعال است. فایل وضعیت پنج ماسک توانایی و پرچم no-new-privileges را نشان میدهد و هر یک به پرسشی متفاوت پاسخ میدهد:
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpnCapEff مجموعهای است که برای فراخوان اهمیت دارد: تواناییای که در CapBnd هست و در CapEff نیست، توسط سقف مجاز است و در حال حاضر نگه داشته نمیشود. CapAmb وقتی مهم میشود که باینری به معنای معمول setuid ممتاز نیست، چون تواناییهای محیطی راهی است که یک سرویس امتیازی را در طول exec بدون تواناییهای فایل نگه میدارد. CapInh بیتهایی دارد که میتوانند در طول exec به ارث برسند، اما تنها وقتی مجموعه قابل ارث فایل اجراشده اجازه دهد، پس یک بیت در آنجا بهتنهایی چیزی نمیدهد.
سپس تأیید کنید کدام نمایه واقعاً متصل است. فرایند میتواند با نام نمایهای که انتظارش را ندارید محدود شود، بهویژه وقتی توزیعی فایل واحد و نمایه خودش را با هم میفرستد، و خواندن /proc/PID/attr/current این را در یک خط روشن میکند. مقایسه فایل ویرایششدهتان با نمایهای که هسته گزارش میدهد تفاوت میان رفع علت و ویرایش فایلی است که هیچکس بار نمیکند.
قاعده توانایی را فقط وقتی اضافه کنید که ردپای حسابرسی میگوید
رکوردهای هسته و حسابرسی لایهای را که رد کرده نام میبرند. به دنبال ردی بگردید که عملیات capable و نام sys_chroot را همراه با نمایهای که آن را تولید کرده دارد. اگر آن رکورد وجود دارد، فرایند از پیش توانایی را داشته و AppArmor آن را رد کرده، و تغییر هدفمند یک قاعده توانایی درون نمایه واقعی است:
capability sys_chroot,آن را به صورت قطعهای در نمایهای که واقعاً متصل است نگه دارید، و کلیدواژه را با مرجع نمایه apparmor.d بسنجید نه با کپی کردن قاعدهای از آموزشی نامرتبط. گذاشتنش در یک include مشترک آن را به هر برنامه محدودشدهای میدهد که آن فایل را میخواند، و تغییر نمایه به حالت complain رد شدن بعدی را پنهان میکند به جای حل این یکی. قاعده نمایه یک رد سیاستی را برمیدارد؛ امتیاز نمیسازد، پس فرایندی که هرگز CAP_SYS_CHROOT را در مجموعه مؤثرش نداشته با وجود قاعده هم شکست میخورد.
اگر رکورد حسابرسی هیچ رد AppArmor نشان ندهد، ویرایش نمایه را متوقف کنید. با مشکلی در اعتبارنامهها روبهرو هستید و کار باقیمانده در فایل واحد یا فرایند والد است، نه در زبان سیاست.
مدیر سرویس سقف خودش را نگه میدارد
systemd برای هر سرویس یک مجموعه مرزی توانایی تعیین میکند و بهطور پیشفرض آن مجموعه تنگ است. هر چیزی که آنجا کنار گذاشته شود بدون توجه به آنچه نمایه میگوید برای فرایند در دسترس نیست، و همین است که واحدهای سختشده میتوانند در عملیاتی شکست بخورند که وقتی سرویسدهنده دستی راهاندازی میشد کار میکرد. دستورهای مربوطه در واحد آسان خوانده میشوند و وقتی برنامه خطا را گزارش میکند آسان از چشم میافتند. drop-inهایی که بسته توزیع میفرستد منبع معمول سقفی هستند که هیچکس به یاد ندارد پیکربندی کرده است.
بالا بردن سقف و اعطای توانایی دو تغییر است، نه یکی. یک ورودی مجموعه مرزی میگوید فرایند میتواند توانایی را نگه دارد؛ آن را به مجموعه مؤثر اضافه نمیکند، و NoNewPrivs میتواند مانع موفقیت یک ارتقای بعدی شود حتی وقتی همه چیز دیگر جور است. فقط آنچه عملیات نیاز دارد را اضافه کنید، بقیه سختسازی را دستنخورده نگه دارید و در برابر میانبر اعطای همه تواناییها برای عبور یک فراخوان مقاومت کنید.
ترتیب اینجا هم مهم است. ابتدا سقف واحد را تغییر دهید، تأیید کنید که CapEff اکنون بیت را دارد، و تنها پس از آن تصمیم بگیرید آیا AppArmor هنوز رد میکند. انجام همزمان هر دو تغییر اگر خطا ادامه یابد شما را دوباره در حال حدس زدن میگذارد.
پس از پاک شدن رد، خود زندان را بررسی کنید
chroot موفق ریشه را جابهجا میکند، پس هر مسیری که سرویسدهنده بعد از آن لمس میکند درون درخت جدید حل میشود. پیکربندی گمشده، گره دستگاه غایب یا پوشه گواهیای که کپی نشده اکنون به شکل خطای فایل ظاهر میشوند و آن خطا مشکلی در چیدمان است نه در توانایی. رویداد بعدی را بخوانید به جای افزودن مجوز گسترده دیگر. این جابهجایی معنای مسیر نسبی را هم عوض میکند، و همین است که راهنماهای سختسازی بر مسیرهای مطلق در پیکربندی سرویسدهنده پافشاری میکنند.
یادآوری اینکه chroot چه نمیکند هم کمک میکند. این یک مرز مسیر است، نه جعبه شنی: فرایندی که امتیاز را نگه دارد میتواند از آن بگریزد، و همین است که توانایی معمولاً پس از فراخوان رها میشود و همین است که محدودسازی باید دور سرویسدهنده باقی بماند. نگه داشتن همزمان نمایه AppArmor، مجموعه مرزی توانایی و محدودیتهای فایلسیستم هدف این تمرین است، نه باقیماندهای برای حذف پس از شروع سرویس.
قاعده تصمیم کوتاه است. اگر رکورد حسابرسی AppArmor را نام ببرد، یک قاعده توانایی به نمایه متصل اضافه کنید. اگر مجموعه مؤثر خالی است، سقف واحد و ترتیب رها کردن امتیاز را درست کنید. اگر نمایه همان نیست که ویرایش کردید، ویرایش را متوقف کنید و نمایه واقعی را بیابید.