SELinux-এর কারণে nginx 403 ঠিক করার উপায়
Permission ঠিক থাকলেও nginx-এর 403 কেন হয় তা বুঝুন। SELinux denial পড়ে semanage ও restorecon দিয়ে file label ঠিক করুন, enforcing চালু রেখেই।
সঠিক permission থাকা সত্ত্বেও nginx কেন কোনো ফাইলে 403 ফেরত দেয়
ফাইলের permission bit সঠিক থাকা সত্ত্বেও nginx 403 ফেরত দিলে প্রায় সব ক্ষেত্রেই কারণ হয় SELinux (security-enhanced Linux) ফাইলটি পড়তে দিচ্ছে না। স্বাভাবিক permission যাচাই পাস করার পর SELinux আরও একটি নিয়মের সেট পরীক্ষা করে। Web server কেবল সেই ফাইল পড়তে পারে যেগুলোতে web content label রয়েছে। আপনার ফাইলে ভিন্ন label আছে। তাই open ব্যর্থ হয় এবং nginx পাঠানোর মতো কোনো কনটেন্ট পায় না।
শুধু mode নয়, label দেখুন:
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.htmldrwxr-xr-x-এর পরে যে dot দেখা যায়, তার অর্থ ফাইলটিতে একটি SELinux label রয়েছে। default_t হলো policy কোনো path সম্পর্কে আগে কখনও না জানলে সেটিতে থাকা label। Web server-এর নিয়মে এই type পড়ার অনুমতি নেই। Error log-এ সাধারণ Unix error দেখা যায়। তাই এটি permission সংক্রান্ত সমস্যা বলে মনে হয়:
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"সাধারণ permission ব্যর্থতা এবং SELinux-এর কারণে ব্যর্থতা—উভয় ক্ষেত্রেই kernel 13: Permission denied ফেরত দেয়। তাই প্রথমে কোন layer প্রত্যাখ্যান করেছে তা নির্ধারণ করুন। setenforce 0 দিয়ে শুরু করবেন না।
মডেলটির প্রয়োজনীয় অংশ
SELinux হলো mandatory access control, সাধারণত MAC নামে লেখা হয়। প্রতিটি process একটি domain-এ চলে, যেমন web server-এর জন্য httpd_t। প্রতিটি file এবং প্রতিটি network port-এর একটি type থাকে, যেমন httpd_sys_content_t। Policy হলো domain, type এবং action-এর অনুমোদিত সমন্বয়ের একটি তালিকা। এই তালিকায় নেই এমন সবকিছু deny করা হয়। এটি প্রচলিত Unix check-এর পরে কার্যকর হয়। তাই drwxr-xr-x-এ থাকা permission bits-কেও আগে access অনুমোদন করতে হবে। উভয় layer-কেই অনুমতি দিতে হবে।
একটি পূর্ণ context-এ colon দিয়ে আলাদা করা চারটি field থাকে, যেমন system_u:system_r:httpd_t:s0: SELinux user, role, type এবং level। Server-এ আপনি প্রায় সব সময় তৃতীয় field, অর্থাৎ type, নিয়েই কাজ করবেন। দুটি command live value দেখায়:
ps -eZ | grep nginx
id -Znginx worker-গুলোর context httpd_t দিয়ে শেষ হয়। আপনার login shell-এ unconfined_u:unconfined_r:unconfined_t:s0 দেখা যায়, কারণ default targeted policy service-গুলোকে সীমাবদ্ধ করে এবং interactive user-দের সাধারণত সীমাবদ্ধ করে না। এটি জানা গুরুত্বপূর্ণ, কারণ SELinux least-privilege user-এর অধীনে service চালানোকে প্রতিস্থাপন করে না। কেউ service-এ অনুপ্রবেশ করার পর service-টি কোন resource-এ পৌঁছাতে পারবে, SELinux তা সীমিত করে।
তিনটি mode এবং কোন image-এ SELinux থাকে
sestatus
getenforceEnforcing সবকিছু block করে এবং log লেখে। Permissive সবকিছু অনুমোদন করে এবং কোন কিছু block করত তা log করে। Disabled কোনো policy-ই load করে না। getenforce বর্তমান mode দেখায়। sestatus /etc/selinux/config থেকেও mode দেখায়; reboot-এর পরে যে mode আবার সক্রিয় হয়, সেটি এখানেই নির্ধারিত।
Rocky Linux, AlmaLinux, Fedora এবং RHEL-এ targeted policy সহ SELinux enforcing অবস্থায় থাকে। এই অভিন্ন default কাকতালীয় নয়; চারটিই একই Red Hat lineage থেকে তৈরি, যা Rocky Linux এবং AlmaLinux প্রকাশের আগে CentOS-এর মধ্য দিয়ে এগিয়েছিল। একই Red Hat lineage সম্পর্কে আরও জানুন। আপনি এই দুইটির কোনটি ব্যবহার করছেন, এই পৃষ্ঠার কোনো বিষয়েই তার পার্থক্য হয় না, কারণ দুটিতেই একই policy এবং একই tools থাকে। তাই Rocky Linux ও AlmaLinux-এর মধ্যে নির্বাচন security default-এর পরিবর্তে তাদের compatibility promise এবং পুরোনো CPU support-এর ওপর নির্ভর করে। Ubuntu এবং Debian-এর সঙ্গে AppArmor থাকে, যা ভিন্ন mechanism ব্যবহার করে একই কাজ করে (শেষ section-এ এটি আলোচনা করা হয়েছে)। তাই একই application আপনার একটি server-এ cleanly install হতে পারে, কিন্তু অন্য server-এ 403 return করতে পারে।
প্রয়োজন হওয়ার আগেই টুলগুলো ইনস্টল করুন
sudo dnf install -y policycoreutils-python-utils setroubleshoot-serverন্যূনতম image-এ semanage: command not found চালালে ব্যর্থ হয়, কারণ policycoreutils-python-utils অনুপস্থিত। ওই package-এ semanage এবং audit2allow থাকে। setroubleshoot-server sealert যোগ করে এবং প্রতিটি denial-এর সহজ ইংরেজি সারাংশ journal-এ লেখে। নতুন server-এ দুটিই আগে ইনস্টল করুন, কারণ এগুলো প্রয়োজন হওয়ার সময় সাধারণত কোনো কিছু ইতিমধ্যেই নষ্ট হয়ে গেছে।
audit log-এ SELinux denial কীভাবে পড়বেন
প্রতিটি প্রত্যাখ্যান audit daemon একটি AVC (access vector cache) message হিসেবে রেকর্ড করে:
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চারটি field থেকেই পুরো বিষয়টি বোঝা যায়। comm হলো যে program-টিকে block করা হয়েছে। scontext হলো source context, অর্থাৎ process যে domain-এ চলছিল। tcontext হলো target context, অর্থাৎ process যে জিনিসে access করার চেষ্টা করেছিল তার label। tclass হলো object-এর ধরন; এখানে এটি একটি file। একসঙ্গে পড়লে অর্থ দাঁড়ায়: httpd_t-এ চলমান process default_t label-যুক্ত একটি file পড়ার চেষ্টা করেছিল, এবং permissive=0 জানায় যে request-টি শুধু log করা হয়নি, সত্যিই block করা হয়েছে।
ausearch কোনো output না দিলে audit daemon হয়তো চলছে না। সে ক্ষেত্রে denial kernel ring buffer-এ জমা হয়:
sudo journalctl -k | grep -i avcএখন record-টিকে একটি ইংরেজি বাক্যে রূপ দিন:
sudo ausearch -m AVC -ts recent | audit2why
sudo journalctl -t setroubleshoot --since today
sudo sealert -a /var/log/audit/audit.logaudit2why একই record পড়ে এবং এটি যে কারণ শনাক্ত করেছে তা জানায়: বন্ধ থাকা একটি boolean, policy-এর সঙ্গে না-মেলা একটি label, অথবা কোনো rule-ই নেই। sealert পুরো log পরীক্ষা করে প্রতিটি denial-এর জন্য একটি প্রস্তাবিত command দেখায়। এই suggestion-কে নির্দেশনা হিসেবে নিন, চূড়ান্ত সমাধান হিসেবে নয়। release ভেদে wording পরিবর্তিত হয়, এবং sealert কখনও এমন একটি custom policy module প্রস্তাব করে, যেখানে আসলে এক লাইনের label সংশোধনই সঠিক সমাধান।
আরও একটি বিষয় জানা দরকার। Policy-তে dontaudit rule থাকে, যেগুলোকে harmless বলে বিবেচনা করা denial আড়াল করে। তাই log খালি থাকলেও কোনো program ভুলভাবে কাজ করতে পারে। একটি test চলাকালীন এগুলো দৃশ্যমান করুন:
sudo semodule -DB
# reproduce the problem, then read the log again
sudo semodule -Bsemanage fcontext এবং restorecon দিয়ে ভুল label-যুক্ত path ঠিক করা
দুটি command ব্যবহার করতে হবে, এবং ক্রমটি গুরুত্বপূর্ণ। semanage fcontext -a কোনো path-এর label কী হওয়া উচিত, তা রেকর্ড করে। restorecon disk-এ থাকা file-গুলোর ওপর সেই রেকর্ড করা default প্রয়োগ করে।
sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?"
sudo restorecon -Rv /data/www
ls -ldZ /data/www/index.htmlPath-টি একটি regular expression। (/.*)? directory নিজে এবং তার নিচের সবকিছু অন্তর্ভুক্ত করে, যা document root-এর জন্য প্রয়োজন। পরিবর্তন করার আগে কী বদলাবে তা দেখুন: sudo restorecon -Rvn /data/www যে relabel করার পরিকল্পনা করা হয়েছে, তা দেখায়, কারণ -n কোনো কাজ না করার অর্থ দেয়। প্রকৃত restorecon চালানোর পরে label httpd_sys_content_t হিসেবে দেখা যায় এবং service restart ছাড়াই 403 চলে যায়।
chcon শুধু পরীক্ষার জন্য ব্যবহার করুন। chcon -t httpd_sys_content_t index.html সরাসরি label নির্ধারণ করে, কিন্তু পরবর্তী restorecon, package update বা full relabel সেটি পুনরায় নির্ধারণ করে, কারণ policy এখনও বলে path-টির অন্য label থাকা উচিত। এমন কোনো machine-এ, যেখানে dnf-automatic নির্ধারিত সময়ে security update প্রয়োগ করে, সেই reset আপনার সামনে বসে থাকা অবস্থায় নয়, বরং নিজস্ব schedule অনুযায়ী ঘটে। ফলে আপনি শেষবার যে পরিবর্তন করেছিলেন, তার কয়েক ঘণ্টা পরে site কাজ করা বন্ধ করতে পারে। semanage fcontext হলো টিকে থাকা সমাধান। আপনি যে rule-গুলো রেকর্ড করেছেন, সেগুলো sudo semanage fcontext -l | grep '^/data' দিয়ে তালিকাভুক্ত করুন।
Service-কে যে content লিখতে হবে, তার জন্য আলাদা type প্রয়োজন। Upload directory বা cache-এর জন্য httpd_sys_rw_content_t ব্যবহার করুন এবং এটি শুধু সেই path-গুলোতেই সীমাবদ্ধ রাখুন। Writable type-এর অধীনে read-only site চালালে application-এর প্রয়োজনের চেয়ে বেশি access দেওয়া হয়।
Label-টি শুরু থেকেই ভুল ছিল কেন? প্রায় সব ক্ষেত্রেই কারণ file-গুলো কীভাবে এসেছে। mv file-এর বিদ্যমান label ধরে রাখে। তাই /root থেকে সরানো site admin_home_t label-সহ আসে এবং সেভাবেই থাকে। সাধারণ cp নতুন file-কে destination directory-এর default label দেয়, যা সাধারণত আপনার প্রয়োজনীয় ফল। অন্যদিকে cp -a এবং rsync -X file-এর সঙ্গে source label-ও কপি করে। নতুন top-level directory-তে একটি git clone চালালে default_t তৈরি হয়। /usr/share/nginx/html থেকে page ঠিকমতো load হয়, কিন্তু আপনার নিজের directory থেকে ব্যর্থ হয়, তাহলে এটাই কারণ।
একটি boolean দিয়ে একটি আচরণ-শ্রেণি ঠিক করুন
সব ব্যর্থতা label-এর সমস্যা নয়। নতুন Rocky বা AlmaLinux সিস্টেমে একটি reverse proxy 502 ফেরত দেয় এবং error log-এ দেখা যায়:
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 domain ডিফল্টভাবে outbound network connection খোলার অনুমতি পায় না। তাই loopback interface-এ পৌঁছানোর আগেই connect() call প্রত্যাখ্যাত হয়। একটি switch এই পুরো আচরণ নিয়ন্ত্রণ করে:
getsebool -a | grep httpd_can_network
sudo setsebool -P httpd_can_network_connect on-P-ই গুরুত্বপূর্ণ flag। এটি value-টি disk-এ লিখে রাখে। -P ছাড়া পরবর্তী reboot-এর সময় পরিবর্তনটি হারিয়ে যায়। ফলে machine restart না হওয়া পর্যন্ত service কাজ করে। semanage boolean -l | grep httpd_can_network_connect দিয়ে নিশ্চিত করুন। এটি running value-এর পাশে stored value প্রিন্ট করে।
যখন এমন কোনো boolean থাকে, তখন হাতে লেখা rule-এর বদলে boolean ব্যবহার করুন। boolean-গুলো distribution policy-এর সঙ্গে release হয়। তাই এগুলো রক্ষণাবেক্ষণ করা, documented এবং পরবর্তী administrator-এর জন্য খুঁজে পাওয়া সহজ। getsebool -a সিস্টেমের প্রতিটি boolean তালিকাভুক্ত করে।
একটি service-কে non-standard port-এ listen করান
Port-গুলোকেও label দেওয়া হয়। nginx-কে 8081-এ সরালে এটি start হতে অস্বীকার করে:
nginx: [emerg] bind() to 0.0.0.0:8081 failed (13: Permission denied)httpd_t শুধু label করা http_port_t port bind করতে পারে, আর 8081 সেগুলোর একটি নয়। এটি যোগ করুন:
sudo semanage port -l | grep -w http_port_t
sudo semanage port -a -t http_port_t -p tcp 8081আগে তালিকাটি পরীক্ষা করুন। 8008 এবং 8443-সহ কয়েকটি high port ইতিমধ্যে অনুমোদিত, এবং একই port দুবার যোগ করলে ValueError: Port tcp/8081 already defined ব্যর্থ হয়। Port-টি যদি ইতিমধ্যে অন্য type-এর অন্তর্ভুক্ত থাকে, তাহলে এটি যোগ না করে semanage port -m -t http_port_t -p tcp 8081 দিয়ে পরিবর্তন করুন।
SSH port পরিবর্তন করার পর সেটি কাজ করাতেও একই command ব্যবহার করতে হয়। journalctl -u sshd-এর Bind to port 2222 on 0.0.0.0 failed: Permission denied-এ বোঝায় যে 2222 ssh_port_t-এ অনুপস্থিত। তাই daemon restart করে session বন্ধ করার আগে sudo semanage port -a -t ssh_port_t -p tcp 2222 চালান। Red Hat family image-এ VPS-এ SSH hardening নিয়ে generic guide অনুসরণ করার সময় মানুষ সাধারণত এই ধাপটি বাদ দেয়। SELinux firewall নয়। তাই port-টি এখনও open করতে হবে: এখানে sudo firewall-cmd --permanent --add-port=8081/tcp && sudo firewall-cmd --reload ব্যবহার করুন, অথবা Debian বা Ubuntu image-এ ufw ব্যবহার করুন। --permanent flag-এ boolean-এর -P-এর মতো একই reboot trap রয়েছে। কোনো rule কোন interface-এ প্রযোজ্য হবে তা নির্ধারণ করা zone-গুলো Rocky বা AlmaLinux VPS-এর firewalld basics-এ একবার পড়ে নেওয়া উচিত।
যখন পরিবর্তন করার মতো কোনো boolean বা label নেই
স্বাভাবিক সার্ভারে এই পরিস্থিতি বিরল। এখানেই মানুষ ক্ষতি করে। লগের denial থেকে audit2allow একটি policy module তৈরি করতে পারে:
sudo ausearch -m AVC -ts recent -c nginx | audit2allow -M nginx_local
cat nginx_local.te
sudo semodule -i nginx_local.ppএটি install করার আগে nginx_local.te পড়ুন। দুটি অভ্যাস এই কাজকে নিরাপদ রাখে। -c দিয়ে input শুধু যে program-টি আপনি ঠিক করছেন, সেটির মধ্যে সীমাবদ্ধ করুন। কারণ এক সপ্তাহের অপ্রাসঙ্গিক denial audit2allow-এ pipe করলে সেগুলোর সবকটিই একসঙ্গে অনুমোদিত হয়। আর যে denial আপনি ব্যাখ্যা করতে পারেন না, তা থেকে তৈরি module কখনো install করবেন না। এমন একটি rule তৈরি করা সহজ, যা httpd_t-কে সিস্টেমের প্রতিটি file পড়তে দেয়; কিন্তু কয়েক মাস পরে সেটি শনাক্ত করা কঠিন। sudo semodule -r nginx_local দিয়ে একটি module সরান।
Permissive একটি diagnostic mode, সমাধান নয়
sudo setenforce 0
# reproduce the problem once, all the way through
sudo ausearch -m AVC -ts recent
sudo setenforce 1Permissive mode access অনুমোদন করে এবং তা log-এ লিখে রাখে। এর প্রকৃত মূল্য হলো সম্পূর্ণতা। Enforcing mode-এ service প্রথম denial-এ থেমে যায়। তাই সেটি ঠিক করে restart করলে পরের denial-টির মুখোমুখি হতে হয়। Permissive mode-এ run চলতে থাকে এবং একবারেই log-এ সব denial সংগ্রহ হয়। এরপর enforcing mode ফিরিয়ে এনে সেগুলো একসঙ্গে ঠিক করা যায়।
setenforce, /etc/selinux/config-এ কোনো পরিবর্তন করে না। তাই reboot হলে system আবার enforcing mode-এ ফিরে যায়। এটি একটি safety net। একই কারণে setenforce 0 দিয়ে করা "fix" সবচেয়ে খারাপ সময়ে আবার দেখা দিতে পারে। কাজ করার সময় কোনো একটি service-এর জন্য সাময়িকভাবে বেশি অনুমতি দরকার হলে পুরো machine-এর mode পরিবর্তন না করে সেই domain-টিকে চিহ্নিত করুন: sudo semanage permissive -a httpd_t বাকি সবকিছুকে enforcing mode-এ রাখে, আর sudo semanage permissive -d httpd_t সেটি ফিরিয়ে দেয়।
SELinux-এর label ঠিক করার বদলে এটি নিষ্ক্রিয় করলে খরচ বেশি
/etc/selinux/config-এ SELINUX=disabled সেট করলে এক লাইনের label ঠিক করার বদলে সার্ভার স্থায়ীভাবে দুর্বল হয়ে যায়। কোনো web application compromised হওয়ার দিন এই পার্থক্যটি স্পষ্ট হয়। enforcing অবস্থায় আক্রমণকারীর code httpd_t-এ চলে। তাই এটি web content পড়তে পারলেও /etc/shadow পড়া বা systemd unit লেখা policy প্রত্যাখ্যান করে, Unix user যেভাবেই অনুমতি দিক না কেন। কোনো policy load না থাকলে একই code service account-এর সব অনুমতি পেয়ে যায়।
SELinux নিষ্ক্রিয় করার খরচ পরে দিতে হয়। কোনো policy load না থাকা অবস্থায় নতুন file কোনো label ছাড়াই তৈরি হয়। ফলে filesystem ধীরে ধীরে policy থেকে বিচ্ছিন্ন হয়ে যায়। পরে SELinux আবার চালু করলে সম্পূর্ণ relabel করতে হয়। তা না হলে একসঙ্গে অনেক service ব্যর্থ হতে পারে:
sudo fixfiles -F onboot
sudo rebootএটি /.autorelabel লেখে এবং পরবর্তী boot-এর সময় প্রতিটি filesystem relabel করে। বড় disk হলে এতে অনেক সময় লাগে এবং console স্থির হয়ে আছে বলে মনে হয়। তাই অপেক্ষা করার সময় থাকলে কাজটি শুরু করুন। যেহেতু machine এমনিতেই shutdown হবে, আগে আর কোন restart অপেক্ষমাণ আছে তা দেখা উপযোগী। dnf update পুরোনো kernel ও library memory-তে রেখে দিলে needs-restarting যে তথ্য দেখায় সেটিই এখানে প্রাসঙ্গিক। Rocky Linux এবং AlmaLinux 9-এ config file আর নিজে থেকে kernel অংশটি নিষ্ক্রিয় করে না। SELinux সম্পূর্ণ নিষ্ক্রিয় করার নথিভুক্ত পদ্ধতি হলো kernel argument ব্যবহার করা (sudo grubby --update-kernel ALL --args selinux=0)। অন্য কারও server দায়িত্বে নিলে এই command জানা কাজে আসে। 403 error ঠিক করার জন্য এটি ব্যবহার করবেন না।
কনটেইনারে আরও একটি লেবেল যোগ করুন
Red Hat family host-এ কনটেইনার process-গুলো container_t-এ চলে এবং শুধু container_file_t লেবেলযুক্ত file পড়তে পারে। Host থেকে করা bind mount কনটেইনারের ভিতরে Permission denied দিয়ে ব্যর্থ হয়, যদিও host-এ ls -l সম্পূর্ণ স্বাভাবিক দেখায়। :Z suffix runtime-কে mount-টি relabel করতে বলে:
docker run -d -v /data/appdata:/var/lib/app:Z myimage:Z directory-টিকে শুধু এই কনটেইনারের জন্য লেবেল করে। :z এটিকে একাধিক কনটেইনারের মধ্যে ভাগ করে ব্যবহারের জন্য লেবেল করে। :Z-এ অন্য service-গুলো ব্যবহার করে এমন directory দিলে এটি সেই directory-টি recursively relabel করে। এতে service-গুলো নষ্ট হতে পারে। তাই কনটেইনারের জন্য আলাদা path ব্যবহার করুন। Engine এখনও host-এ ইনস্টল করা না থাকলে মনে রাখুন, এই distribution-গুলোতে docker command প্রায়ই podman-এর নামে সাড়া দেয়। এটি একটি অতিরিক্ত বিষয়। Rocky এবং AlmaLinux ইনস্টল করার ধাপ-এ এটি আগে ঠিক করা হয়। এরপর এগুলোর কোনোটিই আর সমস্যা হবে না। Setup-এর বাকি অংশ অন্য যেকোনো image-এর মতোই। VPS-এ Docker চালানো-তে তা দেখানো হয়েছে।
Ubuntu এবং Debian আপনাকে AppArmor দেয়
একই কাজ, কিন্তু নকশা আলাদা। AppArmor executable-এর path ব্যবহার করে একটি program-কে সীমাবদ্ধ রাখে। এর জন্য /etc/apparmor.d/-এর অধীনে থাকা profile ব্যবহার করা হয়। ডিস্কে থাকা file-এর label পরিবর্তন করার প্রয়োজন নেই। 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" হিসেবে দেখা যায়। কাজের ধাপ একই রকম: denial পড়ুন, profile খুঁজুন, rule পরিবর্তন করুন। sudo apt install apparmor-utils আপনাকে aa-complain দেয়, যা একটি profile-এর জন্য permissive mode। আবার আগের অবস্থায় ফেরাতে aa-enforce ব্যবহার করুন। Ubuntu নির্বাচিত packaged service-গুলোকে সীমাবদ্ধ রাখে এবং বাকি service-গুলোকে unconfined অবস্থায় রাখে। তাই অনুমান না করে aa-status পড়ুন, যাতে বর্তমানে আসলে কোন service সক্রিয় আছে তা দেখা যায়।
উভয় system-এ একটি অভ্যাস কাজে লাগে। কোনো service সঠিক মনে হওয়া কোনো কিছুর ক্ষেত্রে Permission denied জানালে permission পরিবর্তন করার আগে security log পড়ুন। একই সমস্যার কারণ সাধারণত দুইবার permission bit হয় না।
FAQ
nginx সঠিক file permission থাকা সত্ত্বেও 403 ফেরত দেয় কেন?
কারণ permission bit নয়, SELinux read operation প্রত্যাখ্যান করেছে। Web server httpd_t domain-এ চলে এবং কেবল web content-এর জন্য নির্ধারিত label থাকা file পড়তে পারে। তাই default_t বা admin_home_t label-যুক্ত file প্রত্যাখ্যাত হয় এবং nginx-এর serve করার মতো কিছু থাকে না। sudo ausearch -m AVC -ts recent দিয়ে এটি নিশ্চিত করুন। এতে scontext-এর শেষে httpd_t এবং tcontext-এ ভুল type দেখা যাবে। এরপর সঠিক label নথিবদ্ধ করে প্রয়োগ করুন: sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?", তারপর sudo restorecon -Rv /data/www।
service চালু করাতে setenforce 0 চালানো কি নিরাপদ?
setenforce 0 একটি diagnostic step, সমাধান নয়। সমস্যাটি একবার reproduce করতে এটি ব্যবহার করুন, যাতে log একবারেই সব denial সংগ্রহ করে। এরপর sudo ausearch -m AVC -ts recent দিয়ে সেগুলো পড়ুন, তারপর sudo setenforce 1 চালিয়ে কারণগুলো ঠিক করুন। permissive অবস্থায় রাখা server প্রতিটি denial log করে, কিন্তু কোনোটি block করে না। ফলে অপ্রয়োজনীয় log থেকে যায় এবং protection নষ্ট হয়। কাজ করার সময় কোনো একটি service-এর জন্য সাময়িকভাবে অনুমতি প্রয়োজন হলে sudo semanage permissive -a httpd_t চালান, যাতে বাকি machine enforcing অবস্থায় থাকে।
SELinux enforcing অবস্থায় non-standard port-এ service কীভাবে চালাব?
Service-টি যে type-এ bind করতে পারে, সেই type-এ port যোগ করুন। 8081 port-এ web server-এর জন্য: sudo semanage port -a -t http_port_t -p tcp 8081। 2222 port-এ SSH-এর জন্য: sudo semanage port -a -t ssh_port_t -p tcp 2222। প্রথমে sudo semanage port -l | grep -w http_port_t দিয়ে বর্তমান list পরীক্ষা করুন, কারণ কোনো port আগে থেকেই list-এ থাকলে ValueError: Port tcp/8081 already defined ব্যর্থ হবে। এই ধাপ বাদ দিলে অন্য কোনো process port-টি ব্যবহার না করলেও daemon startup-এর সময় bind() ... Permission denied দিয়ে বন্ধ হয়ে যাবে।
Ubuntu-তে কি SELinux আছে?
না। Ubuntu এবং Debian AppArmor সরবরাহ করে। এটি file-এর label-এর পরিবর্তে executable-এর path-এর সঙ্গে সংযুক্ত profile প্রয়োগ করে। sudo aa-status দিয়ে এটি পরীক্ষা করুন এবং sudo journalctl -k-এ apparmor="DENIED" line খুঁজুন। Ubuntu নির্বাচিত packaged service-গুলোর ওপর confinement প্রয়োগ করে। তাই অনেক program default অবস্থায় unconfined থাকে। Rocky Linux এবং AlmaLinux-এ Fedora ও RHEL-এর পাশাপাশি সাধারণত শুরু থেকেই SELinux enforcing অবস্থায় থাকে।