SSD Nodes Learn 🎉 VPS kutoka $5.50/mwezi
Mwongozo Matt ConnorNa Matt Connor · Imeboreshwa 2026-08-13

Kwa nini Nginx inatoa 403 hata kama ruhusa ni sahihi?

Nginx inarejesha 403 kwa sababu ya sera za SELinux. Jifunze kusoma logi za kukataliwa, kurekebisha lebo kwa kutumia semanage na restorecon, na kuweka mfumo katika hali ya enforcing.

Kwa nini nginx inarejesha 403 kwenye faili ambalo lina ruhusa sahihi

Nginx kurejesha 403 kwenye faili ambalo lina bits za ruhusa sahihi karibu kila mara husababishwa na SELinux (security-enhanced Linux) kukataa usomaji. SELinux hukagua seti ya pili ya sheria baada ya ruhusa za kawaida kupita, na seva ya wavuti inaruhusiwa kusoma faili zenye lebo ya maudhui ya wavuti pekee. Faili lako lina lebo tofauti, kwa hivyo ufunguzi unashindwa na nginx haina cha kutuma.

Angalia lebo, siyo mode pekee:

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

Nukta inayochapishwa baada ya drwxr-xr-x inamaanisha faili lina lebo ya SELinux. default_t ni kile ambacho njia hupata wakati sera haijawahi kuitambua, na hakuna chochote katika sheria za seva ya wavuti kinachoruhusu kusoma aina hiyo. Logi ya makosa huonyesha kosa la kawaida la Unix, ndiyo maana hii inaonekana kama hitilafu ya ruhusa:

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 inarejesha 13: Permission denied kwa aina zote mbili za kukataa, ile ya kawaida na ile ya SELinux. Kwa hivyo kazi ya kwanza ni kubaini ni safu ipi iliyosema hapana. Usianze na setenforce 0.

Sehemu ya mfumo unayohitaji

SELinux ni mfumo wa udhibiti wa ufikiaji wa lazima, ambao kwa kawaida huitwa MAC. Kila mchakato huendeshwa ndani ya domain, kama vile httpd_t kwa seva ya wavuti. Kila faili na kila port ya mtandao hubeba aina (type), kama vile httpd_sys_content_t. Sera (policy) ni orodha ya michanganyiko inayoruhusiwa ya domain, aina, na kitendo, na chochote kisichokuwa kwenye orodha hiyo hukataliwa. Mfumo huu hufanya kazi baada ya ukaguzi wa kawaida wa Unix, kwa hivyo bit za ruhusa katika drwxr-xr-x lazima zikubali ufikiaji kwanza. Tabaka zote mbili lazima zitoe idhini.

Muktadha kamili una sehemu nne zilizotenganishwa na koloni, kama vile system_u:system_r:httpd_t:s0: mtumiaji wa SELinux, jukumu (role), aina (type), na kiwango (level). Kwenye seva, unatumia karibu muda wako wote kwenye sehemu ya tatu, yaani aina. Amri mbili zinaonyesha thamani za sasa:

ps -eZ | grep nginx
id -Z

Wafanyakazi wa nginx wanaonyesha muktadha unaoishia na httpd_t. Shell yako ya kuingia inaonyesha unconfined_u:unconfined_r:unconfined_t:s0, kwa sababu sera chaguo-msingi ya targeted huwekea mipaka huduma na kuwaacha watumiaji wa kawaida bila vikwazo. Hilo ni jambo la muhimu kujua, kwa sababu SELinux haichukui nafasi ya kuendesha huduma chini ya watumiaji wenye upendeleo mdogo. Inapunguza kile ambacho huduma inaweza kufikia baada ya mtu kuivamia.

Njia tatu za uendeshaji, na ni picha zipi zina SELinux

sestatus
getenforce

Enforcing huzuia na kurekodi matukio. Permissive huruhusu kila kitu na kurekodi kile ambacho kingezuiwa. Disabled haipakii sera yoyote. getenforce huchapisha hali ya sasa. sestatus pia huchapisha hali kutoka /etc/selinux/config, ambayo ndiyo itakayorejea baada ya seva kuwaka upya (reboot).

Rocky Linux, AlmaLinux, Fedora na RHEL huja na SELinux ikiwa katika hali ya enforcing kwa kutumia sera ya targeted. Ubuntu na Debian hutumia AppArmor badala yake, ambayo hufanya kazi sawa kwa utaratibu tofauti (sehemu ya mwisho inalielezea hili). Kwa hivyo, programu moja inaweza kusakinika bila matatizo kwenye seva moja na kutoa 403 kwenye nyingine.

