กำหนดเส้นทางทราฟฟิกของโฮสต์ผ่าน VPN ใน VM: หกจุดตรวจ
หกจุดตรวจตามลำดับเพื่อทำให้ VM เป็นเกตเวย์ VPN ที่โฮสต์ของตัวเองกำหนดเส้นทางผ่าน: โหมดอะแดปเตอร์ การส่งต่อ NAT เส้นทางของโฮสต์ DNS MTU
หกจุดตรวจที่ทำตามลำดับเป็นตัวตัดสินว่า VM จะทำหน้าที่เป็นเกตเวย์ VPN ให้โฮสต์ของตัวเองได้หรือไม่ ไฮเปอร์ไวเซอร์ไม่ทำสิ่งเหล่านี้ให้คุณเลย: เกสต์เป็นเจ้าของอุโมงค์ โฮสต์เป็นเจ้าของเส้นทาง และเครือข่ายระหว่างทั้งสองเป็นการกำหนดเส้นทางธรรมดา ผมเก็บรายการนี้ไว้เพราะการข้ามขั้นตอนหนึ่งจะให้อาการเดิมเสมอ คือความเงียบ ที่โฮสต์ปิงเกสต์ได้สบาย ๆ แต่ไปไม่ถึงอะไรภายในเลย
เริ่มจากเหตุผล ไม่ว่าคลไคลแอนต์องค์กรจะติดตั้งได้เฉพาะในเกสต์ หรือนโยบายบังคับให้อยู่ที่นั่น และคุณยังต้องเข้าถึงเซิร์ฟเวอร์บิลด์ภายในจากโฮสต์ ถ้าโฮสต์ได้รับอนุญาตให้มีไคลแอนต์ของตัวเอง ก็ทำแบบนั้นดีกว่า: อุโมงค์เดียวบนโฮสต์ชนะสองเครือข่ายบวก NAT ทุกครั้ง
จุดตรวจ 1: โฮสต์ต้องมีลิงก์ที่กำหนดที่อยู่ให้ได้
โฮสต์ต้องมีอินเทอร์เฟซที่อยู่ในพรีฟิกซ์เดียวกับอะแดปเตอร์ตัวที่สองของเกสต์ host-only ใน VirtualBox, host-only หรือ VMnet ที่กำหนดเองใน VMware Workstation, บริดจ์ภายในหรืออุปกรณ์ tap ใน QEMU/KVM และสวิตช์ Internal ใน Hyper-V ล้วนเข้าเกณฑ์ทั้งหมด เกสต์ 192.168.56.2 โฮสต์ 192.168.56.1 พรีฟิกซ์ 192.168.56.0/24 คือรูปแบบที่ผมใช้ในแล็บ
เครือข่ายโหมดผู้ใช้ของ QEMU (-netdev user แบ็กเอนด์ SLIRP ค่าเริ่มต้น) และ NAT ของ VirtualBox ตกจุดตรวจนี้โดยสิ้นเชิง แพ็กเก็ตของเกสต์ไปถึงโปรเซสพร็อกซีบนโฮสต์ โฮสต์จึงไม่เคยเห็นอุปกรณ์ที่ถือที่อยู่ของเกสต์และไม่มีอะไรให้ชี้เส้นทาง Default Switch ของ Hyper-V ก็เรื่องเดียวกัน ถ้า ping 192.168.56.2 จากโฮสต์ล้มเหลว ให้หยุด: เส้นทางที่เพิ่มทีหลังไม่มีการซ่อมได้
จุดตรวจ 2: เกสต์ส่งต่อและแปลง
แพ็กเก็ตจากโฮสต์มาพร้อมที่อยู่ต้นทางฝั่ง host-only และปลายทางภายใน เกสต์จึงต้องส่งต่อแล้วซ่อนไว้หลังที่อยู่อุโมงค์ของตัวเอง บนเกสต์ Linux นั่นหมายถึงการส่งต่อ คู่ FORWARD accept และ source NAT ไปยังอุโมงค์
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -A FORWARD -i eth1 -o tun0 -j ACCEPT
sudo iptables -A FORWARD -i tun0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 192.168.56.0/24 -o tun0 -j MASQUERADE
กฎ MASQUERADE คือส่วนที่คนมักละไว้ เราเตอร์องค์กรแทบไม่รู้ว่า 192.168.56.0/24 มีอยู่ ด้วยกฎนี้เซิร์ฟเวอร์ภายในจะเห็นที่อยู่อุโมงค์ของเกสต์และตอบกลับ และคำตอบก็หาทางกลับมาถึงเกสต์ได้
เกสต์ Windows งุ่มง่ามกว่า IPEnableRouter=1 ภายใต้ SYSTEM\CurrentControlSet\Services\Tcpip\Parameters เปิดการส่งต่อโดยไม่แปลง จึงใช้ได้เฉพาะที่การกำหนดเส้นทางองค์กรชี้กลับมาที่พรีฟิกซ์ host-only ของคุณอยู่แล้ว Internet Connection Sharing แปลงให้ได้ แต่จะเปลี่ยนหมายเลขอะแดปเตอร์ที่แชร์เป็น 192.168.137.1 ดังนั้นอ่านที่อยู่ที่มันได้มาจริง ๆ ก่อนเขียนเส้นทางของโฮสต์
จุดตรวจ 3: เส้นทางของโฮสต์ระบุชื่อเกสต์ ไม่ใช่อุโมงค์
ส่งเฉพาะพรีฟิกซ์ที่โฮสต์ต้องการผ่านเกสต์ บน Linux เส้นทางเดียวต่อหนึ่งพรีฟิกซ์ก็พอ และย้อนกลับได้สะอาด
sudo ip route add 10.20.0.0/16 via 192.168.56.2
ip route get 10.20.30.40
บน Windows ตัวเทียบเท่าคือ route add 10.20.0.0 mask 255.255.0.0 192.168.56.2 ปล่อย -p ไว้นอกระหว่างทดสอบ เพื่อให้เส้นทางที่ผิดตายเมื่อรีบูต แล้วค่อยเพิ่มเมื่อเส้นทางใช้งานได้ อย่าลบเส้นทางเริ่มต้นของโฮสต์ เว้นแต่ทุกแอปพลิเคชันบนโฮสต์ต้องใช้อุโมงค์ ถ้าต้องการฟูลทันเนลจริง ๆ ให้เพิ่มเส้นทาง /1 สองเส้นและเก็บเส้นทางตรงไปยังเครือข่ายไฮเปอร์ไวเซอร์ไว้
sudo ip route add 0.0.0.0/1 via 192.168.56.2
sudo ip route add 128.0.0.0/1 via 192.168.56.2
บนโฮสต์ Windows ให้ตรวจเมตริกของอินเทอร์เฟซด้วย เส้นทางที่เพิ่มตอนที่อะแดปเตอร์ host-only ถูกปิดจะยังอยู่ในตารางแต่ไม่เคยรับส่งทราฟฟิก และอะแดปเตอร์ตัวที่สองที่มีเมตริกต่ำกว่าอาจชนะการค้นหาสำหรับพรีฟิกซ์เดียวกัน
จุดตรวจ 4: DNS ทำตามเส้นทาง ไม่ใช่ค่าเริ่มต้นของอุโมงค์
เส้นทางย้ายแพ็กเก็ต ตัวแก้ชื่อเป็นตัวตัดสินว่าชื่อใดสร้างแพ็กเก็ต จำกัดตัวแก้ชื่อองค์กรไว้ที่โซนองค์กรแทนการแทนที่ตัวแก้ชื่อของโฮสต์ และชี้ไปยังเซิร์ฟเวอร์ DNS ที่เส้นทางใหม่เข้าถึงได้อยู่แล้ว ด้วย systemd-resolved บนโฮสต์
resolvectl dns eth1 10.20.0.53
resolvectl domain eth1 '~corp.example.com'
บน Windows รายการในตารางนโยบายแก้ชื่อทำหน้าที่เดียวกัน: Add-DnsClientNrptRule -Namespace ".corp.example.com" -NameServers 10.20.0.53 ถ้าเกสต์รันตัวแก้ชื่อของตัวเองบนที่อยู่ host-only ให้ใช้ที่อยู่นั้นแทน แต่ต้องยืนยันก่อนว่ามันตอบบน 192.168.56.2 ไม่ใช่บน loopback
จากนั้นตรวจ MTU ก่อนโทษ DNS เรื่องหน้าเว็บช้า อินเทอร์เฟซอุโมงค์เล็กกว่าอินเทอร์เฟซ 1500 ไบต์ของโฮสต์ โฮสต์ที่ส่งเซกเมนต์ขนาดเต็มโดยตั้งบิต DF จึงถูกทิ้งที่ไหนสักแห่งในเกสต์ อาการคลาสสิกคือ ping ใช้ได้ แต่เว็บแอปภายในค้างหลังแฮนด์เชก TLS ให้บีบ MSS บนเส้นทางส่งต่อของเกสต์
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
จุดตรวจ 5: ตรวจยืนยันจากทั้งสองด้าน
จากโฮสต์ traceroute หรือ tracert ไปยังที่อยู่ภายในควรแสดง 192.168.56.2 เป็นฮอปแรก ไม่ใช่เกตเวย์ทางกาย ในเกสต์ให้ดูความพยายามเดียวกันออกไป: tcpdump -ni any host 10.20.30.40 แสดงคำขอที่ถูกส่งต่อฝั่ง host-only และคำขอที่ถูกแปลงบนอุโมงค์ คำขอที่ออกไป กลับมา และยังทำให้โฮสต์เงียบอยู่ชี้ไปที่เส้นทางขากลับ
สองความล้มเหลวอธิบายสิ่งนี้ได้เกือบทั้งหมด อุโมงค์ของเกสต์ ไม่มีเส้นทางขากลับสำหรับพรีฟิกซ์ host-only เพราะไม่มีกฎ NAT หรือเส้นทางของโฮสต์ชี้ไปยังที่อยู่ที่เกสต์ไม่มีแล้วหลังรีสตาร์ต อ่านที่อยู่ใหม่ด้วย ip -br addr และ ip route get แทนการเชื่อบันทึกจากเซสชันก่อน
จุดตรวจ 6: ทำให้ทนต่อการรีบูตและการอัปเกรดไฮเปอร์ไวเซอร์
ทำให้ทั้งสองส่วนคงอยู่: การตั้งค่าเส้นทางด้วย netplan หรือ NetworkManager บนโฮสต์ route add ... -p บน Windows และ net.ipv4.ip_forward=1 ใน /etc/sysctl.d/ พร้อมชุดกฎไฟร์วอลล์ที่โหลดใหม่ในเกสต์ สแนปช็อตเกสต์ทันทีที่การตั้งค่าผ่านการตรวจ ก่อนการอัปเดตไคลแอนต์องค์กรครั้งถัดไปจะมาถึง
เครือข่าย host-only จะถูกเปลี่ยนหมายเลขเมื่อไฮเปอร์ไวเซอร์สร้างอะแดปเตอร์ใหม่ ซึ่งทำให้ทุกเส้นทางและกฎ NAT ที่สร้างบน 192.168.56.0/24 ใช้ไม่ได้อย่างเงียบ ๆ หลังอัปเกรด ให้ทำสามจุดตรวจแรกซ้ำตามลำดับ ใช้เวลาแค่นาทีเดียวและจับกรณีที่โฮสต์ยังมีเส้นทางของมันอยู่แต่เกสต์ย้ายไปซับเน็ตใหม่แล้ว
IPv6 ต้องมีรอบของตัวเอง กฎ ip6tables ระเบียน AAAA และตระกูลเส้นทางที่สองของโฮสต์ไม่ได้ตามการตั้งค่า IPv4 และบริการองค์กรที่เข้าถึงได้ผ่าน IPv6 จะดูเหมือนพังถาวรในขณะที่ IPv4 ทำงานอยู่
ผ่านหรือไม่ผ่าน ตามลำดับ: โฮสต์ปิงที่อยู่ host-only ของเกสต์ได้ เกสต์ส่งต่อและทำ MASQUERADE ไปยังอุโมงค์ ip route get คืนเกสต์เป็นเกตเวย์สำหรับพรีฟิกซ์ภายใน ชื่อองค์กรแก้ได้ผ่านตัวแก้ชื่อที่จำกัด คำสั่ง traceroute แสดงเกสต์เป็นตัวแรก และคำตอบทั้งหกข้อเดิมอยู่รอดหลังรีบูต