آموزش SELinux در CentOS 7 — بخش سوم: کاربران

۶۴ بازدید
آخرین به‌روزرسانی: ۲۶ شهریور ۱۴۰۲
زمان مطالعه: ۱۹ دقیقه
آموزش SELinux در CentOS 7 — بخش سوم: کاربران

در بخش نخست از این سلسله مطالب آموزشی SElinux (+) به معرفی مفاهیم پایه پرداختیم. همچنین در بخش دوم با طرز امن سازی پردازش‌ها و فایل‌ها در SELinux (+) آشنا شدیم و log-های خطا و شیوه تفسیر پیام‌های خطا نیز توضیح داده شد.

نکته: دستورها، بسته‌ها و فایل‌هایی که در این راهنما می‌بینید، همگی روی توزیع CentOS 7 تست شده‌اند؛ اما همه مفاهیم مطرح شده روی هر توزیع دیگر لینوکس نیز معتبر هستند.

در این راهنما دستورها را به صورت کاربر root اجرا می‌کنیم؛ مگر این که جز آن ذکر شده باشد. اگر به حساب کاربری root دسترسی ندارید می‌توانید از حساب کاربری دیگری با دسترسی sudo بری اجرای دستورها بهره بگیرید. فقط دقت کنید که باید در ابتدای دستورهای کلیدواژه sudo را اضافه کنید.

کاربران SELinux

کاربران SELinux نهادهای متفاوتی از حساب‌های کاربری معمولی لینوکس که شامل root نیز می‌شود، به حساب می‌آیند. یک کاربر SELinux چیزی نیست که بتوان با یک دستور خاص آن را ایجاد کرد و یا دسترسی ورود خاصی برای سرور داشته باشد. بلکه کاربران SELinux در سیاست (policy) که در زمان بوت در حافظه بارگذاری شده است تعریف شده‌اند و تعداد آن‌ها نیز معدود است. نام‌های کاربران با پسوند u_ خاتمه می‌یابد، چنان که نام‌های دامنه با پسوند t_ و نقش‌ها نیز با r_ پایان می‌گیرند.

کاربران مختلف SELinux حقوق متفاوتی در سیستم دارند و همین نکته باعث شده است که مفید باشند.

کاربر SELinux که در بخش نخست چارچوب امنیتی یک فایل مشخص شده است، کاربری است که مالک آن فایل است. این وضعیت همانند زمانی است که به وسیله یک دستور ls –l مالک یک فایل را به طور معمول در لینوکس بررسی می‌کنید. یک برچسب کاربر در چارچوب پردازش نماینده کاربر SELinux-ی است که یک پردازش به وسیله آن اجرا شده است.

زمانی که SELinux در حالت enforced اجرا شده باشد، هر کاربر معمولی لینوکس به یک حساب کاربری SELinux نگاشت می‌شود. این امکان وجود دارد که چندین حساب کاربری به یک کاربر SELinux نگاشت شوند. این نوع نگاشت به یک حساب کاربری معمولی امکان می‌دهد که مجوزهایی را از همتای SELinux خود به ارث ببرد.

برای مشاهده این نگاشت می‌توانیم دستور زیر را اجرا کنیم:

semanage login –l

در CentOS 7 خروجی دستور فوق چیزی مانند زیر خواهد بود:

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

ستون اول این جدول به نام «Login Name» نشان‌دهنده حساب‌های کاربری لینوکس محلی است؛ اما تنها سه مورد در این جا فهرست شده‌اند و ممکن است این سؤال برایتان پیش آید که آن چند حساب کاربری که در بخش پیشین این سلسله مطالب ایجاد کردیم کجا هستند؟ دقت کنید که آن حساب‌های کاربری در مدخل __default__ نمایش می‌یابند. در واقع هر حساب کاربری معمولی لینوکس ابتدا به کاربر default نگاشت می‌شود. سپس به کاربری SELinux به نام unconfined_u نگاشت می‌شود. در مورد مثالی که ارائه کردیم، این حساب کاربری در ستون دوم ردیف نخست قابل مشاهده است. ستون سوم نشان‌دهنده طبقه امنیت چند سطحی/امنیت چند دسته‌ای (MLS / MCS) برای کاربر است. در حال حاضر این بخش و همچنین ستون پس از آن (Service) را نادیده می‌گیریم.

سپس حساب کاربری root را داریم. دقت کنید که این حساب به کاربرهای default نگاشت نشده است و برای خود مدخل مستقلی دارد. در این مورد نیز root به کاربر SELinux به نام unconfined_u نگاشت شده است.

system_u نیز طبقه متفاوتی از کاربران است که به منظور اجرای پردازش‌ها و daemon ها استفاده می‌شود.

برای مشاهده کاربران SELinux موجود در سیستم می‌توانید دستور زیر را اجرا کنیم:

semanage user –l

خروجی آن در CentOS 7 باید چیزی مانند زیر باشد:

                 Labeling   MLS/       MLS/
SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles

guest_u         user       s0         s0                             guest_r
root            user       s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r unconfined_r
staff_u         user       s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r unconfined_r
sysadm_u        user       s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r unconfined_r
unconfined_u    user       s0         s0-s0:c0.c1023                 system_r unconfined_r
user_u          user       s0         s0                             user_r
xguest_u        user       s0         s0                             xguest_r