Sakinisha zana kabla ya kuzihitaji

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

semanage: command not found kwenye image ndogo inamaanisha policycoreutils-python-utils haipo: kifurushi hicho kina semanage na audit2allow. setroubleshoot-server huongeza sealert na kuandika muhtasari wa lugha rahisi wa kila kukataliwa (denial) kwenye journal. Sakinisha zote mbili kwenye seva mpya, kwa sababu wakati unaozihitaji ndio wakati ambapo kitu fulani kimekwishaharibika.

Jinsi ya kusoma kukataliwa kwa SELinux kwenye log ya audit

Kila kukataliwa kurekodiwa na audit daemon kama ujumbe wa 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

Sehemu nne hubeba maelezo yote. comm ni programu iliyozuiwa. scontext ni muktadha wa chanzo, domain ambayo mchakato ulikuwa ukiendeshwa ndani yake. tcontext ni muktadha wa lengo, lebo iliyopo kwenye kitu kilichojaribiwa kufikiwa. tclass ni aina ya kitu, hapa ni faili. Ukisoma pamoja: mchakato katika httpd_t ulijaribu kusoma faili yenye lebo default_t, na permissive=0 inasema ombi lilizuiwa kikweli badala ya kurekodiwa tu.

Kama ausearch haitoi matokeo yoyote, huenda audit daemon haiendeshi. Kukataliwa huko huishia kwenye kernel ring buffer badala yake:

sudo journalctl -k | grep -i avc

Sasa badilisha rekodi hiyo kuwa sentensi:

sudo ausearch -m AVC -ts recent | audit2why
sudo journalctl -t setroubleshoot --since today
sudo sealert -a /var/log/audit/audit.log

audit2why inasoma rekodi zilezile na kutaja sababu inayotambua: boolean iliyozimwa, lebo isiyolingana na sera, au kutokuwepo kwa sheria yoyote. sealert hupitia log nzima na kuchapisha amri inayopendekezwa kwa kila kukataliwa. Ichukulie pendekezo hilo kama dokezo tu. Ufafanuzi hubadilika kati ya releases, na sealert wakati mwingine hupendekeza custom policy module ambapo kurekebisha lebo kwa mstari mmoja ndiyo jibu sahihi.

Jambo moja zaidi la kujua. Sera ina sheria za dontaudit zinazoficha kukataliwa kunakoonekana kutokuwa na madhara, hivyo programu inaweza kufanya kazi vibaya huku log ikiwa tupu. Zifichue kwa muda wa jaribio moja:

sudo semodule -DB
# reproduce the problem, then read the log again
sudo semodule -B

Rekebisha njia yenye lebo isiyo sahihi kwa kutumia semanage fcontext na restorecon

Amri mbili, na mpangilio ni muhimu. semanage fcontext -a hurekodi lebo inayopaswa kuwa kwenye njia husika. restorecon hutumia chaguomsingi hiyo iliyorekodiwa kwenye faili zilizopo kwenye diski.

sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?"
sudo restorecon -Rv /data/www
ls -ldZ /data/www/index.html

Njia hiyo ni usemi wa kawaida (regular expression). (/.*)? inashughulikia saraka yenyewe na kila kitu kilicho chini yake, jambo ambalo ni hitaji la msingi la document root. Angalia mabadiliko yatakayotokea kabla ya kuyafanya: sudo restorecon -Rvn /data/www huchapisha lebo mpya zilizopangwa, kwa sababu -n inamaanisha hakuna hatua itakayochukuliwa. Baada ya restorecon halisi, lebo itasomeka httpd_sys_content_t na hitilafu ya 403 itatoweka bila kuhitaji kuanzisha upya huduma.

Tumia chcon kama jaribio pekee. chcon -t httpd_sys_content_t index.html huweka lebo moja kwa moja, na restorecon inayofuata, sasisho la kifurushi, au relabel kamili itaiweka upya, kwa sababu sera bado inasema njia hiyo inapaswa kuwa na lebo nyingine. semanage fcontext ndiyo toleo linalodumu. Orodhesha ulichorekodi kwa kutumia sudo semanage fcontext -l | grep '^/data'.

Maudhui ambayo huduma lazima yaandike yanahitaji aina tofauti. Tumia httpd_sys_rw_content_t kwa saraka ya kupakia (upload) au cache, na uweke mipaka kwenye njia hizo pekee: tovuti inayoweza kusomwa tu iliyo chini ya aina inayoweza kuandikwa hutoa ufikiaji mwingi kuliko ule unaohitajika na programu.

