איך לשלוח רק תת-רשת פרטית אחת דרך ה-VPN
בחרו בין נתיב בלקוח, נתיב שהשרת דוחף ובוררים מנוהלים, ואז העבירו תת-רשת פרטית אחת בלי מנהרה מלאה.
תת-רשת פרטית אחת דרך המנהרה וכל השאר במסלול הרגיל שלכם היא בקשה קטנה עם הרבה מאוד דרכים לטעות בה. מה שקובע את התוצאה אינו תיבת הסימון בשם split tunneling, אלא היכן ההחלטה נאכפת: טבלת הנתיבים של הלקוח עצמו, נתיב שהשרת דוחף, או מדיניות ששני הקצוות מנהלים משא ומתן עליה. בחרו בשכבה הלא נכונה ותקבלו או מנהרה מלאה שלא רציתם, או נתיב לתוך מנהרה שהצד השני מסרב לקבל. אני משתמש במבחן של שלושה תנאים לפני שאני פותח קונפיגורציה כלשהי, ואז בוחר באחד מהמנגנונים שלמטה.
שלושה תנאים שקובעים את התכנון
ראשית, אתם צריכים את הקידומת והמסכה המדויקות מבעל הרשת. רשת 10.x אינה קידומת; 10.20.0.0/16 היא קידומת. ודאו שהיא אינה חופפת ל-LAN הביתי שלכם, לגשר Docker או ל-VPN אחר שאתם מריצים, כי חפיפה הופכת את הנתיב לדו-משמעי והקרנל בוחר את הרשומה שלא התכוונתם אליה.
שנית, מי עונה לתעבורה החוזרת. ניתוב חבילה לתוך המנהרה רק מביא אותה לשרת ה-VPN. אם תת-הרשת נמצאת מאחורי נתב אחר שאין לו נתיב חזרה למאגר הלקוחות שלכם, השרת חייב לבצע מסכור של התעבורה, או שאותו נתב חייב ללמוד את המאגר שלכם. שום נתיב בצד הלקוח לא מתקן מסלול חזרה חסר.
שלישית, נקודת האכיפה. לקוחות מסוימים מתקינים נתיב ומערכת ההפעלה עושה את השאר. אחרים מנהלים משא ומתן על מדיניות, ומדיניות שהשער אינו מקבל אינה מייצרת מנהרה כלל: חבילות נזרקות בלי שגיאה ברורה. גלו איזה משני המקרים הוא שלכם לפני שתכתבו משהו.
WireGuard: AllowedIPs היא טבלת הנתיבים
בלקוח, AllowedIPs מפרט את היעדים שמותר להם להיכנס למנהרה, ו-wg-quick הופך כל רשומה לנתיב רגיל. לכן חצי הלקוח של המשימה הזו הוא שורה אחת.
[Peer]
PublicKey = ...
Endpoint = vpn.example.net:51820
AllowedIPs = 10.20.0.0/16עכשיו השוו את הרשומה של השרת עבור אותו לקוח. היא מתארת מה השרת ישלח ללקוח, וזה בדרך כלל כתובת המנהרה של הלקוח, לא תת-הרשת הפרטית.
[Peer]
PublicKey = ...
AllowedIPs = 10.99.0.2/32החלפה בין השניים היא הטעות שאני רואה הכי הרבה. הצבה של 10.20.0.0/16 תחת סעיף ה-peer של השרת מצהירה שכל תת-הרשת נמצאת מאחורי אותו לקוח, וזו טענה של אתר לאתר ולא הרשאה. חבילות שאינן תואמות ל-AllowedIPs של אף peer נזרקות בשקט, כך שרשומה שגויה נראית בדיוק כמו בעיית חומת אש.
אם אתם מנהלים את הנתיבים בעצמכם, הגדירו Table = off תחת [Interface] והוסיפו את הנתיב הרצוי. אז שום דבר לא מתווסף אוטומטית, ולכן הנתיב שייך למה שמעלה את הממשק.
OpenVPN: דחפו את הנתיב ואז ודאו שהוא נוחת
בשרת רגיל השינוי הוא הנחיה אחת בקונפיגורציה של השרת, לצד מאגר הלקוחות.
push "route 10.20.0.0 255.255.0.0"הלקוח מוסיף אותו כשהוא מתחבר. מצב הכשל מופיע כשהפרופיל עצמו דוחף גם redirect-gateway, או כשמישהו כבר הוסיף route-nopull: הראשון הופך את החיבור למנהרה מלאה, והשני משליך כל נתיב שנדחף, כולל שלכם.
לקוח שזקוק להתנהגות צרה מול שרת במנהרה מלאה צריך להתעלם רק מהחלקים שהוא לא רוצה. ב-OpenVPN 2.4 ואילך, pull-filter קיים בדיוק בשביל זה.
pull-filter ignore "redirect-gateway"
route 10.20.0.0 255.255.0.0הפרידו בין משפחות כתובות: נתיב IPv4 שנדחף לא עושה דבר לשירות פנימי שמתפרש ל-IPv6, והשירות הזה נכשל דרך המנהרה בזמן שכל השאר נראה תקין.
כשתת-הרשת הפרטית אינה על שרת ה-VPN אלא מאחורי לקוח אחר, צריך ליידע את השרת. הוסיפו קובץ לכל לקוח דרך client-config-dir שמכיל iroute 10.20.0.0 255.255.0.0, יחד עם route המתאים כדי שלקוחות אחרים ילמדו את המסלול. בלי iroute אין לשרת סיבה לשלוח את הקידומת הזו דווקא דרך המנהרה ההיא.
IPsec: בוררי התעבורה חייבים להסכים
ב-IPsec אין נתיב שדוחפים. שני הקצוות מנהלים משא ומתן על אילו כתובות נמצאות בתוך המנהרה, ותעבורה מחוץ לבוררים המוסכמים אינה מוצפנת ואינה נשלחת דרכה. בלקוח strongSwan הבוררים מפורשים.
leftsubnet=192.168.1.0/24
rightsubnet=10.20.0.0/16אם השער מוגדר עם 0.0.0.0/0 בשני הצדדים, הצמצום של IKEv2 מקטין בדרך כלל את ה-SA הבן לחתך של שתי ההצעות, וזו התוצאה הרצויה. חלק מהשערים דווקא דוחים הצעה שאינה תואמת לבוררים המוגדרים שלהם, והלוג אז מציג SA בן שנכשל ולא תלונת ניתוב. כשזה קורה השינוי שייך לשער, כי נתיב בלקוח לא יכול ליצור מנהרה לבורר שהצד השני לא יקבל.
ב-Windows הלקוח המובנה מסתיר את אותה החלטה מאחורי שתי הגדרות. פיצול המנהרה חייב להיות דלוק, והקידומת חייבת להירשם בפרופיל.
Set-VpnConnection -Name Office -SplitTunneling $true
Add-VpnConnectionRoute -ConnectionName Office -DestinationPrefix 10.20.0.0/16רישום הקידומת הוא מה שמאפשר לה לשרוד התחברות מחדש, דבר שנתיב שהוסף ביד לא עושה.
השאירו את נקודת הקצה וה-DNS מחוץ להחלטה
נקודת הקצה של ה-VPN חייבת להישאר נגישה בלי המנהרה, אחרת המנהרה קורסת לתוך עצמה. כשמנותבים רק את 10.20.0.0/16 זה אוטומטי, מפני שנקודת הקצה היא כתובת ציבורית. בפרופיל של מנהרה מלאה זה לא כך, והלקוח זקוק ל-נתיב מארח מפורש לנקודת הקצה לפני שהמנהרה עולה.
DNS הוא החלטה שנייה שדחיפת נתיב לא מקבלת בשבילכם. שמות פנימיים מתפרשים בדרך כלל דרך פותר שרק הרשת הפרטית יכולה להגיע אליו. אם השרת דוחף שרת DNS, כל שם עובר דרכו; אם לא, שמות פנימיים או נכשלים או מתפרשים פומבית למשהו שלא התכוונתם אליו. DNS מפוצל מחזיק דומיין חיפוש אחד בפותר הפנימי, אבל זה תלוי בתמיכת הלקוח, אז התייחסו לזה כמשימה בפני עצמה ולא כתופעת לוואי של הניתוב.
אמתו, ואז אתחלו ואמתו שוב
שלוש בדיקות מכסות את שאלת הניתוב. שאלו את הקרנל לאן תלך כתובת מרוחקת, משכו משהו מתוך תת-הרשת, ואז ודאו שבקשה ציבורית רגילה עדיין יוצאת דרך החיבור הרגיל שלכם.
ip route get 10.20.5.7
curl -sS http://10.20.5.7/health
curl -sS https://ifconfig.meהשורה הראשונה מציינת ממשק. אם היא מציינת את הממשק הפיזי שלכם, הנתיב לא נחת מעולם. ב-Windows Find-NetRoute -RemoteIPAddress 10.20.5.7 עונה על אותה שאלה, והבדיקה האחרונה אמורה עדיין להציג את הכתובת הרגילה שלכם ולא את זו של שרת ה-VPN.
אחר כך אתחלו. נתיבים שהוספו ביד נעלמים, בעוד נתיבים מהפרופיל, מקונפיגורציה שנדחפה או מ-AllowedIPs חוזרים. אם גישה צרה עובדת לפני האתחול ונכשלת אחריו, התקלה היא באופן שבו המנהרה עולה, לא בנתיב עצמו.
לפני שאתם קוראים לזה גמור, ודאו ארבעה דברים: הקידומת הגיעה מבעל הרשת ואינה חופפת לשום דבר מקומי, מסלול החזרה קיים או שכלל מסכור מחליף אותו, המנגנון שהגדרתם הוא זה שאוכף ניתוב בפלטפורמה שלכם, ואותן בדיקות עוברות אחרי אתחול נקי. ההערות של strongSwan על העברה שוות קריאה אם מסלול החזרה הוא החלק שממשיך להיכשל.