SSD Nodes Learn Hosting plans →
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-09-04

Nginx מחזיר 403? כך תפתרו בעיות SELinux בשרת

נתקלתם בשגיאת 403 ב-Nginx למרות הרשאות קבצים תקינות? המדריך מסביר איך לאבחן חסימות SELinux, להשתמש ב-semanage ו-restorecon כדי לתקן תוויות ולהשאיר את המערכת מאובטחת.

מדוע Nginx מחזיר שגיאת 403 על קובץ שהרשאותיו תקינות

כאשר Nginx מחזיר שגיאת 403 על קובץ שביטי ההרשאות שלו תקינים, כמעט תמיד מדובר ב-SELinux (Security-Enhanced Linux) שמונע את הקריאה. SELinux בודק סט חוקים נוסף לאחר מעבר בדיקת ההרשאות הרגילה, ושרת האינטרנט מורשה לקרוא רק קבצים הנושאים תווית (label) של תוכן אינטרנט. הקובץ שלך נושא תווית שונה, לכן פעולת ה-open נכשלת ול-Nginx אין מה לשלוח.

בדוק את התווית, לא רק את ה-mode:

ls -ldZ /data/www /data/www/index.html
drwxr-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 היא התווית שקובץ מקבל כאשר המדיניות אינה מכירה את הנתיב, ושום חוק בחוקי שרת האינטרנט אינו מאפשר קריאה של סוג זה. לוג השגיאות מציג שגיאת 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"

הליבה (kernel) מחזירה 13: Permission denied עבור שני סוגי הסירוב, הרגיל וזה של SELinux. לכן, המשימה הראשונה היא לגלות איזו שכבה סירבה לבקשה. אל תתחיל עם setenforce 0.

החלק במודל שדרוש לך

SELinux הוא מנגנון בקרת גישה מחייבת, המכונה בדרך כלל MAC. כל תהליך רץ בתוך domain, כגון httpd_t עבור שרת האינטרנט. לכל קובץ ולכל פורט רשת מוצמד type, כגון httpd_sys_content_t. המדיניות היא רשימה של שילובים מותרים בין domain, type ופעולה; כל מה שאינו מופיע ברשימה זו נחסם. המנגנון פועל לאחר בדיקת ה-Unix הקלאסית, לכן ביטי ההרשאות ב-drwxr-xr-x עדיין חייבים לאפשר את הגישה תחילה. שתי השכבות חייבות לאשר את הפעולה.

הקשר (context) מלא מורכב מארבעה שדות המופרדים בנקודתיים, כמו system_u:system_r:httpd_t:s0: משתמש SELinux, התפקיד (role), ה-type, והרמה (level). בשרת, רוב העבודה מתמקדת בשדה השלישי, ה-type. שתי פקודות מציגות את הערכים בזמן אמת:

ps -eZ | grep nginx
id -Z

תהליכי ה-worker של 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 מסתכמת בהבטחות תאימות ובתמיכה במעבדים ישנים, ולא בהגדרות אבטחה. לעומת זאת, 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 וכותב סיכום בשפה פשוטה של כל חסימה לתוך ה-journal. התקינו את שניהם על שרת חדש, כיוון שהרגע שבו תזדקקו להם הוא בדיוק הרגע שבו משהו כבר אינו תקין.

כיצד לקרוא סירוב של SELinux ביומן ה-audit

כל סירוב נרשם על ידי ה-audit daemon כהודעת AVC (ראשי תיבות של access vector cache):

sudo ausearch -m AVC,USER_AVC,SELINUX_ERR -ts recent
type=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 הוא ה-source context, כלומר ה-domain שבו התהליך רץ. tcontext הוא ה-target context, התווית (label) על האובייקט שהתהליך ניסה לגשת אליו. tclass הוא סוג האובייקט, במקרה זה קובץ. בקריאה משולבת: התהליך ב-httpd_t ניסה לקרוא קובץ בעל תווית default_t, והערך permissive=0 מציין שהבקשה אכן נחסמה ולא רק תועדה.

אם ausearch אינו מציג דבר, ייתכן שה-audit daemon אינו פעיל. במקרה כזה, הסירובים יופיעו ב-kernel ring buffer:

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

הנתיב הוא ביטוי רגולרי. (/.*)? מכסה את הספרייה עצמה ואת כל מה שתחתיה, וזה בדיוק מה שנדרש עבור document root. בדקו מה ישתנה לפני ביצוע השינוי: 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'.

תוכן שהשירות חייב לכתוב אליו זקוק לסוג (type) שונה. השתמשו ב-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

ה־upstream שלכם תקין. ה־domain של httpd_t אינו מורשה לפתוח חיבורי רשת יוצאים כברירת מחדל, לכן הקריאה ל־connect() נדחית עוד לפני שהיא מגיעה לממשק ה-loopback. מתג אחד שולט בכל ההתנהגות הזו:

getsebool -a | grep httpd_can_network
sudo setsebool -P httpd_can_network_connect on

-P הוא הדגל הקובע: הוא כותב את הערך לדיסק. ללא -P השינוי יאבד באתחול הבא, מה שיוביל לשירות שעובד עד שהמכונה עוברת reboot. אשרו את השינוי באמצעות semanage boolean -l | grep httpd_can_network_connect, שמציג את הערך הפעיל לצד הערך השמור.

