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

ההחלטה שייכת ל-stream, לא ל-HTTP

בהקשר stream NGINX עובד עם הפעלות TCP. הוא רואה את כתובת העמית של החיבור ויכול להחיל עליה כללי allow ו-deny מסודרים לפני ששום בייט מגיע ל-backend. זוהי החלטת קבלה מלאה והיא אינה דורשת מפתח פרטי, תעודה או ניתוח HTTP.

MechanismWhat it decidesWhere it lives
stream allow/denyWhether this TCP peer is relayedstream server block
ssl_preread mapWhich backend a named connection goes tostream, with the preread module
HTTP location ruleWhether an HTTP request is servedhttp server block

שלוש השורות האלה אף פעם לא מחליפות זו את זו. העתקת הוראת גישה של HTTP לקובץ שמזכיר את פורט 443 לא משנה דבר, כי שרת ה-stream אף פעם לא קורא את הקשר ה-http.

קבלו את החיבור לפני שמעבירים אותו

נניח התקנה קיימת עם המודולים stream ו-stream-access זמינים, לקוחות VPN שמחזיקים כתובות מקור ב-10.90.0.0/24, ו-backend ב-192.168.50.20:443 שכבר מגיש את זהות ה-HTTPS המיועדת.

stream {
    server {
        listen 443;

        allow 10.90.0.0/24;
        deny all;

        proxy_pass 192.168.50.20:443;
    }
}

קראו זאת כקטע של listener חדש, לא כתחליף ישיר לתצורה עובדת. עברו קודם על ה-listeners וה-include הקיימים, כי listener שני על פורט שכבר תפוס נכשל ב-reload, לא בזמן הבקשה.

שימו לב למה שחסר: אין listen ... ssl, אין הוראת תעודה. NGINX מעביר את הזרם המוצפן, וה-backend מסיים TLS ומציג את התעודה שהלקוח מאמת. לקוח שנכשל ב-allowlist מקבל את הפעלת ה-TCP שלו נדחית, לא עמוד HTTP 403, וכל כלי שמצפה לקוד סטטוס ידווח על משהו חסר תועלת.

מה ה-allowlist מכסה ומה לא

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

כלל הדוגמה מכסה את כל listener ה-stream. זה הופך אותו לבלתי מתאים לנקודת כניסה אחת שמשרתת תערובת של אתרים ציבוריים ואתרים ל-VPN בלבד, שם יידרש חריג לכל שם; listeners נפרדים בדרך כלל ברורים יותר ממדיניות תלוית שם שאיש אינו יכול לקרוא.

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

איזו כתובת NGINX באמת רואה?

מאזן עומסים, proxy אחר או source NAT יכולים לשים מתווך לפני NGINX, ולכן העמית שהוא רואה אינו הכתובת הפנימית של לקוח ה-VPN. בדקו את הנתיב האמיתי לפני בחירת קידומת, כי allowlist שנכתב לכתובת הלא נכונה או מכניס את כולם מאחורי המתרגם או אף אחד.

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

בדקו את שתי התוצאות לפני reload

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

nginx -t
ss -tnp | grep ':443'

בדקו גם את הנתיב ל-backend ואת התשובה בנפרד. מעבר ב-allowlist מוכיח רק ש-NGINX קיבל את ההפעלה; הוא לא יכול לגרום ל-backend שאינו נגיש להשיב, והוא לא יכול לתקן נתיב חזור שהיה חסר לפני שהתחלתם.

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