SSD Nodes Learn Hosting plans →
Mwongozo Matt ConnorNa Matt Connor · Imeboreshwa 2026-08-31

Jinsi ya Kuzuia Docker Kupita Kando ya UFW

Docker huweka sheria za iptables zinazopitisha UFW, hivyo port iliyokataliwa kama 8080 bado hujibu kutoka Internet. Jifunze chanzo na marekebisho mawili yanayofanya kazi.

Kwa nini Docker hupita kando ya UFW

Docker hupita kando ya UFW kwa sababu port za container zilizochapishwa hazipiti kamwe kwenye sheria za firewall ambazo UFW inasimamia. Unapoendesha docker run -p 8080:80, Docker huandika sheria ya DNAT (destination network address translation) kwenye chain ya PREROUTING ya table ya nat ya kernel. Sheria hiyo hubadilisha destination ya kila packet kuwa address ya faragha ya container kabla kernel haijaamua packet hiyo inaelekea wapi. Packet iliyobadilishwa hu-forwardiwa kwenye container kupitia chain ya FORWARD, ambayo Docker inasimamia. Sheria za UFW ziko kwenye chain ya INPUT, na packet haiingii humo kamwe. Kwa hiyo ufw status huonyesha default deny, sudo ufw deny 8080 huripoti mafanikio, lakini port 8080 bado hujibu kutoka Internet nzima.

Hili si kosa la Docker, na UFW haijaharibika. Zana zote mbili hu-program firewall ileile ya kernel. Sheria za Docker hutenda katika hatua ya awali zaidi ya njia ya packet, kwa hiyo UFW haiombwi kamwe kuchuja packet hiyo. Mwongozo huu unaonyesha jinsi bypass hiyo inavyotokea, unaeleza utaratibu wake, kisha unashughulikia marekebisho mawili yanayofanya kazi: kuchapisha port kwenye 127.0.0.1, na kuchuja kwenye chain ya DOCKER-USER. Ikiwa UFW bado ni mpya kwako, iandae kwanza kwa kutumia mwongozo wa misingi ya firewall ya UFW, kwa sababu firewall ya default-deny bado ndiyo msingi unaofaa kwa kila kitu kingine kwenye seva.

Tazama bypass kwenye seva yako mwenyewe

Anza na VPS ambayo UFW inatumika ikiwa na sera chaguo-msingi ya kukataa traffic inayoingia. Endesha container ya web yenye port iliyochapishwa:

sudo ufw status verbose
docker run -d --name web -p 8080:80 nginx:1.29-alpine

ufw status verbose inaonyesha Default: deny (incoming), allow (outgoing) na hakuna rule ya port 8080. Kulingana na ripoti ya firewall yenyewe, port imefungwa. Sasa fanya jaribio kutoka kwenye mashine nyingine, si kutoka kwenye seva yenyewe:

curl -I http://your-vps-ip:8080/
HTTP/1.1 200 OK

Container inajibu. Ongeza rule ya wazi ya deny kisha fanya jaribio tena:

sudo ufw deny 8080/tcp

Port bado inajibu, kwa sababu deny rule iko kwenye chain ambayo packet haipiti. UFW haikushindwa. Haikuwahi kushauriwa. Hii pia ndiyo sababu tatizo hili hufichika kwa urahisi: hakuna error inayochapishwa popote, deployment inafanya kazi, na output ya hali ya firewall inaonekana kabisa kama ya seva iliyofungwa kwa usalama.

Mchakato: PREROUTING huendeshwa kabla ya INPUT

Kernel huchakata packet inayoingia kwa mpangilio maalumu, na tatizo lote linatokana na mpangilio huo.

  1. PREROUTING huanza. Rules zilizo hapa zinaweza kubadilisha destination ya packet, na rule ya Docker ya port iliyochapishwa hufanya hivyo hasa.
  2. Uamuzi wa routing hufuata. Packet iliyoelekezwa kwa host yenyewe huenda kwenye chain ya INPUT. Packet iliyoelekezwa kwa mashine nyingine yoyote huenda kwenye chain ya FORWARD.
  3. Rules za UFW ziko kwenye INPUT. Rules za Docker ziko kwenye FORWARD.

Angalia rule ya Docker ya container uliyoanzisha hivi karibuni:

sudo iptables -t nat -L DOCKER -n
Chain 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:80

Mstari wa DNAT unaeleza kila kitu. Packet yoyote inayofika kwa port 8080 hubadilishiwa destination kuwa 172.17.0.2:80, yaani address ya container kwenye private bridge network ya Docker. Baada ya mabadiliko hayo, packet haielekezwi tena kwa host. Kwa hiyo, uamuzi wa routing huipeleka kwenye njia ya FORWARD, ambako Docker tayari imeongeza rules zinazoruhusu traffic kuingia kwenye networks zake. Rule yako ya deny 8080/tcp inasubiri kwenye INPUT packet ambayo haifiki kamwe.

Kwenye Ubuntu 24.04, command ya iptables ni front end ya nftables, lakini mpangilio wa chains na matokeo ni yaleyale. UFW na Docker huandika zote kwenye pipeline ileile ya kernel ya packet, na entry point ya Docker iko mapema zaidi. Hili si jambo la UFW pekee: firewalld kwenye VPS ya Rocky au AlmaLinux huchuja traffic katika sehemu hiyo hiyo ya pipeline na hupitwa na rule hiyo hiyo ya DNAT. Kwa hiyo, fixes zilizo hapa chini ndizo unazotumia huko pia.

Suluhisho la kawaida: chapisha ports kwenye 127.0.0.1

Containers nyingi hazikupaswa kuwa za umma tangu mwanzo. Database, app server iliyo nyuma ya reverse proxy, admin panel na metrics endpoint havipaswi kujibu Internet moja kwa moja. Zichapishe kwenye loopback address:

docker run -d --name web -p 127.0.0.1:8080:80 nginx:1.29-alpine

Au kwenye Compose file:

services:
  web:
    image: nginx:1.29-alpine
    ports:
      - "127.0.0.1:8080:80"

Hii hufanya kazi kwa sababu Docker DNAT rule sasa inalinganisha packets zinazoelekezwa kwenye 127.0.0.1 pekee. Packet kutoka Internet haiwezi kuwa na destination hiyo kihalali, kwa hiyo kernel huiacha kabla firewall rule yoyote haijaendeshwa. Port inafikika kutoka kwenye host, na haifikiki kutoka sehemu nyingine yoyote. Thibitisha binding:

sudo ss -tlnp | grep 8080

Unataka kuona 127.0.0.1:8080 kwenye output, si 0.0.0.0:8080 au [::]:8080. Kisha thibitisha kutoka kwenye machine nyingine kwamba curl http://your-vps-ip:8080/ inakataliwa.

Kwa services zinazopaswa kukabili Internet, endesha reverse proxy moja inayomiliki ports 80 na 443 na inayoroute kwa hostname, kisha usichapishe port nyingine yoyote. Huu ndio muundo unaojengwa na mwongozo wa Traefik reverse proxy, na hivi ndivyo app ya self-hosted kama Nextcloud kwenye VPS inavyobaki bila kufikika isipokuwa kupitia proxy yake. Jinsi entries za ports: zinavyotangazwa, pamoja na workflow nyingine ya Compose, imeelezwa kwenye mwongozo wa misingi ya Docker Compose.

Kila internal container inapokuwa kwenye loopback, UFW hurudi kufanya kazi yake ya kawaida: kulinda ports zinazotolewa na host yenyewe. Tengeneza seti hiyo ya rules hapa, kisha endesha commands kwa mpangilio:

ToolUFW rule generator

Uchujaji halisi: chain ya DOCKER-USER

Wakati mwingine port ya container lazima ibaki imechapishwa kwenye mtandao lakini iwe na vizuizi, kwa mfano port ya replica ya database ambayo anwani moja tu ya ofisi inapaswa kuifikia. Kwa hilo, Docker hutoa chain ya DOCKER-USER. Kila packet inayoelekea kwenye container yoyote hupitia DOCKER-USER kabla ya rules za accept za Docker, na Docker haiandiki rules ndani yake. Chain hii ipo kwa matumizi yako, na Docker haiathiri yaliyomo ndani yake wakati wa kuwasha daemon upya.

