SSH ציבורי דרך ממסר נכשל בשני גבולות לא קשורים, ותיקון אחד מגלה בדרך כלל את השני. או שהקצה מחזיר תגובת HTTP במקום להעביר זרם, או שהממסר מוסיף לפנים כותרת שהקצה האחורי מעולם לא הסכים לקרוא. שלושה תנאים קובעים איזה משניהם אתם רואים: למה הרשומה הציבורית מצביעה באמת, מה בלוק הזרם שולח למעלה, ואיזה שם לקוח ה-SSH מאמת. עבדו עליהם בסדר הזה.

שני גבולות, שני סימפטומים

כשל בקצה נראה כמו דף סטטוס HTTP שמגיע למקום שבו אמור להופיע באנר SSH. החיבור מושלם ברמת TCP והבתים הראשונים הם טקסט ששייך לשרת אינטרנט. זו אי-התאמת פרוטוקול, לא מנה שאבדה. זה גם אומר לכם שהקצה מגיש בכוונה משהו אחר לאותו שם.

כשל במעלה נראה אחרת. החיבור מגיע ל-sshd, sshd קורא בתים שאינם חילופי הזיהוי שהוא מצפה להם, וההפעלה מתה או נתלית בלי להציע מפתח מעולם.

קל לבלבל בין השניים כי שניהם מייצרים לקוח שמתחבר ואז נכשל. תגובת הפרוטוקול הראשונה שאתם מקבלים היא מה שמבדיל ביניהם, ולכן היא שייכת לכרטיס.

כותרת PROXY היא הודעה, לא מתג

בתוך מודול הזרם, proxy_protocol on מורה ל-NGINX לכתוב כותרת פרוטוקול PROXY כלפי המעלה לפני המטען. היא אינה מפעילה פרוקסי TCP, ואין לה שום קשר להצפנת SSH. זו הערה המופנית לנמען. אם הנמען אינו יכול לקרוא אותה, היא הופכת לפסולת בתחילת הזרם.

השירות המקבל חייב לצרוך את הכותרת הזו בכוונה. sshd רגיל מצפה לחילופי הזיהוי של SSH כבתים הראשונים בחיבור, ולכן הקדמה בלתי צפויה שוברת את לחיצת היד באופן שנראה כמו שרת פגום.

stream {
    server {
        listen 2222;
        proxy_pass 10.77.0.20:22;
    }
}

הקטע הזה הוא ממסר עובד בשלמותו לקצה אחורי שנגיש דרך ה-VPN. שום הוראת PROXY אינה מופיעה משום שהנמען אינו מדבר את הפרוטוקול, והיעדרות זו היא החלטה ולא השמטה.

הפרידו גם בין שני הכיוונים. הגדרת proxy_protocol בשורת listen פירושה ש-NGINX מצפה שהלקוח שלו עצמו ישלח את הכותרת ראשון; הפעלתה ללקוח SSH רגיל יוצרת בדיוק את ההיפך מהבעיה שאנשים בדרך כלל מנסים לפתור.

למה שם המארח הציבורי מגיע באמת

דף HTTP 400 בהפעלת SSH הוא ראיה על הקצה. משהו בנתיב מדבר HTTP, ושום עבודה על מפתח או חשבון בקצה האחורי לא תשנה זאת. הפסיקו לשנות את הקצה האחורי עד שהקצה ייושב.

הרשומה המתווכת הרגילה של Cloudflare נושאת את תעבורת ה-HTTP וה-HTTPS הנתמכת שלה בפורטים מוגדרים. הפעלת הפרוקסי עבור שם מארח אינה יוצרת ממסר TCP כללי, והעברת SSH לפורט 443 אינה הופכת אותו ל-HTTPS.

קיימים שני כיוונים מעשיים. רשומת DNS בלבד יכולה להצביע על ממסר ה-TCP שלכם, מה שמשאיר את החלטת החשיפה בידיים שלכם. לחלופין, השתמשו במוצר שמתעד במפורש נשיאת SSH, עם תצורת מקור, לקוח וגישה משלו.

בכל בחירה שתבחרו, הממסר עדיין זקוק למסלול VPN לקצה האחורי, לחיבור מורשה ולמסלול חזרה. המסלול אינו אופציונלי משום שהצד הציבורי עובד.

שמרו על זהות SSH יציבה באמצעות כינוי

משתמשים בדרך כלל רוצים להמשיך להקליד את שם המארח הקיים שלהם. כינוי בתצורת SSH יכול לעשות זאת בלי לשנות דבר בממסר, והוא משאיר את השינוי בצד הלקוח, במקום שאליו הוא שייך.

Host git.example.com
    HostName ssh-entry.example.net
    Port 2222
    User git
    HostKeyAlias git.example.com

בלי HostKeyAlias, OpenSSH מחפש את מפתח המארח תחת השם שהוא מתחבר אליו, ולכן הרשומה זזה וכל משתמש רואה אזהרת מפתח שהשתנה בשימוש הראשון. הגדרת הכינוי משאירה את רשומות known-hosts יציבות בזמן שהחיבור עדיין הולך לממסר.

כינוי זה חל רק על מאגרים מרוחקים מבוססי SSH. כתובת Git מסוג HTTPS אינה מתייעצת בו, וכתובת המרוחק הטיפוסית חייבת להתאים לתעבורה שבאמת הקציתם.

מה שאסור לכם לעשות הוא לכבות את אימות מפתח המארח כדי שהאזהרה תיעלם. זה מסיר הגנה שתיקון ההעברה מעולם לא ביקש לגעת בה, והופך מטרד קוסמטי לחשיפה אמיתית.

אמתו כל קפיצה בנפרד

בדקו את המאזין הציבורי לבדו, אחר כך את החיבור מהממסר לקצה האחורי ממארח הממסר, ורק אחר כך את הלקוח מקצה לקצה. כל אחת משלוש הבדיקות האלה שוללת שכבה אחרת. דילוג על אחת פירושו ששתי התוצאות הנותרות אינן ניתנות לפרשנות. שלוש תצפיות, שלושה מקומות שבהם כשל יכול לחיות.

שמרו את כתובת היעד, את הפורט ואת בתי הפרוטוקול הראשונים עם כל תוצאה. השלישייה הזו מבדילה בין שגיאת קצה לבין בעיית ניתוב VPN ולבין כשל אימות SSH, וזה ההבדל בין תיקון לבין ניחוש.

לבסוף, תעדו את החשיפה המיועדת. ממסר TCP שנושא SSH לקצה אחורי פרטי הוא פתח מכוון בהיקף הציבורי, והוא ראוי לאותה סקירה כמו כל שירות נכנס אחר.

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