معنی جدول بزرگ‌تر چیست؟ قبل از همه این جدول نشان می‌دهد که کاربران SELinux بر اساس سیاست (policy) تعریف شده‌اند. ما کاربرانی مانند unconfined_u and system_u را قبلاً دیدیم؛ اما اینک انواع دیگری از کاربران مانند guest_u, staff_u, sysadm_u, user_u و غیره را می‌بینیم. این نام‌ها تا حدودی نمایانگر میزان دسترسی‌هایی خود هستند. برای نمونه احتمالاً می‌توانیم حدس بزنیم که کاربر sysadm_u دسترسی‌های بیشتری نسبت به guest_u دارد.

برای تأیید حساب guest باید به ستون پنجم یعنی SELinux Roles نگاه کنیم. اگر از بخش نخست این سلسله مطالب به خاطر داشته باشید، نقش‌های SELinux را به عنوان دروازه‌هایی بین کاربر و پردازش تصور کردیم. همچنین آن‌ها را با فیلترها مقایسه کریم یعنی یک کاربر می‌تواند وارد یک نقش شود، به شرط این که آن نقش به وی اعطا شده باشد. اگر یک نقش مجوز دسترسی به یک دامنه پردازشی را داشته باشد، کاربران مرتبط با آن نقش می‌توانند وارد آن دامنه پردازشی شوند.

اینک از جدول فوق درمی‌یابیم که کاربر unconfined_u به نقش‌های system_r و unconfined_r نگاشت شده است. با این که این موضوع در اینجا مشخص نیست؛ اما سیاست SELinux در واقع به این نقش‌ها امکان اجرای پردازش‌هایی در دامنه unconfined_t را می‌دهد. به طور مشابه کاربر sysadm_u برای نقش sysadmr مجوز دارد؛ اما guestu به نقش guest_r نگاشت شده است. هر یک از این نقش‌ها دامنه‌های مجاز خاصی برای خود دارند.

اکنون اگر یک گام به عقب برداریم، می‌بینیم که نخستین قطعه کد که کاربر default را به کاربر unconfinedu نگاشت می‌کند، مانند این است که کاربر root را به کاربر unconfined_u نگاشت کرده است. از آنجا که **default_** login نماینده همه حساب‌های کاربری معمولی لینوکس است، آن حساب‌ها مجوز نقش‌های system_r و unconfined_r را نیز کسب خواهند کرد.

بنابراین معنی عملی گفته‌های فوق این است که هر حساب کاربری لینوکس که به کاربر unconfined_u نگاشت شود مجوزهای اجرای هر اپلیکیشنی درون دامنه unconfined_t را خواهد داشت.

برای نمایش این مسئله دستور زیر را به عنوان کاربر root اجرا کنید:

id –Z

خروجی این دستور چارچوب امنیتی حساب کاربری root را نشان می‌دهد:

unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

بنابراین حساب root به حساب کاربری unconfined_u در SELinux نگاشت شده است و چون unconfined_u مجوز نقش unconfined_r را دارد، می‌تواند پردازش‌هایی که در دامنه unconfined_t قرار دارند را اجرا کند.

پیشنهاد ما این است که در این مرحله، چند نشست SSH جدید را با چهار حساب کاربری که قبلاً ایجاد کردیم در چهار پنجره متفاوت باز کنید تا به تناوب در موارد نیاز بین آن‌ها سوئیچ بکنید:

  • regularuser
  • switcheduser
  • guestuser
  • restricteduser

سپس به پنجره نشست ترمینالی که با حساب regularuser وارد شده‌اید بروید. اگر به خاطر داشته باشید ما چند حساب کاربری را در بخش دوم این سلسله آموزش ایجاد کردیم و regularuser یکی از این حساب‌های کاربری بود. اگر قبلاً این حساب را ایجاد نکرده‌اید، هم اکنون می‌توانید یک پنجره جداگانه باز کنید تا به عنوان یک regularuser به سیستم CentOS 7 خود متصل شوید. اگر همین دستور id –Z را در این پنجره اجرا کنید، خروجی چیزی شبیه زیر خواهد بود:

[regularuser@localhost ~]$ id –Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

در این مورد حساب کاربری regulauser به حساب کاربری unconfined_u در SELinux نگاشت شده است و می‌تواند نقش unconfined_r را داشته باشد. این نقش می‌تواند پردازش‌هایی را در دامنه نامحدود اجرا کند. این همان user/role/domain در SELinux است که کاربر root به آن نگاشت می‌شود. به همین دلیل است که سیاست هدف‌گیری شده SELinux به کاربران وارد شده به سیستم امکان اجرای دامنه‌های نامحدود را می‌دهد.

ما برخی از کاربران SELinux را قبلاً مشاهده کرده‌ایم:

  • guest_u: این کاربر به سیستم o X-Window یا همان GUI و یا شبکه‌بندی دسترسی ندارد و نمی‌تواند دستورهای su/sudo را اجرا کند.
  • xguest_u: این کاربر به ابزارهای GUI و شبکه‌بندی دسترسی دارد و از طریق مرورگر فایرفاکس در دسترسی است.
  • user_u: این کاربر دسترسی‌هایی بالاتر از حساب‌های میهمان دارد (GUI و شبکه‌بندی)؛ اما نمی‌تواند از کلیدواژه‌های su/sudo برای سوئیچ کاربر استفاده کند.
  • staff_u: برخی حقوق آن شبیه کاربر user_u است، به جز این که می‌تواند دستور sudo را برای کسب دسترسی‌های root اجرا کند.
  • system_u: این کاربر به منظور اجرای سرویس‌های سیستمی طراحی شده است و به حساب‌های کاربری معمولی نگاشت نمی‌شود.

بخش اول بررسی عملی SELinux: محدودسازی دسترسی کاربر سوئیچ شده

