SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-13

رفع خطای 403 در nginx و مشکلات SELinux

اگر nginx با وجود مجوزهای صحیح فایل 403 می‌دهد مشکل از SELinux است. با دستورات semanage و restorecon برچسب فایل را اصلاح کنید و بدون غیرفعال کردن امنیت به کار ادامه دهید.

چرا nginx برای فایلی که مجوزهای آن صحیح است، خطای 403 برمی‌گرداند

اگر nginx برای فایلی که مجوزهای (permission bits) آن درست است خطای 403 برمی‌گرداند، تقریباً همیشه به این دلیل است که SELinux (مخفف Security-Enhanced Linux) دسترسی خواندن را مسدود کرده است. SELinux پس از بررسی مجوزهای استاندارد، مجموعه قوانین دومی را چک می‌کند و وب‌سرور تنها اجازه خواندن فایل‌هایی را دارد که دارای برچسب (label) محتوای وب باشند. فایل شما برچسب متفاوتی دارد، بنابراین عملیات باز کردن فایل با شکست مواجه می‌شود و 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 وضعیتی است که یک مسیر در صورت عدم تعریف در سیاست‌های امنیتی دریافت می‌کند و هیچ قانونی در وب‌سرور اجازه خواندن این نوع فایل را نمی‌دهد. لاگ خطا یک خطای معمولی یونیکس را نشان می‌دهد و به همین دلیل است که این مشکل به عنوان یک خطای مجوز (permissions bug) دیده می‌شود:

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) برای هر دو نوع مسدودسازی، چه نوع معمولی و چه نوع SELinux، خطای 13: Permission denied را برمی‌گرداند. بنابراین اولین قدم این است که مشخص کنید کدام لایه درخواست را رد کرده است. کار را با setenforce 0 شروع نکنید.

بخشی از مدل که به آن نیاز دارید

SELinux یک سیستم کنترل دسترسی اجباری است که معمولاً با نام MAC شناخته می‌شود. هر پردازش در یک دامنه (domain) اجرا می‌شود، مانند httpd_t برای وب‌سرور. هر فایل و هر پورت شبکه دارای یک نوع (type) است، مانند httpd_sys_content_t. پالیسی، فهرستی از ترکیب‌های مجاز دامنه، نوع و عملیات است و هر چیزی که در این فهرست نباشد، مسدود می‌شود. این سیستم پس از بررسی‌های کلاسیک یونیکس اجرا می‌شود، بنابراین بیت‌های دسترسی در drwxr-xr-x همچنان باید ابتدا اجازه دسترسی را صادر کنند. هر دو لایه باید پاسخ مثبت بدهند.

یک کانتکست کامل دارای چهار فیلد است که با دو نقطه از هم جدا شده‌اند، مانند system_u:system_r:httpd_t:s0: کاربر SELinux، نقش (role)، نوع (type) و سطح (level). در یک سرور، شما تقریباً تمام وقت خود را صرف فیلد سوم، یعنی نوع (type) می‌کنید. دو دستور زیر مقادیر زنده را نمایش می‌دهند:

ps -eZ | grep nginx
id -Z

ورکرهای nginx کانتکستی را نشان می‌دهند که به httpd_t ختم می‌شود. شل ورود شما unconfined_u:unconfined_r:unconfined_t:s0 را نشان می‌دهد، زیرا پالیسی پیش‌فرض targeted سرویس‌ها را محدود می‌کند اما کاربران تعاملی را به حال خود می‌گذارد. دانستن این نکته ارزشمند است، زیرا SELinux جایگزین اجرای سرویس‌ها با کاربران دارای حداقل امتیاز نیست. این سیستم محدود می‌کند که یک سرویس پس از نفوذ احتمالی، به چه منابعی می‌تواند دسترسی پیدا کند.

سه حالت مختلف و توزیع‌هایی که از SELinux استفاده می‌کنند

sestatus
getenforce

حالت Enforcing دسترسی‌ها را مسدود کرده و آن‌ها را ثبت می‌کند. حالت Permissive همه چیز را مجاز می‌شمارد، اما مواردی که در حالت Enforcing مسدود می‌شدند را در لاگ ثبت می‌کند. حالت Disabled هیچ سیاستی را بارگذاری نمی‌کند. دستور getenforce حالت فعلی را نمایش می‌دهد. دستور sestatus نیز حالت ذخیره‌شده در /etc/selinux/config را چاپ می‌کند؛ این همان حالتی است که پس از reboot مجدداً اعمال می‌شود.

توزیع‌های Rocky Linux، AlmaLinux، Fedora و RHEL به‌صورت پیش‌فرض SELinux را در حالت Enforcing و با سیاست targeted عرضه می‌کنند. در مقابل، Ubuntu و Debian از AppArmor استفاده می‌کنند که وظیفه‌ای مشابه را با مکانیزمی متفاوت انجام می‌دهد (بخش آخر به این موضوع می‌پردازد). بنابراین، ممکن است یک برنامه روی یکی از سرورهای شما به‌درستی نصب شود، اما روی سرور دیگر خطای 403 بازگرداند.

ابزارها را پیش از نیاز نصب کنید

sudo dnf install -y policycoreutils-python-utils setroubleshoot-server

semanage: command not found در یک image حداقلی به این معناست که policycoreutils-python-utils نصب نشده است: این بسته شامل semanage و audit2allow است. setroubleshoot-server ابزار sealert را اضافه کرده و خلاصه‌ای به زبان ساده از هر مورد مسدودشده را در journal می‌نویسد. هر دو را روی یک سرور تازه نصب کنید، زیرا لحظه‌ای که به آن‌ها نیاز پیدا می‌کنید، همان لحظه‌ای است که مشکلی پیش آمده است.

نحوه خواندن یک denial در 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) است، یعنی دامنه‌ای که پردازش در آن در حال اجرا بوده است. tcontext کانتکست مقصد (target context) است، یعنی برچسبی که روی شیء مورد نظر برای دسترسی قرار دارد. tclass نوع شیء است، که در اینجا یک فایل است. با کنار هم قرار دادن این موارد: پردازش موجود در httpd_t سعی کرده است فایلی با برچسب default_t را بخواند و permissive=0 نشان می‌دهد که درخواست واقعاً مسدود شده است و نه فقط ثبت (log) شده باشد.

اگر 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

این مسیر یک عبارت منظم (regular expression) است. (/.*)? خود دایرکتوری و تمام محتویات زیرمجموعه آن را پوشش می‌دهد که دقیقاً همان چیزی است که یک document root به آن نیاز دارد. پیش از اعمال تغییرات، بررسی کنید چه چیزی تغییر خواهد کرد: sudo restorecon -Rvn /data/www تغییرات برنامه‌ریزی‌شده را چاپ می‌کند، زیرا -n به معنای عدم انجام عملیات است. پس از یک restorecon واقعی، برچسب به httpd_sys_content_t تغییر می‌یابد و خطای 403 بدون نیاز به راه‌اندازی مجدد سرویس برطرف می‌شود.

از chcon فقط برای تست استفاده کنید. chcon -t httpd_sys_content_t index.html برچسب را مستقیماً تنظیم می‌کند و با اجرای بعدی restorecon، به‌روزرسانی بسته یا relabel کامل، تنظیمات به حالت قبل بازمی‌گردد، زیرا سیاست (policy) همچنان می‌گوید که مسیر باید چیز دیگری باشد. 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 بارگذاری می‌شود اما از دایرکتوری شخصی شما با خطا مواجه می‌شود، دلیل آن همین است.

اصلاح یک کلاس از رفتارها با استفاده از boolean

