पूरी मशीन के बिना एक ऐप को VPN से कैसे भेजें
कोई भी अनियंत्रित डेस्कटॉप प्रति-प्रक्रिया VPN स्विच नहीं देता। वह परत चुनें जो ट्रैफ़िक चुनती है: बंधा सॉकेट, UID नियम, फ़ायरवॉल प्रोफ़ाइल या SOCKS हॉप।
VPN क्लाइंट जुड़ता है, ब्राउज़र काम करता है, और जिस एक कमांड की आपको सचमुच परवाह है वह अब भी आपके घर के कनेक्शन से बाहर जाता है। या उलटा होता है: टनल डिफ़ॉल्ट रूट पकड़ लेती है और स्थानीय फ़ाइल सर्वर डेस्कटॉप से गायब हो जाता है। मेरा सामना एक बिल्ड एजेंट के साथ हुआ जिसे आंतरिक आर्टिफ़ैक्ट मिरर तक पहुँचना था जबकि बाकी होस्ट ऑफ़िस LAN पर रहा, और क्लाइंट ने ठीक दो विकल्प दिए: सारा ट्रैफ़िक या कुछ नहीं। दोनों दिशाओं में माँग एक ही है, यह प्रोग्राम और पूरी मशीन नहीं, और ज़्यादातर क्लाइंट में इसके लिए कोई चेकबॉक्स नहीं है, क्योंकि रूटिंग तालिका ने कभी नहीं जाना कि प्रोग्राम क्या है।
रूटिंग तालिका आपका प्रोग्राम क्यों नहीं देख सकती
जब कोई प्रक्रिया पैकेट भेजती है, स्टैक रूटिंग तालिका में गंतव्य पता खोजता है। न निष्पादन योग्य फ़ाइल का नाम, न पोर्ट उस खोज में आता है। डोमेन नाम भी उसी कारण विफल होते हैं: टनल ट्रैफ़िक को किसी पते तक ले जाती है, और जब नाम किसी पते में हल हो जाता है, तो रूटिंग ही वह एकमात्र लीवर है जो कर्नेल के पास बचा है। इसीलिए «VPN सिर्फ़ इस साइट के लिए» आख़िर में प्रॉक्सी का अर्थ देता है, और «VPN सिर्फ़ इस प्रोग्राम के लिए» छोटे से तंत्र-समूह में से एक का।
| Mechanism | What selects | Covers | On tunnel drop |
|---|---|---|---|
| Socket bind | source address | a client you control | connections fail |
| UID rule (Linux) | owning user | every process of one account | connections fail |
| Firewall profile (Windows) | adapter profile | one program | fails closed |
| Network namespace | namespace membership | everything started inside | connections fail |
| SOCKS hop | app proxy setting | one app or one browser profile | connections fail |
इनमें से कोई भी प्रति-प्रक्रिया रूट नहीं है। हर एक निर्णय को ऐसी परत पर ले जाता है जो प्रोग्राम, उपयोगकर्ता या ऐप के अपने कॉन्फ़िगरेशन को देख सकती है।
एक अकेला गंतव्य अब भी रूट है, प्रोग्राम नियम नहीं। macOS पर आम तरीका है «VPN पर सारा ट्रैफ़िक भेजें» से टिक हटाना और होस्ट रूट जोड़ना: sudo route add -host internal-tuner.example -interface ppp0, जिसे फिर sudo route delete -host internal-tuner.example -interface ppp0 से हटाया जाता है। पहले ifconfig से इंटरफ़ेस का नाम पढ़ें, क्योंकि ppp0 केवल एक उदाहरण है। होस्ट रूट में वाइल्डकार्ड काम नहीं करते, इसलिए कई नामों से बात करने वाली सेवा को हर नाम के लिए एक प्रविष्टि चाहिए, और नियम मशीन की हर प्रक्रिया को कवर करता है, आपके मन वाली प्रक्रिया को नहीं।
सॉकेट को टनल के पते से बाँधें
जब क्लाइंट आपके नियंत्रण में हो, बाँधना सबसे सस्ता चयनक है। टनल के स्थानीय पते से बंधा सॉकेट उसी इंटरफ़ेस से बाहर जाता है जब तक वहाँ गंतव्य का रूट मौजूद है, और मशीन पर बाकी सब अपना सामान्य रास्ता रखता है। बारीकी यह है कि बंधन स्रोत पता चुनता है जबकि रूटिंग तालिका अब भी निकास चुनती है। अकेला बंधन उस ट्रैफ़िक को नहीं हिलाता जिसका टनल में कोई रूट नहीं है, और «मैंने इसे बाँधा और यह अब भी ग़लत तरफ़ से निकलता है» वाली ज़्यादातर शिकायतों के पीछे यही ग़लती है।
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))उच्च-स्तरीय उपकरण वही विचार देते हैं: curl --interface 10.8.0.2, PHP में CURLOPT_INTERFACE, और Python के HTTP क्लाइंट में source_address तर्क। पता टनल का ही होना चाहिए, और यहाँ की टाइपो रूटिंग शिकायत के रूप में नहीं, सामान्य कनेक्शन त्रुटि के रूप में विफल होती है। पते की जगह डिवाइस नाम से बाँधना Linux का विशेषाधिकार है और Windows या macOS पर नहीं जाता, इसलिए पोर्टेबल स्क्रिप्ट पता देती हैं।
Linux: मालिक का मिलान करें, प्रोग्राम के नाम का नहीं
कर्नेल सॉकेट के मालिक उपयोगकर्ता का मिलान कर सकता है, इसलिए एक समर्पित खाता चयनक बन जाता है। टनल को अपनी रूटिंग तालिका में रखें और वहाँ केवल उसी खाते का ट्रैफ़िक भेजें:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100यह तभी काम करता है जब टनल पहले से डिफ़ॉल्ट रूट न हो। क्लाइंट को --route-nopull के साथ शुरू करें, या --pull-filter ignore "redirect-gateway" के साथ OpenVPN 2.4 और बाद के संस्करणों पर धकेले गए रीडायरेक्ट को फ़िल्टर करें, फिर तालिका प्रविष्टि स्वयं जोड़ें। वरना दोनों डिफ़ॉल्ट मौजूद रहते हैं और कम प्राथमिकता वाला जीत जाता है। इसका जानबूझकर किया गया रूप है डिफ़ॉल्ट रूट को टनल गेटवे की ओर मोड़ना, या इसे डिस्कनेक्ट स्क्रिप्ट से करना: टनल गिरने पर यह ट्रैफ़िक को इंटरनेट से दूर रखता है, पर यह एक किल स्विच है, चयनक नहीं, और तब तक रहता है जब तक कोई इसे हटा न दे।
इसका मज़बूत रूप नेटवर्क नेमस्पेस है। इसे बनाएँ, क्लाइंट को अंदर चलाएँ और ऐप को ip netns exec के साथ शुरू करें। नेमस्पेस के बाहर कुछ भी टनल इंटरफ़ेस नहीं देखता, इसलिए ऐप गलती से होस्ट रूट पर वापस नहीं गिर सकता। क़ीमत असली है: नेमस्पेस को भौतिक नेटवर्क तक पहुँचने के लिए veth जोड़ी और अपनी रिज़ॉल्वर फ़ाइल चाहिए, क्योंकि माउंट नेमस्पेस होस्ट की हल करने की सेटिंग्स साझा नहीं करता।
दो विफलता मोड ज़्यादातर लीक समझाते हैं। नाम हल करना अब भी होस्ट के रिज़ॉल्वर में होता है, इसलिए TCP कनेक्शन अंदर जाने पर भी क्वेरी टनल से बाहर जाती है। और IPv6 को अपना ip -6 rule चाहिए, क्योंकि v4 नियम उसे कवर नहीं करता।
Windows और macOS: फ़ायरवॉल सीमित करें या प्रॉक्सी सौंपें
Windows में भी प्रति-ऐप रूट चयनक नहीं है, पर उसके फ़ायरवॉल नियम प्रोफ़ाइल तक सीमित किए जा सकते हैं, और VPN अडैप्टर को «सार्वजनिक» के रूप में वर्गीकृत किया जा सकता है जबकि घर का LAN «निजी» रहे। «निजी» और «डोमेन» प्रोफ़ाइल में प्रोग्राम के लिए आउटबाउंड ब्लॉक नियम बनाएँ, «सार्वजनिक» खुला छोड़ें, और ऐप नेटवर्क तक केवल टनल से पहुँच सकेगा। पहले कनेक्शन की नेटवर्क श्रेणी तय करें, फिर पुष्टि करें कि फ़ायरवॉल के मॉनिटर पैनल में नियम मेल खाया। यह ब्लॉक है, रूट नहीं: टनल गिरने पर यह बंद होकर विफल होता है, और फ़ायरवॉल को नज़रअंदाज़ करने वाले ऐप को रोक नहीं सकता।
macOS अन्य उपकरणों के साथ वही तर्क अपनाता है। SSH से पहुँचा जाने वाला SOCKS श्रोता सबसे कम दख़ल देने वाला विकल्प है, क्योंकि ssh -D 1080 user@host एक स्थानीय पोर्ट बनाता है और केवल उसका उपयोग करने के लिए कॉन्फ़िगर किए गए ऐप ही वह रास्ता लेते हैं। pf नियम प्रोग्राम को उपयोगकर्ता या समूह के अनुसार ब्लॉक कर सकता है, पर अब भी केवल उस प्रोग्राम के लिए टनल नहीं चुन सकता। Tunnelblick डोमेन के अनुसार फ़िल्टर ही नहीं कर सकता, क्योंकि रूट पतों के साथ काम करते हैं; OpenConnect के उपयोगकर्ता vpn-slice की ओर जाते हैं जब सर्वर बहुत चौड़ी प्रीफ़िक्स सूची धकेलता है।
दो वैकल्पिक उपाय वह कवर करते हैं जो प्लेटफ़ॉर्म उपकरण चूक जाते हैं। डोमेन के अनुसार प्रॉक्सी बदलने वाले ब्राउज़र एक्सटेंशन «सिर्फ़ यह साइट» के लिए व्यावहारिक उत्तर हैं, क्योंकि वे ऐप परत पर काम करते हैं जहाँ नाम अब भी मौजूद हैं; वे एक ब्राउज़र कवर करते हैं और कुछ नहीं। कठोर उपाय विक्रेता के क्लाइंट को वर्चुअल मशीन में चलाना है ताकि मेज़बान रूट बदलाव का मालिक हो और होस्ट अपना रास्ता रखे, जिसकी क़ीमत दो नेटवर्क स्टैक बनाए रखना है।
बाँधने का विकल्प न रखने वाले उपकरणों के लिए SOCKS हॉप
ज़्यादातर ऐप को HTTP या SOCKS प्रॉक्सी की ओर इंगित किया जा सकता है, भले ही उन्हें इंटरफ़ेस के बारे में कुछ न पता हो। SSH क्लाइंट सर्वर पक्ष पर बिना किसी बदलाव के एक देता है:
ssh -N -D 127.0.0.1:9999 [email protected]उस पोर्ट का उपयोग करने के लिए कॉन्फ़िगर किया गया सब कुछ बास्टियन पर बाहर जाता है जबकि डेस्कटॉप का बाकी हिस्सा अपना सामान्य कनेक्शन रखता है। टनल के पते से बंधा स्थानीय 3proxy इंस्टेंस SSH के बिना एक पोर्ट के लिए वही करता है, और stunnel एक ही TLS-लिपटे पोर्ट को कवर करता है। सीमाएँ व्यावहारिक हैं: प्रॉक्सी केवल वे प्रोटोकॉल ले जाता है जो उसका उपयोग करना जानते हैं, UDP को SOCKS5 UDP साहचर्य चाहिए जिसे कई क्लाइंट की «SOCKS5» सेटिंग सक्रिय नहीं करती, और DNS प्रॉक्सी द्वारा हल होना चाहिए (curl में socks5h), वरना नाम स्थानीय रिज़ॉल्वर तक लीक होता है।
श्रोता को लूपबैक पर रखें जब तक कि किसी दूसरी मशीन को इसकी ज़रूरत न हो।
रास्ता स्थिति संदेश से नहीं, कैप्चर से सत्यापित करें
ऐप की स्थिति विंडो सेटिंग का वर्णन करती हैं, पैकेट का नहीं। दोनों इंटरफ़ेस पर कैप्चर करें और तुलना करें:
tcpdump -i any -n port 443
ss -tnp | grep 443अगर कनेक्शन भौतिक इंटरफ़ेस पर दिखता है और टनल पर मेल खाती प्रविष्टि नहीं है, तो क्लाइंट चाहे जो रिपोर्ट करे, चयनक लागू नहीं है। IPv6 और रिज़ॉल्वर अलग-अलग जाँचें, और जाँचें कि सहायक प्रक्रिया या विरासत में मिली सिस्टम प्रॉक्सी जैसा दूसरा रास्ता है या नहीं।
फिर आवश्यकता से मेल खाती सीमा चुनें: एक साइट के लिए प्रॉक्सी पर्याप्त है, एक खाते के लिए UID नियम पर्याप्त है, जब कनेक्शन को बंद होकर विफल होना हो तो फ़ायरवॉल प्रोफ़ाइल पर्याप्त है, और नेमस्पेस ही एकमात्र विकल्प है जो टनल को उसके बाहर की हर चीज़ से छिपाता है।