Kuna jambo moja la kuzingatia kabla ya kutumia command: packet inapofika DOCKER-USER, mabadiliko ya DNAT tayari yamefanyika. Destination port ya packet ni port ya container, ambayo ni 80 katika mfano wetu, si port iliyochapishwa ya 8080. Kwa hiyo, rule inayolingana na --dport 8080 haitalingana na packet yoyote. Njia ya kuaminika ni kulinganisha port ambayo client aliunganisha hapo awali; kernel's connection tracker huihifadhi:

sudo iptables -I DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL ! -s 10.0.0.10 -j DROP

Isome hivi: kwa packets zilizoingia kupitia eth0 na zilizo sehemu ya connection ambayo destination port yake ya awali ilikuwa 8080, drop kila packet ambayo haikutumwa kutoka 10.0.0.10. Match ya --ctdir ORIGINAL inaweka rule hii kwenye mwelekeo wa client kwenda container pekee, hivyo reply packets hazikamatwi kimakosa. Badilisha eth0 na interface yako ya umma; ip route | grep default huiitaja. Ijaribu kwa njia ileile ya awali: curl kutoka kwenye anwani iliyoruhusiwa inafanikiwa, na kutoka mahali pengine connection inaisha kwa timeout. Kukwama huko ni dalili kwamba rule ya DROP inafanya kazi, badala ya kuonyesha port ambayo haina huduma nyuma yake. Tofauti kati ya connection iliyokataliwa na inayokwisha kwa timeout ndiyo njia ya haraka zaidi ya kutofautisha port iliyochujwa na huduma ambayo haisikilizi.

Rules zilizoongezwa kwa command ya iptables hupotea baada ya reboot. Kwa kuwa UFW tayari inasimamia firewall hii, mahali pazuri pa kuzihifadhi ni /etc/ufw/after.rules. Ongeza block mwishoni mwa file:

*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
COMMIT

Kisha endesha sudo ufw reload. UFW hurudia kutumia file hiyo kwenye kila reload na kila boot. Kwa hiyo, uchujaji wa container zako sasa unahifadhiwa mahali pamoja na sehemu nyingine za firewall yako, na unabaki baada ya reboot na Docker upgrade.

Kwa nini hupaswi kuzima ujumuishaji wa Docker na iptables

Majibu ya zamani kuhusu tatizo hili yanapendekeza kuweka { "iptables": false } katika /etc/docker/daemon.json. Usifanye hivyo. Sheria za firewall za Docker hufanya mengi zaidi ya kuchapisha port. Sheria ya masquerade ndiyo huwezesha containers kufikia Internet kupitia anwani ya host, kwa hiyo ukiuzima ujumuishaji huo, containers haziwezi kuvuta images, kufikia package mirrors au kuita API yoyote ya nje (application programming interface). Sheria za DNAT ndizo huwezesha -p kufanya kazi, kwa hiyo port zilizochapishwa huacha kufanya kazi kabisa. Sheria za isolation zinazotenganisha Compose networks pia huondolewa. Ungerekebisha bypass kwa kuvuruga networking ya containers, na ungehitajika kuandika na kudumisha kila moja ya sheria hizo mwenyewe. Documentation ya Docker inaeleza setting hiyo kuwa ni ya watu wanaokusudia kufanya hivyo hasa. Chain ya DOCKER-USER ipo mahsusi ili mtu yeyote asihitaji switch hii.

Upande wa IPv6 wa tatizo hilohilo

Kwanza angalia port iliyochapishwa inavyoonekana kwenye IPv6:

sudo ss -tlnp | grep 8080

Kuanzia Docker Engine 27, Docker husimamia ip6tables kwa chaguo-msingi. Kwenye Docker network iliyo na IPv6, port iliyochapishwa hupitia matibabu yale yale ya DNAT kwenye IPv6 tables. Kwa hiyo, bypass hiyo hiyo ipo na marekebisho yaleyale yanatumika: chain ya DOCKER-USER pia ipo kwenye ip6tables, hivyo rudia rule yako kwa kutumia sudo ip6tables -I DOCKER-USER ... na ujaribu kutoka nje kwa kutumia curl dhidi ya public IPv6 address ya seva yako, kwa mfano curl -6 http://[2001:db8:2a::1]:8080/.

