לקוח הרשת המקומית מגיע ל־VPN שלו כהלכה, ובכל זאת שאילתות ה־DNS שלו מופיעות על הקישור העולה הלא נכון. זו אינה דליפה במובן הרגיל; זה מה שקורה כשהנתב מפסיק להעביר שאילתה ועונה עליה בעצמו. היירוט משכתב את היעד לסוקט מקומי, ואז הפותר פותח שיחה שנייה, לא קשורה, עם כתובת המקור שלו ועם החלטת הניתוב שלו. הסימונים שהוחלו על זרימת הלקוח מתארים את השיחה הראשונה ושום דבר על השנייה.

הסימון לא נוסע עם השאילתה

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

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

שתי השיחות כן חולקות שם ותזמון, וזה מה שגורם להן להיראות כמו זרימה אחת כשמעיפים מבט בלכידה.

היכן ההעברה הופכת לפתרון מקומי

התחילו בכך שתבררו אם פורט 53 מופנה, מתורגם למארח אחר או נשאר כפי שהוא, מפני ששלושת המקרים מתנהגים אחרת. הפניה לפורט מקומי מסיימת את ההעברה, ואילו תרגום למכונה אחרת משאיר את המנה בתנועה ומשאיר את ה־tuple של הלקוח בעל משמעות.

הסתכלו על שני הפרוטוקולים. פותר שנגיש ב־UDP וב־TCP ייבדק בשניהם, וסט כללים שמכסה רק UDP משאיר חור שקט לתשובות גדולות. גם כללים שמנהל התצורה של הנתב עצמו מייצר נחשבים, ועריכת קובץ מיוצר מפסידה בדרך כלל לטעינה מחדש הבאה.

sudo nft list ruleset | grep -n -B2 -A4 'dport 53'
sudo iptables -t nat -L -n -v | grep -E ':53|dns'
sudo ss -lunp | grep ':53'

הפלטים האלה עונים על שלוש שאלות לפי הסדר: מה משכתב את השאילתה, מה מקבל אותה, ואיזה תהליך מחזיק בסוקט המקשיב. לאחר שידוע הבעלים, למדיניות ה־upstream יש מקום לחיות בו.

תנו לפותר מדיניות יציאה משל עצמו

upstream ייעודי לתחום הוא הכלי הנקי ביותר כשנדרשת מנהרה רק לחלק מהשמות. dnsmasq מקבל שורת server המוגבלת לתחום, עם ממשק או כתובת מקור אופציונליים שמפנים את השאילתה אל מחוץ להתקן נבחר. ההיגוי הזה הוא שמביא את השיחה השנייה ללכת אחרי ה־VPN.

server=/corp.example/10.30.0.53@tun_work
log-queries

התחום, הכתובת והממשק שלמעלה הם מצייני מקום לערכים שלכם. בחירת התחום ובקרת היציאה נשארות החלטות נפרדות: הנתיב חייב להתקיים, כתובת המקור חייבת להיות מותרת על ידי פותר ה־upstream, והחומה חייבת לאפשר את התשובה. היגוי ממשק בלבד אינו fail-closed, מפני שמנהרה שנעלמה עלולה להשאיר את הפותר נופל לנתיב אחר. הרחיקו כל חלופה ציבורית מתחום שמות שחייב להישאר פרטי.

שימו את השורה אצל בעל התצורה שהנתב תומך בו ולא בקובץ מיוצר, אחרת הטעינה מחדש הבאה תמחק את התיקון.

מה מטמון משותף אינו יכול לשמר

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

תשובה שמורה אינה ראיה לגבי הנתיב החדש. התשובה יכולה להגיע מהזיכרון בעוד נתיב ה־upstream נשאר שבור.

כללי פורט 53 לא אומרים דבר על יישומים שמדברים DNS מעל HTTPS או מעל TLS. השאילתות האלה לא מגיעות לעולם אל ההפניה, לכן מדיניות שנבנתה רק על פורט 53 מתארת תת־קבוצה של התעבורה ויש לתעד אותה כתת־קבוצה.

אמתו את השיחה השנייה, לא את הראשונה

בדקו עם שם אחד שאתם שולטים בו והביטו בשני הצדדים. לכידה בצד הלקוח מוכיחה שהשאילתה עזבה את הרשת המקומית; רק צד הנתב מוכיח לאן היא הלכה לאחר מכן.

sudo tcpdump -ni tun_work udp port 53
ip route get 10.30.0.53
dig +short corp.example @10.30.0.53

רוקנו את המטמון לפני כל ניסיון, מפני שתשובה חמה מסתירה upstream שבור. חזרו על הבדיקה ב־TCP, ואז חזרו עליה כשהמנהרה כבויה כדי לראות מה הפותר עושה כשהנתיב המועדף שלו נעלם. ההתנהגות האחרונה הזו היא שקובעת אם התכנון fail-closed או רק בר מזל.

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