SSD Nodes Learn Hosting plans →
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-23

Docker, UFW को बायपास क्यों करता है और इसे कैसे ठीक करें

Docker द्वारा UFW को बायपास करने का कारण iptables और DNAT नियम हैं। जानें कि कैसे आपके कंटेनर पोर्ट्स सुरक्षित न होने के बावजूद इंटरनेट पर खुले रह जाते हैं और इन्हें कैसे फिक्स करें।

Docker, UFW को बायपास क्यों करता है

Docker, UFW को इसलिए बायपास करता है क्योंकि पब्लिश किए गए कंटेनर पोर्ट्स कभी भी उन फायरवॉल नियमों से नहीं गुजरते जिन्हें UFW मैनेज करता है। जब आप docker run -p 8080:80 रन करते हैं, तो Docker कर्नल की nat टेबल की PREROUTING चेन में एक DNAT (डेस्टिनेशन नेटवर्क एड्रेस ट्रांसलेशन) नियम लिख देता है। यह नियम कर्नल द्वारा यह तय करने से पहले कि पैकेट कहाँ जा रहा है, प्रत्येक पैकेट के डेस्टिनेशन को कंटेनर के प्राइवेट एड्रेस में बदल देता है। इसके बाद बदला हुआ पैकेट FORWARD चेन के माध्यम से कंटेनर में फॉरवर्ड कर दिया जाता है, जिसे Docker कंट्रोल करता है। UFW के नियम INPUT चेन में होते हैं, और पैकेट कभी भी उसमें प्रवेश नहीं करता है। इसलिए ufw status डिफ़ॉल्ट डिनाई (deny) दिखाता है, sudo ufw deny 8080 सफलता की रिपोर्ट देता है, और पोर्ट 8080 अभी भी पूरे इंटरनेट को रिस्पॉन्स देता है।

यह Docker का कोई बग नहीं है, और न ही UFW खराब है। दोनों टूल्स एक ही कर्नल फायरवॉल को प्रोग्राम करते हैं। Docker के नियम पैकेट के रास्ते पर पहले ही काम कर देते हैं, इसलिए UFW से कभी पूछा ही नहीं जाता। यह गाइड इस बायपास को प्रदर्शित करती है, इसकी कार्यप्रणाली समझाती है, और फिर उन दो समाधानों को कवर करती है जो काम करते हैं: 127.0.0.1 पर पोर्ट पब्लिश करना, और DOCKER-USER चेन में फ़िल्टरिंग करना। यदि आप UFW के लिए नए हैं, तो पहले UFW फायरवॉल बेसिक्स गाइड के साथ इसे सेटअप करें, क्योंकि डिफ़ॉल्ट-डिनाई फायरवॉल अभी भी सर्वर पर बाकी सब चीजों के लिए सही आधार है।

अपने सर्वर पर बाईपास देखें

एक ऐसे VPS से शुरुआत करें जहाँ UFW सक्रिय है और incoming traffic के लिए default deny policy लागू है। एक published port के साथ एक web container चलाएँ:

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

ufw status verbose यह दिखाता है कि Default: deny (incoming), allow (outgoing) और port 8080 के लिए कोई rule नहीं है। firewall की अपनी रिपोर्ट के अनुसार, port बंद है। अब सर्वर से नहीं, बल्कि किसी अन्य मशीन से परीक्षण करें:

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

Container जवाब देता है। एक स्पष्ट deny rule जोड़ें और फिर से परीक्षण करें:

sudo ufw deny 8080/tcp

Port अभी भी जवाब देता है, क्योंकि deny rule ऐसी chain में स्थित है जहाँ packet कभी पहुँचता ही नहीं है। UFW विफल नहीं हुआ। उससे कभी परामर्श ही नहीं किया गया। यही कारण है कि यह समस्या इतनी अच्छी तरह से छिपी रहती है: कहीं कोई error print नहीं होता, deploy काम करता है, और firewall status output बिल्कुल एक सुरक्षित और स्वस्थ सर्वर जैसा दिखता है।