Kwa nini lebo ilikuwa sahihi? Karibu kila mara ni kwa sababu ya jinsi faili zilivyofika. mv huhifadhi lebo ya sasa ya faili, kwa hivyo tovuti iliyohamishwa kutoka /root hufika ikiwa na lebo ya admin_home_t na kubaki hivyo. cp ya kawaida huipa faili mpya lebo chaguomsingi ya saraka ya lengwa, ambayo kwa kawaida ndiyo unayotaka, wakati cp -a na rsync -X hunakili lebo za chanzo pamoja na faili. git clone kwenye saraka mpya ya ngazi ya juu huzalisha default_t. Wakati ukurasa unapopakia vizuri kutoka /usr/share/nginx/html na kushindwa kutoka kwenye saraka yako mwenyewe, hii ndiyo sababu.

Kurekebisha aina ya tabia kwa kutumia boolean

Baadhi ya hitilafu si tatizo la lebo. Reverse proxy kwenye seva mpya ya Rocky au AlmaLinux inarejesha 502, na logi ya hitilafu inasema:

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 yako iko sawa. Domain ya httpd_t hairuhusiwi kufungua miunganisho ya mtandao ya nje kwa chaguo-msingi, kwa hivyo ombi la connect() linakataliwa kabla halijafika kwenye loopback interface. Swichi moja inadhibiti tabia hiyo yote:

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

-P ndiyo flag muhimu: inaandika thamani hiyo kwenye diski. Bila -P mabadiliko yanapotea wakati wa reboot inayofuata, jambo linalokupa huduma inayofanya kazi hadi mashine iwashwe upya. Thibitisha kwa semanage boolean -l | grep httpd_can_network_connect, ambayo huchapisha thamani inayotumika sasa kando ya ile iliyohifadhiwa.

Pendelea kutumia boolean badala ya sheria iliyoandikwa kwa mkono wakati wowote inapopatikana. Booleans huja na sera ya usambazaji, kwa hivyo hudumishwa, huandikiwa nyaraka, na ni rahisi kwa mtu mwingine kuzipata. getsebool -a huorodhesha kila moja kwenye mfumo.

Ruhusu huduma isikilize kwenye port isiyo ya kawaida

Ports pia huwekewa lebo. Hamisha Nginx kwenye 8081 na itakataa kuanza:

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

httpd_t inaweza kufunga ports zilizowekewa lebo ya http_port_t, na 8081 si mojawapo. Iongeze:

sudo semanage port -l | grep -w http_port_t
sudo semanage port -a -t http_port_t -p tcp 8081

Kagua orodha kwanza. Ports kadhaa za juu zimeruhusiwa tayari, ikiwemo 8008 na 8443, na kuongeza port mara mbili kutasababisha ValueError: Port tcp/8081 already defined. Ikiwa port tayari ni ya aina nyingine, ibadilishe kwa kutumia semanage port -m -t http_port_t -p tcp 8081 badala ya kuiongeza.

Amri hiyo hiyo ndiyo inayofanya port ya SSH iliyohamishwa kufanya kazi. Bind to port 2222 on 0.0.0.0 failed: Permission denied katika journalctl -u sshd inamaanisha 2222 haipo kwenye ssh_port_t, kwa hivyo endesha sudo semanage port -a -t ssh_port_t -p tcp 2222 kabla ya kuanzisha upya daemon na kufunga session yako. Hiyo ndiyo hatua ambayo watu huruka wanapofuata mwongozo wa jumla wa kuimarisha usalama wa SSH kwenye VPS kwenye image ya familia ya Red Hat. SELinux si firewall, kwa hivyo port bado lazima iwe wazi: sudo firewall-cmd --permanent --add-port=8081/tcp && sudo firewall-cmd --reload hapa, au ufw kwenye image ya Debian au Ubuntu.

Wakati hakuna boolean wala lebo ya kubadilisha

Hali hii ni nadra kwenye seva ya kawaida, na hapa ndipo watu husababisha uharibifu. audit2allow inaweza kutengeneza moduli ya sera kutokana na kukataliwa (denials) zilizopo kwenye log:

sudo ausearch -m AVC -ts recent -c nginx | audit2allow -M nginx_local
cat nginx_local.te
sudo semodule -i nginx_local.pp

Soma nginx_local.te kabla ya kuiweka. Tabia mbili huiweka hali hii kuwa salama. Chuja maingizo (input) kwa programu moja unayorekebisha ukitumia -c, kwa sababu kuingiza (pipe) kukataliwa kwa wiki nzima ambako hakuhusiani kwenye audit2allow kutatoa ruhusa kwa yote hayo kwa wakati mmoja. Na usiwahi kuweka moduli iliyotengenezwa kutokana na kukataliwa usikoweza kukielezea: sheria inayoruhusu httpd_t kusoma kila faili kwenye mashine ni rahisi kutengeneza na ni vigumu kuigundua miezi kadhaa baadaye. Ondoa moduli ukitumia sudo semodule -r nginx_local.

