ทำไมคิวรี dnsmasq ถึงออกนอก VPN หลังการเปลี่ยนเส้นทาง DNS
REDIRECT ฝั่งเราเตอร์เปลี่ยนคิวรี DNS ของ LAN ให้เป็นซ็อกเก็ตใหม่ของตัวแก้ไข เครื่องหมายบนโฟลว์ของไคลเอนต์ไม่เคยไปถึงที่นั่น จึงต้องกำหนดทางออกต้นน้ำโดยตรง
ไคลเอนต์ใน LAN ไปถึง VPN ของตนได้อย่างถูกต้อง แต่คิวรี DNS ของมันกลับปรากฏบนลิงก์ขาขึ้นที่ผิด นี่ไม่ใช่การรั่วไหลในความหมายปกติ มันคือสิ่งที่เกิดขึ้นเมื่อเราเตอร์หยุดส่งต่อคิวรีและตอบเอง การดักจับเขียนปลายทางใหม่เป็นซ็อกเก็ตในเครื่อง จากนั้นตัวแก้ไขจะเปิดบทสนทนาที่สองที่ไม่เกี่ยวข้องกัน โดยมีที่อยู่ต้นทางของตัวเองและการตัดสินใจเรื่องเส้นทางของตัวเอง เครื่องหมายที่ใส่บนโฟลว์ของไคลเอนต์อธิบายบทสนทนาแรกและไม่บอกอะไรเลยเกี่ยวกับบทสนทนาที่สอง
เครื่องหมายไม่เดินทางไปกับคิวรี
เครื่องหมายแพ็กเก็ตอาศัยอยู่บนรายการ conntrack และบนแพ็กเก็ตที่กำลังเดินทาง กฎ REDIRECT ยุติการเดินทางนั้น เพย์โหลดถูกส่งมอบให้ซ็อกเก็ตในเครื่อง และรายการเดิมได้ทำหน้าที่ของมันเสร็จแล้ว สิ่งที่ออกจากเราเตอร์หลังจากนั้นคือซ็อกเก็ตใหม่ที่สร้างโดยโปรเซสตัวแก้ไข โดยมีที่อยู่ของเราเตอร์เองเป็นต้นทาง
นั่นคือเหตุผลที่เส้นทางนโยบายซึ่งผูกกับที่อยู่ของไคลเอนต์ใน LAN ไม่เคยตรงกับคิวรีต้นน้ำ ซ็อกเก็ตใหม่เป็นของ dnsmasq ไม่ใช่ของไคลเอนต์ และเคอร์เนลประเมินเส้นทางสำหรับทราฟฟิกของเราเตอร์เอง กฎต้นทาง กฎอินเทอร์เฟซ และกฎเครื่องหมายต่างเห็นทูเพิลคนละแบบ การสมมติว่าเครื่องหมายแพร่ไปด้วยเป็นข้อผิดพลาดที่พบบ่อยที่สุดในการตั้งค่านี้ และมองไม่เห็นในการดักจับฝั่งไคลเอนต์
บทสนทนาทั้งสองใช้ชื่อและเวลาเดียวกันจริง ๆ และนั่นคือสิ่งที่ทำให้มันดูเหมือนโฟลว์เดียวเมื่อคุณกวาดตาดูการดักจับ
จุดที่การส่งต่อกลายเป็นการแก้ไขในเครื่อง
เริ่มจากการหาว่าพอร์ต 53 ถูกเปลี่ยนเส้นทาง ถูกแปลไปยังโฮสต์อื่น หรือถูกปล่อยไว้เฉย ๆ เพราะทั้งสามกรณีทำงานต่างกัน การเปลี่ยนเส้นทางไปยังพอร์ตในเครื่องยุติการส่งต่อ ขณะที่การแปลไปยังเครื่องอื่นทำให้แพ็กเก็ตยังคงเคลื่อนที่และทำให้ทูเพิลของไคลเอนต์ยังมีความหมาย
ดูทั้งสองโปรโตคอล ตัวแก้ไขที่เข้าถึงได้ทั้ง 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'
ผลลัพธ์เหล่านั้นตอบสามคำถามตามลำดับ อะไรเขียนคิวรีใหม่ อะไรรับมัน และโปรเซสใดเป็นเจ้าของซ็อกเก็ตที่รอฟัง เมื่อรู้เจ้าของแล้ว นโยบายต้นน้ำก็มีที่อยู่อาศัย
ให้ตัวแก้ไขมีนโยบายทางออกของตัวเอง
ต้นน้ำที่เจาะจงโดเมนเป็นเครื่องมือที่สะอาดที่สุดเมื่อมีเพียงบางชื่อที่ต้องใช้ทันเนล dnsmasq ยอมรับบรรทัด server ที่จำกัดขอบเขตไว้ที่โดเมน พร้อมอินเทอร์เฟซหรือที่อยู่ต้นทางแบบเลือกได้ซึ่งนำคิวรีออกจากอุปกรณ์ที่เลือก การนำทางนั้นคือสิ่งที่ทำให้บทสนทนาที่สองตาม VPN ไป
server=/corp.example/10.30.0.53@tun_work
log-queries
โดเมน ที่อยู่ และอินเทอร์เฟซข้างต้นเป็นตัวแทนสำหรับค่าของคุณเอง การเลือกโดเมนและการควบคุมทางออกยังเป็นการตัดสินใจแยกกัน เส้นทางต้องมีอยู่ ที่อยู่ต้นทางต้องได้รับอนุญาตจากตัวแก้ไขต้นน้ำ และไฟร์วอลล์ต้องอนุญาตคำตอบ การนำทางด้วยอินเทอร์เฟซอย่างเดียวไม่ใช่ fail-closed เพราะอุโมงค์ที่หายไปอาจทำให้ตัวแก้ไขตกลงไปยังเส้นทางอื่น เก็บทางเลือกสำรองสาธารณะใด ๆ ให้ห่างจากเนมสเปซที่ต้องคงเป็นส่วนตัว
ใส่บรรทัดนั้นในเจ้าของการตั้งค่าที่เราเตอร์รองรับ ไม่ใช่ในไฟล์ที่ถูกสร้างขึ้น ไม่อย่างนั้นการโหลดใหม่ครั้งถัดไปจะลบการแก้ไข
สิ่งที่แคชที่ใช้ร่วมกันรักษาไว้ไม่ได้
ตัวส่งต่อตัวเดียวที่มีแคชเดียวให้บริการไคลเอนต์ทุกตัวใน LAN และนั่นทำให้ความแตกต่างที่บางเครือข่ายต้องการพังทลายลง หากไคลเอนต์สองตัวต้องแก้ชื่อเดียวกันต่างกัน ซ็อกเก็ตที่ใช้ร่วมกันก็ไม่มีทางรู้ว่าไคลเอนต์ใดเป็นผู้ถาม นโยบายต่อไคลเอนต์ต้องอยู่ในอินสแตนซ์ของตัวแก้ไขที่แยกจากกัน หรือในฟีเจอร์การส่งต่อที่รักษาตัวตนไว้
คำตอบที่แคชไว้ก็ไม่ใช่หลักฐานเกี่ยวกับเส้นทางใหม่ คำตอบอาจมาจากหน่วยความจำในขณะที่เส้นทางต้นน้ำยังคงเสีย
กฎของพอร์ต 53 ไม่ได้บอกอะไรเกี่ยวกับแอปพลิเคชันที่พูด DNS ผ่าน HTTPS หรือผ่าน TLS คิวรีเหล่านั้นไม่เคยไปถึงการเปลี่ยนเส้นทางเลย ดังนั้น นโยบายที่สร้างขึ้นบนพอร์ต 53 เท่านั้นจึงอธิบายเพียงสับเซตของทราฟฟิก และควรบันทึกไว้ในฐานะสับเซต
ตรวจสอบบทสนทนาที่สอง ไม่ใช่บทสนทนาแรก
ทดสอบด้วยชื่อหนึ่งชื่อที่คุณควบคุมและดูทั้งสองด้าน การดักจับฝั่งไคลเอนต์พิสูจน์ว่าคิวรีออกจาก LAN แล้ว มีเพียงฝั่งเราเตอร์เท่านั้นที่พิสูจน์ว่ามันไปที่ไหนต่อ
sudo tcpdump -ni tun_work udp port 53
ip route get 10.30.0.53
dig +short corp.example @10.30.0.53
ล้างแคชก่อนทุกความพยายาม เพราะคำตอบที่อุ่นอยู่อาจซ่อนต้นน้ำที่เสีย ทำซ้ำผ่าน TCP จากนั้นทำซ้ำโดยปิดอุโมงค์เพื่อดูว่าตัวแก้ไขทำอะไรเมื่อเส้นทางที่ต้องการหายไป พฤติกรรมสุดท้ายนั้นคือสิ่งที่ตัดสินว่าการออกแบบเป็น fail-closed หรือแค่โชคดี