برای این که ببینیم SELinux چگونه امنیت را برای حساب‌های کاربری برقرار می‌سازد، باید ابتدا حساب کاربری regularuser را در نظر بگیریم. شما به عنوان یک مدیر سیستم می‌دانید که این کاربر هم اینک دسترسی‌های نامحدود SELinux همانند حساب root دارد و می‌خواهید که این وضعیت را تغییر دهید. به طور خاص شما نمی‌خواهید که این کاربر بتواند به حساب‌های کاربری دیگر مانند حساب root سوئیچ کند.

ابتدا بررسی می‌کنیم که آیا کاربر توانایی سوئیچ به حساب دیگری را دارد یا نه. در قطعه کد زیر، regularuser به حساب switcheduser سوئیچ می‌کند. ما فرض می‌کنیم که وی رمز عبور switcheduser را می‌داند:

[regularuser@localhost ~]$ su - switcheduser
Password:
[switcheduser@localhost ~]$

سپس به پنجره ترمینال که با حساب root وارد شده بودیم، بازمی‌گردیم و نگاشت کاربری SELinux حساب regularuser را تغییر می‌دهیم. ما regularuser را به user_u تغییر خواهیم داد:

semanage login -a -s user_u regularuser

بنابراین ما با اضافه کردن یک a- بیان کردیم که حساب regularuser به حساب کاربری SELinux -s به نام user_u اضافه شود. اگر به پنجره ترمینال حساب regularuser بازگردیم، ابتدا مجدد به حساب switcheduser سوئیچ می‌کنیم:

[switcheduser@localhost ~]$ logout

سپس از regularuser نیز خارج می‌شویم:

[regularuser@localhost ~]$ logout

سپس یک پنجره ترمینال باز می‌کنیم تا به regularuser وصل شویم. در این زمان دوباره به switcheduser سوئیچ می‌کنیم:

[regularuser@localhost ~]$ su – switcheduser
Password:

این آن چیزی است که ما در این مرحله مشاهده می‌کنیم:

su: Authentication failure

اگر اکنون دستور id –Z را مجدداً اجرا کنیم تا چارچوب SELinux را برای regularuser ببینیم، شاهد خواهیم بود که خروجی آن کاملاً متفاوت از آن چیزی است که قبلاً دیدیم: regularuser اینک به user_u نگاشت شده است:

[regularuser@localhost ~]$ id -Z
user_u:user_r:user_t:s0

شاید از خود بپرسید که چرا ما باید به چنین محدودیتی نیاز داشته باشیم؟ برای مثال یک تیم توسعه اپلیکیشن را تصور کنید که درون واحد IT یک سازمان مستقر هستند. این تیم ممکن است چند توسعه‌دهنده و یک تست‌کننده داشته باشند که به کدنویسی و تست کردن جدیدترین نسخه اپلیکیشن شرکت می‌پردازند. شما به عنوان مدیر سیستم می‌دانید که توسعه‌دهندگان از حساب‌های خودشان به برخی حساب‌های با دسترسی‌های بالا‌تر سوئیچ می‌کنند تا تغییرات خاصی را در سرور ایجاد کنند. شما می‌توانید این فرایند را با محدودسازی توانایی آن‌ها برای سوئیچ بین حساب‌ها متوقف کنید. البته دقت کنید که این فرایند آن‌ها را از ورود مستقیم به حساب‌های کاربری با دسترسی‌های بالاتر بازنمی‌دارد.

بخش دوم بررسی عملی SELinux: محدودسازی مجوزهای اجرای اسکریپت‌ها

در این بخش مثال دیگری از محدودسازی حساب‌های کاربری را از طریق SELinux مشاهده می‌کنیم دستورهای زیر را در نشست root اجرا کنید.

به طور پیش‌فرض SELinux اجازه نگاشت کاربران به حساب guest_t برای اجرای اسکریپت‌ها از دایرکتوری‌های home خودشان را می‌دهد. ما می‌توانیم دستور getsebool را برای بررسی مقدار بولی آن اجرا کنیم:

getsebool allow_guest_exec_content

خروجی دستور فوق نشان می‌دهد فلگ مربوطه روشن است:

guest_exec_content --> on

برای تأیید این تأثیر، ابتدا باید نگاشت کاربر SELinux را برای حساب guestuser که در ابتدای راهنما ایجاد کردیم، تغییر دهیم. این کار را به عنوان کاربر root انجام می‌دهیم:

semanage login -a -s guest_u guestuser

این اقدام را از طرق اجرای دستور زیر می‌توانیم تأیید کنیم:

semanage login –l

همان طور که می‌بینید، guestuser اینک به حساب کاربری guest_u در SELinux نگاشت شده است:

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
guestuser            guest_u              s0                   *
regularuser          user_u               s0                   *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

اگر یک پنجره ترمینال با کاربر guestuser باز کرده باشیم باید از آن خارج شده و با پنجره ترمینال جدیدی با حساب کاربری guestuser وارد شویم.

سپس یک اسکریپت bash خیلی ساده در دایرکتوری home کاربر ایجاد می‌کنیم. در قطعه کد زیر ابتدا دایرکتوری home بررسی می‌شود و سپس فایلی ایجاد شده و آن را در کنسول می‌خوانیم. در نهایت مجوز اجرایی تغییر می‌یابد.

دایرکتوری home را با دستور زیر بررسی کنید:

[guestuser@localhost ~]$ pwd
/home/guestuser

اسکریپت را ایجاد کنید:

[guestuser@localhost ~]$ vi myscript.sh

محتوای اسکریپت چنین خواهد بود:

#!/bin/bash
echo "This is a test script"

