תקנו את התעבורה של Docker ו-WSL שמתעלמת מה-VPN של המארח
מכולות ו-WSL שומרות על מסלולים, פותר ומסלול העברה משלהן, כך שמנהר מארח עובד משאיר אותן עם DNS מיושן או בלי יציאה. תקנו כל שכבה.
ביליתי את רובו של בוקר באביב הזה במרדף אחרי מכולה שלא הצליחה לפתור אף שם פנימי אחד, בעוד הדפדפן שלידה פתח את הוויקי בלי תלונה. המנהר הייתה פעילה, הדפדפן הוכיח זאת, והמכולה נותרה ב מרחב שמות רשת אחר עם טבלת מסלולים אחרת ופותר אחר. מה שלפניכם הוא הסדר שאני בודק כעת: קודם טבלאות מסלולים, שנייה פתרון שמות, שלישית העברה, ולבסוף פלטפורמת המארח. עצרו בצעד הראשון שכבר נכשל, כי הוספת כללים נמוך יותר במחסנית מסתירה את הגבול במקום להזיז אותו.
השוו את שלוש טבלאות המסלולים לפני שאתם משנים כלל
למכולה על הגשר המוגדר כברירת מחדל של Docker יש מסלול ברירת מחדל אחד שמצביע לשער docker0, ואין לה מושג ש-tun0 קיים. למארח יש tun0, את המסלולים שה-VPN דחף, ואולי כלל מדיניות ששולח תעבורת בדיקה דרך ממשק אחר מזה שאתם מצפים לו. WSL מוסיף טבלה שלישית מאחורי המתאם הווירטואלי שלו. הדפיסו את כל השלוש לפני שתערכו אחת מהן.
| Context | Command | What a wrong answer looks like |
|---|---|---|
| Host | ip route get 10.8.0.20 | Leaves a physical interface instead of tun0 |
| Container | ip route | Only 172.17.0.0/16 via 172.17.0.1 |
| Container DNS | cat /etc/resolv.conf | A public resolver, or an upstream the tunnel never pushed |
| WSL | ip addr; ip route | Only the WSL subnet, no Windows VPN adapter |
אם המסלול היחיד של המכולה הוא הגשר ו-tun0 לא מופיע לעולם במרחב השמות שלה, שום דבר למטה לא יעזור: שום שינוי שרת שמות לא יוצר מסלול, ושום מסלול לא יוצר זהות מנהר שהעמית יקבל. הסדר חשוב כי כשל DNS וכשל מסלול נראים זהים מבפנים ל-shell.
כוונו את הפותר למנהר במקום לפותר ציבורי
במארח, resolvectl status מפרט שרתי DNS לכל קישור, וגם readlink -f /etc/resolv.conf אומר לכם אם הקובץ הוא stub של systemd, קובץ NetworkManager או משהו שספק לקוח שכתב מחדש. בתוך מכולה ברשת מוגדרת על ידי המשתמש, /etc/resolv.conf נותן את השם 127.0.0.11, הפותר המובנה של Docker, שמעביר ל-upstream שאיתו הוגדר הדמון. מכולה על הגשר המוגדר כברירת מחדל קיבלה לעומת זאת עותק של קובץ המארח כשהיא נוצרה, והיא שומרת עליו.
העותק הזה הוא הסיבה שמנהר שעולה לאחר שהמכולה מתחילה משאיר את המכולה לשאול את ה-upstream הישן. או שתועברו את פותר ה-VPN במפורש עם docker run --dns 10.8.0.53, או שתגדירו "dns": ["10.8.0.53"] ב- /etc/docker/daemon.json ותאתחלו את הדמון, או שתיצרו מחדש את המכולה כשהמנהר פעילה. יצירה מחדש היא בדרך כלל זולה יותר מאבחון פותר שהיה נכון לפני שעה.
החלפה בפותר ציבורי היא התיקון שמשמר את התסמין. הוא פותר את האינטרנט ונכשל בכל אזור פרטי, והוא יכול לעקוף בשקט את הסינון שלמנהר נועדה לאכוף. אם תנסו לכתוב בתוך /etc/resolv.conf של מכולה פועלת, צפו ל- Device or resource busy: Docker עושה bind-mount לקובץ הזה, כך שהעריכה דורשת קודם ביטול עגינה. הגדרת --dns ביצירה היא הרצף ששורד אתחול.
העבירו ותרגמו כשמכולות שואלות את המנהר
עם הגשר המוגדר כברירת מחדל, המארח הוא נתב בין docker0 ל-tun0, והוא לא עושה אף אחת מהשתיים כברירת מחדל. הפעילו העברה ותרגמו את כתובת המקור כדי שהעמית יראה כתובת מנהר של המארח ולא כתובת שאין לו מסלול חזרה אליה (החליפו את tun0 במה שהלקוח שלכם קורא לממשק):
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i docker0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTכללי FORWARD חשובים במארחים שמדיניות ההעברה שלהם היא DROP. Docker מכניס שרשראות משלו, אז בדקו עם מנה ולא בקריאה של iptables -S: מהמכולה, ip route get 10.8.0.20 אמור לנקוב בשער הגשר, וגם nc -vz 10.8.0.20 443 אמור להתחבר או לפקוע בזמן במנהר ולא בפותר.
בלי MASQUERADE העמית זקוק למסלול חזרה אל 172.17.0.0/16, שרוב ה-VPN לגישה מרחוק לא ידחפו. כאן גם לקוח מבוסס מדיניות שובר את התוכנית: אם הוא לוכד רק תעבורה מ-UID או cgroups מסוימים, כללי ההעברה שלכם לא רואים את המנה בתוך המנהר לעולם, והתיקון שייך למדיניות הניתוב של הלקוח ולא ל-iptables. כללים שהוספתם ביד נעלמים באתחול, אז שימו אותם ביחידת systemd או בטבלת netfilter-persistent ובדקו שוב לאחר אתחול, כי מנהר שעולה באיחור יכולה לסחוף אותם.
שימו את המנהר בתוך המכולה כשהמטרה היא בידוד
אם מכולה אחת חייבת לצאת דרך צומת מסוים, הריצו את הלקוח במכולה הזו. היא זקוקה ל- --cap-add NET_ADMIN ול- --device /dev/net/tun, ואז היא מחזיקה tun0, מסלולים וחומת אש משלה. אם מכולות אחרות עוברות דרכה, היא זקוקה גם להעברה ולכלל MASQUERADE בתוך המכולה, והעמיתים האלה זקוקים למסלול מפורש או לפרוקסי שמצביע עליה; הגשר המוגדר כברירת מחדל של Docker לא מוסיף את המסלול הזה עבורכם.
רשת המארח, docker run --network host, היא הגרסה הזולה: המכולה מצטרפת למרחב השמות של המארח ורואה את מסלולי המנהר ישירות. היא גם מפילה את בידוד הרשת, מזמינה התנגשויות פורטים, ובפועל היא אפשרות של Linux. פורט מפורסם אינו תחליף. -p 8080:80 הופך שירות לנגיש מהמארח; הוא לא הופך כתובות פנימיות 10.x לנגישות מתוך המכולה, וערבוב השניים הוא הדרך שבה בעיית ניתוב הופכת לפורט מסד נתונים חשוף.
הבדילו כשל שם מרשת משנה חופפת
הגשר המוגדר כברירת מחדל של Docker הוא 172.17.0.0/16, והרבה מאגרי VPN ארגוניים יושבים בתוך 172.16.0.0/12. ברגע שהמנהר מחזיקה בטווח הזה, תעבורה שנועדה למכולה יכולה לנתב לתוך המנהר במקום. שמות נכשלים, השגיאה נראית כמו DNS, והכתובות הן הבעיה. בדקו את היעד, לא את הפותר: ip route get 172.17.0.5 במארח, ואז השוו את קידומת הגשר למאגר ה-VPN ול- docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.
התיקון הוא להזיז אחד מהטווחים: או מאגר כתובות ברירת מחדל אחר ל-Docker ב- daemon.json או מאגר אחר במרכזן. הוספת שרת שמות נוסף לא עושה כאן דבר, וגם ssh -b לא: קשירת הלקוח לממשק משנה את כתובת המקור, לא מסלול יעד שמצביע לתוך המנהר. חלוקת עבודה מהירה: שם שנפתר במארח אבל לא במכולה הוא בעיית פותר, בעוד חיבור IP ישיר שנתקע במכולה אבל עובד במארח הוא בעיית מסלול או שרשרת העברה.
WSL ו-Docker Desktop לא הולכים לפי כללי Linux
במצב NAT המוגדר כברירת מחדל, WSL יושב מאחורי המתאם הווירטואלי שלו ולא מקבל את המסלולים הפנימיים או סיומות ה-DNS של ה-VPN של Windows, ולכן הדפדפן עובד ו- ping בתוך ההפצה לא. רשת מראה, הזמינה בגרסאות Windows 11 אחרונות, משתפת במקום זאת את ממשקי המארח, אבל צריך להפעיל אותה ב- .wslconfig ולהפעיל מחדש את ההפצה עם wsl --shutdown. אשרו את המצב הפעיל עם wslinfo --networking-mode לפני יישום תיקון, כי תשובת ה-NAT שגויה למצב מראה וההפך.
Docker Desktop מוסיף שכבה נוספת. מכולות Linux ב-Windows יושבות מאחורי מימוש NAT שנקרא היסטורית vpnkit, והנתיב שלהן שונה ממארח Linux ילידי. מכולות Windows מתחברות דרך מתג וירטואלי של Hyper-V, ולקוח ארגוני יכול לחסום תעבורה מהמתג הזה, כך שמכולות Linux באותו מחשב יכולות להגיע לרשת בעוד שלמכולות Windows אין כלום. מתג חיצוני המחובר למתאם הפיזי הוא הפתרון המקובל, והוא משנה את זהות הרשת של המכולה בדרכים שהמדיניות אולי לא מאפשרת.
לפני שתכתבו כלל נוסף, עברו את ארבע השכבות לפי הסדר ועצרו היכן שאחת מהן שגויה:
- בתוך המכולה: האם
ip routeמגיע לקידומת הפרטית, והאם/etc/resolv.confנותן שם ל-upstream שפותר אותו? - במארח: האם ההעברה פועלת, האם NAT קשור לממשק המנהר, והאם שרשרת ההעברה מקבלת גשר למנהר?
- בעמית: האם הוא מכיר מסלול חזרה, או שאתם מסתמכים על NAT כדי להסתיר את רשת המשנה של המכולה?
- בפלטפורמה: האם WSL במצב NAT או מראה, והאם המכולה היא Linux או Windows?
הריצו שוב כל בדיקה במכולה או ב-shell חדשים. תהליך ארוך טווח עם תשובה שמורה במטמון או חיבור מבוסס ימשיך להיכשל אחרי שהתצורה נכונה, והכשל הזה נראה בדיוק כמו תיקון שלא עבד.