Permissive ni hali ya uchunguzi, si suluhisho

sudo setenforce 0
# reproduce the problem once, all the way through
sudo ausearch -m AVC -ts recent
sudo setenforce 1

Hali ya permissive inaruhusu ufikiaji na kuweka kumbukumbu zake. Thamani yake halisi ni ukamilifu. Chini ya enforcing, huduma husimama kwenye kizuizi cha kwanza, kwa hivyo unarekebisha hicho, unawasha upya, na kukutana na cha pili. Chini ya permissive, utendaji huendelea na logi hukusanya kila kizuizi katika hatua moja, kisha unabadilisha kurudi nyuma na kuvirekebisha vyote kwa pamoja.

setenforce haigusi /etc/selinux/config, kwa hivyo reboot huirudisha seva kwenye hali ya enforcing. Hiyo ni nyavu ya usalama, na ndiyo sababu "suluhisho" lililojumuisha setenforce 0 hujitokeza tena wakati mbaya zaidi. Ikiwa huduma moja inahitaji nafasi wakati unaifanyia kazi, weka alama kwenye domain hiyo badala ya mashine nzima: sudo semanage permissive -a httpd_t huacha kila kitu kingine kikiwa enforcing, na sudo semanage permissive -d httpd_t huiondoa hali hiyo.

Kwa nini kuzima SELinux kunagharimu zaidi kuliko kurekebisha lebo

Kuweka SELINUX=disabled katika /etc/selinux/config kunabadilisha urekebishaji wa lebo wa mstari mmoja na seva iliyo dhaifu kabisa. Tofauti hiyo huonekana siku ambayo programu ya wavuti inapoingiliwa. Chini ya hali ya enforcing, msimbo wa mshambuliaji huendeshwa ndani ya httpd_t, kwa hivyo unaweza kusoma maudhui ya wavuti, wakati kusoma /etc/shadow au kuandika unit ya systemd kunakataliwa na sera bila kujali kile mtumiaji wa Unix angekuwa ameruhusu. Bila sera yoyote kupakiwa, msimbo huo huo hupata kila kitu ambacho akaunti ya huduma inacho.

Kuzima pia kuna gharama unayolipa baadaye. Wakati hakuna sera iliyopakiwa, faili mpya huundwa bila lebo, kwa hivyo mfumo wa faili hutofautiana na sera. Kuwasha tena SELinux kunahitaji relabel kamili, au huduma nyingi zitafeli kwa wakati mmoja:

sudo fixfiles -F onboot
sudo reboot

Hiyo huandika /.autorelabel na kuweka lebo upya kila mfumo wa faili wakati wa boot inayofuata. Kwenye diski kubwa hii huchukua muda mrefu na console huonekana kama imekwama, kwa hivyo ianzishe wakati unaweza kusubiri. Kwenye Rocky Linux na AlmaLinux 9 faili ya usanidi haizimi tena sehemu ya kernel peke yake, na njia iliyothibitishwa ya kuzima SELinux kikamilifu ni hoja ya kernel (sudo grubby --update-kernel ALL --args selinux=0). Kujua amri hiyo husaidia unaporithi seva ya mtu mwingine. Hiyo si suluhisho la hitilafu ya 403.

Containers ongeza lebo moja zaidi

Kwenye seva ya familia ya Red Hat, michakato ya container huendeshwa ndani ya container_t na inaweza kusoma faili zilizopewa lebo ya container_file_t pekee. Bind mount kutoka kwenye seva mwenyeji inafeli na kuleta Permission denied ndani ya container, wakati ls -l kwenye seva mwenyeji inaonekana kuwa ya kawaida kabisa. Suffix ya :Z huiambia runtime ibadilishe lebo ya mount hiyo:

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

:Z huweka lebo kwenye saraka kwa ajili ya container hii pekee. :z huweka lebo kwa ajili ya kushiriki kati ya container mbalimbali. Elekeza :Z kwenye saraka inayotumiwa na huduma nyingine na itabadilisha lebo ya saraka hiyo kwa njia ya recursive, jambo ambalo litavuruga huduma hizo, kwa hivyo pea container njia zao wenyewe. Kila kitu kingine kuhusu usanidi huu kinafanana na image nyingine yoyote, ambayo imeelezewa katika kuendesha Docker kwenye VPS.

