इस वसंत में मैंने एक सुबह का ज़्यादातर हिस्सा एक ऐसे कंटेनर के पीछे बिताया जो एक भी आंतरिक नाम हल नहीं कर पा रहा था, जबकि बगल का ब्राउज़र बिना किसी शिकायत के विकी खोल रहा था। टनल चालू थी, ब्राउज़र उसे साबित कर रहा था, और कंटेनर एक अलग रूट टेबल और अलग रिज़ॉल्वर वाले दूसरे नेटवर्क नेमस्पेस में था. आगे जो है वह वह क्रम है जिसकी मैं अब जाँच करता हूँ: पहले रूट टेबल, दूसरा रिज़ॉल्यूशन, तीसरा फ़ॉरवर्डिंग और आख़िर में होस्ट प्लेटफ़ॉर्म। पहले ही विफल हो चुके चरण पर रुकें, क्योंकि स्टैक में नीचे नियम जोड़ना सीमा को खिसकाने के बजाय छिपा देता है।

कोई नियम बदलने से पहले तीनों रूट टेबल की तुलना करें

Docker के डिफ़ॉल्ट ब्रिज पर मौजूद कंटेनर के पास docker0 गेटवे की ओर जाता एक डिफ़ॉल्ट रूट है, और उसे पता भी नहीं कि tun0 मौजूद है। होस्ट के पास tun0 है, VPN द्वारा धकेले गए रूट हैं, और शायद एक नीति नियम भी है जो जाँच ट्रैफ़िक को आपकी अपेक्षा से अलग इंटरफ़ेस से बाहर भेजता है। WSL अपने वर्चुअल अडैप्टर के पीछे तीसरी टेबल जोड़ता है। इनमें से कोई भी संपादित करने से पहले तीनों छापें।

ContextCommandWhat a wrong answer looks like
Hostip route get 10.8.0.20Leaves a physical interface instead of tun0
Containerip routeOnly 172.17.0.0/16 via 172.17.0.1
Container DNScat /etc/resolv.confA public resolver, or an upstream the tunnel never pushed
WSLip addr; ip routeOnly the WSL subnet, no Windows VPN adapter

अगर कंटेनर का इकलौता रूट ब्रिज है और tun0 उसके नेमस्पेस में कभी नहीं दिखता, तो नीचे का कुछ भी मदद नहीं करेगा: कोई नेमसर्वर बदलाव रूट नहीं बनाता, और कोई रूट ऐसी टनल पहचान नहीं बनाता जिसे पीयर स्वीकार करे। क्रम मायने रखता है क्योंकि DNS विफलता और रूट विफलता शेल के अंदर से एक जैसी दिखती हैं।

रिज़ॉल्वर को सार्वजनिक के बजाय टनल की ओर इंगित करें

होस्ट पर, resolvectl status हर लिंक के लिए DNS सर्वर सूचीबद्ध करता है, और readlink -f /etc/resolv.conf बताता है कि फ़ाइल systemd स्टब है, NetworkManager फ़ाइल है, या कुछ ऐसा जिसे किसी विक्रेता के क्लाइंट ने फिर से लिखा है। उपयोगकर्ता-परिभाषित नेटवर्क पर कंटेनर के अंदर, /etc/resolv.conf नाम देता है 127.0.0.11, Docker का अंतर्निहित रिज़ॉल्वर, जो उस upstream की ओर फ़ॉरवर्ड करता है जिसके साथ डेमन कॉन्फ़िगर था। डिफ़ॉल्ट ब्रिज पर मौजूद कंटेनर को बनते समय होस्ट फ़ाइल की एक प्रति मिली थी, और वह उसे रखता है।

यही प्रति वह कारण है कि कंटेनर शुरू होने के बाद उठने वाली टनल कंटेनर को पुराने upstream से पूछते रहने देती है। या तो VPN रिज़ॉल्वर स्पष्ट रूप से पास करें docker run --dns 10.8.0.53, या "dns": ["10.8.0.53"] में /etc/docker/daemon.json सेट करें और डेमन पुनः आरंभ करें, या टनल उठने के बाद कंटेनर फिर से बनाएँ। फिर से बनाना आमतौर पर एक घंटे पहले सही रहे रिज़ॉल्वर को डीबग करने से सस्ता है।

