המחשב הנייד שלכם מחובר ל-VPN, dig service.internal.example משיב בכתובת 10.x, ו-curl עדיין מסתיים ב-Could not resolve host. המנהרה אינה הבעיה: היא העבירה את שאילתת dig בלי תלונות. ההבדל הוא מי שואל את השאלה. dig שולח שאילתה אחת לשרת אחד שאתם נוקבים בשמו, ואילו curl עובר בשרשרת החיפוש של מערכת ההפעלה, ולשרשרת הזו יש כללים משלה לגבי איזה שרת נשאל.

שתי פתירות לאותו שם

dig הוא stub resolver משל עצמו. הוא קורא את /etc/resolv.conf, בוחר שרת שמות, שולח שאילתת A או AAAA אחת ומדפיס את התשובה. הוא מדלג לגמרי על nsswitch ואינו יודע דבר על המצב לפי קישור ש-systemd-resolved או NetworkManager מחזיקים מחוץ לקובץ הזה, ולכן תשובת dig יכולה להיות נכונה ובכל זאת לא לומר לכם דבר על מה שהאפליקציה שלכם תראה.

אפליקציות קוראות ל-getaddrinfo, ו-getaddrinfo עובר בשרשרת ארוכה יותר: סדר המודולים של nsswitch, אחר כך המודול שמטפל בשם, ולבסוף ה-resolver שנבנה מהתצורה הנוכחית של הקישור. במחשב systemd השאילתה הולכת אל 127.0.0.53 ו-systemd-resolved בוחר שרת במעלה הזרם לכל קישור לפי דומיין הניתוב שמקושר לממשק tun.

שתי פקודות מפרידות בין השכבות האלה בלי לנחש:

getent ahosts service.internal.example
resolvectl query service.internal.example

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

קראו את ה-resolver שהאפליקציה באמת משתמשת בו

/etc/resolv.conf נוצר ברוב שולחנות העבודה, ולקוח VPN יש שלוש דרכים להתקשר איתו: להוסיף שרת שמות בהתחלה, לדרוס את הקובץ, או למסור את השרתים שלו ל-NetworkManager ולתת לדמון לשכתב אותו. רק התבנית השלישית משמרת DNS לפי קישור, משום שהשרתים נשארים מקושרים לממשק במקום להיות שטוחים לרשימה גלובלית אחת.

במערכות NetworkManager התוצאה תלויה ב-backend שבקובץ /etc/NetworkManager/NetworkManager.conf:

[main]
dns=systemd-resolved

עם dns=default השרתים של הלקוח ממוזגים ל-resolv.conf לפי הסדר שמנצח, ושאילתה פרטית עדיין עלולה להישלח ל-resolver הציבורי. עם backend שמבין קישורים, systemd-resolved או dnsmasq, השרתים של המנהרה נשארים מוגבלים למנהרה ורק שמות שמתאימים לדומיין הניתוב שלה הולכים לשם. ההגבלה הזו היא מה ש-split DNS אומר בפועל, וזה החלק שלרוב חסר כשלקוח טוען שהוא תומך בו.

קראו את המצב לפני שאתם משנים משהו:

resolvectl status
resolvectl dns tun0

אתם רוצים לראות את ממשק tun רשום עם שרת DNS ועם דומיין ניתוב לסיומת הפרטית, מוצגים כ-~internal.example. אין שרת בקישור פירושו שהלקוח לא דחף דבר. שרת בלי דומיין פירושו שהשאילתות או כולן הולכות לשם או שאף אחת לא, בהתאם ללקוח, ושניהם אינם split DNS.

איפה nsswitch מסיים את החיפוש מוקדם מדי

שורת שולחן עבודה סטנדרטית נראית כך:

hosts: files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns myhostname

mdns4_minimal מטפל ב-multicast DNS ותובע כל שם שמסתיים ב-.local. שירות Kubernetes כמו api.infra.svc.cluster.local מסתיים גם הוא ב-.local. כשמודול mDNS מדווח NOTFOUND, הפעולה [NOTFOUND=return] מסיימת את כל החיפוש במקום להמשיך למודול dns, ולכן השם לעולם לא נשלח ל-resolver של ה-VPN. dig עוקף את הקובץ לגמרי, וזו בדיוק הסיבה ששני הכלים אינם מסכימים.

