כיצד לאוטמט התחברות VPN שממתינה לקוד אסימון
לקוח VPN בשורת פקודה שמבקש קלט במסוף חוסם סקריפט שרק מעביר סיסמה בצינור. בצעו את חמשת הבדיקות האלה לפני שתפנו אל expect.
סקריפט שמפעיל לקוח VPN של ספק נתקע עד לפקיעת הזמן, והיומן מראה שהלקוח מבקש קוד אימות שאיש לא הקליד. הסיסמה נשלחה, ולכן המסקנה המתבקשת היא שהסיסמה שגויה. היא לא. בקשת הקוד היא שיחה נפרדת בערוץ נפרד, והסקריפט שלכם מעולם לא פתח אותה.
עברו על חמש הבדיקות שלמטה לפי הסדר. עצרו בבדיקה הראשונה שנכשלת, כי כל שלב מאוחר מניח שהקודמים מתקיימים.
בדיקה 1: האם הלקוח קורא בכלל קלט תקני?
בדקו לפני שתכתבו אוטומציה כלשהי: העבירו סוד מדומה אל הלקוח בצינור וראו מה קורה. אם הוא מדפיס את בקשתו ואז ממתין, הוא אינו קורא את הצינור.
לקוחות רבים פותחים את התקן המסוף ישירות, או מסרבים לקבל סוד מזרם שאינו מסוף, כדי שצינור תועה לא ידליף סיסמה. OpenVPN הוא החריג המתועד, כי --auth-user-pass מקבל נתיב קובץ. ללקוחות ספקים שמבקשים קוד חד-פעמי בדרך כלל אין דגל מקביל, והבקשה שלהם ממתינה למסוף שהסקריפט שלכם מעולם לא סיפק.
בדיקה 2: האם לתהליך הבן יש מסוף?
pty מקצה מסוף לבן, מה שהופך בקשות לפלט קריא שאפשר להשיב לו לפי הסדר.
import pexpect
child = pexpect.spawn("openconnect vpn.example.net", encoding="utf-8", timeout=30)
child.expect("GROUP:")
child.sendline("staff")
child.expect("USERNAME:")
child.sendline("alice")
child.expect("PASSWORD:")
child.sendline(PASSWORD)
child.expect("Connected as", timeout=60)מחרוזות הבקשה שלמעלה הן להמחשה; העתיקו את הטקסט המדויק שהלקוח שלכם מדפיס, כולל רישיות ופיסוק. לכל המתנה יש פסק זמן, ובן סגור זורק חריגה במקום להחזיר סטטוס, לכן עטפו את הרצף והתייחסו לסוף קובץ ככישלון. שימו לב למה שהבלוק הזה לא עושה: הבן מת עם הסקריפט, כי pty אינו מנגנון ניתוק ו-disown הוא פקודה מובנית של המעטפת שאינה מגיעה לתהליך שהסקריפט שלכם יצר.
בדיקה 3: האם יש קלט לא-אינטראקטיבי נתמך?
גירוד מסך נשבר בעדכוני לקוח, לכן חפשו קודם דגל מתועד.
בדקו בפלט העזרה אם יש אפשרות אסימון או קוד חד-פעמי, ובדקו אם השירות מקבל את הקוד שמצורף לשדה הסיסמה. במקום שבו הלקוח קורא קובץ אישורים, תנו לו את הקובץ במקום בקשה:
openvpn --config office.ovpn --auth-user-pass <(printf "%s\n%s\n" "$USER_NAME" "$PASSWORD")החלפת תהליכים נותנת ללקוח נתיב קריא במערכות שמספקות /dev/fd. הסוד נשאר סוד מנקודת מבטו של הלקוח, לכן החזיקו אותו מחוץ ליומנים, להיסטוריית המעטפת ולרשימות תהליכים. אם הלקוח דוחה את /dev/fd או פותח את נתיב האישורים יותר מפעם אחת, כתבו קובץ אמיתי עם הרשאות מגבילות ומחקו אותו באותו סקריפט.
בדיקה 4: האם המנהרה תשרוד את הסקריפט?
כברירת מחדל המנהרה מתה עם האב שלה, שזה ההיפך ממה שסקריפט אתחול צריך.
disown -h עוזר רק כשהפעלתם את הלקוח כמשימת מעטפת, כי הוא עורך את טבלת המשימות של אותה מעטפת ואינו מגיע לתהליך שהסקריפט שלכם יצר דרך pty. מנהל שירותים הוא הבית הטוב יותר לכל דבר שחייב להתחבר מחדש ללא השגחה, כי הוא מפעיל מחדש את הלקוח ורושם למה הוא מת. בכל מקרה, צפו לקוד חדש אחרי כל התחברות מחדש: אסימון חד-פעמי נצרך באימות המוצלח הראשון.
שימו לב למה ש-sudo עושה לרצף. בקשת הסיסמה של sudo אינה הבקשה של ה-VPN, ומענה עליה בסיסמת ה-VPN הוא הדרך הנפוצה ביותר שבה סדר מתנות נכון אחרת עדיין נכשל.
בדיקה 5: האם הקוד של אדם צריך להיות בסקריפט?
זו הבדיקה שאנשים מדלגים עליה. אסימון שייך לאדם, ואילו מנהרה שחייבת לעלות לפי לוח זמנים שייכת למכונה.
אם משימה חייבת להתחבר מחדש ללא השגחה, שאלו אם תעודת מכונה או חשבון שירות יכולים להחליף את ההתחברות האינטראקטיבית. אוטומציה של קוד חד-פעמי של מישהו הופכת גורם שני לסוד מאוחסן, וזה שינוי מדיניות ולא נוחות תסריט. במקום שהשירות תומך בשניהם, בחרו את האישור שמתאים לאיך שהמשימה באמת רצה. כתבו את התשובה, כי האדם הבא ישאל למה הסקריפט מחזיק אסימון.
איך נראה סקריפט שעובד
הסקריפט מדפיס את השלב שאליו הגיע, לעולם לא את הסוד, ויוצא בקוד שאינו אפס בכל המתנה שנכשלה.
בדקו אותו על ידי שבירה מכוונת: סיסמה שגויה, קבוצה שגויה, אסימון שפג. כל כישלון צריך לעצור בשלב בעל שם במקום להיתקע. אחר כך הריצו אותו פעם אחת מול השירות האמיתי ובדקו את המנהרה מחוץ לסקריפט, לא מבפנים.