คำสั่งเดียวกันเชื่อมต่อได้จากเชลล์แต่ตายภายใต้ cron ดังนั้นข้อสรุปตามธรรมชาติคืออุโมงค์หรือใบรับรองเป็นตัวการ ก่อนจะเชื่อสิ่งนั้นให้ดูว่าการรันทั้งสองครั้งมีอะไรเหมือนกัน ไบนารี การตั้งค่า และข้อมูลรับรองเหมือนกันทุกอย่าง มีเพียงสภาพแวดล้อมของโปรเซสที่เปลี่ยนไป เมื่อหาโปรแกรมไม่เจอเลย การดีบัก TLS ปริมาณเท่าใดก็ไม่ช่วย การแก้ไขมักอยู่ในพาธ ไดเรกทอรีทำงาน และตัวแปรสภาพแวดล้อมไม่กี่ตัว ไม่ใช่ใน VPN

command -v openvpn ในเชลล์ที่ใช้งานได้จะพิมพ์คำตอบออกมา และบ่อยครั้งคือ /usr/sbin/openvpn ซึ่งเป็นไดเรกทอรีที่ cron ไม่ได้ใส่ไว้ในพาธค้นหาของมัน

อุโมงค์ไม่เคยเริ่มทำงาน อุโมงค์จึงไม่ใช่ปัญหา

งานที่เริ่มโดย cron สืบทอดสภาพแวดล้อมขั้นต่ำ ตามปกติคือ PATH=/usr/bin:/bin และไม่มีเทอร์มินัลควบคุม ทุกอย่างที่ติดตั้งใน /usr/sbin หรือ /usr/local/sbin จะหายไปจากรายการนั้นเลย และบรรทัดล็อกจะอ่านว่า openvpn: not found แทนที่จะเป็นข้อผิดพลาด TLS subprocess ของ Python ล้มเหลวต่างออกไป มันสืบทอดสภาพแวดล้อมของโปรเซสตัวแปล ซึ่งอาจสมบูรณ์กว่า cron แต่ก็ยังไม่มีเชลล์ที่จะขยายเครื่องหมาย tilde ความล้มเหลวทั้งสองเกิดก่อนที่แพ็กเก็ตแรกจะออกจากโฮสต์

วิธีเร็วที่สุดในการแยกแยะคืออ่านข้อผิดพลาดแรกแทนข้อสุดท้าย ไบนารีที่หายไปทำให้เกิดข้อยกเว้นของเชลล์หรือ 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() เพื่อไม่ต้องขยาย tilde เลย เมื่อจำเป็นต้องใช้ไวยากรณ์ของเชลล์จริง ๆ ให้ใช้ 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 ที่หายไปเมื่อรีสตาร์ตด้วยมือ

บทความที่เกี่ยวข้อง