Kwenye network isiyo na IPv6, IPv6 clients hushughulikiwa na docker-proxy badala yake. Huu ni mchakato wa kawaida wa user-space unaosikiliza kwenye [::]:8080 na kupeleka traffic kwenye container kupitia IPv4. Traffic inayoelekezwa kwenye host process hupitia INPUT, hivyo UFW inaweza kuchuja njia hiyo, lakini tu wakati UFW inasimamia IPv6. Ikiwa inasimamia, pamoja na njia nyingine ambazo pengo la IPv6 linaweza kujitokeza kwenye VPS, imeelezwa katika mwongozo wa UFW na IPv6.

Kuchapisha kwenye loopback huepuka swali hili lote: -p 127.0.0.1:8080:80 hufunga IPv4 loopback pekee, hivyo hakuna IPv6 listener wala kitu cha kufikiwa kutoka nje kwenye stack yoyote.

Mfumo unaodumu

  • Chapisha kila port ya ndani kwenye 127.0.0.1, ili isifichuliwe tangu mwanzo.
  • Mpe reverse proxy moja upande wa umma, ikiwa inamiliki port 80 na 443.
  • Weka UFW katika sera ya msingi ya kukataa kwa host, huku ukiruhusu SSH na port za proxy.
  • Chuja port za container zilizo za umma kweli kwenye DOCKER-USER, ukilinganisha na port ya awali ya kulengwa, na uhifadhi usanidi huo kwenye /etc/ufw/after.rules.
  • Acha integration ya Docker ya iptables ikiwa imewashwa.

Ukishaweka usanidi huu mara moja, huondoa mshangao: ufw status inaeleza host, na DOCKER-USER inaeleza container. Hakuna kitu kinachochapishwa kwa bahati mbaya, na docker run -p inayofuata utakayoandika itafichua hasa ulichokusudia.

FAQ

Kwa nini ninaweza kufikia Docker container yangu wakati UFW imezuia port?

Kwa sababu Docker huchapisha port hiyo kwa kutumia sheria ya DNAT katika chain ya PREROUTING, ambayo hubadilisha destination ya packet kuwa anwani ya container kabla ya filtering yoyote kufanyika. Kisha packet hupitia njia ya FORWARD, huku sheria za UFW zikiwa katika INPUT, chain ambayo packet haiingii. Firewall haiulizwi kabisa, kwa hiyo sheria zake za deny hazina athari kwenye port za container zilizochapishwa.

Ninawezaje kufanya UFW izuie port zilizochapishwa na Docker?

UFW yenyewe haiwezi kufanya hivyo, kwa sababu sheria zake ziko kwenye chain isiyo sahihi. Ama acha kufichua port hiyo kwa kuichapisha kama 127.0.0.1:8080:80 ili host pekee iweze kuifikia, au fanya filtering katika chain ya DOCKER-USER kwa kutumia sheria ya iptables inayolingana na port ya awali ya destination kupitia conntrack. Hifadhi sheria hiyo katika /etc/ufw/after.rules ili iendelee kuwepo baada ya reboot na ufw reload.

Je, niweke "iptables": false katika Docker's daemon.json?

Hapana. Mpangilio huo huondoa sheria zote za firewall na NAT za Docker, jambo linalovuruga zaidi ya bypass pekee. Containers hupoteza ufikiaji wa Internet wa kutoka kwa sababu sheria ya masquerade huondolewa, na port zilizochapishwa huacha kufanya kazi kwa sababu sheria za DNAT huondolewa. Tumia loopback publishing pamoja na chain ya DOCKER-USER; njia hizi hutatua kufichuliwa bila kuvuruga container networking.

Je, Docker hupita UFW kwenye IPv6 pia?

Kwenye Docker Engine 27 na matoleo ya baadaye, usimamizi wa ip6tables huwashwa kwa default, kwa hiyo port iliyochapishwa kwenye Docker network yenye IPv6 huandikwa upya kuzunguka UFW kama ilivyo kwenye IPv4, na inahitaji sheria hiyo hiyo ya DOCKER-USER iakisiwe kwa ip6tables. Kwenye network zisizo na IPv6, mchakato wa docker-proxy husikiliza kwenye [::], na traffic hiyo hupitia INPUT, ambako UFW inaweza kuifilter ikiwa UFW inasimamia IPv6. Kuchapisha kwenye 127.0.0.1 huepuka hali zote mbili, kwa sababu hakuna kinachosikiliza kwenye IPv6 hata kidogo.