אותה פקודה מתחברת מהמעטפת ומתה תחת 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 אבל עדיין אין לה מעטפת שתפתח טילדה. שני הכשלים קורים לפני שחבילה אחת יוצאת מהמארח.

דרך מהירה להבדיל ביניהם היא לקרוא את השגיאה הראשונה ולא את האחרונה. קובץ בינרי חסר מייצר חריגה של מעטפת או של 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 שנעלמות בהפעלה מחדש ידנית.

מאמרים קשורים