העדיפו ערך בוליאני על פני חוק שנכתב ידנית בכל פעם שקיים כזה. ערכים בוליאניים מגיעים עם מדיניות ההפצה, לכן הם מתוחזקים, מתועדים וקלים לאיתור עבור האדם הבא שיעבוד על המערכת. getsebool -a מציג את כל הערכים הקיימים במערכת.

הגדרת שירות להאזנה בפורט לא סטנדרטי

פורטים מסומנים באמצעות תוויות (labels). העברת Nginx לפורט 8081 גורמת לכך שהוא מסרב לעלות:

nginx: [emerg] bind() to 0.0.0.0:8081 failed (13: Permission denied)

httpd_t רשאי לבצע bind לפורטים בעלי תווית 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 לפני שתבצעו restart ל-daemon ותסגרו את ה-session שלכם. זהו השלב שאנשים מדלגים עליו כשהם עוקבים אחר מדריך כללי ל-הקשחת SSH ב-VPS. SELinux אינו firewall, לכן הפורט עדיין חייב להיות פתוח: sudo firewall-cmd --permanent --add-port=8081/tcp && sudo firewall-cmd --reload כאן, או שימוש ב-ufw ב-Debian או Ubuntu. הדגל --permanent טומן בחובו את אותה מלכודת של אי-שמירה לאחר reboot כמו -P בערך בוליאני, וכדאי לקרוא פעם אחת על ה-zones הקובעים לאילו ממשקים חל חוק מסוים ב-יסודות firewalld עבור Rocky או AlmaLinux VPS.

כאשר אין ערך בוליאני או תווית לשינוי

מצב זה נדיר בשרת תקין, וזהו השלב שבו משתמשים עלולים לגרום נזק. audit2allow יכול לבנות מודול מדיניות מתוך חסימות (denials) המופיעות בלוג:

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 צץ מחדש ברגע הכי פחות מתאים. אם שירות מסוים זקוק למרחב פעולה בזמן שאתה עובד עליו, הגדר את ה-domain הספציפי במקום את כל המכונה: 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 דורשת לאחר מכן תיוג מחדש מלא (relabel), אחרת שירותים רבים יקרסו בבת אחת:

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. ביצוע bind mount מהמארח נכשל עם שגיאת Permission denied בתוך המכולה, בעוד שפקודת ls -l במארח מציגה הרשאות תקינות לחלוטין. הסיומת :Z מורה לסביבת ההרצה לבצע תיוג מחדש (relabel) לנקודת העיגון:

docker run -d -v /data/appdata:/var/lib/app:Z myimage

התווית :Z מגדירה תווית לספרייה עבור מכולה זו בלבד. התווית :z מגדירה אותה לשיתוף בין מכולות. אם תפנו עם :Z לספרייה ששירותים אחרים משתמשים בה, המערכת תבצע תיוג מחדש רקורסיבי לספרייה, מה שיגרום לתקלות בשירותים אלו; לכן, הקצו למכולות נתיבים משלהן. אם המנוע עדיין לא מותקן על השרת, שימו לב שהפקודה docker בהפצות אלו היא לעיתים קרובות podman המגיב לשם זה, עניין ש־שלבי ההתקנה ב-Rocky ו-AlmaLinux מסדירים לפני שתיתקלו בבעיה זו. כל שאר ההיבטים של ההגדרה זהים לכל image אחר, כפי שמתואר ב־הרצת 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 (מצב מאפשר עבור פרופיל אחד) ואת aa-enforce כדי להחזיר את המצב לקדמותו. Ubuntu מגבילה קבוצה נבחרת של שירותים ארוזים ומשאירה את השאר ללא הגבלה, לכן קראו את aa-status כדי לראות מה פעיל באמת במקום להניח הנחות.

הרגל אחד משותף לשתי המערכות. כאשר שירות מדווח על Permission denied לגבי משהו שנראה תקין, קראו את לוג האבטחה לפני שאתם נוגעים בהרשאות. לעיתים רחוקות הבעיה היא בהרשאות פעמיים.

FAQ

מדוע Nginx מחזיר שגיאת 403 למרות שהרשאות הקבצים תקינות?

הסיבה היא ש־SELinux מנע את הקריאה, ולא הרשאות ה־filesystem. שרת האינטרנט רץ בתוך ה-domain מסוג httpd_t ומורשה לקרוא רק קבצים המסומנים כתוכן אינטרנט. לכן, קובץ שמסומן כ-default_t או admin_home_t נדחה, ול-Nginx אין מה להגיש. ניתן לאמת זאת באמצעות sudo ausearch -m AVC -ts recent, שמציג scontext שמסתיים ב-httpd_t, כאשר tcontext מחזיק בטיפוס (type) שגוי. לאחר מכן יש לתעד את התווית הנכונה ולהחיל אותה: 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 במצב enforcing?

יש להוסיף את הפורט לטיפוס (type) שהשירות מורשה להאזין לו. עבור שרת אינטרנט בפורט 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 מגבילה קבוצה נבחרת של שירותים ארוזים, לכן תוכנות רבות רצות ללא הגבלה (unconfined) כברירת מחדל. Rocky Linux ו-AlmaLinux הן המערכות שבהן תפגשו SELinux במצב enforcing כברירת מחדל, יחד עם Fedora ו-RHEL.