कार्यप्रणाली: PREROUTING, INPUT से पहले चलता है

कर्नेल एक आने वाले पैकेट को एक निश्चित क्रम में प्रोसेस करता है, और पूरी समस्या इसी क्रम में निहित है।

  1. PREROUTING सबसे पहले चलता है। यहाँ के नियम पैकेट के डेस्टिनेशन को फिर से लिख (rewrite) सकते हैं, और पब्लिश किए गए पोर्ट के लिए Docker का नियम ठीक यही करता है।
  2. इसके बाद राउटिंग का निर्णय लिया जाता है। होस्ट को संबोधित पैकेट INPUT चेन में जाता है। किसी अन्य मशीन को संबोधित पैकेट FORWARD चेन में जाता है।
  3. UFW के नियम INPUT में रहते हैं। Docker के नियम FORWARD में रहते हैं।

आपके द्वारा अभी शुरू किए गए कंटेनर के लिए Docker का नियम देखें:

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

DNAT लाइन ही पूरी कहानी है। पोर्ट 8080 के लिए आने वाले किसी भी पैकेट का डेस्टिनेशन बदलकर 172.17.0.2:80 कर दिया जाता है, जो Docker के प्राइवेट ब्रिज नेटवर्क पर कंटेनर का पता है। रीराइट के बाद पैकेट अब होस्ट को संबोधित नहीं रहता, इसलिए राउटिंग निर्णय इसे FORWARD पथ पर भेज देता है, जहाँ Docker ने पहले ही ऐसे नियम जोड़ रखे हैं जो अपने नेटवर्क में ट्रैफिक को स्वीकार करते हैं। आपका deny 8080/tcp नियम INPUT में उस पैकेट की प्रतीक्षा करता रहता है जो कभी नहीं आता।

Ubuntu 24.04 पर iptables कमांड nftables के ऊपर एक फ्रंट एंड है, लेकिन चेन का क्रम और परिणाम समान हैं। UFW और Docker दोनों एक ही कर्नेल पैकेट पाइपलाइन में लिखते हैं, और Docker का एंट्री पॉइंट पहले आता है। यह सब केवल UFW तक सीमित नहीं है: Rocky या AlmaLinux VPS पर firewalld भी उसी पाइपलाइन में उसी बिंदु पर फिल्टर करता है और उसी DNAT नियम द्वारा उसे दरकिनार कर दिया जाता है, इसलिए नीचे दिए गए समाधान वहां भी काम आते हैं।

दैनिक समाधान: ports को 127.0.0.1 पर publish करें

ज्यादातर containers को कभी भी public होने की आवश्यकता नहीं होती है। एक database, reverse proxy के पीछे का एक app server, एक admin panel, या एक metrics endpoint: इनमें से किसी को भी सीधे internet पर जवाब नहीं देना चाहिए। इन्हें loopback address पर publish करें:

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

या Compose file में:

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

यह इसलिए काम करता है क्योंकि Docker का DNAT rule अब केवल उन packets से मेल खाता है जिन्हें 127.0.0.1 पर भेजा गया है, और internet से आने वाला कोई भी packet वैध रूप से वह destination नहीं रख सकता है, इसलिए kernel किसी भी firewall rule के चलने से पहले ही उसे drop कर देता है। यह port केवल host से ही पहुँचा जा सकता है, और किसी अन्य जगह से नहीं। Binding को verify करें:

sudo ss -tlnp | grep 8080

आपको output में 127.0.0.1:8080 चाहिए, न कि 0.0.0.0:8080 या [::]:8080। फिर किसी दूसरी machine से पुष्टि करें कि curl http://your-vps-ip:8080/ connection को refuse कर रहा है।