اسکریپت را به حالت اجرایی درمی‌آوریم:

chmod u+x myscript.sh

زمانی که تلاش می‌کنیم اسکریپت را با حساب guestuser اجرا کنیم، همان طور که انتظار داریم کار می‌کند:

[guestuser@localhost ~]$ ~/myscript.sh
This is a test script

سپس به پنجره ترمینال بازمی‌گردیم و تنظیمات بولی allow_guest_exec_content را off کرده و آن را بررسی می‌کنیم:

setsebool allow_guest_exec_content off

getsebool allow_guest_exec_content
guest\_exec\_content --> off

دوباره به عنوان کاربر guestuser بازمی‌گردیم و سعی می‌کنیم اسکریپت را اجرا کنیم. این بار دسترسی ما انکار می‌شود:

[guestuser@localhost ~]$ ~/myscript.sh
-bash: /home/guestuser/myscript.sh: Permission denied

SELinux بدین ترتیب می‌تواند یک لایه اضافی امنیت را روی DAC اعمال کند. حتی زمانی که کاربر دسترسی کامل خواندن، نوشتن و اجرایی برای اسکریپت ایجاد شده در دایرکتوری home خودش داشته باشد، می‌توان وی را از اجرای آن بازداشت. شاید برای شما سؤال باشد که چنین امکانی کجا به درد می‌خورد؟ به یک سیستم production فکر کنید که توسعه‌دهندگان برای اجرای برخی قراردادهای پیمانکاری برای شرکت به آن دسترسی دارند. شما قصد دارید به آن‌ها دسترسی صرفاً جهت مشاهده پیام‌های خطا و فایل‌های log بدهید؛ اما نمی‌خواهید آن‌ها بتوانند اسکریپت‌های شل (shell) را روی سرور اجرا کنند. بدین منظور می‌توانید ابتدا SELinux را فعال کنید و سپس اطمینان حاصل کنید که مقدار بولی متناظر تنظیم شده است.

ما قبلاً به اختصار در مورد پیام‌های خطا در SELinux صحبت کرده‌ایم؛ اما اکنون اگر علاقه‌مند باشید بدانید پیام‌های انکار دسترسی در کجا ذخیره می‌شوند، می‌توانید به فایل var/log/messages/ مراجعه کنید. دستور زیر را در نشست root اجرا کنید:

grep "SELinux is preventing" /var/log/messages

دو پیام آخر در فایلی که روی سرور CentOS 7 قرار دارد، انکار دسترسی را نشان می‌دهند:

Aug 23 12:59:42 localhost setroubleshoot: SELinux is preventing /usr/bin/bash from execute access on the file. For complete SELinux messages. run sealert -l 8343a9d2-ca9d-49db-9281-3bb03a76b71a
Aug 23 12:59:42 localhost python: SELinux is preventing /usr/bin/bash from execute access on the file.

این پیام همچنین یک مقدار ID طولانی را نشان می‌دهد و به ما پیشنهاد می‌کند که دستور sealer را با این ID برای کسب اطلاعات بیشتر اجرا کنیم. دستور را ما در بخش زیر اجرا کرده‌ایم (شما باید از ID مربوط به خود استفاده کنید):

sealert -l 8343a9d2-ca9d-49db-9281-3bb03a76b71a

خروجی دستور فوق در واقع جزییات بیشتری در مورد خطا به ما نشان می‌دهد:

SELinux is preventing /usr/bin/bash from execute access on the file .

*****  Plugin catchall_boolean (89.3 confidence) suggests   ******************

If you want to allow guest to exec content
Then you must tell SELinux about this by enabling the 'guest\_exec\_content' boolean.
You can read 'None' man page for more details.
Do
setsebool -P guest\_exec\_content 1

*****  Plugin catchall (11.6 confidence) suggests   **************************

...

این خروجی حجم بالایی دارد؛ اما کافی است به چند خط ابتدایی آن توجه کنید:

SELinux is preventing /usr/bin/bash from execute access on the file.

این جمله ایده مناسبی در مورد این که خطا از چه ناشی می‌شود در اختیار ما قرار می‌دهد. در چند خط بعدی نیز روش اصلاح خطا نشان داده شده است:

If you want to allow guest to exec content
Then you must tell SELinux about this by enabling the 'guest\_exec\_content' boolean.
...
setsebool -P guest\_exec\_content 1

بخش سوم بررسی عملی SELinux: محدودسازی دسترسی به سرویس‌ها

در بخش نخست این سلسله مطالب آموزشی SELinux (+) هنگام معرفی کاربران، نقش‌ها، دامنه‌ها و نوع‌ها، در مورد نقش‌های SELinux توضیح دادیم. اینک به طور عملی وظیفه نقش‌ها (roles) در محدودسازی دسترسی کاربر را بررسی می‌کنیم. همان طور که پیش‌تر اشاره کردیم، یک نقش در SELinux بین کاربر و دامنه پردازشی قرار می‌گیرد و تعیین می‌کند که کدام دامنه‌ها می‌توانند وارد آن شوند. نقش‌ها وقتی آن‌ها را در چارچوب امنیتی فایل بررسی می‌کردیم، چنین اهمیتی را نداشتند. در مورد فایل‌ها اهمیت نقش زمانی است که یک مقدار نوعی object_r برای آن فهرست می‌شود. نقش‌ها زمانی که با کاربران و پردازش‌ها سر و کار داریم اهمیت واقعی خود را می‌یابند.

ابتدا باید اطمینان حاصل کنید که httpd daemon روی سیستم اجرا نمی‌شود. شما به عنوان کاربر root می‌توانید دستور زیر را اجرا کنید تا مطمئن شوید که پردازش متوقف شده است:

