Product SiteDocumentation Site

14.5. مقدمة إلى SELinux

14.5.1. المبادئ

SELinux‏ (Security Enhanced Linux) هو نظام تحكم إلزامي بالوصول Mandatory Access Control مبني على واجهة LSM‏ (Linux Security Modules) في لينكس. عملياً، تستشير النواة SELinux قبل كل استدعاء للنظام حتى تعرف إذا كانت العملية المستدعية مخولة لتنفيذ الإجراء المطلوب.
يستخدم SELinux مجموعة من القواعد — تُعرَف باسم السياسة policy — لحظر الإجراءات أو السماح بها. إنشاء هذه القواعد صعب. لكن لحسن الحظ، هناك سياستين قياسيتين (targeted و strict) متاحتين لتوفير عناء معظم عملية الإعداد.
إدارة الصلاحيات مع SELinux تختلف تماماً عن نظم يونكس التقليدية. تعتمد صلاحيات العملية على سياقها الأمني. يتحدد السياق بهوية المستخدم الذي بدأ تنفيذ العملية، والدور والنطاق الذين كان يحملهما المستخدم في ذلك الوقت. تعتمد الصلاحيات فعلياً على النطاق، لكن الأدوار هي التي تحكم الانتقالات بين النطاقات. أخيراً، تعتمد الانتقالات بين الأدوار على هوية المستخدم.
السياق الأمني ومستخدمي يونكس

شكل 14.1. السياق الأمني ومستخدمي يونكس

In practice, during login, the user gets assigned a default security context (depending on the roles that they should be able to endorse). This defines the current domain, and thus the domain that all new child processes will carry. If you want to change the current role and its associated domain, you must call newrole -r role_r -t domain_t (there is usually only a single domain allowed for a given role, the -t parameter can thus often be left out). This command authenticates you by asking you to type your password. This feature forbids programs to automatically switch roles. Such changes can only happen if they are explicitly allowed in the SELinux policy.
من الواضح أن الصلاحيات نفسها لا تنطبق على جميع الكائنات objects (ملفات، مجلدات، مقابس شبكية، أجهزة، الخ)، بل تختلف من كائن لآخر. لتحقيق هذا، يُربَط كل كائن مع نوع type (تعرف هذه العملية بالوسم labeling). تُمثَّل صلاحيات النطاق إذن بمجموعات من الإجراءات المسموحة (أو الممنوعة) على هذه الأنواع (وبالتالي، تنطبق بشكل غير مباشر على الكائنات التي وسمت بهذا النوع).
افتراضياً، يرث البرنامج نطاقه من المستخدم الذي بدأ تنفيذه، لكن سياسات SELinux القياسية تتوقع تشغيل برامج مهمة عديدة في نطاقات خاصة بها. لتحقيق ذلك، توسم هذه البرامج التنفيذية بأنواع خاصة بها (مثلاً، يوسم ssh بالنوع ssh_exec_t وعند تشغيل البرنامج، سينتقل تلقائياً إلى النطاق ssh_t) . تسمح آلية الانتقال التلقائي بين النطاقات هذه بمنح كل برنامج الصلاحيات التي يحتاجها فقط. هذا أحد المبادئ الأساسية في SELinux.
الانتقالات الآلية بين النطاقات

شكل 14.2. الانتقالات الآلية بين النطاقات

14.5.2. إعداد SELinux