जो services internet के सामने होनी चाहिए, उनके लिए एक reverse proxy चलाएं जो ports 80 और 443 का उपयोग करे और hostname के आधार पर traffic route करे, और इसके अलावा कुछ भी publish न करें। यही वह pattern है जिसे Traefik reverse proxy guide तैयार करता है, और इसी तरह Nextcloud on a VPS जैसा self-hosted app अपने proxy के अलावा कहीं और से unreachable रहता है। ports: entries को कैसे declare किया जाता है, और बाकी Compose workflow क्या है, यह the Docker Compose basics guide में बताया गया है।

हर internal container के loopback पर होने के साथ, UFW अपने सामान्य काम पर वापस आ जाता है: उन ports की सुरक्षा करना जिन्हें host स्वयं serve करता है। उस rule set को यहाँ बनाएँ, फिर commands को क्रम में चलाएँ:

ToolUFW rule generator

वास्तविक फ़िल्टरिंग: DOCKER-USER चेन

कभी-कभी किसी कंटेनर पोर्ट को नेटवर्क पर पब्लिश तो रखना पड़ता है, लेकिन उसे प्रतिबंधित करना आवश्यक होता है। उदाहरण के लिए, डेटाबेस रेप्लिका पोर्ट जिसे केवल एक ऑफिस एड्रेस से ही एक्सेस किया जा सके। इसके लिए, Docker DOCKER-USER चेन प्रदान करता है। किसी भी कंटेनर की ओर जाने वाला प्रत्येक पैकेट Docker के अपने accept नियमों से पहले DOCKER-USER से होकर गुजरता है, और Docker इसमें कभी कोई नियम नहीं लिखता। यह चेन आपके उपयोग के लिए है, और Docker डेमन रीस्टार्ट होने पर भी इसकी सामग्री को नहीं बदलता है।

कमांड चलाने से पहले एक सावधानी: जब तक पैकेट DOCKER-USER तक पहुँचता है, तब तक DNAT रीराइट हो चुका होता है। पैकेट का डेस्टिनेशन पोर्ट कंटेनर पोर्ट (हमारे उदाहरण में 80) होता है, न कि पब्लिश किया गया पोर्ट (8080)। इसलिए, --dport 8080 से मेल खाने वाला नियम किसी भी चीज़ से मैच नहीं करेगा। सबसे विश्वसनीय तरीका उस पोर्ट से मैच करना है जिसे क्लाइंट ने मूल रूप से डायल किया था, जिसे कर्नल का कनेक्शन ट्रैकर याद रखता है:

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

इसे इस प्रकार पढ़ें: उन पैकेटों के लिए जो eth0 पर आए हैं और ऐसे कनेक्शन से संबंधित हैं जिसका मूल डेस्टिनेशन पोर्ट 8080 था, 10.0.0.10 से न भेजे गए सभी पैकेटों को ड्रॉप करें। --ctdir ORIGINAL मैच नियम को केवल क्लाइंट-से-कंटेनर दिशा तक सीमित करता है, ताकि रिप्लाई पैकेट गलती से न पकड़े जाएँ। eth0 को अपने पब्लिक इंटरफ़ेस से बदलें; ip route | grep default इसका नाम है। इसे पहले की तरह टेस्ट करें: अनुमत एड्रेस से curl सफल होता है, और कहीं और से कनेक्शन टाइम आउट हो जाता है। यह हैंग होना इस बात का संकेत है कि एक DROP नियम अपना काम कर रहा है, न कि यह कि पोर्ट के पीछे कुछ भी नहीं है, और refused कनेक्शन और टाइम आउट होने वाले कनेक्शन के बीच का अंतर यह बताने का सबसे तेज़ तरीका है कि पोर्ट फ़िल्टर किया गया है या सर्विस बस लिसन नहीं कर रही है।

