Docker kupita UFW na jinsi ya kurekebisha
Docker hutumia iptables kupita sheria za UFW. Jifunze jinsi ya kuzuia bandari za kontena zisijibu kwenye mtandao kwa kutumia njia sahihi za kurekebisha.
Kwa nini Docker hupita UFW
Docker hupita UFW kwa sababu bandari (ports) za kontena zilizochapishwa hazipiti kwenye sheria za kizuizi (firewall rules) zinazodhibitiwa na UFW. Unapofanya docker run -p 8080:80, Docker huandika sheria ya DNAT (destination network address translation) kwenye PREROUTING chain ya nat table ya kernel. Sheria hiyo hubadilisha anuani ya mwisho ya kila paketi kwenda kwenye anuani ya ndani ya kontena kabla ya kernel kuamua mahali paketi inapoenda. Paketi iliyobadilishwa kisha hutumwa ndani ya kontena kupitia FORWARD chain, ambayo Docker ndiyo inayodhibiti. Sheria za UFW zipo kwenye INPUT chain, na paketi haipiti hapo kamwe. Kwa hivyo ufw status inaonyesha kuzuia kwa asili (default deny), sudo ufw deny 8080 inaripoti mafanikio, lakini bandari 8080 bado inajibu kutoka kwenye mtandao mzima.
Hii siyo hitilafu ya Docker, na UFW haijaharibika. Zana zote mbili zinatengeneza firewall ileile ya kernel. Sheria za Docker hufanya kazi mapema zaidi kwenye njia ya paketi, hivyo UFW hauiulizwi kamwe. Mwongozo huu unaonyesha jinsi upitaji huo unavyofanyika, unaelezea mchakato huo, na kisha unatoa suluhisho mbili zinazofanya kazi: kuchapisha bandari kwenye 127.0.0.1, na kuchuja kwenye DOCKER-USER chain. Ikiwa UFW ni mpya kwako, iandae kwa kutumia mwongozo wa misingi ya firewall ya UFW kwanza, kwa sababu firewall ya default-deny bado ndiyo msingi sahihi kwa kila kitu kingine kwenye seva.
Angalia njia ya kuepuka (bypass) kwenye seva yako mwenyewe
Anza na VPS ambapo UFW imewashwa na ina sera ya kuzuia (deny) trafiki inayokuja kwa kimawazo. Run container ya web yenye port iliyowazi:
sudo ufw status verbose
docker run -d --name web -p 8080:80 nginx:1.29-alpineufw status verbose inaonyesha Default: deny (incoming), allow (outgoing) na hakuna sheria kwa port 8080. Kwa ripoti ya firewall yenyewe, port imefungwa. Sasa jaribu kutoka mashine nyingine, si kutoka kwenye seva yenyewe:
curl -I http://your-vps-ip:8080/HTTP/1.1 200 OKContainer inajibu. Ongeza sheria ya kuzuia (deny rule) na ujaribu tena:
sudo ufw deny 8080/tcpPort bado inajibu, kwa sababu sheria ya kuzuia ipo kwenye mnyororo (chain) ambao paketi haipiti kamwe. UFW haikufeli. Haikuombwa hata mara moja. Hii ndiyo sababu tatizo hufichika vizuri: hakuna kosa linalochapishwa popote, deployment inafanya kazi, na matokeo ya hali ya firewall yanaonekana kama seva iliyofungwa vizuri.
Utaratibu: PREROUTING hufanya kazi kabla ya INPUT
Kernel huchakata paketi inayokuja kwa mpangilio maalum, na tatizo lote linatokana na mpangilio huo.
PREROUTINGhufanya kazi kwanza. Sheria hapa zinaweza kubadilisha mwisho wa paketi, na sheria ya Docker kwa port iliyochapishwa hufanya hivyo.- Uamuzi wa kuelekeza (routing decision) unakuja baada ya hapo. Paketi inayoelekezwa kwenye host yenyewe huenda kwenye mnyororo wa
INPUT. Paketi inayoelekezwa kwenye mashine nyingine yoyote huenda kwenye mnyororo waFORWARD. - Sheria za UFW zipo kwenye
INPUT. Sheria za Docker zipo kwenyeFORWARD.
Angalia sheria ya Docker kwa kontena uliowasha hivi punde:
sudo iptables -t nat -L DOCKER -nChain DOCKER (2 references)
target prot opt source destination
RETURN 0 -- 0.0.0.0/0 0.0.0.0/0
DNAT 6 -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80Mstari wa DNAT ndio sababu kuu. Paketi yoyote inayofika kwa port 8080 hubadilishwa mwisho wake kuwa 172.17.0.2:80, ambayo ni anwani ya kontena kwenye mtandao wa bridge wa Docker. Baada ya kubadilishwa, paketi haielekezwi tena kwenye host, hivyo uamuzi wa kuelekeza huituma kwenye njia ya FORWARD, ambapo Docker tayari imeongeza sheria zinazokubali trafiki kwenye mitandao yake. Sheria yako ya deny 8080/tcp inasubiri kwenye INPUT kwa paketi ambayo haitakuja kamwe.
Kwenye Ubuntu 24.04, amri ya iptables ni msimamizi wa nftables, lakini mpangilio wa mnyororo na matokeo ni sawa. UFW na Docker zote huandika kwenye mchakato mmoja wa paketi za kernel, na sehemu ya kuanzia ya Docker iko mapema zaidi.
Suluhisho la kila siku: chapisha bandari kwenye 127.0.0.1
Konta nyingi hazihitaji kuwa wazi kwa umma tangu mwanzo. Database, seva ya programu nyuma ya reverse proxy, jopo la usimamizi, au sehemu ya vipimo: hakuna kati ya hizi inayopaswa kujibu mtandao wa internet moja kwa moja. Chapisha kwenye anwani ya loopback:
docker run -d --name web -p 127.0.0.1:8080:80 nginx:1.29-alpineAu kwenye faili ya Compose:
services:
web:
image: nginx:1.29-alpine
ports:
- "127.0.0.1:8080:80"Hii inafanya kazi kwa sababu sheria ya DNAT ya Docker sasa inalinganisha paketi zinazoelekezwa kwenye 127.0.0.1 pekee, na paketi kutoka internet haiwezi kuwa na lengo hilo kwa uhalali, hivyo kernel huiondoa kabla ya sheria yoyote ya firewall kufanya kazi. Bandari inaweza kufikiwa kutoka kwenye host, na hakuna mahali pengine. Hakiki ufungaji (binding):
sudo ss -tlnp | grep 8080Unahitaji 127.0.0.1:8080 kwenye matokeo, si 0.0.0.0:8080 au [::]:8080. Kisha thibitisha kutoka mashine nyingine kuwa curl http://your-vps-ip:8080/ imekataliwa.
Kwa huduma zinazopaswa kukutana na internet, tumia reverse proxy moja inayomiliki bandari 80 na 443 na inayopitisha trafiki kwa kutumia hostname, kisha usichapishe kitu kingine. Huo ndio mfumo ambao mwongozo wa Traefik reverse proxy unauunda, na ndivyo programu inayojihost kama Nextcloud kwenye VPS inavyobaki isiweze kufikiwa isipokuwa kupitia proxy yake. Jinsi entries za ports: zinavyotangazwa, na mfululizo wa kazi wa Compose, kunaelezewa kwenye mwongozo wa misingi ya Docker Compose.
Kwa kila konta ya ndani kwenye loopback, UFW inarudi kufanya kazi yake ya kawaida: kulinda bandari ambazo host yenyewe inazitumikia. Unda seti hiyo ya sheria hapa, kisha endesha amri kwa mpangilio:
Kuchuja kwa uhalisia: mnyororo wa DOCKER-USER
Wakati mwingine port ya container lazima ibaki imechapishwa kwenye mtandao lakini iwe na vizuizi, kwa mfano port ya database replica ambayo ofisi moja tu inaweza kuifikia. Kwa ajili hiyo, Docker hutoa mnyororo wa DOCKER-USER. Kila paketi inayoelekea kwenye container inapitia DOCKER-USER kabla ya sheria za Docker za accept. Docker haiongezi sheria kwenye mnyororo huo. Mnyororo huo ni kwa ajili yako, na Docker haubadilishi maudhui yake hata baada ya daemon kuanza upya.
Tahadhari moja kabla ya amri: wakati paketi inapofika DOCKER-USER, mabadiliko ya DNAT tayari yameshafanyika. Port ya mwisho ya paketi ni port ya container (80 katika mfano wetu), siyo port iliyochapishwa (8080). Kwa sababu hiyo, sheria inayolingana na --dport 8080 haitapatia kitu. Njia ya uhakika ni kulinganisha port ambayo mteja aliiita awali, ambayo kernel's connection tracker hukumbuka:
sudo iptables -I DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL ! -s 10.0.0.10 -j DROPIsome kama: kwa paketi zilizoingia kupitia eth0 na ni sehemu ya muunganisho whose original destination port ilikuwa 8080, kata kila kitu ambacho hakijatumwa kutoka 10.0.0.10. Match ya --ctdir ORIGINAL huweka mipaka ya sheria hiyo kwenye mwelekeo wa mteja kwenda kwenye container, hivyo paketi za majibu hazitakatwa kimakosa. Badilisha eth0 na interface yako ya umma; ip route | grep default huipa jina. Ijaribu kwa njia ile ile kama awali: curl kutoka kwenye anwani iliyoruhusiwa itafanikiwa, na kutoka mahali popote kwingine muunganisho utakatika (timeout).
Sheria zinazoongezwa kwa amri ya iptables hupotea baada ya kuwasha upya mfumo. Kwa kuwa UFW tayari inasimamia firewall hii, sehemu bora ya kuzihifadhi ni /etc/ufw/after.rules. Ongeza sehemu mwishoni mwa faili:
*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL ! -s 10.0.0.10 -j DROP
COMMITKisha run sudo ufw reload. UFW huweka faili hiyo kila wakati inapoanzishwa upya au wakati wa kuwasha mfumo, hivyo kuchuja kwa container zako sasa kunaishi sehemu moja na sehemu nyingine za firewall yako, na itadumu baada ya kuwasha upya mfumo au baada ya Docker kupewa toleo jipya.
Kwa nini usizime uunganishaji wa iptables wa Docker
Majibu ya zamani kuhusu tatizo hili yanashauri kuweka { "iptables": false } kwenye /etc/docker/daemon.json. Usifanye hivyo. Sheria za firewall za Docker zina kazi nyingi zaidi ya kuchapisha ports. Sheria ya masquerade ndiyo inayozipa container uwezo wa kupata mtandao wa nje kupitia anwani ya host, hivyo ukiizima, container haziwezi kuvuta images, kufikia package mirrors, au kuita API yoyote ya nje. Sheria za DNAT ndizo zinazofanya -p ifanye kazi, hivyo ports zilizochapishwa zitacha kufanya kazi kabisa. Sheria za ulinzi zinazotenganisha mitandao tofauti ya Compose pia zitapotea. Ungekuwa unarekebisha tatizo la bypass kwa kuharibu mtandao wa container, na kila moja ya sheria hizo ingehitaji uandike na kuzisimamia mwenyewe. Dokumentashon ya Docker inataja mipangilio hiyo kwa ajili ya watu wanaokusudia kufanya hivyo. Chain ya DOCKER-USER ipo ili mtu yeyote asihitaji swichi hii.
Upande wa IPv6 wa tatizo lile lile
Kwanza kagua jinsi port iliyochapishwa inavyoonekana kwenye IPv6:
sudo ss -tlnp | grep 8080Tangu Docker Engine 27, Docker inasimamia ip6tables kwa kima kawaida. Kwenye mtandao wa Docker wenye IPv6 imewashwa, port iliyochapishwa hupata mchakato ule ule wa DNAT kwenye ip6tables, hivyo tatizo lile lile lipo na suluhisho lile lile linatumika: DOCKER-USER chain pia ipo kwenye ip6tables, hivyo nakili rule yako kwa kutumia sudo ip6tables -I DOCKER-USER ... na ufanye majaribio kutoka nje kwa kutumia curl dhidi ya IPv6 ya umma ya seva yako, kwa mfano curl -6 http://[2001:db8:2a::1]:8080/.
Kwenye mtandao usio na IPv6, wateja wa IPv6 hushughulikiwa na docker-proxy, ambayo ni user-space process ya kawaida inayosikiliza kwenye [::]:8080 na kuelekeza trafiki ndani ya container kupitia IPv4. Trafiki kwenda kwenye host process hupitia INPUT, hivyo UFW inaweza kuchuja njia hiyo, lakini ni wakati tu UFW inaposimamia IPv6. Je, inasimamia, na njia nyingine ambazo IPv6 hupata pengo kwenye VPS, ni mada ya mwongozo wa UFW na IPv6.
Kuchapisha kwenye loopback huepuka swali zima: -p 127.0.0.1:8080:80 inafunga IPv4 loopback pekee, hivyo hakuna IPv6 listener na hakuna kitu cha kufikiwa kutoka nje kwenye stack yoyote.
Mfumo unaoshikilia
- Chapisha kila port ya ndani kwenye
127.0.0.1, ili isionekane nje kabisa. - Toa upande wa umma kwa reverse proxy moja inayomiliki port 80 na 443.
- Weka UFW iwe deny kwa default kwa host, ikiruhusu SSH na port za proxy.
- Chuja port za container ambazo ni za umma kwenye
DOCKER-USER, kwa kulinganisha port ya mwisho ya asili, iliyohifadhiwa kwenye/etc/ufw/after.rules. - Acha uunganishaji wa iptables wa Docker ukiwa wazi.
Ikiwa imewekwa mara moja, hii huondoa mshangao: ufw status inaelezea host, na DOCKER-USER inaelezea container. Hakuna kinachochapishwa kwa bahati mbaya, na docker run -p inayofuata unayoiandika inaweka wazi kile ulichokusudia.
FAQ
Kwa nini naweza kuifikia container yangu ya Docker wakati UFW imefunga port?
Kwa sababu Docker huchapisha port kwa kutumia sheria ya DNAT katika chain ya PREROUTING, ambayo hubadilisha anuani ya mwisho ya paketi kwenda kwenye anuani ya container kabla ya kuchuja. Paketi hiyo kisha hupita kwenye njia ya FORWARD, na sheria za UFW zipo kwenye INPUT, chain ambayo paketi haipiti kamwe. Firewall haishauriwi, hivyo sheria zake za kuzuia (deny) hazina athari kwenye port za container zilizochapishwa.
Ninawezaje kuifanya UFW ifunge port za Docker zilizochapishwa?
UWF yenyewe haiwezi, kwa sababu sheria zake zipo kwenye chain isiyo sahihi. Aina mbili za suluhisho ni: acha kuweka wazi port hiyo kwa kuichapisha kama 127.0.0.1:8080:80 ili host pekee iweze kuifikia, au chuja kwenye chain ya DOCKER-USER kwa kutumia sheria ya iptables inayofanana na port ya mwisho asilia kupitia conntrack. Hifadhi sheria hiyo kwenye /etc/ufw/after.rules ili iendelee kufanya kazi baada ya kuwasha upya mfumo na ufw reload.
Je, nianze kuweka "iptables": false kwenye daemon.json ya Docker?
Hapana. Mpangilio huo huondoa sheria zote za firewall na NAT za Docker, jambo ambalo husababisha matatizo mengi zaidi ya kupita kwa firewall. Container hupoteza uwezo wa kupata intaneti kwa sababu sheria ya masquerade imeondoka, na port zilizochapishwa huacha kufanya kazi kwa sababu sheria za DNAT zimeondoka. Tumia kuchapisha kwenye loopback na chain ya DOCKER-USER badala yake; hizi hurekebisha tatizo la uwekaji wazi bila kuharibu mtandao wa container.
Je, Docker hupita UFW kwenye IPv6 pia?
Kwenye Docker Engine 27 na baadaye, usimamizi wa ip6tables umewashwa kwa kima kawaida, hivyo port iliyochapishwa kwenye mtandao wa Docker wenye IPv6 hubadilishwa ili kupita pembeni ya UFW sawa na IPv4, na inahitaji sheria sawa ya DOCKER-USER iliyofuatiliwa na ip6tables. Kwenye mitandao isiyo na IPv6, mchakato wa docker-proxy husikiliza kwenye [::] na trafiki hiyo hupita kwenye INPUT, ambapo UFW inaweza kuichuja ikiwa UFW inasimamia IPv6. Kuchapisha kwenye 127.0.0.1 huepuka hali zote mbili, kwa sababu hakuna kinachosikiliza kwenye IPv6 kabisa.