แล็ปท็อปของคุณอยู่ใน 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 ปลอดภัย

แก้เฉพาะชั้นแรกที่ล้มเหลว แล้วรันคำสั่งเดิมอีกครั้ง การเปลี่ยนสองอย่างพร้อมกันทำให้คุณบอกไม่ได้ว่าอย่างไหนสำคัญ และการขัดข้องครั้งถัดไปจะเริ่มสอบสวนจากศูนย์

บทความที่เกี่ยวข้อง