service httpd stop

سپس به پنجره ترمینالی که به عنوان کاربر restricteduser وارد شده بودیم بازمی‌گردیم تا چارچوب امنیتی SELinux آن را ببینیم. اگر پنجره ترمینال بازی ندارید، می تونید یک نشست ترمینال جدید روی سیستم باز کنید و به عنوان حساب restricteduser که در ابتدای راهنما ایجاد کردیم، وارد شوید.

[restricteduser@localhost ~]$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

بدین ترتیب حساب کاربری رفتار پیش‌فرض اجرای کاربر unconfined_u را دارد و به نقش unconfined_r نیز دسترسی دارد. با این وجود، این حساب کاربری حق آغاز هیچ پردازشی درون سیستم را ندارد. قطعه کد زیر نشان می‌دهد که restricteduser تلاش می‌کند تا httpd daemon را اجرا کند و با خطای انکار دسترسی مواجه می‌شود:

[restricteduser@localhost ~]$ service httpd start
Redirecting to /bin/systemctl start httpd.service
Failed to issue method call: Access denied

سپس به پنجره ترمینال کاربر root بازمی‌گردیم و اطمینان حاصل می‌کنیم که حساب restricteduser به فایل etc/sudoers/ اضافه شده است. این اقدام حساب restricteduser را قادر می‌سازد تا از دسترسی‌های root بهره بگیرد:

Visudo

و سپس خط زیر را به فایل افزوده و پس از ذخیره‌سازی آن، خارج می‌شویم:

restricteduser ALL=(ALL) ALL

اگر اکنون از پنجره ترمینال restricteduser خارج شده و دوباره وارد شویم، می‌توانیم سرویس httpd را با دسترسی‌های sudo آغاز و متوقف کنیم:

[restricteduser@localhost ~]$ sudo service httpd start
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for restricteduser:
Redirecting to /bin/systemctl start  httpd.service

این کاربر اکنون می‌تواند سرویس را آغاز و متوقف کند:

[restricteduser@localhost ~]$ sudo service httpd stop
Redirecting to /bin/systemctl stop httpd.service

این وضعیت نرمالی است، چون مدیران سیستم دسترسی sudo را به حساب‌های کاربری که اعتماد دارند اعطا می‌کنند. اما اگر بخواهیم کاربر خاصی را از آغاز سرویس httpd بازداریم و در عین حال این حساب کاربری در فهرست فایل sudo قرار داشته باشد، چه باید بکنیم؟

برای این که راه‌حل این مسئله را ببینیم، باید به پنجره ترمینال کاربر root بازگردیم و restricteduser را به حساب کاربری user_r در SELinux نگاشت کنیم. این همان کاری است که برای حساب کاربری restricteduser در مثال قبلی انجام دادیم:

semanage login -a -s user_u restricteduser

در این مرحله به پنجره ترمینال restricteduser بازگردید و از آن حساب خارج شوید و با ایجاد یک نشست ترمینال جدید با حساب کاربری restricteduser وارد شوید.

اکنون که restricteduser به صورت user_u یعنی نقش user_r و دامنه user_t محدود شده است، می‌توانیم این سطح از دسترسی را با استفاده از دستور seinfo از پنجره کاربر root تأیید کنیم:

seinfo -uuser_u –x

خروجی نشان می‌دهد که نقش user_u وجود دارد. همچنین می‌توانیم object_r و user_r را مشاهده کنیم:

   user_u
      default level: s0
      range: s0
      roles:
         object_r
         user_r

اگر یک گام پیش‌تر برویم می‌توانیم با اجرای دستور seinfo بررسی کنیم که نقش user_r به کدام دامنه‌ها مجوز ورود دارد:

seinfo -ruser_r –x

چندین دامنه هستند که نقش user_r اجازه ورود به آن‌ها را دارد:

   user_r
      Dominated Roles:
         user_r
      Types:
         git_session_t
         sandbox_x_client_t
         git_user_content_t
         virt_content_t
         policykit_grant_t
         httpd_user_htaccess_t
         telepathy_mission_control_home_t
         qmail_inject_t
         gnome_home_t
         ...
         ...

اما آیا این فهرست httpd_t را به عنوان یکی از دامنه‌ها نشان می‌دهد؟ فرض کنید همین دستور را با یک فیلتر اجرا کنیم:

seinfo -ruser_r -x | grep httpd

چندین دامنه مرتبط با httpd وجود دارند که این نقش به آن‌ها دسترسی دارد؛ اما httpd_t در میان آن‌ها نیست:

         httpd_user_htaccess_t
         httpd_user_script_exec_t
         httpd_user_ra_content_t
         httpd_user_rw_content_t
         httpd_user_script_t
         httpd_user_content_t

با در نظر گرفتن این مثال اگر حساب restricteduser تلاش کند نا damon مربوط به httpd را آغاز کند، دسترسی باید انکار شود، زیرا پردازش httpd درون دامنه httpd_t اجرا می‌شود و نه یکی از دامنه‌هایی که نقش user_r مجوز دسترسی به آن‌ها را دارد. همچنین می‌دانیم که user_u که به restricteduser نگاشت شده است، می‌تواند نقش user_r را داشته باشد. این وضعیت حتی در مواردی که دسترسی sudo نیز به حساب restricteduser اعطا شده باشد، همچنان با شکست مواجه می‌شود.

اگر به پنجره ترمینال حساب کاربری restricteduser بازگردیم، تلاش می‌کنیم تا daemon مربوط به httpd را آغاز کنیم. دقت کنید که ما قبلاً امکان توقف آن را داشتیم، چون دسترسی sudo به حساب داده شده بود:

[restricteduser@localhost ~]$ sudo service httpd start

این بار دسترسی انکار می‌شود:

sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted

بنابراین نمونه دیگری از چگونگی عملکرد SELinux به عنوان یک دروازه‌بان را شاهد بودیم.

Log-های حسابرسی در SELinux

شما به عنوان یک مدیر سیستم احتمالاً علاقه‌مند خواهید بود که پیام‌های خطای گزارش شده از سوی SELinux را مشاهده کنید. این پیام‌ها در فایل‌های خاصی گزارش می‌شوند و اطلاعاتی تفصیلی در مورد انکارهای دسترسی ارائه می‌دهند. در یک سیستم CentOS 7 این اطلاعات در دو فایل زیر قرار دارند:

  • var/log/audit/audit.log/
  • var/log/messages/

این فایل‌ها به ترتیب از سوی auditd daemon و rsyslogd daemon ایجاد شده‌اند. شاید از خود بپرسید کار این daemon ها چیست؟ صفحه‌های راهنمای auditd daemon همتای فضای کاربر برای سیستم حسابرسی لینوکس محسوب می‌شود و rsyslogd نیز یک ابزار سیستمی است که از گزارش‌گیری پیام‌ها پشتیبانی می‌کند. به بیان ساده‌تر این daemon ها پیام‌های خطا را در این دو فایل به صورت log ارائه می‌کنند.

فایل var/log/audit/audit.log/ در صورتی استفاده می‌شود که auditd daemon در حال اجرا باشد. فایل var/log/messages/ در صورتی استفاده می‌شود که auditd متوقف شده و rsyslogd در حال اجرا باشد. اگر هر دو daemon در حال اجرا باشند، هر دو فایل مورد استفاده قرار می‌گیرند: رکوردهای فایل var/log/audit/audit.log/ حاوی اطلاعات تفصیلی است در حالی که نسخه‌های با خوانایی بیشتر در فایل var/log/messages/ نگهداری می‌شوند.

توضیح پیام‌های خطای SELinux

ما در بخش قبلی این راهنما (بخش دوم بررسی عملی SELinux: محدودسازی مجوز اجرای اسکریپت‌ها) به محتوای یک پیام خطای SELinux نگاهی داشتیم. سپس با استفاده از دستور grep به محتوای فایل var/log/messages/ دسترسی یافتیم. خوشبختانه SELinux چندین ابزار برای سهولت کار ارائه کرده است. این ابزارها به صورت پیش‌فرض روی سیستم نصب نمی‌شوند و نیازمند نصب چندین بسته هستند که احتمالاً در بخش نخست این راهنما نصب کرده‌اید.

دستور نخست ausearch است. ما می‌توانیم از این دستور در مواردی که auditd daemon در حال اجرا است استفاده کنیم. در قطعه کد زیر تلاش می‌کنیم تا به دنبال پیام‌های خطای مرتبط با httpd daemon نگاه کنیم. ابتدا اطمینان حاصل کنید که در حساب کاربری root هستید:

ausearch -m avc -c httpd

در سیستم ما چند مدخل فهرست شده‌اند؛ اما ما روی آخرین مدخل متمرکز می‌شویم:

----
time->Thu Aug 21 16:42:17 2014
...
type=AVC msg=audit(1408603337.115:914): avc: denied { getattr } for pid=10204 comm="httpd" path="/www/html/index.html" dev="dm-0" ino=8445484 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file

