لپ‌تاپ شما روی 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 به 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 آن منطقه پاسخ داده می‌شود، و تنها تا وقتی نقطه پایانی VPN شما در همان منطقه‌ای است که منبع در آن قرار دارد. به نقطه پایانی در فرانکفورت وصل شوید، نامی compute.internal از ایرلند بپرسید، و هیچ تنظیم محلی کمکی نمی‌کند: زونی که رکورد را دارد از آنجا در دسترس نیست.

دامنه‌های جست‌وجو تصویر آینه‌ای می‌سازند. service خالی با پسوندی که resolver آن را قابل اعمال می‌داند کامل می‌شود، و با پیوند VPN دامنه محلی می‌تواند بر دامنه شرکتی چیره شود. با نقطه‌ای در پایان بپرسید تا حدس حذف شود، getent ahosts service.internal.example.، و اگر این کار می‌کند در حالی که نام کوتاه شکست می‌خورد، اصلاح دامنه جست‌وجو روی تونل است، نه نشانی سرور.

کلاینت‌های مدیریت‌شده لایه سومی بالای resolver سیستم اضافه می‌کنند. یک پروفایل VPN ویندوز می‌تواند سیاست حل نام خودش، قواعد هر برنامه و جدول NRPT را با خود بیاورد که حتی وقتی تونل خاموش است اعمال می‌شود. برنامه‌هایی با کش DNS خودشان، که در محیط‌های .NET و Java رایج‌اند، یک شکست کهنه را مدت‌ها پس از اصلاح زنجیره نگه می‌دارند. کلاینت‌های Apple ویژگی split DNS را از طریق دامنه‌های تطبیق در تنظیمات DNS و نه از طریق resolv.conf تنظیم می‌کنند، و Chromium یا Firefox در حالت DNS امن می‌توانند resolver سیستم را برای نام‌های عمومی دور بزنند. مرجع Microsoft درباره حل نام VPN سمت ویندوز آن لایه سیاست را توضیح می‌دهد.

تصمیم بگیرید کدام لایه را اصلاح کنید

از نشانه به لایه پیش بروید به جای آزمودن اصلاح‌ها به ترتیب:

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 امن را نگه دارد.

تنها نخستین لایه شکست‌خورده را اصلاح کنید، سپس همان فرمان‌ها را دوباره اجرا کنید. دو تغییر همزمان نمی‌گذارد بگویید کدام مؤثر بود، و خرابی بعدی بررسی را از صفر آغاز می‌کند.

مقالات مرتبط