OpenVPN शेल में चलता है, पर cron के तहत विफल होता है: कारण PATH है
शेल से जुड़ने वाला कमांड टनल शुरू होने से पहले ही cron या subprocess के तहत विफल हो सकता है। पथ, कार्य निर्देशिका और टर्मिनल ठीक करें।
वही कमांड शेल से जुड़ता है और cron के तहत मर जाता है, इसलिए सहज निष्कर्ष यही है कि टनल या प्रमाणपत्र दोषी है। इस पर विश्वास करने से पहले देखें कि दोनों रन में क्या समान है: बाइनरी, कॉन्फ़िगरेशन और क्रेडेंशियल एक जैसे हैं, और केवल प्रक्रिया का वातावरण बदला है। जब प्रोग्राम ही नहीं मिलता, तब TLS डिबगिंग की कोई मात्रा मदद नहीं करती। सुधार आमतौर पर एक पथ, एक कार्य निर्देशिका और मुट्ठी भर पर्यावरण चर में होता है, VPN में नहीं।
जो शेल काम करता है, उसमें command -v openvpn उत्तर छापता है, और अक्सर वह /usr/sbin/openvpn होता है, यानी ऐसी निर्देशिका जिसे cron अपने खोज पथ में नहीं रखता।
टनल कभी शुरू ही नहीं हुआ, इसलिए टनल समस्या नहीं है
cron द्वारा शुरू किए गए जॉब न्यूनतम वातावरण उत्तराधिकार में पाते हैं, परंपरागत रूप से PATH=/usr/bin:/bin, और कोई नियंत्रक टर्मिनल नहीं पाते। /usr/sbin या /usr/local/sbin में स्थापित कोई भी चीज़ उस सूची से बस अनुपस्थित होती है, और लॉग की पंक्ति TLS त्रुटि के बजाय openvpn: not found पढ़ती है। Python का subprocess अलग ढंग से विफल होता है: वह इंटरप्रेटर प्रक्रिया का वातावरण उत्तराधिकार में पाता है, जो cron से समृद्ध हो सकता है पर टिल्डा फैलाने के लिए अभी भी कोई शेल नहीं है। दोनों विफलताएँ एक भी पैकेट मेज़बान छोड़ने से पहले होती हैं।
उन्हें अलग पहचानने का तेज़ तरीका है अंतिम के बजाय पहली त्रुटि पढ़ना। गायब बाइनरी शेल या Python अपवाद पैदा करती है, खराब कॉन्फ़िगरेशन OpenVPN का उपयोग संदेश पैदा करता है, और वास्तविक हैंडशेक विफलता समय-चिह्न और सहकर्मी पते पैदा करती है। केवल तीसरा ही टनल की समस्या है।
निरपेक्ष पथ और तर्क सूचियाँ शेल स्ट्रिंग्स से बेहतर हैं
हल किया गया पथ स्पष्ट रूप से दें और शेल को पूरी तरह छोड़ दें। subprocess.run(["/usr/sbin/openvpn", "--config", config, "--verb", "4"], capture_output=True, text=True, timeout=90) आपको रिटर्न कोड, stdout और stderr देता है, बिना शेल से फ़ाइल नाम की व्याख्या करने को कहे। कॉन्फ़िगरेशन का पथ os.environ["HOME"] या pathlib.Path.home() से बनाएँ, ताकि टिल्डा कभी फैलाना न पड़े। जब सचमुच शेल सिंटैक्स चाहिए, shlex.split का उपयोग करें, क्योंकि .split() से स्ट्रिंग बाँटने पर खाली जगह वाले किसी भी पथ पर वह टूट जाता है। stderr भी पढ़ें, क्योंकि OpenVPN अपनी अधिकांश शुरुआती विफलताएँ वहीं लिखता है।
यही नियम cron पर भी लागू होता है: जॉब की पंक्ति लॉगिन शेल नहीं है, इसलिए कोई आपकी प्रोफ़ाइल लोड नहीं करता और कोई आपके उपनाम परिभाषित नहीं करता।
crontab वास्तव में क्या उत्तराधिकार में पाता है
crontab प्रविष्टि को वह वातावरण मिलता है जो crontab घोषित करता है, साथ में कुछ विरासत में मिली डिफ़ॉल्ट, इसलिए पथ पूरा लिखें या कॉल को ऐसी स्क्रिप्ट में लपेटें जो यह करती हो। फ़ाइल के शीर्ष पर PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin जैसी पंक्ति सामान्य संस्थापन स्थानों को ढक देती है, और >> cron.log 2>&1 के साथ रीडायरेक्ट करना ही अगली विफलता को पढ़ने योग्य बनाता है। पासफ़्रेज़ से सुरक्षित प्रोफ़ाइल के पास cron के तहत अपना रहस्य माँगने का कोई रास्ता नहीं है, क्योंकि पूछने के लिए कोई टर्मिनल नहीं है। ऐसा कनेक्शन systemd इकाई में होना चाहिए जो क्रेडेंशियल फ़ाइल पढ़ सके, ऐसी जॉब में नहीं जो खुद को इंटरैक्टिव दिखाती है।
सापेक्ष पथ दूसरा जाल हैं। परीक्षण अक्सर उस फ़ोल्डर से होता है जिसमें प्रोफ़ाइल है, और cron जॉब बिल्कुल कहीं और शुरू होती है, इसलिए --config office.ovpn ऐसी फ़ाइल में हल होता है जो वहाँ नहीं है और संदेश गायब टनल के बजाय गायब फ़ाइल के रूप में पढ़ा जाता है।
वातावरण हटाकर इसे सिद्ध करें
न्यूनतम वातावरण सीधे पुनः बनाएँ: env -i PATH=/usr/bin:/bin /usr/sbin/openvpn --config office.ovpn --verb 4। उस रन से पथ या टर्मिनल की शिकायत का अर्थ है कि समस्या VPN के ऊपर बैठी है। अगर वह जुड़ता है, तो बचे हुए संदिग्ध हैं वह खाता जिसके तहत वह चलता है, वह निर्देशिका जिसमें वह शुरू होता है, और वह क्रेडेंशियल जो वह प्रस्तुत करता है। दोनों में से कोई भी परिणाम प्रमाणपत्र जाँच के एक और दौर से बेहतर है।
फिर रैपर को स्व-रिपोर्टिंग बनाएँ। जिस भी चरण तक वह पहुँचा, उसके लिए अलग स्थिति के साथ बाहर निकलें, दोनों स्ट्रीम समय-चिह्न के साथ लॉग करें, और अगली विफलता आपको बताएगी कि वह पथ हल करने में, प्रमाणीकरण में, या टनल पहले से ऊपर होने के बाद मरा।
इकाई को सही क्रम भी चाहिए: Wants=network-online.target के साथ After=network-online.target क्लाइंट को पता या डिफ़ॉल्ट मार्ग बनने से पहले शुरू होने से रोकते हैं, जो अन्यथा प्रमाणपत्र या TLS त्रुटियों के रूप में सामने आता है और हाथ से पुनः आरंभ करने पर गायब हो जाता है।