حتی مدیران با تجربه سیستم نیز ممکن است با مشاهده پیام‌های خطای این چنین دچار سردرگمی شوند؛ مگر این که بدانند دقیقاً در حال مشاهده چه هستند. برای درک این پیام باید آن را به چند بخش تقسیم کنیم:

  • type=AVC و avc: عبارت AVC اختصاری برای عبارت «Access Vector Cache» است. SELinux تصمیم‌های کنترل دسترسی به منابع و پردازش‌ها را کش می‌کند. این کش به نام Access Vector Cache یا به اختصار AVC شناخته می‌شود. به همین دلیل است که پیام‌های انکار دسترسی SElinux به نام انکارهای AVC نامیده می‌شوند. این دو فیلد از اطلاعات بیان می‌کنند که این مدخل از log مربوط به AVC می‌آید و یک رویداد AVC محسوب می‌شود.
  • { denied { getattr : مجوزی که تلاش برای دسترسی صورت گرفته و نتیجه آن در این بخش اعلام می‌شود. در این مورد عملیات دریافت خصوصیت انکار شده است.
  • pid=10204: این id پردازشی است که تلاش برای دسترسی به آن صورت گرفته است.
  • Comm: بخش id پردازش به خودی خود معنی چندانی ندارد. خصوصیت comm دستور پردازش را نشان می‌دهد. در این مورد این پردازش httpd است. بدین ترتیب بلافاصله درک می‌کنیم که این خطا از وب‌سرور ناشی شده است.
  • Path: این متغیر مسیر منبعی که مورد تلاش برای دسترسی قرار گرفته است را نشان می‌دهد. در این مورد مسیر به صورت www/html/index.html/ است.
  • dev and ino: دستگاهی که منبع هدف در آن قرار دارد و آدرس داخلی آن را شامل می‌شود.
  • scontext: چارچوب امنیتی پردازش را شامل می‌شود. می‌توانیم ببینیم که منبع زیر دامنه httpd_t اجرا شده است.
  • tcontext: چارچوب امنیتی منبع هدف است. در این مورد نوع فایل default_t است.
  • tclass: کلاس منبع هدف است که در این مورد file است.

اگر به دقت به پیام خطای فوق نگاه کنید، می‌بینید که پردازش httpd_t و نوع فایل نیز default_t است. از آنجا که daemon برای httpd درون یک دامنه محدود قرار دارد و سیاست SELinux روی این دامنه اعمال شده است، نوع default_t هیچ گونه دسترسی به این فایل ندارد و دسترسی انکار می‌شود.

قبلاً ابزار sealer را مشاهده کردیم. این دستور می‌تواند با مقدار id پیام خطای log شده در فایل var/log/messages/ استفاده شود. در قطعه کد زیر یک بار دیگر روی فایل var/log/message/ دستور grep را اجرا می‌کنیم تا خطای مرتبط SELinux را دریافت کنیم:

cat /var/log/messages | grep "SELinux is preventing"

ما در سیستم خود به دنبال آخرین خطا می‌گردیم. این همان خطایی است که هنگام تلاش restricteduser برای اجرای httpd daemon گزارش شده است:

...
Aug 25 11:59:46 localhost setroubleshoot: SELinux is preventing /usr/bin/su from using the setuid capability. For complete SELinux messages. run sealert -l e9e6c6d8-f217-414c-a14e-4bccb70cfbce

همان طور که در پیام فوق پیشنهاد شده است، دستور sealer را با مقدار ID ارائه شده اجرا کردیم و شاهد جزییات خطا بودیم (ID خطای شما مختص سیستم شما خواهد بود):

sealert -l e9e6c6d8-f217-414c-a14e-4bccb70cfbce
SELinux is preventing /usr/bin/su from using the setuid capability.

...

Raw Audit Messages
type=AVC msg=audit(1408931985.387:850): avc:  denied  { setuid } for  pid=5855 comm="sudo" capability=7  scontext=user_u:user_r:user_t:s0 tcontext=user_u:user_r:user_t:s0 tclass=capability


type=SYSCALL msg=audit(1408931985.387:850): arch=x86_64 syscall=setresuid success=no exit=EPERM a0=ffffffff a1=1 a2=ffffffff a3=7fae591b92e0 items=0 ppid=5739 pid=5855 auid=1008 uid=0 gid=1008 euid=0 suid=0 fsuid=0 egid=0 sgid=1008 fsgid=0 tty=pts2 ses=22 comm=sudo exe=/usr/bin/sudo subj=user_u:user_r:user_t:s0 key=(null)

Hash: su,user_t,user_t,capability,setuid

ما دیدیم که چگونه چند خط نخست خروجی دستور sealer اطلاعاتی در مورد مراحل رفع اشکال ارائه می‌کند. با این وجود، اگر دقیق‌تر به بخش انتهایی پیام فوق نگاه کنیم، بخش «Raw Audit Messages» را می‌بینیم. این مدخل در این بخش از فایل audit.log ناشی می‌شود که قبلاً بررسی کردیم و از این رو می‌توانید از این بخش برای کمک به تفسیر خروجی استفاده کنید.

امنیت چند سطحی

امنیت چند سطحی یا MLS بخش تنظیمات دقیق چارچوب امنیتی SELinux محسوب می‌شود.

تا به این جا بررسی ما در مورد چارچوب‌های امنیتی برای پردازش‌ها، کاربران یا منابع بود و در مورد سه خصوصیت کاربر SELinux، نقش SELinux و نوع یا دامنه SELinux صحبت کردیم. فیلد چهارم چارچوب امنیتی، حساسیت (sensitivity) و به طور اختیاری دسته‌بندی (category) منابع را نشان می‌دهد.

برای درک این مبحث، چارچوب امنیتی فایل پیکربندی FTP daemon را بررسی می‌کنیم:

ls -Z /etc/vsftpd/vsftpd.conf

فیلد چهارم چارچوب امنیتی حساسیت s0 را نشان می‌دهد:

-rw-------. root root system_u:object_r:etc_t:s0/etc/vsftpd/vsftpd.conf

حساسیت، یخشی از مکانیسم امنیت چند سطحی سلسله مراتبی محسوب می‌شود. منظور از سطوح حساسیت این است که می‌توانیم در مورد محتواهای امنیتی‌تر در سیستم فایل از سطوح شدیدتر حساسیت استفاده کنیم. سطح 0 (که با s0 نشان داده می‌شود) پایین‌ترین سطح حساسیت است که آن را می‌توان با تنظیمات «عمومی» (public) مقایسه کرد. همچنین سطوح حساسیت دیگری نیز با مقادیر s بالاتر وجود دارند. برای نمونه سطوح داخلی، محرمانه یا رگولاتوری را به ترتیب با مقادیر s1، s2 و s3 نشان می‌دهیم. این نگاشت از سوی سیاست تعیین نشده است؛ بلکه مدیران سیستم می‌توانند معنی هر سطح امنیتی را خودشان تعیین کنند.

زمانی که یک سیستم با SELinux فعال از MLS برای نوع سیاست (که در فایل etc/selinux/config/ پیکربندی شده است) استفاده می‌کند می‌تواند فایل‌ها و پردازش‌های خاصی را با سطوح خاصی از حساسیت نشانه‌گذاری کند. پایین‌ترین سطح به نام «حساسیت جاری» (current sensitivity) نامیده می‌شود و بالاترین سطح نیز «حساسیت ایمن» (clearance sensitivity) نام دارد.

دسته‌بندی نیز مفهومی است که همراه با حساسیت در مورد منابع تعریف می‌شود؛ اما برخلاف آن، دسته‌بندی‌ها را می‌توان مانند برچسب‌های اختصاص یافته به منابع در نظر گرفت. نمونه‌هایی از دسته‌بندی مانند نام دپارتمان‌ها، نام مشتریان، پروژه‌ها و غیره است. هدف از دسته‌بندی، تنظیم دقیق‌تر کنترل دسترسی است. برای نمونه می‌توانید فایل‌های خاصی را با حساسیت محرمانه برای کاربرانی که در دو دپارتمان داخلی متفاوت هستند، علامت‌گذاری کنید.

حساسیت و دسته‌بندی در مورد چارچوب‌های امنیتی SELinux، می‌توانند همراه با هم در زمان پیاده‌سازی یک دسته‌بندی کار کنند. وقتی از یک بازه از سطوح حساسیت بهره می‌گیریم، قالبی که برای نمایش حساسیت استفاده می‌شود به وسیله یک خط تیره دو سطح ابتدایی و انتهایی را از هم جدا می‌کند، برای نمونه s0-s2. زمانی که از یک دسته استفاده می‌کنیم، بازه‌ها را با یک نقطه بین نقطه شروع و خاتمه نشان می‌دهیم. مقادیر حساسیت و دسته‌بندی به وسیله دونقطه (:) از هم جدا می‌شوند.

در ادامه مثالی از جفت حساسیت/دسته ارائه شده است:

user_u:object_r:etc_t:s0:c0.c2

در کد فوق تنها یک سطح حساسیت وجود دارد که s0 است. سطح دسته‌بندی را می‌توان به صورت c0-c2 نیز نوشت.

بنابراین شاید از خود بپرسید سطوح دسته‌بندی در کجا انتساب می‌یابند؟ جزئیات در این خصوص را می‌توانید در فایل etc/selinux/targeted/setrans.conf/ مشاهده کنید:

cat /etc/selinux/targeted/setrans.conf
#
# Multi-Category Security translation table for SELinux
#
#
# Objects can be categorized with 0-1023 categories defined by the admin.
# Objects can be in more than one category at a time.
# Categories are stored in the system as c0-c1023.  Users can use this
# table to translate the categories into a more meaningful output.
# Examples:
# s0:c0=CompanyConfidential
# s0:c1=PatientRecord
# s0:c2=Unclassified
# s0:c3=TopSecret
# s0:c1,c3=CompanyConfidentialRedHat
s0=SystemLow
s0-s0:c0.c1023=SystemLow-SystemHigh
s0:c0.c1023=SystemHigh

ما قصد نداریم در این بخش وارد جزییات حساسیت‌ها و دسته‌بندی‌ها بشویم. کافی است بدانید که یک پردازش تنها زمانی مجاز به دسترسی خواندن یک فایل می‌شود که حساسیت و سطح دسته‌بندی‌اش بالاتر از منبع باشد (یعنی دامنه پردازش بر نوع منبع مسلط باشد). یک پردازش زمانی می‌تواند در یک منبع بنویسد که سطح حساسیت/دسته‌بندی‌اش کمتر از مقدار مربوطه برای منبع باشد.

سخن پایانی

در این سری مقالات تلاش کردیم که موضوعات اساسی در خصوص امنیت لینوکس را در یک سری مطالب خلاصه سه بخشی ارائه کنیم. اینک اگر به سیستم خود نگاه کنید، می‌بینید که یک وب‌سرور ساده آپاچی نصب شده است و محتوای آن از یک دایرکتوری سفارشی عرضه می‌شود. همچنین یک FTP daemon داریم که روی سرور اجرا شده است. چند کاربر ایجاد کرده‌ایم که دسترسی‌هایشان محدود شده‌اند. همچنین از بسته‌های SELinux، فایل‌ها و دستورها برای ایجاد نیازهای امنیتی خود استفاده کردیم. در این مسیر آموختیم که چگونه پیام‌های خطای SELinux را پیدا کنیم و معنای آن‌ها را نیز درک نماییم.

کتاب‌های زیادی صرفاً به موضوع SELinux پرداخته‌اند و می‌توان ساعت‌ها صرف تلاش برای بررسی بسته‌های مختلف، فایل‌های پیکربندی، دستورها و تأثیرات آن‌ها روی امنیت کرد. نکته‌ای که باید در مورد آن هوشیار باشید، این است که نباید هیچ چیزی را روی یک سیستم production تست کنید. زمانی که به طور کامل مبانی SELinux را آموختید، می‌توانید از طریق فعال‌سازی‌اش روی یک نسخه تست از محیط production شروع به کار با آن بکنید. در این زمان باید مطمئن شوید که daemon های حسابرسی در حال اجرا هستند و نیم‌نگاهی نیز به پیام‌های خطا داشته باشید. هر گونه جزییاتی که از آغاز سرویس جلوگیری می‌کنند را به ‌دقت بررسی کنید. با تنظیمات بولی کار کنید. فهرستی از گام‌های ممکن برای امن سازی سیستم مانند ایجاد کاربران جدید که به حساب‌های با دسترسی حداقلی SELinux نگاشت شده‌اند، تهیه کنید و یا از چارچوب‌های دسترسی برای مکان‌های فایل غیراستاندارد استفاده کنید. شیوه خواندن پیام‌های خطا را یاد بگیرید. پورت‌ها برای daemon های مختلف بررسی کنید و اگر پورت غیراستانداردی مورد استفاده قرار گرفته است، اطمینان حاصل کنید که در حال حاضر به یک سیاست انتساب یافته است.

اگر این مطلب برایتان مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
digitalocean
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *