SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-09-04

رفع خطای 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 برچسبی است که یک مسیر در صورت عدم تعریف در سیاست‌های امنیتی دریافت می‌کند و هیچ قانونی در وب‌سرور اجازه خواندن این نوع فایل را نمی‌دهد. لاگ خطا یک خطای معمولی یونیکس را نشان می‌دهد، به همین دلیل است که این مشکل به عنوان یک باگ مجوز به نظر می‌رسد:

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"

هسته سیستم‌عامل برای هر دو نوع امتناع (هم نوع معمولی و هم نوع SELinux) خطای 13: Permission denied را برمی‌گرداند. بنابراین اولین وظیفه شما این است که بفهمید کدام لایه دسترسی را رد کرده است. کار را با setenforce 0 شروع نکنید.

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

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

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

ps -eZ | grep nginx
id -Z

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

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

sestatus
getenforce

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

توزیع‌های Rocky Linux، AlmaLinux، Fedora و RHEL به‌صورت پیش‌فرض SELinux را در حالت enforcing با سیاست targeted ارائه می‌دهند. این پیش‌فرض مشترک تصادفی نیست، زیرا هر چهار توزیع از تبار یکسان Red Hat که پیش از ظهور Rocky Linux و AlmaLinux از طریق CentOS جریان داشت نشأت گرفته‌اند. انتخاب هر یک از این دو توزیع تفاوتی در محتوای این صفحه ایجاد نمی‌کند، زیرا هر دو از سیاست‌ها و ابزارهای یکسانی استفاده می‌کنند؛ بنابراین انتخاب بین Rocky Linux و AlmaLinux بیشتر به وعده‌های سازگاری و پشتیبانی از CPUهای قدیمی بستگی دارد تا تنظیمات امنیتی پیش‌فرض. در مقابل، 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 نشان می‌دهد که درخواست واقعاً مسدود شده است و نه اینکه فقط لاگ شده باشد.

اگر 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) همچنان می‌گوید که مسیر باید چیز دیگری باشد. در سیستمی که 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 به‌درستی بارگذاری می‌شود اما از دایرکتوری شخصی شما با خطا مواجه می‌شود، دلیل آن همین است.

اصلاح یک کلاس از رفتارها با استفاده از 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 وجود دارد، آن را به جای نوشتن دستی rule ترجیح دهید. مقادیر 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. آن فلگ --permanent همان تلهٔ عدم ماندگاری پس از reboot را دارد که -P در یک مقدار boolean دارد، و ارزشش را دارد که یک‌بار در اصول firewalld برای یک VPS با Rocky یا AlmaLinux درباره زون‌هایی که تعیین می‌کنند یک قانون روی کدام اینترفیس‌ها اعمال شود، مطالعه کنید.

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

این وضعیت در یک سرور معمولی نادر است و همان جایی است که افراد باعث ایجاد خرابی می‌شوند. audit2allow می‌تواند از روی denialهای موجود در لاگ، یک ماژول سیاست (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 تنها به برنامه‌ای که در حال اصلاح آن هستید محدود کنید، زیرا ارسال یک هفته denial نامرتبط به audit2allow، دسترسی همه آن‌ها را به‌طور هم‌زمان اعطا می‌کند. هرگز ماژولی را که از روی یک denial غیرقابل‌توضیح ساخته شده است، نصب نکنید: قانونی که به 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، اجرا ادامه می‌یابد و لاگ تمام موارد منع دسترسی را در یک مرحله جمع‌آوری می‌کند، سپس می‌توانید به حالت قبل بازگشته و همه آن‌ها را یک‌جا اصلاح کنید.

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

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

sudo fixfiles -F onboot
sudo reboot

این دستور /.autorelabel را می‌نویسد و در بوت بعدی، تمام سیستم فایل را بازنشانی (relabel) می‌کند. روی دیسک‌های بزرگ، این فرآیند زمان‌بر است و کنسول ممکن است متوقف به نظر برسد، بنابراین زمانی این کار را انجام دهید که محدودیت زمانی ندارید. از آنجا که سیستم در هر صورت باید خاموش شود، بهتر است ابتدا بررسی کنید چه موارد دیگری در صف انتظار برای راه‌اندازی مجدد هستند؛ این همان کاری است که 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 به runtime دستور می‌دهد که mount را دوباره برچسب‌گذاری کند:

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

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

اوبونتو و دبیان از AppArmor استفاده می‌کنند

وظیفه یکسان، طراحی متفاوت. AppArmor یک برنامه را بر اساس مسیر فایل اجرایی آن محدود می‌کند و از پروفایلی در /etc/apparmor.d/ استفاده می‌کند، نه برچسب‌گذاری فایل‌ها روی دیسک. در اینجا نیازی به برچسب‌گذاری مجدد یا restorecon نیست. از اینجا شروع کنید:

sudo aa-status
sudo journalctl -k | grep -i apparmor

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

آیا Ubuntu دارای SELinux است؟

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