זהו הניגוד החריף ביותר בבעיה הזו, והוא מצטמצם למילה אחת בקובץ תצורה ישן מה-VPN שלכם. שינוי הפעולה ל-[NOTFOUND=continue] משמר mDNS למדפסות ולמכשירים מקומיים תוך מתן אפשרות ל-DNS לראות את השם. החלפת השורה ב-hosts: files dns גם עובדת והיא גסה יותר: היא מוותרת על פתרון .local עבור המדפסת שבה אתם באמת משתמשים.

שמות שחיים ברשת אחת בדיוק

אזורים פרטיים סמכותיים במקום אחד בדיוק. שם AWS כמו ip-10-0-4-31.eu-west-1.compute.internal נענה דרך ה-resolver של ה-VPC של אותו אזור, ורק כל עוד נקודת הקצה של ה-VPN שלכם נמצאת באותו אזור כמו המשאב. התחברו לנקודת קצה בפרנקפורט, שאלו שם compute.internal מאירלנד, ואף הגדרה מקומית לא תעזור: האזור שמחזיק ברשומה אינו נגיש משם.

דומיינים לחיפוש יוצרים את התמונה ההפוכה. service חשוף מושלם בסיומת שה-resolver מחשיב כרלוונטית, ועם קישור VPN הדומיין המקומי יכול לנצח את זה הארגוני. שאלו עם נקודה בסוף כדי להסיר את הניחוש, getent ahosts service.internal.example., ואם זה עובד בזמן שהשם הקצר נכשל, התיקון הוא דומיין החיפוש במנהרה, ולא כתובת השרת.

לקוחות מנוהלים מוסיפים שכבה שלישית מעל ה-resolver של המערכת. פרופיל VPN של Windows יכול לשאת מדיניות פתרון שמות משלו, כללים לכל אפליקציה וטבלת NRPT שחלה גם כשהמנהרה למטה. אפליקציות עם מטמון DNS משלהן, נפוצות בסביבות .NET ו-Java, מחזיקות כשל מיושן הרבה אחרי שהשרשרת תוקנה. לקוחות Apple מגדירים split DNS דרך דומיינים תואמים בהגדרות ה-DNS ולא דרך resolv.conf, ו-Chromium או Firefox במצב DNS מאובטח עשויים לעקוף את ה-resolver של המערכת עבור שמות ציבוריים. תיעוד Microsoft לפתרון שמות ב-VPN מתאר את הצד של Windows בשכבת המדיניות הזו.

החליטו איזו שכבה לתקן

עבדו מהתסמין אל השכבה במקום לנסות תיקונים לפי הסדר:

SymptomWhat it meansCheck first
dig answers, getent failslookup chain, not the packet pathnsswitch line and resolvectl status
Timeout rather than NXDOMAINprivate resolver unreachable through the tunnelresolvectl dns tun0, route to that address
NXDOMAINan authority answered and does not know the namezone owner, endpoint region
FQDN works, short name failssearch suffix mismatchresolvectl domain on the tun link
getent works, app failsapp cache or its own resolverrestart the app, check secure DNS

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

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

תעדו את המצב לפני שאתם עורכים קובץ תצורה, כדי שתוכלו לומר אם העריכה עשתה משהו:

resolvectl status > before.txt
getent ahosts service.internal.example >> before.txt

הריצו את הבדיקות בסדר הזה ועצרו בראשונה שנכשלת. getent ו-resolvectl query חייבים להסכים; אם getent נכשל בזמן ש-dig משיב, הישארו בשרשרת החיפוש ואל תיגעו בניתוב. resolvectl status חייב להראות שרת DNS ודומיין ניתוב בממשק tun. nsswitch אסור שיחזור מוקדם עם NOTFOUND עבור הסיומת שאתם בודקים. השם חייב להיות סמכותי ברשת שאליה מחוברת נקודת הקצה שלכם, נבדק עם נקודה בסוף. האפליקציה אסור שתחזיק מטמון משלה או עקיפה של DNS מאובטח.

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

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