सार्वजनिक रिज़ॉल्वर लगाना वह मरम्मत है जो लक्षण बनाए रखती है। यह इंटरनेट हल करता है और हर निजी ज़ोन में विफल होता है, और यह उस फ़िल्टरिंग को चुपचाप बायपास कर सकता है जिसके लिए टनल मौजूद है। अगर आप चालू कंटेनर के /etc/resolv.conf के अंदर लिखने की कोशिश करें, तो अपेक्षा करें Device or resource busy: Docker उस फ़ाइल को bind-mount करता है, इसलिए संपादन के लिए पहले अनमाउंट चाहिए। --dns बनाते समय सेट करना वह क्रम है जो पुनः आरंभ में टिकता है।

जब कंटेनर टनल उधार लें तो फ़ॉरवर्ड करें और अनुवाद करें

डिफ़ॉल्ट ब्रिज के साथ, होस्ट docker0 और tun0 के बीच एक राउटर है, और डिफ़ॉल्ट रूप से यह दोनों में से कोई काम नहीं करता। फ़ॉरवर्डिंग चालू करें और स्रोत पते का अनुवाद करें ताकि पीयर होस्ट टनल पता देखे, ऐसा पता नहीं जिसके लिए उसके पास वापसी रूट नहीं है (tun0 को उस नाम से बदलें जो आपका क्लाइंट इंटरफ़ेस को देता है):

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i docker0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

FORWARD नियम उन होस्ट पर मायने रखते हैं जिनकी फ़ॉरवर्ड नीति DROP है। Docker अपनी खुद की चेन डालता है, इसलिए पढ़ने के बजाय पैकेट से परीक्षण करें iptables -S: कंटेनर से, ip route get 10.8.0.20 ब्रिज गेटवे का नाम लेना चाहिए, और nc -vz 10.8.0.20 443 रिज़ॉल्वर पर नहीं बल्कि टनल पर जुड़ना या समय-समाप्त होना चाहिए।

MASQUERADE के बिना पीयर को 172.17.0.0/16 तक वापसी रूट चाहिए, जिसे ज़्यादातर रिमोट-एक्सेस VPN धकेलेंगे नहीं। यहीं नीति-आधारित क्लाइंट योजना तोड़ता है: अगर वह केवल विशिष्ट UID या cgroups का ट्रैफ़िक पकड़ता है, तो आपके फ़ॉरवर्ड नियम टनल के अंदर पैकेट कभी नहीं देखते, और सुधार iptables में नहीं बल्कि क्लाइंट की रूटिंग नीति में है। हाथ से जोड़े गए नियम रीबूट पर गायब हो जाते हैं, इसलिए उन्हें systemd यूनिट या netfilter-persistent टेबल में रखें और रीबूट के बाद फिर से परीक्षण करें, क्योंकि देर से उठने वाली टनल उन्हें बहा सकती है।

जब लक्ष्य अलगाव हो तो टनल को कंटेनर के अंदर रखें

अगर एक कंटेनर को किसी विशिष्ट नोड से बाहर निकलना है, उस कंटेनर में क्लाइंट चलाएँ. इसे --cap-add NET_ADMIN और --device /dev/net/tun चाहिए, और फिर यह अपना tun0, रूट और फ़ायरवॉल रखता है। अगर दूसरे कंटेनर इससे होकर गुज़रते हैं, तो इसे कंटेनर के अंदर फ़ॉरवर्डिंग और MASQUERADE नियम भी चाहिए, और उन पीयर को इसकी ओर इंगित करता स्पष्ट रूट या प्रॉक्सी चाहिए; Docker का डिफ़ॉल्ट ब्रिज आपके लिए वह रूट नहीं जोड़ता।

होस्ट नेटवर्किंग, docker run --network host, सस्ता संस्करण है: कंटेनर होस्ट के नेमस्पेस में शामिल हो जाता है और टनल रूट सीधे देखता है। यह नेटवर्क अलगाव भी छोड़ देता है, पोर्ट टकराव को न्योता देता है, और व्यवहार में यह Linux विकल्प है। प्रकाशित पोर्ट इसका विकल्प नहीं है। -p 8080:80 किसी सेवा को होस्ट से पहुँचने योग्य बनाता है; यह आंतरिक 10.x पतों को कंटेनर के अंदर से पहुँचने योग्य नहीं बनाता, और दोनों को मिलाना ही वह तरीका है जिससे रूटिंग समस्या खुला डेटाबेस पोर्ट बन जाती है।

नाम विफलता को ओवरलैपिंग सबनेट से अलग करें