iptables कमांड के साथ जोड़े गए नियम रीबूट पर हट जाते हैं। चूँकि UFW पहले से ही इस फ़ायरवॉल को मैनेज करता है, इसलिए इन्हें स्थायी बनाने के लिए सबसे सही जगह /etc/ufw/after.rules है। फ़ाइल के अंत में एक ब्लॉक जोड़ें:

*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

इसके बाद sudo ufw reload चलाएँ। UFW हर रीलोड और हर बूट पर उस फ़ाइल को फिर से लागू करता है, इसलिए आपकी कंटेनर फ़िल्टरिंग अब आपके फ़ायरवॉल के बाकी हिस्सों के साथ एक ही जगह पर रहती है, और यह रीबूट और Docker अपग्रेड दोनों के बाद भी सुरक्षित रहती है।

Docker के iptables integration को disable क्यों नहीं करना चाहिए

इस समस्या के पुराने समाधानों में { "iptables": false } को /etc/docker/daemon.json में set करने का सुझाव दिया जाता है। ऐसा न करें। Docker के firewall rules केवल ports publish करने से कहीं अधिक काम करते हैं। Masquerade rule ही वह नियम है जो containers को host के address के माध्यम से outbound internet access देता है, इसलिए integration बंद होने पर containers images pull नहीं कर पाएंगे, package mirrors तक नहीं पहुँच पाएंगे, या किसी बाहरी API (application programming interface) को call नहीं कर पाएंगे। DNAT rules ही वे नियम हैं जो -p को काम करने योग्य बनाते हैं, इसलिए इन्हें बंद करने पर published ports पूरी तरह काम करना बंद कर देंगे। वे isolation rules भी हट जाएंगे जो अलग-अलग Compose networks को एक-दूसरे से अलग रखते हैं। आप इस bypass को ठीक करने के चक्कर में container networking को ही तोड़ देंगे, और उन सभी नियमों को खुद हाथ से लिखने और maintain करने की जिम्मेदारी आपकी हो जाएगी। Docker का अपना documentation इस setting को उन लोगों के लिए बताता है जो वास्तव में यही करना चाहते हैं। DOCKER-USER chain का अस्तित्व ही इसलिए है ताकि किसी को भी इस switch की आवश्यकता न पड़े।

इसी समस्या का IPv6 पक्ष

सबसे पहले जाँचें कि published port IPv6 पर कैसा दिखता है:

sudo ss -tlnp | grep 8080

Docker Engine 27 के बाद से, Docker डिफ़ॉल्ट रूप से ip6tables को manage करता है। IPv6 सक्षम वाले Docker network पर, एक published port को IPv6 tables में भी वही DNAT treatment मिलता है, इसलिए वहाँ भी वही bypass मौजूद है और वही समाधान लागू होता है: DOCKER-USER chain ip6tables में भी मौजूद होती है, इसलिए अपने rule को sudo ip6tables -I DOCKER-USER ... के साथ mirror करें और अपने सर्वर के public IPv6 address के विरुद्ध बाहर से curl के साथ test करें, उदाहरण के लिए curl -6 http://[2001:db8:2a::1]:8080/

बिना IPv6 वाले network पर, IPv6 clients को इसके बजाय docker-proxy द्वारा संभाला जाता है, जो एक सामान्य user-space process है जो [::]:8080 पर listen करती है और traffic को IPv4 के माध्यम से container में forward करती है। Host process के लिए traffic INPUT से होकर गुजरता है, इसलिए UFW उस path को filter कर सकता है, लेकिन केवल तभी जब UFW IPv6 को manage कर रहा हो। क्या यह ऐसा कर रहा है, और VPS पर IPv6 gap खुलने के अन्य तरीके, UFW और IPv6 गाइड का विषय हैं।

Loopback पर publish करना पूरे प्रश्न को ही दरकिनार कर देता है: -p 127.0.0.1:8080:80 केवल IPv4 loopback को bind करता है, इसलिए कोई IPv6 listener नहीं होता और बाहर से किसी भी stack पर पहुँचने के लिए कुछ नहीं होता।