دعم SELinux مبني ضمن النَوَى القياسية التي توفرها دبيان. كما تدعم أدوات يونكس الأساسية SELinux دون أي تعديلات. إذن من السهل نسبياً تفعيل SELinux.
سوف يثبت الأمر apt install selinux-basics selinux-policy-default آلياً الحزم اللازمة لإعداد نظام SELinux.
تحوي الحزمة selinux-policy-default مجموعة من الأدوات القياسية. افتراضياً، تُقيّد هذه السياسة الوصول لبضعة خدمات واسعة الانتشار. جلسات عمل المستخدمين غير مقيدة ولذلك يُستبعَد أن يمنع SELinux إجراءات المستخدمين المشروعة. ومع ذلك، يزيد هذا من أمان خدمات النظام التي تعمل على الجهاز. لإعداد سياسة تشبه القواعد ”الصارمة“ القديمة، عليك فقط تعطيل وحدة unconfined (سنشرح إدارة الوحدات لاحقاً في هذا القسم).
بعد تثبيت السياسة، عليك وسم جميع الملفات المتوفرة (أي إعطائهم نوعاً). يجب بدء تنفيذ هذه العملية يدوياً باستدعاء fixfiles relabel.
نظام SELinux جاهز الآن. لتفعيله عليك إضافة المتغير selinux=1 security=selinux إلى النواة لينكس. أما المتغير audit=1 فيفعّل سجلات SELinux التي تسجل كل الإجراءات التي منعها. أخيراً، يضع المتغير enforcing=1 القواعد في حيز التطبيق، إذ بدونه يعمل SELinux في وضع permissive الافتراضي حيث يكتفي بتسجيل الإجراءات الممنوعة لكنه يسمح بتنفيذها. عليك إذن تعديل ملف إعداد محمل الإقلاع GRUB لإضافة المتغيرات المطلوبة. إحدى الطرق السهلة لتنفيذ ذلك تعديل المتغير GRUB_CMDLINE_LINUX في الملف /etc/default/grub ثم استدعاء update-grub. سينشط SELinux بعد إعادة الإقلاع.
يجدر بالملاحظة أن السكربت selinux-activate يؤتمت هذه الخطوات ويفرض عملية الوسم عند الإقلاع التالي (وبذلك يتفادى إنشاء ملفات جديدة غير موسومة قبل تنشيط SELinux أو أثناء تنفيذ عملية الوسم).

14.5.3. إدارة نظام SELinux

سياسة SELinux هي مجموعة تجزيئية (modular) من القواعد، وتكتشف أثناء تثبيتها جميع الوحدات الملائمة وتُفعّلها آلياً اعتماداً على الخدمات المثبتة سابقاً. أي أن النظام جاهز للعمل مباشرة. لكن، إذا ثبَّتّ خدمة بعد تثبيت سياسة SELinux، يجب أن تتمكن بطريقة ما من تفعيل الوحدة المناسبة يدوياً. هذا هو الهدف من الأمر semodule. بالإضافة لذلك، يجب أن تتمكن من تعريف الأدوار التي يمكن أن يأخذها كل مستخدم، ويمكن تنفيذ هذا باستخدام الأمر semanage.
يمكن استخدام هذين الأمرين إذن لتعديل إعدادات SELinux الحالية، المخزنة في /etc/selinux/default/. بخلاف ملفات الإعداد الأخرى التي تجدها في /etc/، لا يجب تعديل أي من هذه الملفات يدوياً. بل يجب استخدام البرامج المخصصة لهذا الغرض.

14.5.3.1. إدارة وحدات SELinux

تخزن وحدات SELinux في المجلد /usr/share/selinux/default/. لتنشيط إحدى هذه الوحدات في الإعداد الحالي، عليك استخدام semodule -i module.pp.bz2. ترمز الإضافة pp.bz2 للعبارة policy package (مضغوطة باستخدام bzip2).
يمكن إزالة وحدة من الإعداد الحالي باستخدام semodule -r module. أخيراً، يسرد الأمر semodule -l الوحدات المُثبّتة حالياً، كما يطبع أرقام إصدارها. يمكن تفعيل الوحدات اختيارياً عبر semodule -e كما يمكن تعطيلها عبر semodule -d.
# semodule -i /usr/share/selinux/default/abrt.pp.bz2
libsemanage.semanage_direct_install_info: abrt module will be disabled after install as there is a disabled instance of this module present in the system.
# semodule -l
accountsd
acct
[...]
# semodule -e abrt
# semodule -d accountsd
# semodule -l
abrt
acct
[...]
# semodule -r abrt
libsemanage.semanage_direct_remove_key: abrt module at priority 100 is now active.semodule -l
يُحمّل semodule الإعدادات الجديدة فوراً ما لم تستخدم معه الخيار -n. يجدر بالملاحظة أن البرنامج يُعدِّل افتراضياً على الإعدادات الحالية (التي يشير لها المتغير SELINUXTYPE في /etc/selinux/config)، لكن يمكنك جعله يعدل على إعداد آخر عبر استخدام الخيار -s.

