कंटेनर का पैकेट कभी tun0 तक नहीं पहुँचता, और कंटेनर पर अतिरिक्त क्षमताएँ इसे नहीं बदलेंगी। अतिथि और टनल के बीच दो रूट लुकअप हैं: एक अतिथि के नेटवर्क नेमस्पेस के भीतर, एक होस्ट पर फ्रेम के ब्रिज पर उतरने के बाद, और दूसरा अलग मैच नियमों वाली अलग तालिका से पूछता है। मेरा संदर्भ लेआउट एक Linux होस्ट है जो OpenVPN क्लाइंट चलाता है, जो 10.8.0.10 को tun0 पर रखता है, 172.18.0.0/16 पर Docker ब्रिज, और एक निजी पता जिस तक केवल टनल पक्ष पहुँच सकता है। लक्ष्य संकीर्ण ही रहता है: वह अतिथि सबनेट टनल से बाहर जाता है जबकि होस्ट अपना सामान्य अपलिंक रखता है।

कंटेनर के अंदर VPN क्लाइंट चलाना एक अलग डिज़ाइन है, कोई शॉर्टकट नहीं। तब कंटेनर अपनी टनल और क्रेडेंशियल का मालिक होता है, और काम iptables में रहने के बजाय Dockerfile में चला जाता है।

फ़ॉरवर्डिंग गंतव्य से रूट चुनता है, फिर NAT स्रोत फिर से लिखता है

जब net.ipv4.ip_forward=1 सेट होता है, तो फ़ॉरवर्ड किया गया पैकेट केवल गंतव्य के अनुसार, मुख्य तालिका में रूट होता है। उस निर्णय में स्रोत की कोई भूमिका नहीं है। रूट चुने जाने के बाद ही कर्नेल POSTROUTING चलाता है, और वहीं Docker का अपना नियम बैठता है: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE। ब्रिज से दूसरे इंटरफ़ेस की ओर जाने वाली हर चीज़ का अनुवाद होता है, और वह जो पता बनती है वह उस इंटरफ़ेस पर निर्भर करता है जिस ओर रूट ने उसे भेजा।

यही नियम मुझसे पूछे जाने वाले अधिकांश लक्षण समझाता है। अगर मुख्य तालिका में निजी गंतव्य के लिए कोई रूट नहीं है, तो पैकेट होस्ट के भौतिक पते के साथ अपलिंक से बाहर निकलता है। आपका प्रदाता गंतव्य गिरा देता है, या दूर की साइट ऐसा RFC 1918 स्रोत गिरा देती है जिसकी उसे कभी अपेक्षा नहीं थी, और टनल चालू और स्वस्थ रहते हुए कंटेनर का समय समाप्त हो जाता है। tun0 पर कैप्चर कुछ नहीं दिखाता, और यही संकेत है। अगर रूट वास्तव में tun0 की ओर इशारा करता है, तो MASQUERADE स्रोत को 10.8.0.10 पर फिर से लिखता है और वही कंटेनर आपके कुछ छुए बिना काम करता है।

वह लुकअप माँगें जो अतिथि के स्रोत का नाम लेता है। from और iif के बिना होस्ट अपने ही सॉकेट के लिए जवाब देता है और ऐसा रास्ता बताता है जिस पर फ़ॉरवर्ड किया गया पैकेट कभी नहीं जाएगा:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

अगर वह जवाब अपलिंक का नाम लेता है, तो टनल तस्वीर में नहीं है और टनल पक्ष की कोई सेटिंग मदद नहीं करेगी।

अतिथि को टनल के अपने पते पर लाएँ

सबसे छोटा काम करने वाला बदलाव बाहर जाते समय स्पष्ट स्रोत पुनर्लेखन है। इसे जोड़ें, अंत में न लगाएँ: Docker पहले से POSTROUTING के शीर्ष पर बैठा है, इसलिए -A से जोड़ा गया नियम Docker के नियम के बाद मूल्यांकित होता है और कभी नहीं चलता।

iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10

दो शर्तें तय करती हैं कि यह टिकेगा या नहीं। पता वही होना चाहिए जो क्लाइंट वास्तव में रखता है, इसलिए इसे प्रत्येक क्लाइंट के लिए ifconfig-push 10.8.0.10 255.255.255.0 वाली प्रविष्टि से पिन करें; जो नियम ऐसे पते का नाम लेता है जो कभी आवंटित नहीं हुआ, वह मौन ड्रॉप पैदा करता है, त्रुटि नहीं। और दूर के पक्ष को वह स्रोत स्वीकार करना होगा और जवाब वापस रूट करने होंगे, इसलिए वहाँ की अनुमति सूची अतिथि सबनेट के बजाय टनल पते का नाम लेती है। जब क्लाइंट पता गतिशील हो, तो -j MASQUERADE के साथ -o tun0 इंटरफ़ेस जो रखता है उसी का अनुसरण करता है, पर ऐसी दूर की नीति की कीमत पर जिसके विरुद्ध आप निश्चित मान नहीं लिख सकते।

फ़िल्टरिंग दूसरा आधा है। Docker FORWARD नीति को drop पर रखता है और हर डेमॉन रीस्टार्ट पर अपनी चेन खाली करता है, इसलिए स्थानीय नीति DOCKER-USER में रखें, जो टिकती है:

iptables -I DOCKER-USER -i br-appnet -o tun0 -j ACCEPT
iptables -I DOCKER-USER -i tun0 -o br-appnet -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

वापसी नियम मायने रखता है: tun0 पर आने वाला जवाब ब्रिज से खुले कनेक्शन के लिए केवल इसलिए स्वीकार नहीं होता कि बाहर जाने वाली दिशा स्वीकार हुई थी।

स्रोत नियम से होस्ट को सीधा रखें

जब पूरे ब्रिज को टनल इस्तेमाल करनी चाहिए पर होस्ट पर और कुछ नहीं, तो NAT छोड़ें और उस स्रोत को अपनी तालिका दें। अतिथि पते तब शुरू से अंत तक दिखाई देते रहते हैं।

echo "200 vpnbox" >> /etc/iproute2/rt_tables
ip rule add from 172.18.0.0/16 lookup vpnbox pref 1000
ip route add default dev tun0 table vpnbox

नियम मुख्य तालिका से पहले देखे जाते हैं, और from स्रोत से मेल खाता है। टनल के अपने बाहरी पैकेट होस्ट का भौतिक स्रोत ढोते हैं, इसलिए वे कभी from 172.18.0.0/16 से मेल नहीं खाते और टनल स्वयं को अपने भीतर वापस रूट नहीं कर सकती। यही स्व-अपवर्जन लेआउट को स्थिर बनाता है।

वापसी पथ को अब मदद चाहिए। दूर का पक्ष अनुवाद न किया गया 172.18.0.x देखता है और उसके रहने की जगह जानने का कोई कारण नहीं है। सर्वर पर, प्रत्येक क्लाइंट के लिए फ़ाइल को iroute 172.18.0.0 255.255.0.0 चाहिए ताकि सर्वर की आंतरिक तालिका वह प्रीफ़िक्स इस क्लाइंट को दे, और धकेला गया route 172.18.0.0 255.255.0.0 चाहिए ताकि अन्य क्लाइंट जानें कि यह मौजूद है।

भरोसा करने से पहले दो जाँच। चलाएँ ip route show default: redirect-gateway def1 के साथ तालिका में 0.0.0.0/1 और 128.0.0.0/1 हैं tun0 से, और होस्ट बिल्कुल भी सीधा नहीं है, क्योंकि वे आधे-डिफ़ॉल्ट default को बदले बिना जीत जाते हैं। ip rule add से जोड़े गए नियम भी रीबूट पर गायब हो जाते हैं, इसलिए उन्हें up स्क्रिप्ट में रखें।

पैकेट का आकार दूसरी विफलता है जिससे आपको मिलना होगा

1500 बाइट का ब्रिज इंटरफ़ेस बाहरी UDP और IP हेडर जुड़ने के बाद टनल को उसकी क्षमता से अधिक देता है। छोटे आदान-प्रदान निकल जाते हैं, इसलिए DNS हल होता है और TLS हैंडशेक पूरा होता है, फिर बड़ा अपलोड कहीं भी बिना किसी त्रुटि के अटक जाता है। प्रेषक वही सेगमेंट आकार दोहराता रहता है क्योंकि ICMP का फ़्रैगमेंटेशन-आवश्यकता संदेश, जो उसे छोटा होने को कहता, कभी नहीं पहुँचता।

इसे इंटरफ़ेस पर, MSS पर, या दोनों पर ठीक करें। क्लैंपिंग को अतिथि के किसी सहयोग की जरूरत नहीं:

ip link set dev tun0 mtu 1400
iptables -t mangle -A FORWARD -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

क्लैंप केवल TCP फिर से लिखता है। UDP, QUIC सहित, को इसके बजाय अतिथि इंटरफ़ेस घटाना होता है, --opt com.docker.network.driver.mtu=1400 के साथ नेटवर्क बनाते समय या मेल खाती compose कुंजी।

DNS और वे जाँच जो दोहराने योग्य हैं

नाम समाधान उसी पथ पर चलता है। Docker का अंतर्निहित रिज़ॉल्वर 127.0.0.11 पर होस्ट के कॉन्फ़िगर किए गए सर्वरों को फ़ॉरवर्ड करता है, इसलिए कंटेनर वही विरासत में लेता है जो होस्ट उपयोग करता है। जब निजी ज़ोन का जवाब केवल टनल से पहुँचने योग्य रिज़ॉल्वर देता है, तो अतिथि को उस पते पर इंगित करें, और getent hosts से कंटेनर के अंदर परखें, होस्ट पर नहीं।

  • विफल होने वाला अनुरोध अतिथि के अंदर चलाएँ; होस्ट पक्ष की जाँच दूसरा स्रोत पता उपयोग करती है और समस्या छिपा देती है।
  • अपने जोड़े गए NAT और फ़िल्टर नियमों के काउंटर पढ़ें। शून्य मैच वाला नियम रूटिंग की गलती है, नीति की नहीं।
  • पुष्टि करें कि आपके SNAT नियम में टनल पता वही है जो क्लाइंट वास्तव में रखता है।
  • अटकने के लिए सर्वर को दोष देने से पहले अतिथि का MTU घटाएँ या MSS क्लैंप करें।

सूची को नीचे चलाएँ और पहली विफलता पर रुकें। वही परत ठीक करनी है; नीचे जोड़ी गई कोई भी चीज़ केवल टूटती सीमा छिपाती है।

संबंधित लेख