כתובת IP של לקוח VPN ב-Kubernetes: לשמור אותה או לעשות SNAT?
בחרו בין שמירת כתובת הלקוח האמיתית של ה-VPN לבין NAT מקור ב-Kubernetes לפי שלושה תנאים, ואז אמתו את נתיב החזרה ואת החלפת הפודים.
איפשהו בין המנהרה ל-backend, כתובת של לקוח או שורדת או מוחלפת, ושתי התוצאות מוצדקות. הבחירה שייכת למי שמחזיק בשירות המקבל, לא למי שהתקין את ה-CNI. כשהרשאה לכל לקוח, מכסות או רשומות ביקורת חשובות, הכתובת חייבת לשרוד כל קפיצה. כשהקצאה משותפת מקובלת, NAT מקור מסיר בעיית ניתוב שהייתם מתחזקים לנצח.
היכן הכתובת של הלקוח באמת נעלמת
החבילה של לקוח מרוחק מגיעה לפוד ה-VPN עם מקור פנימי בטווח המנהרה, נניח 10.90.0.42. כשהפוד מעביר את החבילה הזו אל ClusterIP או אל IP של פוד, שלושה מנגנונים שונים יכולים לשכתב את המקור. כלל ה-masquerade של ה-CNI בצומת מכסה תעבורה שהפוד שולח מחוץ ל-CIDR של האשכול. רשומת ה-conntrack של kube-proxy בצומת היעד מכסה את קפיצת ה-Service עצמה. פרוקסי שמסיים את החיבור אינו מעביר את החבילה כלל, ולכן המקור שאתם רואים בסוף שייך לפרוקסי.
כל אחד מהם חי בשכבה אחרת, ולכן שדה Service אחד כמעט אף פעם לא מתקן את כל הנתיב.
externalTrafficPolicy ב-Service הציבורי של ה-VPN קובע רק מה קורה לחיבור החיצוני: Local שומר את הכתובת המרוחקת על הצומת שקיבל אותה, בעוד Cluster מאפשר ל-kube-proxy למסור את החבילה לצומת אחר ולעשות לה masquerade שם. ההגדרה הזו שולטת במאזין שהלקוחות שלכם מתחברים אליו. היא לא אומרת דבר על החבילה המפוענחת שתהליך ה-VPN שלכם מנתב אחר כך אל תוך האשכול.
שלושה תנאים שמכריעים את הוויכוח
אני שואל את אותן שלוש שאלות באותו סדר בכל פעם שזה עולה, כי הראשונה בדרך כלל מסיימת את הדיון. ענו עליהן עבור עומס העבודה הספציפי ולא עבור האשכול כולו. שני אשכולות בתוך חברה אחת יכולים להגיע לתשובות שונות ושתיהן נכונות.
| Question | Preserve | SNAT |
|---|---|---|
| Does the backend enforce policy per client? | Yes, the address is the identity | Only if a trusted proxy supplies identity |
| Can the cluster route 10.90.0.0/24 back to a stable gateway? | Required | Not needed |
| Is the workload a forwarder or a proxy? | Forwarder | Proxy is fine |
אם ה-backend עונה כן לשאלה הראשונה ולא לשנייה, יש לכם עבודת רשת לעשות ולא מניפסט לערוך. אם השורה השלישית אומרת פרוקסי, הכתובת מעולם לא הייתה שורדת, ולטעון אחרת עולה שבוע של לכידת חבילות.
מה מצב preserve דורש מהרשת
שמירת המקור משמעה שצומת היעד חייב להחזיר תעבורה אל 10.90.0.42 דרך שער VPN פעיל. מאגר ה-VPN אינו CIDR של האשכול, ולכן שום CNI לא מנתב אותו עבורכם כברירת מחדל. אתם מוסיפים את הנתיב בעצמכם, ואז מוודאים שנקודת הקצה של השער שאליה הוא מצביע שורדת תזמון מחדש.
ip route add 10.90.0.0/24 via 10.244.3.17 dev eth0
sysctl -w net.ipv4.conf.eth0.rp_filter=2
הקפיצה הבאה היא כתובת פוד, וכתובות פודים זזות. כוונו את הנתיב אל VIP של Service או אל כתובת צומת שנשארת במקום, או השלימו עם כך שתרדפו אחרי השער לאחר כל פינוי. סינון נתיב הפוך הוא המלכודת האחרת: מצב קפדני מפיל חבילות שנתיב החזרה שלהן אינו תואם את הממשק שהן הגיעו דרכו, וזה בדיוק הצורה של זרם VPN נשמר.
אימות רופף הוא לעתים קרובות ההבדל בין נתיב חזרה עובד לבין תשובות שנעלמות לאחר עזיבת ה-backend. גם כללי masquerade בצומת ה-VPN חייבים להוציא את טווח המנהרה, אחרת החבילה המועברת יוצאת עם כתובת הפוד, ולא משנה איזה נתיב בניתם. עדיף לבטא את ההוצאה הזו בקונפיגורציה של ה-CNI עצמו במקום לערוך iptables ביד, כי הסוכן משכתב את השרשראות שלו באתחול מחדש והכלל שכתבתם ביד שורד בדיוק עד השדרוג הבא.
NetworkPolicy הוא בקרת גישה, לא ניתוב. החמרת מדיניות לעולם לא תשחזר כתובת שה-masquerade כבר החליף.
מה עולה SNAT, ואיך לתעד אותו
NAT מקור אל האשכול הוא העיצוב הזול יותר, וזו אינה טעות. ה-backend רואה את כתובת הפוד או הצומת של ה-VPN, הלוגים מתיישבים זה עם זה, ואין צורך שנתיב חזרה יהיה קיים. מה שאתם מאבדים הוא היכולת להבחין בין שני לקוחות בשכבת הרשת, ולכן מכסות, חסימות ועקבות ביקורת חייבים לבוא ממקום אחר.
בדרך כלל אותו מקום אחר הוא היישום: אסימון סשן, כותרת חתומה או שורת פרוטוקול PROXY שנכתבת על ידי פרוקסי שאתם שולטים בו. אלה מעניקים למקבל זהות שהוא יכול לסמוך עליה רק כשהפרוקסי נמצא בנתיב ולא ניתן לקבוע את הכותרת מצד הלקוח. היו מפורשים ב-runbook לגבי איזו מהן חלה, כי המהנדס הבא יניח שהכתובת בלוג היא הלקוח. היא לא, ותקרית אחת מספיקה כדי לייקר את ההנחה הזו.
ההחלפה נשארת ישרה כל עוד היא כתובה.
בדיקות שמפרידות נתיב עובד מנתיב בר מזל
בדקו עם חיבור חדש ולא עם חיבור שמור במטמון, ובדקו בשני הכיוונים. כמה כשלים נראים לסירוגין רק מפני שזרמים חדשים עוברים בנתיב המשוכתב.
לכדו את ה-tuple בשלוש נקודות במהלך בקשה מורשית אחת: מיד לאחר הפענוח, בממשק היציאה של פוד ה-VPN, וב-backend. אם המקור משתנה בין הלכידה השנייה לשלישית, הסיבה נמצאת במורד הזרם מהפוד. אם הוא משתנה לפני השנייה, הפוד עצמו מתרגם.
לאחר מכן מחקו את פוד ה-VPN וחזרו על הבדיקה בזמן שהוא מתוזמן מחדש. נתיב נשמר שנשבר בפינוי אינו שלם, וכך גם כלל NAT שנעוץ בכתובת שכבר אינה קיימת. לבסוף שלחו בקשה אחת ממקור שאתם מצפים שיידחה ואשרו שהדחייה מגיעה ממדיניות ולא מנתיב חסר.