14.5.3.2. إدارة الهويات

في كل مرة يُسجّل فيها المستخدم دخوله، تُسنَد له هوية SELinux. تحدد هذه الهوية الأدوار التي يستطيع أخذها. يمكن إدارة هذه التقابلات (بين اسم المستخدم والهويّة، وبين هذه الهوية والأدوار) بالأمر semanage.
You should definitely read the semanage(8) manual page. All the managed concepts have their own manual page; for instance, semanage-login(8). Even if the command's syntax tends to be similar for all the concepts which are managed, it is recommended to read its manual page. You will find common options to most sub-commands: -a to add, -d to delete, -m to modify, -l to list, and -t to indicate a type (or domain).
يسرد الأمر semanage login -l التقابلات الحالية بين هويات المستخدمين وهويات SELinux. يحصل المستخدمون الذين ليس لهم مدخلة تقابل صريحة على الهوية المذكورة في المدخلة __default__. يربط الأمر semanage login -a -s user_u user المستخدم المحدد بالهويّة user_u. وأخيراً، يُسقِط semanage login -d user مدخلة التقابل المسندة للمستخدم.
# semanage login -a -s user_u rhertzog
# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
rhertzog             user_u               s0                   *
root                 unconfined_u         s0-s0:c0.c1023       *
# semanage login -d rhertzog
يسرد الأمر semanage user -l التقابلات بين هويات المستخدمين في SELinux والأدوار المسموحة. تحتاج إضافة هوية جديدة إلى تعريف الأدوار الموافقة لها بالإضافة لتعريف سابقة الوسم التي تستخدم لتعيين أنواع الملفات الشخصية (/home/user/*). إما أن تكون قيمة هذه السابقة user أو staff أو sysadm. استخدام السابقة ”staff“ ينتج ملفات لهذا النوع ”staff_home_dir_t“. تنشأ هويات SELinux الجديدة باستخدام semanage user -a -R roles -P prefix identity. أخيراً، يمكنك إزالة هوية مستخدم SELinux باستخدام semanage user -d identity.
# semanage user -a -R 'staff_r user_r' -P staff test_u
# semanage user -l

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

root            sysadm     s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r
staff_u         staff      s0         s0-s0:c0.c1023                 staff_r sysadm_r
sysadm_u        sysadm     s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r
test_u          staff      s0         s0                             staff_r user_r
unconfined_u    unconfined s0         s0-s0:c0.c1023                 system_r unconfined_r
user_u          user       s0         s0                             user_r
# semanage user -d test_u

14.5.3.3. إدارة سياقات الملفات والمنافذ والمتغيرات البوليانية

توفر كل وحدة من وحدات SELinux مجموعة من قواعد وسم الملفات، لكن يمكن أيضاً إضافة قواعد وسم مخصصة استجابة لحالة خاصة. مثلاً، إذا أردت أن يتمكن مخدم الوب من قراءة الملفات ضمن الشجرة /srv/www/، عليك تنفيذ semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?" يتبعه restorecon -R /srv/www/. يسجل الأمر الأول قواعد الوسم الجديدة ويعيد الأمر الثاني ضبط أنواع الملفات وفق قواعد الوسم الجديدة هذه.
بشكل مشابه، توسم منافذ TCP/IP بطريقة تضمن أن الخدمات المناسبة فقط تستطيع الإنصات لها. مثلاً، إذا أردت مخدم الوب أن يتمكن من الإنصات للمنفذ 8080، عليك تنفيذ semanage port -m -t http_port_t -p tcp 8080.
تُصدِّر بعض وحدات SELinux خيارات بوليانية يمكنك تعديلها لتغيير سلوك القواعد الافتراضية. يمكن استخدام الأداة getsebool لفحص هذه الخيارات (يعرض الأمر getsebool boolean خياراً واحداً، أما getsebool -a فيعرضها كلها). يغيّر الأمر setsebool boolean value القيمة الحالية للخيار البولياني. استخدام الخيار -P يجعل التغيير نهائياً، أي أن القيمة الجديدة ستصبح القيمة الافتراضية وسيحتفظ بها بعد إعادة الإقلاع. يمنح المثال التالي مخدمات الوب صلاحية الوصول لمجلدات بيوت المستخدمين (يفيد هذا عندما يملك المستخدمون مواقع شخصية في ~/public_html/).
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off
# setsebool -P httpd_enable_homedirs on
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on

14.5.4. ملائمة القواعد

بما أن سياسة SELinux تجزيئية، فقد يهمُّك تطوير وحدات جديدة (أو وحدات مخصصة) للتطبيقات التي تفتقر لهذه الوحدات. عندها سوف تكمل هذه الوحدات السياسة المرجعية reference policy.
لإنشاء وحدة جديدة، ستحتاج للحزمة selinux-policy-dev. بالإضافة إلى selinux-policy-doc تحوي الأخيرة وثائق القواعد القياسية (/usr/share/doc/selinux-policy-doc/html/) وملفات أمثلة يمكن استخدامها كقوالب لإنشاء الوحدات الجديدة. ثَبِّت هذه الملفات وادرسها بعمق:
$ cp /usr/share/doc/selinux-policy-doc/Makefile.example Makefile
$ cp /usr/share/doc/selinux-policy-doc/example.fc ./
$ cp /usr/share/doc/selinux-policy-doc/example.if ./
$ cp /usr/share/doc/selinux-policy-doc/example.te ./
ملف .te هو الأهم بينها. فهو الذي يحدد القواعد. يُعرِّف الملف .fc ”سياقات الملفات“، وهي الأنواع التي تسند إلى الملفات المرتبطة بهذه الوحدة. تُستَخدَم البيانات من الملف .fc أثناء مرحلة وسم الملفات. أخيراً، يُعرِّف الملف .if واجهة الوحدة: وهي مجموعة من ”الدوال العامة“ التي تستطيع الوحدات الأخرى استخدامها للتفاعل مع هذه الوحدة الجديدة بشكل سليم.

14.5.4.1. كتابة ملف .fc

يجب أن تفي قراءة المثال التالي لفهم بنية هذا الملف. يمكنك استخدام التعابير المنتظمة لإسناد السياق الأمني نفسه لعدة ملفات، أو لشجرة ملفات كاملة حتى.

مثال 14.2. ملف example.fc

# myapp executable will have:
# label: system_u:object_r:myapp_exec_t
# MLS sensitivity: s0
# MCS categories: <none>

/usr/sbin/myapp         --      gen_context(system_u:object_r:myapp_exec_t,s0)

14.5.4.2. كتابة ملف .if

في المثال التالي، تتحكم الواجهة الأولى (”myapp_domtrans“) بمن يستطيع تنفيذ التطبيق. أما الثانية (”myapp_read_log“) فتمنح صلاحيات القراءة على ملفات سجلات التطبيق.
يجب أن تولد كل واجهة مجموعة صالحة من القواعد التي يمكن تضمينها في ملف .te. عليك إذن التصريح عن جميع الأنواع التي تستخدمها (باستخدام الماكرو gen_require)، واستخدام التعليمات التوجيهية القياسية لمنح الصلاحيات. لاحظ، على أي حال، أنك تستطيع استخدام الواجهات التي توفرها الوحدات الأخرى. يتفصّل القسم التالي أكثر في شرح طريقة تمثيل هذه الصلاحيات.

مثال 14.3. ملف example.if

## <summary>Myapp example policy</summary>
## <desc>
##      <p>
##              More descriptive text about myapp.  The <desc>
##              tag can also use <p>, <ul>, and <ol>
##              html tags for formatting.
##      </p>
##      <p>
##              This policy supports the following myapp features:
##              <ul>
##              <li>Feature A</li>
##              <li>Feature B</li>
##              <li>Feature C</li>
##              </ul>
##      </p>
## </desc>
#

########################################
## <summary>
##      Execute a domain transition to run myapp.
## </summary>
## <param name="domain">
##      Domain allowed to transition.
## </param>
#
interface(`myapp_domtrans',`
        gen_require(`
                type myapp_t, myapp_exec_t;
        ')

        domtrans_pattern($1,myapp_exec_t,myapp_t)
')

########################################
## <summary>
##      Read myapp log files.
## </summary>
## <param name="domain">
##      Domain allowed to read the log files.
## </param>
#
interface(`myapp_read_log',`
        gen_require(`
                type myapp_log_t;
        ')

        logging_search_logs($1)
        allow $1 myapp_log_t:file r_file_perms;
')

14.5.4.3. كتابة ملف .te

لنلق نظرة على الملف example.te:
policy_module(myapp,1.0.0) 1

########################################
#
# Declarations
#

type myapp_t; 2
type myapp_exec_t;
domain_type(myapp_t)
domain_entry_file(myapp_t, myapp_exec_t) 3

type myapp_log_t;
logging_log_file(myapp_log_t) 4

type myapp_tmp_t;
files_tmp_file(myapp_tmp_t)

########################################
#
# Myapp local policy
#

allow myapp_t myapp_log_t:file { read_file_perms append_file_perms }; 5

allow myapp_t myapp_tmp_t:file manage_file_perms;
files_tmp_filetrans(myapp_t,myapp_tmp_t,file)

1

يجب تعريف الوحدة باسمها ورقم إصدارها. هذه التعليمة إلزامية.

2

إذا كانت الوحدة تُعرّف أنواعاً جديدة، فعليها التصريح عنها باستخدام تعليمات كهذه. لا تتردد في إنشاء أنواع كثيرة بقدر الحاجة بدلاً من منح صلاحيات عديمة الجدوى.

3

تُعرّف هذه الواجهات النوع myapp_t كنطاق للعمليات الذي يجب استخدامه مع أي ملف تنفيذي موسوم بالنوع myapp_exec_t. ضمنياً، هذا يضيف الصفة exec_type إلى هذه الكائنات، التي تسمح بدورها للوحدات الأخرى بمنح صلاحيات تنفيذ هذه البرامج: مثلاً، تسمح الوحدة userdomain للعمليات ذات النطاقات user_t، و staff_t، و sysadm_t بتنفيذها. أما نطاقات البرمجيات المقيّدة الأخرى فلا تملك صلاحيات تنفيذها، إلا إذا منحتها القواعد صلاحيات مشابهة (هذه هي حالة dpkg ونطاقه dpkg_t، على سبيل المثال).

4

logging_log_file is an interface provided by the reference policy. It indicates that files labeled with the given type are log files which ought to benefit from the associated rules (for example, granting rights to logrotate so that it can manipulate them).

5

تعليمة allow هي التعليمة الأساسية المستخدمة للسماح بتنفيذ إجراء. المتغير الأول هو نطاق العملية المخولة بتنفيذ الإجراء. أما المتغير الثاني فهو يُعرّف الكائنات التي سيسمح للعمليات من النطاق السابق بالتعديل عليها. صيغة هذا المتغير هي ”type:class“ حيث type هو النوع في SELinux ويحدد الصنف class طبيعة الكائن (ملف، مجلد، مقبس شبكي، fifo، الخ). أخيراً، يحدد المتغير الأخير الصلاحيات (الإجراءات المسموحة).
تُعرّف الصلاحيات بشكل مجموعة من الإجراءات المسموحة وهي تتبع هذا القالب: { operation1 operation2 }. لكن يمكنك أيضاً استخدام ماكروات تعبر عن الصلاحيات التي تفيدك. يسرد الملف /usr/share/selinux/devel/include/support/obj_perm_sets.spt الماكروات المتاحة.
The following web page provides a relatively exhaustive list of object classes, and permissions that can be granted.
كل ما عليك الآن هو إيجاد أقل مجموعة من القواعد اللازمة لضمان عمل التطبيق أو الخدمة بشكل صحيح. لتحقيق هذا، يجب أن تعرف طريقة عمل التطبيق جيداً وأن تعرف نوع البيانات التي يديرها أو يولدها.
على أي حال، يمكن استخدام الطريقة التجريبية. حيث يمكنك فور وسم الكائنات المناسبة بشكل صحيح، استخدام التطبيق في الوضع المتساهل: فالعمليات التي ستحظر سوف تسجل لكنها ستنفّذ بنجاح مع ذلك. يمكنك الآن عبر تحليل السجلات معرفة العمليات التي يجب السماح بها. إليك مثالاً عن هذا النوع من مدخلات السجل:
avc:  denied  { read write } for  pid=1876 comm="syslogd" name="xconsole" dev=tmpfs ino=5510 scontext=system_u:system_r:syslogd_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=fifo_file permissive=1
دعنا ندرس هذه الرسالة قطعة بعد أخرى حتى نفهمها بشكل أفضل.

جدول 14.1. تحليل أثر SELinux

الرسالةالوصف
avc: deniedهذا الإجراء قد مُنع.
{ read write }يحتاج هذا الإجراء لصلاحيات read و write.
pid=1876نفّذت العملية ذات PID رقم 1876 الإجراء (أو حاولت تنفيذه).
comm="syslogd"كانت العملية تنفذ البرنامج syslogd.
name="xconsole"العنصر المستهدف اسمه xconsole. أحياناً قد يعطيك متغير ”path“ —يحوي المسار الكامل— بدلاً من هذا.
dev=tmpfsThe device hosting the target object is a tmpfs (an in-memory filesystem). For a real disk, you could see the partition hosting the object (for example, “sda3”).
ino=5510الكائن مُعرّف برقم inode يساوي 5510.
scontext=system_u:system_r:syslogd_t:s0هذا هو السياق الأمني للعملية التي نفّذت الإجراء.
tcontext=system_u:object_r:device_t:s0هذا هو السياق الأمني للكائن الهدف.
tclass=fifo_fileالكائن الهدف هو ملف FIFO.
By observing this log entry, it is possible to build a rule that would allow this operation. For example, allow syslogd_t device_t:fifo_file { read write }. This process can be automated, and it is exactly what the audit2allow command (of the policycoreutils package) offers. This approach is only useful if the various objects are already correctly labeled according to what must be confined. In any case, you will have to carefully review the generated rules and validate them according to your knowledge of the application. Effectively, this approach tends to grant more rights than are really required. The proper solution is often to create new types and to grant rights on those types only. It also happens that a denied operation isn't fatal to the application, in which case it might be better to just add a “dontaudit” rule to avoid the log entry despite the effective denial.

14.5.4.4. ترجمة الملفات

Once the 3 files (example.if, example.fc, and example.te) match your expectations for the new rules, rename them to myapp.extension and run make NAME=devel to generate a module in the myapp.pp file (you can immediately load it with semodule -i myapp.pp). If several modules are defined, make will create all the corresponding .pp files.