Docker का डिफ़ॉल्ट ब्रिज 172.17.0.0/16 है, और कई कॉर्पोरेट VPN पूल 172.16.0.0/12 के अंदर बैठते हैं। जैसे ही टनल उस रेंज की मालिक हो जाती है, कंटेनर के लिए इरादा किया गया ट्रैफ़िक इसके बजाय टनल में रूट हो सकता है। नाम विफल होते हैं, त्रुटि DNS जैसी दिखती है, और समस्या पतों में है। गंतव्य जाँचें, रिज़ॉल्वर नहीं: ip route get 172.17.0.5 होस्ट पर, फिर ब्रिज प्रीफ़िक्स की VPN पूल और docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' से तुलना करें।

मरम्मत यह है कि रेंजों में से एक को हटाया जाए: या तो daemon.json में Docker के लिए अलग डिफ़ॉल्ट पता पूल, या कॉन्संट्रेटर पर अलग पूल। यहाँ एक और नेमसर्वर जोड़ने से कुछ नहीं होता, और ssh -b से भी नहीं: क्लाइंट को इंटरफ़ेस से बाँधने से स्रोत पता बदलता है, टनल में इंगित गंतव्य रूट नहीं। तेज़ काम-विभाजन: होस्ट पर हल होने वाला लेकिन कंटेनर में न होने वाला नाम रिज़ॉल्वर समस्या है, जबकि कंटेनर में अटकने वाला लेकिन होस्ट पर काम करने वाला सीधा IP कनेक्शन रूट या फ़ॉरवर्ड-चेन समस्या है।

WSL और Docker Desktop Linux के नियमों का पालन नहीं करते

अपने डिफ़ॉल्ट NAT मोड में, WSL अपने वर्चुअल अडैप्टर के पीछे बैठता है और Windows VPN के आंतरिक रूट या DNS सफ़िक्स नहीं पाता, इसलिए ब्राउज़र काम करता है और ping डिस्ट्रिब्यूशन के अंदर नहीं। हाल के Windows 11 बिल्ड में उपलब्ध मिरर्ड नेटवर्किंग इसके बजाय होस्ट इंटरफ़ेस साझा करती है, पर इसे .wslconfig में सक्षम करना होता है और डिस्ट्रिब्यूशन को wsl --shutdown से पुनः आरंभ करना होता है। सक्रिय मोड की पुष्टि wslinfo --networking-mode से करें, सुधार लागू करने से पहले, क्योंकि NAT का उत्तर मिरर्ड मोड के लिए ग़लत है और इसका उल्टा भी।

Docker Desktop एक और परत जोड़ता है। Windows पर Linux कंटेनर ऐतिहासिक रूप से vpnkit कहलाने वाले NAT कार्यान्वयन के पीछे बैठते हैं, और उनका पथ मूल Linux होस्ट से अलग है। Windows कंटेनर Hyper-V वर्चुअल स्विच के ज़रिए जुड़ते हैं, और कोई कॉर्पोरेट क्लाइंट उस स्विच से आने वाले ट्रैफ़िक को ब्लॉक कर सकता है, इसलिए एक ही मशीन पर Linux कंटेनर नेटवर्क तक पहुँच सकते हैं जबकि Windows कंटेनर के पास कुछ नहीं होता। भौतिक अडैप्टर से बँधा बाहरी स्विच सामान्य उपाय है, और यह कंटेनर की नेटवर्क पहचान उन तरीक़ों से बदल देता है जिनकी नीति अनुमति न दे।

एक और नियम लिखने से पहले, चारों परतों को क्रम में चलें और जहाँ उनमें से कोई ग़लत हो वहीं रुकें:

  • कंटेनर के अंदर: क्या ip route निजी प्रीफ़िक्स तक पहुँचता है, और क्या /etc/resolv.conf ऐसे upstream का नाम देता है जो उसे हल करता है?
  • होस्ट पर: क्या फ़ॉरवर्डिंग चालू है, क्या NAT टनल इंटरफ़ेस से बँधा है, और क्या फ़ॉरवर्ड चेन ब्रिज से टनल स्वीकार करती है?
  • पीयर पर: क्या उसे वापसी पथ पता है, या आप कंटेनर सबनेट छिपाने के लिए NAT पर निर्भर हैं?
  • प्लेटफ़ॉर्म पर: क्या WSL NAT या मिरर्ड मोड में है, और कंटेनर Linux है या Windows?

हर परीक्षण नए कंटेनर या शेल में दोबारा चलाएँ। कैश किए गए उत्तर या स्थापित कनेक्शन वाली लंबी प्रक्रिया कॉन्फ़िगरेशन सही होने के बाद भी विफल रहेगी, और वह विफलता ठीक ऐसी मरम्मत जैसी दिखती है जो काम नहीं कर पाई।

संबंधित लेख