أساسيات SELinux للخادم: لماذا يعيد nginx الرمز 403؟
إذا أعاد nginx الخطأ 403 رغم صحة الأذونات، اقرأ رفض SELinux، أصلح التسمية باستخدام semanage وrestorecon، واترك الوضع enforcing مفعّلاً.
لماذا يعيد nginx الرمز 403 لملف أذوناته صحيحة
إعادة nginx للرمز 403 لملف تحمل بتات أذونات صحيحة تعني في الغالب أن SELinux (Linux المعزّز بالأمان) يرفض القراءة. يفحص SELinux مجموعة ثانية من القواعد بعد اجتياز فحص الأذونات العادي، ولا يُسمح لخادم الويب إلا بقراءة الملفات التي تحمل تسمية محتوى ويب. يحمل ملفك تسمية مختلفة، لذلك تفشل عملية الفتح ولا يجد nginx ما يرسله.
افحص التسمية، وليس النمط فقط:
ls -ldZ /data/www /data/www/index.htmldrwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /data/www
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 /data/www/index.htmlتعني النقطة المطبوعة بعد drwxr-xr-x أن الملف يحمل تسمية SELinux. تمثّل default_t التسمية التي يحصل عليها المسار عندما لا يكون policy على علم به مطلقاً، ولا تسمح أي قاعدة في قواعد خادم الويب بقراءة هذا النوع. يعرض سجل الأخطاء خطأ Unix عادياً، ولذلك يبدو الأمر كأنه مشكلة في الأذونات:
2026/08/11 09:14:02 [error] 1183#1183: *1 open() "/data/www/index.html" failed (13: Permission denied), client: 203.0.113.9, server: _, request: "GET / HTTP/1.1"تعيد النواة 13: Permission denied لنوعي الرفض، العادي ورفض SELinux. لذلك تتمثل الخطوة الأولى في تحديد الطبقة التي رفضت الطلب. لا تبدأ باستخدام setenforce 0.
الجزء الذي تحتاج إليه من النموذج
SELinux هو نظام للتحكم الإلزامي في الوصول، ويُكتب عادةً MAC. تعمل كل عملية ضمن نطاق، مثل httpd_t لخادم الويب. ويحمل كل ملف وكل منفذ شبكة نوعاً، مثل httpd_sys_content_t. تتكون السياسة من قائمة بالتركيبات المسموح بها للنطاق والنوع والإجراء، ويُرفض أي شيء غير موجود في هذه القائمة. تُطبَّق السياسة بعد فحص Unix التقليدي، لذلك يجب أن تسمح بتات الصلاحيات في drwxr-xr-x بالوصول أولاً. يجب أن تسمح كلتا الطبقتين بالوصول.
يتكون السياق الكامل من أربعة حقول تفصل بينها نقطتان، مثل system_u:system_r:httpd_t:s0: مستخدم SELinux، والدور، والنوع، والمستوى. في الخادم، ستتعامل تقريباً طوال الوقت مع الحقل الثالث، أي النوع. يعرض أمران القيم الحالية:
ps -eZ | grep nginx
id -Zتُظهر عمليات nginx العاملة سياقاً ينتهي بـ httpd_t. وتُظهر shell الخاصة بتسجيل الدخول unconfined_u:unconfined_r:unconfined_t:s0، لأن سياسة targeted الافتراضية تقيّد الخدمات وتترك المستخدمين التفاعليين دون تقييد. من المهم معرفة ذلك، لأن SELinux لا يحل محل تشغيل الخدمات باستخدام مستخدمين ذوي أقل صلاحيات ممكنة. بل يحدّ مما يمكن للخدمة الوصول إليه بعد اختراقها.
الأوضاع الثلاثة، والصور التي تتضمن SELinux
sestatus
getenforceيمنع وضع Enforcing العمليات ويسجلها. يسمح وضع Permissive بكل شيء ويسجل ما كان سيمنعه. لا يحمّل وضع Disabled أي سياسة على الإطلاق. يعرض getenforce الوضع الحالي. ويعرض sestatus أيضاً الوضع من /etc/selinux/config، وهو الوضع الذي يعود بعد إعادة التشغيل.
تأتي Rocky Linux وAlmaLinux وFedora وRHEL مع SELinux في وضع Enforcing وبسياسة targeted. هذا الإعداد الافتراضي المشترك موروث وليس مصادفة، لأن التوزيعات الأربع نشأت من السلالة نفسها من Red Hat التي مرّت عبر CentOS قبل ظهور Rocky Linux وAlmaLinux. لا يؤثر اختيار أيٍّ منهما في أي شيء في هذه الصفحة، لأنهما يأتيان بالسياسة والأدوات نفسيهما. لذلك يعتمد الاختيار بين Rocky Linux وAlmaLinux على تعهدات التوافق ودعم وحدات CPU الأقدم، وليس على الإعدادات الافتراضية للأمان. تأتي Ubuntu وDebian مع AppArmor بدلاً من ذلك، وهو يؤدي المهمة نفسها بآلية مختلفة (ويغطيه القسم الأخير). لذلك قد يُثبَّت التطبيق نفسه بنجاح على أحد خوادمك، ويُرجع 403 على خادم آخر.
ثبّت الأدوات قبل أن تحتاج إليها
sudo dnf install -y policycoreutils-python-utils setroubleshoot-serverيعني semanage: command not found على صورة نظام مصغّرة أن policycoreutils-python-utils مفقود: تحتوي تلك الحزمة على semanage وaudit2allow. يضيف setroubleshoot-server sealert ويكتب ملخصاً واضحاً لكل حالة رفض في السجل. ثبّت الأداتين على خادم جديد، لأنك تحتاج إليهما عادةً عندما يكون شيء ما معطلاً بالفعل.
كيفية قراءة رفض SELinux في سجل التدقيق
يسجّل برنامج التدقيق كل رفض على هيئة رسالة AVC (ذاكرة التخزين المؤقت لمتجه الوصول):
sudo ausearch -m AVC,USER_AVC,SELINUX_ERR -ts recenttype=AVC msg=audit(1754896442.881:412): avc: denied { read } for pid=1183 comm="nginx" name="index.html" dev="vda1" ino=17203 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0تحمل أربعة حقول القصة كاملة. comm هو البرنامج الذي مُنع. scontext هو سياق المصدر، أي النطاق الذي كانت العملية تعمل ضمنه. tcontext هو سياق الهدف، أي الوسم الموجود على العنصر الذي حاولت العملية الوصول إليه. tclass هو نوع العنصر، وهو هنا ملف. عند قراءتها معاً، يتضح أن العملية في httpd_t حاولت قراءة ملف موسوم بـdefault_t، وأن permissive=0 يوضح أن الطلب مُنع فعلاً، وليس أنه سُجّل فقط.
إذا لم يطبع ausearch شيئاً، فقد لا يكون برنامج التدقيق قيد التشغيل. عندها تُسجّل حالات الرفض في ذاكرة kernel الحلقية بدلاً من ذلك:
sudo journalctl -k | grep -i avcحوّل السجل الآن إلى جملة إنجليزية:
sudo ausearch -m AVC -ts recent | audit2why
sudo journalctl -t setroubleshoot --since today
sudo sealert -a /var/log/audit/audit.logيقرأ audit2why السجلات نفسها، ويسمي السبب الذي يتعرّف إليه: boolean معطّل، أو وسم لا يطابق السياسة، أو عدم وجود قاعدة على الإطلاق. يفحص sealert السجل بأكمله، ويطبع أمراً مقترحاً لكل حالة رفض. تعامل مع الاقتراح باعتباره تلميحاً. تختلف الصياغة بين الإصدارات، ويقترح sealert أحياناً وحدة سياسة مخصصة، بينما يكون إصلاح الوسم في سطر واحد هو الحل الصحيح.
هناك أمر آخر يجب معرفته. تحتوي السياسة على قواعد dontaudit تخفي حالات الرفض التي تعتبرها غير ضارة، ولذلك قد يتصرف أحد البرامج بشكل غير صحيح بينما يبقى السجل فارغاً. أظهر هذه الحالات لمدة اختبار واحد:
sudo semodule -DB
# reproduce the problem, then read the log again
sudo semodule -Bإصلاح وسم مسار غير صحيح باستخدام semanage fcontext وrestorecon
يتطلب الأمر تنفيذ أمرين، ويهم ترتيب التنفيذ. يسجّل semanage fcontext -a الوسم الذي ينبغي أن يكون للمسار. ويطبّق restorecon هذا الإعداد الافتراضي المسجّل على الملفات الموجودة على القرص.
sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?"
sudo restorecon -Rv /data/www
ls -ldZ /data/www/index.htmlالمسار تعبير عادي. يغطي (/.*)? الدليل نفسه وكل ما تحته، وهذا ما يحتاج إليه جذر المستندات. اعرض التغييرات المتوقعة قبل تطبيقها: يطبع sudo restorecon -Rvn /data/www عمليات إعادة الوسم المخطط لها، لأن -n يعني عدم تنفيذ أي إجراء. بعد تنفيذ restorecon فعلياً، يظهر الوسم عبر httpd_sys_content_t ويختفي الخطأ 403 من دون إعادة تشغيل الخدمة.
استخدم chcon للاختبار فقط. يعيّن chcon -t httpd_sys_content_t index.html الوسم مباشرة، ثم تعيد restorecon التالية أو تحديث الحزمة أو إعادة الوسم الكاملة ضبطه، لأن السياسة لا تزال تحدد أن المسار يجب أن يحمل وسماً آخر. على خادم يطبّق فيه dnf-automatic التحديثات الأمنية وفق مؤقت، تصل إعادة الضبط وفق جدولها الخاص، لا أثناء وجودك أمام الجهاز. لذلك قد يتعطل الموقع بعد ساعات من آخر إجراء نفذته. أما semanage fcontext فهو الإصدار الذي يستمر. اعرض ما سجّلته باستخدام sudo semanage fcontext -l | grep '^/data'.
تحتاج المحتويات التي يجب أن تكتب الخدمة فيها إلى نوع مختلف. استخدم httpd_sys_rw_content_t لدليل الرفع أو ذاكرة التخزين المؤقت، واقصره على هذه المسارات: الموقع للقراءة فقط تحت نوع قابل للكتابة يمنح التطبيق صلاحيات وصول أكبر من حاجته.
لماذا كان الوسم خاطئاً أصلاً؟ السبب في الغالب هو طريقة وصول الملفات. يحافظ mv على الوسم الحالي للملف، لذلك يصل الموقع المنقول من /root بوسم admin_home_t ويبقى عليه. يمنح cp العادي الملف الجديد الوسم الافتراضي لدليل الوجهة، وهذا ما تريده عادةً، بينما ينسخ cp -a وrsync -X وسوم المصدر مع الملف. يؤدي تنفيذ git clone في دليل علوي جديد إلى إنشاء default_t. عندما تُحمّل الصفحة بشكل صحيح من /usr/share/nginx/html وتفشل من دليلك الخاص، فهذا هو السبب.
إصلاح فئة من السلوكيات باستخدام قيمة منطقية
ليست بعض حالات الفشل ناتجة عن مشكلة في label. يعرض Reverse Proxy على خادم Rocky أو AlmaLinux جديد الخطأ 502، ويقول سجل الأخطاء:
2026/08/11 10:02:55 [crit] 1183#1183: *3 connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstreamتعمل الخدمة الخلفية لديك بشكل صحيح. لا يسمح نطاق httpd_t بفتح اتصالات شبكة صادرة افتراضياً، لذلك يُرفض استدعاء connect() قبل وصوله إلى واجهة loopback. يتحكم مفتاح واحد في هذا السلوك بالكامل:
getsebool -a | grep httpd_can_network
sudo setsebool -P httpd_can_network_connect onالعلامة -P هي المهمة؛ فهي تكتب القيمة على القرص. من دون -P، يضيع التغيير عند إعادة التشغيل التالية، وتعمل الخدمة إلى أن يُعاد تشغيل الجهاز. تحقّق باستخدام semanage boolean -l | grep httpd_can_network_connect، الذي يطبع القيمة قيد التشغيل بجوار القيمة المخزنة.
استخدم قيمة منطقية بدلاً من قاعدة مكتوبة يدوياً كلما توفرت قيمة منطقية مناسبة. تأتي القيم المنطقية مع سياسة التوزيعة، لذلك تتم صيانتها وتوثيقها، ويسهل على الشخص التالي العثور عليها. يسرد getsebool -a جميع القيم المنطقية الموجودة على النظام.
اجعل الخدمة تستمع على منفذ غير قياسي
تُصنَّف المنافذ أيضاً. انقل nginx إلى 8081، وسيرفض التشغيل:
nginx: [emerg] bind() to 0.0.0.0:8081 failed (13: Permission denied)يمكن لـhttpd_t ربط المنافذ المصنَّفة http_port_t، لكن 8081 ليس من بينها. أضفه:
sudo semanage port -l | grep -w http_port_t
sudo semanage port -a -t http_port_t -p tcp 8081تحقق من القائمة أولاً. توجد عدة منافذ عالية مسموحة بالفعل، منها 8008 و8443، وتؤدي إضافة المنفذ مرتين إلى فشل مع ValueError: Port tcp/8081 already defined. إذا كان المنفذ ينتمي بالفعل إلى نوع مختلف، فغيّره باستخدام semanage port -m -t http_port_t -p tcp 8081 بدلاً من إضافته.
الأمر نفسه يجعل منفذ SSH المنقول يعمل. يعني Bind to port 2222 on 0.0.0.0 failed: Permission denied في journalctl -u sshd أن 2222 مفقود من ssh_port_t، لذا شغّل sudo semanage port -a -t ssh_port_t -p tcp 2222 قبل إعادة تشغيل الخدمة وإغلاق جلستك. هذه هي الخطوة التي يتجاوزها الناس عند اتباع دليل عام لـتقوية SSH على VPS يعمل على صورة من عائلة Red Hat. لا يُعد SELinux جداراً نارياً أيضاً، لذلك يجب فتح المنفذ: باستخدام sudo firewall-cmd --permanent --add-port=8081/tcp && sudo firewall-cmd --reload هنا، أو ufw على صورة Debian أو Ubuntu. ويحمل الخيار --permanent فخ إعادة التشغيل نفسه الذي يحمله -P عند استخدام قيمة منطقية، كما تستحق المناطق التي تحدد الواجهات التي تنطبق عليها القاعدة قراءةً واحدة في أساسيات firewalld لخادم VPS يعمل بـRocky أو AlmaLinux.
عندما لا توجد قيمة منطقية ولا تسمية يمكن تغييرها
هذا نادر على خادم عادي، وهنا يتسبب المستخدمون في أضرار. يمكن لـ audit2allow إنشاء وحدة سياسة من حالات الرفض المسجلة في السجل:
sudo ausearch -m AVC -ts recent -c nginx | audit2allow -M nginx_local
cat nginx_local.te
sudo semodule -i nginx_local.ppاقرأ nginx_local.te قبل تثبيتها. تساعد عادتان على إبقاء هذه العملية آمنة. رشّح الإدخال ليقتصر على البرنامج الذي تصلحه باستخدام -c، لأن تمرير حالات رفض غير مرتبطة لأسبوع كامل إلى audit2allow يمنحها جميعاً دفعة واحدة. ولا تثبّت أبداً وحدة مبنية على حالة رفض لا يمكنك تفسيرها؛ فمن السهل إنشاء قاعدة تسمح لـ httpd_t بقراءة كل ملف على الخادم، لكن من الصعب اكتشافها بعد أشهر. أزل الوحدة باستخدام sudo semodule -r nginx_local.
الوضع permissive مخصّص للتشخيص وليس للإصلاح
sudo setenforce 0
# reproduce the problem once, all the way through
sudo ausearch -m AVC -ts recent
sudo setenforce 1يسمح الوضع permissive بالوصول ويسجّله في السجلات. وتتمثل قيمته الحقيقية في الشمولية. في الوضع enforcing، تتوقف الخدمة عند أول رفض، فتصلح هذا الرفض ثم تعيد التشغيل وتواجه الرفض الثاني. أما في الوضع permissive، فتستمر العملية ويجمع السجل كل حالات الرفض في عملية واحدة، ثم تعود إلى الوضع enforcing وتصلحها معاً.
لا يؤثر setenforce في /etc/selinux/config، لذلك تعيد إعادة التشغيل الخادم إلى الوضع enforcing. وهذا إجراء أمان، وهو أيضاً سبب ظهور ما يُسمى «إصلاحاً» والمكوّن من setenforce 0 في أسوأ لحظة ممكنة. إذا احتاجت خدمة واحدة إلى مساحة أثناء عملك عليها، فحدّد ذلك النطاق بدلاً من تعطيل الحماية عن الجهاز كله: يترك sudo semanage permissive -a httpd_t كل ما عداه في الوضع enforcing، بينما يعكس sudo semanage permissive -d httpd_t هذا التغيير.
لماذا يكلف تعطيل SELinux أكثر من إصلاح السياق
يؤدي ضبط SELINUX=disabled في /etc/selinux/config إلى استبدال إصلاح سياق يستغرق سطراً واحداً بخادم أضعف بشكل دائم. يظهر الفرق في اليوم الذي يتم فيه اختراق تطبيق ويب. عند تفعيل وضع enforcing، تعمل شيفرة المهاجم ضمن httpd_t، لذلك قد تتمكن من قراءة محتوى الويب، بينما ترفض السياسة قراءة /etc/shadow أو كتابة وحدة systemd، بصرف النظر عما كان مستخدم Unix سيسمح به. عند عدم تحميل أي سياسة، تحصل الشيفرة نفسها على كل ما يملكه حساب الخدمة من صلاحيات.
ويترتب على التعطيل أيضاً عبء تدفعه لاحقاً. ما دامت السياسة غير محمّلة، تُنشأ الملفات الجديدة من دون سياق، فينحرف نظام الملفات عن السياسة. وعند إعادة تفعيل SELinux، ستحتاج إلى إعادة وسم كاملة، وإلا فستفشل مجموعة من الخدمات دفعة واحدة:
sudo fixfiles -F onboot
sudo rebootيكتب ذلك /.autorelabel ويعيد وسم كل نظام ملفات أثناء الإقلاع التالي. يستغرق الأمر وقتاً طويلاً على قرص كبير، وقد تبدو وحدة التحكم متوقفة، لذلك شغّله عندما يتوفر لديك وقت للانتظار. وبما أن الجهاز سيتوقف على أي حال، فمن المفيد التحقق أولاً مما تم وضعه في قائمة الانتظار لإعادة التشغيل، وهذا ما توضحه أداة needs-restarting بعد أن يترك dnf تحديثاً نواة ومكتبات قديمة في الذاكرة. في Rocky Linux وAlmaLinux 9، لم يعد ملف الإعداد يعطّل جزء النواة تلقائياً، والطريقة الموثقة لتعطيل SELinux بالكامل هي استخدام وسيطة نواة (sudo grubby --update-kernel ALL --args selinux=0). يفيدك معرفة هذا الأمر عندما تتولى إدارة خادم أنشأه شخص آخر. لكنه ليس الحل لخطأ 403.
تضيف الحاويات تسمية أخرى
على مضيف من عائلة Red Hat، تعمل عمليات الحاويات ضمن container_t، ولا يمكنها قراءة سوى الملفات الموسومة بـcontainer_file_t. يفشل الربط من المضيف داخل الحاوية مع Permission denied، بينما تبدو ls -l على المضيف طبيعية تماماً. تخبر اللاحقة :Z محرك التشغيل بإعادة تسمية نقطة الربط:
docker run -d -v /data/appdata:/var/lib/app:Z myimageتضع :Z تسمية على الدليل لهذه الحاوية وحدها. أما :z فتضع تسمية تتيح مشاركته بين الحاويات. إذا أشرت بـ:Z إلى دليل تستخدمه خدمات أخرى، فستعيد تسمية ذلك الدليل بشكل تكراري، ما يعطّل تلك الخدمات. لذلك خصّص للحاويات مساراتها الخاصة. إذا لم يكن المحرك مثبتاً على الخادم بعد، فلاحظ أن الأمر docker في هذه التوزيعات يكون غالباً podman باسم مستعار، وهو تفصيل تعالجه خطوات التثبيت على Rocky وAlmaLinux قبل أن تواجه أياً من ذلك. كل ما عدا ذلك في الإعداد يطابق إعداد أي صورة أخرى، كما هو موضح في تشغيل Docker على VPS.
يحصل Ubuntu وDebian على AppArmor
الهدف واحد، لكن التصميم مختلف. يقيّد AppArmor البرنامج وفق المسار إلى الملف التنفيذي، باستخدام ملف تعريف ضمن /etc/apparmor.d/، بدلاً من وسم الملفات على القرص. لا توجد ملفات تحتاج إلى إعادة وسم، ولا يوجد restorecon. ابدأ هنا:
sudo aa-status
sudo journalctl -k | grep -i apparmorيظهر الرفض بصيغة apparmor="DENIED" operation="open" profile="/usr/sbin/nginx" name="/data/www/index.html" requested_mask="r". يتبع سير العمل النمط نفسه: اقرأ الرفض، واعثر على ملف التعريف، ثم عدّل القاعدة. يمنحك sudo apt install apparmor-utils الأمر aa-complain (وضع permissive لملف تعريف واحد)، ويمنحك aa-enforce لإعادته إلى وضعه السابق. يقيّد Ubuntu مجموعة محددة من الخدمات المضمّنة في الحزم، ويترك الباقي بلا قيود. لذلك اقرأ aa-status لمعرفة ما هو نشط فعلياً، بدلاً من الافتراض.
هناك عادة واحدة تنطبق على النظامين. عندما تبلغ خدمة عن Permission denied لعنصر يبدو صحيحاً، اقرأ سجل الأمان قبل تعديل الصلاحيات. نادراً ما تكون بتات الصلاحيات هي المشكلة نفسها مرتين.
FAQ
لماذا يعرض nginx الخطأ 403 رغم صحة أذونات الملف؟
لأن SELinux منع القراءة، وليس لأن بتات الأذونات غير صحيحة. يعمل خادم الويب ضمن النطاق httpd_t، ولا يمكنه قراءة إلا الملفات الموسومة كمحتوى ويب. لذلك يُرفض الملف الموسوم بـdefault_t أو admin_home_t، ولا يجد nginx شيئاً يقدّمه. أكّد ذلك باستخدام sudo ausearch -m AVC -ts recent، الذي يعرض scontext المنتهي بـhttpd_t، ويُظهر أن tcontext يحتوي على النوع الخاطئ. بعد ذلك سجّل الوسم الصحيح وطبّقه: sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?" ثم sudo restorecon -Rv /data/www.
هل من الآمن تشغيل setenforce 0 لكي تعمل الخدمة؟
يُعد setenforce 0 خطوة تشخيصية، وليس حلاً. استخدمه لإعادة إنتاج المشكلة مرة واحدة، حتى يجمع السجل كل حالات الرفض في مرور واحد. اقرأها باستخدام sudo ausearch -m AVC -ts recent، ثم شغّل sudo setenforce 1 وأصلح الأسباب. يسجّل الخادم الذي تُترك حالته permissive كل حالات الرفض ولا يمنع أياً منها. بذلك تحتفظ بالضجيج وتفقد الحماية. إذا احتاجت خدمة واحدة إلى مساحة أثناء العمل، فشغّل sudo semanage permissive -a httpd_t، لكي تظل بقية الجهاز في وضع enforcing.
كيف أشغّل خدمة على منفذ غير قياسي مع تفعيل SELinux؟
أضف المنفذ إلى النوع الذي يُسمح لتلك الخدمة بالربط به. لتشغيل خادم ويب على 8081: sudo semanage port -a -t http_port_t -p tcp 8081. ولتشغيل SSH على 2222: sudo semanage port -a -t ssh_port_t -p tcp 2222. تحقّق أولاً من القائمة الحالية باستخدام sudo semanage port -l | grep -w http_port_t، لأن المنفذ المدرج مسبقاً يفشل مع ValueError: Port tcp/8081 already defined. من دون هذه الخطوة، تنتهي عملية daemon عند بدء التشغيل مع bind() ... Permission denied، حتى لو لم تكن هناك عملية أخرى تشغل المنفذ.
هل يتضمن Ubuntu SELinux؟
لا. يتضمن Ubuntu وDebian نظام AppArmor، الذي يفرض ملفاً تعريفياً مرتبطاً بمسار الملف التنفيذي، وليس بوسوم الملفات. تحقّق منه باستخدام sudo aa-status، وابحث عن الأسطر apparmor="DENIED" في sudo journalctl -k. يفرض Ubuntu قيوداً على مجموعة محددة من الخدمات المضمّنة في الحزم، لذلك تعمل برامج كثيرة دون قيود افتراضياً. ستجد SELinux في وضع enforcing مفعّلاً افتراضياً في Rocky Linux وAlmaLinux، وكذلك في Fedora وRHEL.