वह पैटर्न जो प्रभावी रहता है

  • हर internal port को 127.0.0.1 पर publish करें, ताकि वह कभी भी सार्वजनिक रूप से expose न हो।
  • सार्वजनिक पक्ष (public side) को एक ऐसे reverse proxy को सौंपें जो ports 80 और 443 को नियंत्रित करता हो।
  • host के लिए UFW को default deny पर रखें, केवल SSH और proxy ports को अनुमति दें।
  • वास्तविक रूप से public container ports को DOCKER-USER में filter करें, जो original destination port से मेल खाते हों और /etc/ufw/after.rules में persist किए गए हों।
  • Docker के iptables integration को चालू रहने दें।

एक बार सेट हो जाने पर, यह अनिश्चितताओं को दूर करता है: ufw status host का वर्णन करता है, और DOCKER-USER containers का वर्णन करता है। कोई भी चीज़ गलती से publish नहीं होती है, और अगली बार जब आप docker run -p टाइप करेंगे, तो वही expose होगा जो आप वास्तव में करना चाहते थे।

FAQ

UFW द्वारा port ब्लॉक होने के बावजूद मैं Docker container तक कैसे पहुँच सकता हूँ?

ऐसा इसलिए होता है क्योंकि Docker port को PREROUTING chain में एक DNAT rule के साथ publish करता है। यह rule किसी भी filtering से पहले packet के destination को container के address में बदल देता है। इसके बाद packet FORWARD path से होकर गुजरता है, जबकि UFW के rules INPUT chain में होते हैं, जहाँ packet कभी पहुँचता ही नहीं है। Firewall से कभी अनुमति नहीं ली जाती, इसलिए इसके deny rules का published container ports पर कोई प्रभाव नहीं पड़ता।

मैं UFW से Docker के published ports को कैसे ब्लॉक करूँ?

UFW स्वयं ऐसा नहीं कर सकता, क्योंकि इसके rules गलत chain में होते हैं। या तो port को expose करना बंद करें और उसे 127.0.0.1:8080:80 के रूप में publish करें ताकि केवल host ही उस तक पहुँच सके, या फिर DOCKER-USER chain में एक iptables rule का उपयोग करके filter करें जो conntrack के माध्यम से मूल destination port से मेल खाता हो। इस rule को /etc/ufw/after.rules में सुरक्षित रखें ताकि यह reboot और ufw reload के बाद भी बना रहे।

क्या मुझे Docker के daemon.json में "iptables": false सेट करना चाहिए?

नहीं। यह setting Docker के सभी firewall और NAT rules को हटा देती है, जिससे bypass के अलावा और भी बहुत कुछ खराब हो जाता है। Masquerade rule के हट जाने के कारण containers का outbound internet access बंद हो जाता है, और DNAT rules के न होने से published ports काम करना बंद कर देते हैं। इसके बजाय loopback publishing और DOCKER-USER chain का उपयोग करें; ये container networking को तोड़े बिना exposure की समस्या को ठीक कर देते हैं।

क्या Docker IPv6 पर भी UFW को bypass करता है?

Docker Engine 27 और उसके बाद के versions में, ip6tables management डिफ़ॉल्ट रूप से चालू रहता है। इसलिए IPv6-enabled Docker network पर publish की गई port भी IPv4 की तरह ही UFW के बाहर re-write हो जाती है, और इसके लिए भी DOCKER-USER rule को ip6tables के साथ mirror करने की आवश्यकता होती है। IPv6 के बिना वाले networks पर, docker-proxy process [::] पर listen करती है और वह traffic INPUT से होकर गुजरता है, जहाँ UFW उसे filter कर सकता है यदि UFW IPv6 को manage कर रहा हो। 127.0.0.1 पर publish करने से ये दोनों स्थितियाँ टल जाती हैं, क्योंकि तब IPv6 पर कुछ भी listen नहीं कर रहा होता।