برخی از خطاها ناشی از مشکل برچسب‌گذاری (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 شما مشکلی ندارد. دامنه 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 که مقدار در حال اجرا را در کنار مقدار ذخیره‌شده چاپ می‌کند، وضعیت را تأیید کنید.

هرگاه یک گزینه boolean وجود دارد، آن را به قوانین دستی ترجیح دهید. مقادیر boolean همراه با سیاست توزیع (distribution policy) ارائه می‌شوند، بنابراین نگهداری و مستندسازی شده‌اند و یافتن آن‌ها برای نفر بعدی آسان است. دستور 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 وجود ندارد، بنابراین پیش از restart کردن daemon و بستن نشست خود، 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.

زمانی که هیچ مقدار boolean یا برچسبی برای تغییر وجود ندارد

این وضعیت در یک سرور معمولی نادر است و همان جایی است که افراد باعث ایجاد خرابی می‌شوند. audit2allow می‌تواند بر اساس موارد رد شده (denials) در لاگ، یک ماژول سیاست (policy module) بسازد:

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 دسترسی را مجاز کرده و آن را ثبت (log) می‌کند. ارزش واقعی این حالت در جامعیت آن است. در حالت Enforcing، سرویس با اولین مورد عدم دسترسی (denial) متوقف می‌شود؛ بنابراین شما آن مورد را رفع می‌کنید، سرویس را restart می‌کنید و سپس با مورد بعدی مواجه می‌شوید. در حالت Permissive، اجرای سرویس ادامه می‌یابد و لاگ‌ها تمام موارد عدم دسترسی را در یک مرحله جمع‌آوری می‌کنند؛ سپس می‌توانید به حالت قبل بازگشته و همه آن‌ها را یک‌جا اصلاح کنید.

setenforce تغییری در /etc/selinux/config ایجاد نمی‌کند، بنابراین با یک reboot، سرور به حالت 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 یا نوشتن در یک unit فایل systemd، فارغ از اینکه کاربر Unix چه اجازه‌ای داشته باشد، توسط سیاست‌های امنیتی رد می‌شود. بدون بارگذاری سیاست‌ها، همان کد به تمام منابعی که حساب کاربری سرویس به آن‌ها دسترسی دارد، دسترسی پیدا می‌کند.

غیرفعال کردن SELinux هزینه‌ای دارد که بعداً پرداخت خواهید کرد. تا زمانی که هیچ سیاستی بارگذاری نشده باشد، فایل‌های جدید بدون برچسب ایجاد می‌شوند و در نتیجه سیستم فایل با سیاست‌های امنیتی هماهنگ نخواهد بود. فعال کردن مجدد SELinux در این شرایط نیازمند یک relabel کامل است، در غیر این صورت بسیاری از سرویس‌ها همزمان از کار می‌افتند:

sudo fixfiles -F onboot
sudo reboot

این دستور فایل /.autorelabel را ایجاد کرده و در بوت بعدی، تمام سیستم فایل را مجدداً برچسب‌گذاری می‌کند. روی دیسک‌های بزرگ، این فرآیند زمان‌بر است و ممکن است کنسول متوقف به نظر برسد؛ بنابراین زمانی این کار را انجام دهید که فرصت کافی دارید. در Rocky Linux و AlmaLinux 9، فایل پیکربندی دیگر به تنهایی بخش هسته (kernel) را غیرفعال نمی‌کند و روش مستند برای غیرفعال‌سازی کامل SELinux، استفاده از یک آرگومان هسته (sudo grubby --update-kernel ALL --args selinux=0) است. دانستن این دستور زمانی که مدیریت سروری را از شخص دیگری تحویل می‌گیرید، مفید است. این دستور راهکار رفع خطای 403 نیست.

افزودن یک برچسب (label) اضافی به کانتینرها

روی میزبان‌های خانواده Red Hat، پردازش‌های کانتینر در container_t اجرا می‌شوند و ممکن است فقط اجازه خواندن فایل‌هایی را داشته باشند که با container_file_t برچسب‌گذاری شده‌اند. یک bind mount از سمت میزبان با خطای Permission denied در داخل کانتینر مواجه می‌شود، در حالی که وضعیت ls -l روی میزبان کاملاً عادی به نظر می‌رسد. پسوند :Z به runtime دستور می‌دهد که mount را دوباره برچسب‌گذاری کند:

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

:Z دایرکتوری را فقط برای همین کانتینر برچسب‌گذاری می‌کند. :z آن را برای اشتراک‌گذاری بین کانتینرها برچسب‌گذاری می‌کند. اگر :Z را به دایرکتوری‌ای اشاره دهید که سرویس‌های دیگر از آن استفاده می‌کنند، آن دایرکتوری به‌صورت بازگشتی (recursively) دوباره برچسب‌گذاری می‌شود که باعث از کار افتادن آن سرویس‌ها خواهد شد؛ بنابراین برای کانتینرها مسیرهای اختصاصی خودشان را در نظر بگیرید. سایر موارد مربوط به این پیکربندی با هر image دیگری مشابه است که در اجرای Docker روی یک VPS پوشش داده شده است.

اوبونتو و دبیان از 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 را برای بازگرداندن آن ارائه می‌دهد. اوبونتو مجموعه محدودی از سرویس‌های بسته‌بندی‌شده را محدود می‌کند و بقیه را بدون محدودیت باقی می‌گذارد، بنابراین برای مشاهده آنچه واقعاً فعال است، به جای فرض کردن، aa-status را بخوانید.

یک عادت در هر دو سیستم مشترک است. هنگامی که یک سرویس در مورد چیزی که درست به نظر می‌رسد، خطای Permission denied گزارش می‌دهد، پیش از تغییر مجوزها، لاگ امنیتی را بخوانید. بیت‌های مجوز به‌ندرت دو بار عامل مشکل هستند.

FAQ

چرا با وجود درست بودن مجوزهای فایل، Nginx خطای 403 برمی‌گرداند؟

دلیل این است که SELinux دسترسی خواندن را مسدود کرده است، نه مجوزهای فایل (permission bits). وب‌سرور در دامنه 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 روی پورتی غیر استاندارد اجرا کنم؟

پورت مورد نظر را به نوع (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 مواجه می‌شود. بدون این گام، دیمون هنگام شروع با خطای bind() ... Permission denied متوقف می‌شود، حتی اگر هیچ پردازش دیگری پورت را اشغال نکرده باشد.

آیا اوبونتو دارای SELinux است؟

خیر. اوبونتو و دبیان از AppArmor استفاده می‌کنند که پروفایل‌هایی را بر اساس مسیر فایل اجرایی اعمال می‌کند، نه برچسب‌های روی فایل‌ها. وضعیت آن را با sudo aa-status بررسی کنید و به دنبال خطوط apparmor="DENIED" در sudo journalctl -k باشید. اوبونتو تنها مجموعه‌ای از سرویس‌های بسته‌بندی‌شده را محدود می‌کند، بنابراین بسیاری از برنامه‌ها به‌صورت پیش‌فرض بدون محدودیت (unconfined) اجرا می‌شوند. در توزیع‌های Rocky Linux، AlmaLinux، Fedora و RHEL است که با SELinux در حالت enforcing به‌صورت پیش‌فرض مواجه می‌شوید.