Ubuntu na Debian hukupa AppArmor

Kazi ni ileile, usanifu ni tofauti. AppArmor huzuia programu kulingana na njia ya faili inayoweza kutekelezwa (executable path), ikitumia profile iliyo chini ya /etc/apparmor.d/, badala ya kuweka lebo kwenye faili zilizopo kwenye diski. Hakuna haja ya kuweka lebo upya na hakuna restorecon. Anza hapa:

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

Kukataliwa kwa ombi huonekana kama apparmor="DENIED" operation="open" profile="/usr/sbin/nginx" name="/data/www/index.html" requested_mask="r". Mtiririko wa kazi una umbo lilelile: soma sababu ya kukataliwa, tafuta profile husika, kisha ubadili sheria. sudo apt install apparmor-utils hukupa aa-complain (hali ya kuruhusu kwa profile moja) na aa-enforce ili kuirudisha katika hali ya awali. Ubuntu huzuia seti teule ya huduma zilizowekwa (packaged services) na kuziacha nyingine bila vizuizi, kwa hivyo soma aa-status ili kuona ni nini kinachofanya kazi kikamilifu badala ya kudhani.

Kuna tabia moja inayofaa katika mifumo yote miwili. Huduma inaporipoti Permission denied kwenye kitu kinachoonekana kuwa sahihi, soma logi ya usalama kabla ya kugusa ruhusa (permissions). Mara chache sana tatizo huwa ni bits za faili.

FAQ

Kwa nini nginx inarejesha 403 wakati ruhusa za faili ni sahihi?

Kwa sababu SELinux imekataa usomaji, si kwa sababu ya permission bits. Web server huendeshwa ndani ya domain ya httpd_t na inaweza kusoma faili zilizowekewa lebo ya maudhui ya wavuti pekee, kwa hivyo faili iliyowekewa lebo ya default_t au admin_home_t hukataliwa na nginx haina cha kuhudumia. Thibitisha hili kwa sudo ausearch -m AVC -ts recent, ambayo inaonyesha scontext ikiishia na httpd_t na tcontext ikiwa na aina isiyo sahihi. Kisha rekodi lebo sahihi na uitumie: sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?" ikifuatiwa na sudo restorecon -Rv /data/www.

Je, ni salama kuendesha setenforce 0 ili kufanya huduma ifanye kazi?

setenforce 0 ni hatua ya utambuzi, si suluhisho. Itumie ili kurudia tatizo mara moja ili logi ikusanye kila kizuizi katika hatua moja, yasome kwa sudo ausearch -m AVC -ts recent, kisha uendeshe sudo setenforce 1 na urekebishe visababishi. Seva iliyoachwa katika hali ya permissive hurekodi kila kizuizi na haizuii chochote, kwa hivyo unapata kelele nyingi na kupoteza ulinzi. Ikiwa huduma moja inahitaji nafasi wakati unafanya kazi, endesha sudo semanage permissive -a httpd_t ili mashine nyingine ibaki katika hali ya enforcing.

Ninawezaje kuendesha huduma kwenye port isiyo ya kawaida huku SELinux ikiwa katika hali ya enforcing?

Ongeza port hiyo kwenye aina ambayo huduma hiyo inaruhusiwa kuunganishwa nayo. Kwa web server kwenye 8081: sudo semanage port -a -t http_port_t -p tcp 8081. Kwa SSH kwenye 2222: sudo semanage port -a -t ssh_port_t -p tcp 2222. Angalia orodha ya sasa kwanza kwa sudo semanage port -l | grep -w http_port_t, kwa sababu port ambayo tayari imeorodheshwa itafeli kwa ValueError: Port tcp/8081 already defined. Bila hatua hii, daemon itajifunga wakati wa kuanza na bind() ... Permission denied ingawa hakuna mchakato mwingine unaoshikilia port hiyo.

Je, Ubuntu ina SELinux?

Hapana. Ubuntu na Debian hutumia AppArmor, ambayo hutekeleza profile iliyofungwa kwenye njia ya faili inayoweza kutekelezwa (executable) badala ya lebo kwenye faili. Iangalie kwa sudo aa-status na utafute mistari ya apparmor="DENIED" ndani ya sudo journalctl -k. Ubuntu huzuia seti teule ya huduma zilizopakiwa, kwa hivyo programu nyingi huendeshwa bila kizuizi kwa chaguo-msingi. Rocky Linux na AlmaLinux ndizo unazokutana na SELinux ikiwa imewezeshwa kuanzia mwanzo, pamoja na Fedora na RHEL.