OpenVPN אינו מוצא את ca.crt למרות שהפרופיל נטען
נתיב הפרופיל אינו הבסיס לקבצים שבתוכו. ראו מדוע --config מוחלט עדיין נכשל ב-ca.crt וכיצד ההפעלה מכריעה את התשובה.
השירות מתחיל, קורא את הפרופיל בלי תלונות ואז מת על שורה שנראית לא קשורה: הוא אינו יכול לפתוח את ca.crt. דבר לא השתנה בתעודה, ונתיב הפרופיל בקובץ היחידה מוחלט. השילוב הזה הוא הרמז. הנתיב שהעברתם מאתר קובץ אחד, והשמות שבתוך אותו קובץ נפתרים במקום אחר לגמרי, כך שהפעלה שנראית מוסמכת לחלוטין עדיין עשויה לקרוא את המפתחות שלה מתיקייה שאיש לא התכוון אליה.
לכשל יש מעט סיבות, וכדאי להפריד אותן לפי הסדר: תיקיית הבסיס שהייתה לתהליך בפועל, הקשר ההפעלה של השירות וההרשאות או ארגז החול סביב הקובץ לאחר שהנתיב נכון. כל אחת משאירה עקבה אחרת — ביחידה, בתהליך ובמערכת הקבצים.
נתיב הפרופיל אינו תיקיית בסיס
דמיינו תהליך שמתחיל ב-/tmp ומקבל את /etc/vpn/prod/tenant-a.ovpn. בתוך הפרופיל הזה, השורה ca ca.crt מציינת נתיב יחסי. שום דבר בארגומנט שאיתר את הפרופיל אינו כותב מחדש את השם הזה, ולכן התהליך מחפש את /tmp/ca.crt ומדווח על קובץ חסר.
המבלבל הוא סדר השגיאות. OpenVPN מנתח תחילה את התצורה ופותח קבצים מוזכרים כשהם מגיעים אליו, ולכן שגיאת כתיב בפרופיל מדווחת לפני שנוגעים במפתח כלשהו, בעוד שתעודה חסרה מופיעה רק כשהניתוח מגיע לשם. פרופיל שנטען הוא אפוא הוכחה שהתחביר תקין, לא שהקבצים שהוא מציין נמצאו.
מה שהמדריך מפריד: --config ו--cd
המדריך של OpenVPN 2.6 מתאר שתי אפשרויות נפרדות. האחת טוענת קובץ תצורה. האחרת משנה את תיקיית התהליך לפני קריאת התצורה, המפתח, הסקריפט וקבצים אחרים, והיא דורשת תיקייה מוחלטת בלי התייחסות לנוכחית.
openvpn --cd /etc/vpn/prod --config tenant-a.ovpn
הקריאה הזאת היא צורה, לא הוראה להפעיל פרופיל של מישהו אחר. מה שהיא נותנת הוא דטרמיניזם: הבסיס לכל שם יחסי בשרשרת נאמר בפקודה במקום לעבור בירושה מהמעטפת או המנהל שהפעילו את התהליך. החלופה תקפה באותה מידה ולעיתים קרובות טובה יותר לשירות: הניחו לתיקיית העבודה ותנו לכל קובץ מוזכר להיות מוחלט בתוך הפרופיל.
מה שתבחרו, בדקו את כל השרשרת ולא את הכשל הראשון. פרופיל יכול למשוך קובץ תצורה נוסף, והשמות היחסיים של הקובץ הכלול נפתרים מול אותה תיקיית תהליך, לא מול התיקייה של הקובץ שכלל אותם. נתיבי לוג, קבצי אימות וסקריפטים עזר הולכים אחרי אותו כלל.
השירות מתחיל במקום אחר
מעטפת אינטראקטיבית בתיקיית הפרופיל אינה מוכיחה דבר על יחידה ארוזה. מנהלים קובעים תיקיית עבודה משלהם, וסקריפט עטיפה עשוי לשנות אותה שוב לפני שהבינארי רץ. היחידה שנכשלת היא ההפעלה היחידה שכדאי לנמק לגביה.
systemctl show -p WorkingDirectory -p ExecStart --value openvpn@tenant-a
ls -l /proc/$(pgrep -n openvpn)/cwd
הפקודה השנייה מציגה את התיקייה שהתהליך החי באמת מחזיק, וזה אמין יותר מקובץ היחידה כשעטיפה מעורבת. אם השניים אינם מסכימים, העטיפה היא התשובה והיחידה היא עקבה מטעה.
אחרי החיפוש: הרשאות, chroot וארגז חול
תיקיית בסיס נכונה עדיין עשויה להפיק את אותה הודעה, כי פתרון והרשאה הם שלבים נפרדים. התהליך זקוק להרשאת הפעלה בכל תיקיית אב ולהרשאת קריאה בקובץ, ויחידת שירות שמסתירה חלק ממערכת הקבצים משנה את מה שהתהליך בכלל יכול לראות.
שלושה וריאנטים נפוצים מפיקים שגיאה זהה. הקובץ קריא רק ל-root בעוד השירות מוותר על הרשאות לפני שהוא פותח אותו. היחידה מגדירה תצוגת מערכת קבצים מגינה שאינה כוללת את תיקיית הפרופיל. הדמון רץ עם chroot, ולכן הנתיב שקיים במארח אינו קיים בתוך התצוגה הזאת.
אבחנו לפי הבעלות על הקובץ וזהות התהליך, לא בהרפיה של הרשאות. הפיכת מפתח פרטי לקריא לכולם כדי להשתיק את השגיאה הזאת מחליפה כשל רועש בשקט, והמפתח הוא החלק בתיקייה שאתם הכי פחות רוצים לחשוף.
הוכיחו את התיקון בהפעלה שבבעלותכם
הפעילו מחדש את היחידה האמיתית מתיקייה שאינה קשורה לפרופיל וצפו באותה שורת אתחול. אחר כך אשרו שהתהליך מחזיק בתיקייה המיועדת בזמן ריצה, ושהתעודה שהוא טען היא זו שאתם מצפים לה.
systemctl restart openvpn@tenant-a
systemctl status openvpn@tenant-a --no-pager
journalctl -u openvpn@tenant-a -n 20 --no-pager
כשטעינת הנתיבים מצליחה, אימות וניתוב הם עדיין שלבים נפרדים עם הכשלים שלהם, וחיבור שעולה אך אינו מעביר תעבורה הוא חקירה אחרת. התיקון כאן נוגע רק לשאלה מאיזו תיקייה הגיעו הקבצים, והוא צריך להיות גלוי בקריאה במקום להיזכר על ידי מי שהריץ אותה ביד בפעם האחרונה.
אם הפרופיל נפרס על ידי מנהל תצורה, שימו שם גם את תיקיית הבסיס או את שמות הקבצים המוחלטים. תיקון ידני במארח אחד הוא הגרסה של הבעיה הזאת שחוזרת אחרי הבנייה מחדש הבאה.