ทำไมคอนเทนเนอร์ macvlan จึงตอบกลับไคลเอนต์ VPN ไม่ได้
คอนเทนเนอร์ macvlan ไม่สามารถสื่อสารกับโฮสต์ของตัวเองได้ และคำตอบอาจออกผ่านเกตเวย์ผิดเส้นทาง เพิ่มเส้นทาง shim ฝั่งโฮสต์ แล้วเลือกโมเดลต้นทาง
ลองนึกภาพโฮสต์ Linux ที่ทำหน้าที่สิ้นสุด VPN และรันคอนเทนเนอร์บนเครือข่าย macvlan ไปพร้อมกัน LAN คือ 192.168.60.0/24 คอนเทนเนอร์ถือ 192.168.60.210 และไคลเอนต์ VPN มาจาก 10.95.0.0/24 ไคลเอนต์เข้าถึงเซอร์วิสได้ แต่คำตอบไม่เคยย้อนกลับมา และกฎไฟร์วอลล์ทุกข้อบนโฮสต์ก็ดูถูกต้อง อาการนี้เกิดจากข้อจำกัดสองข้อที่แยกจากกัน และการแก้ข้อหนึ่งยังคงเหลืออีกข้อไว้ที่เดิม
สองข้อจำกัด อาการเดียว
ข้อจำกัดแรกคือการแยกโดด ไดรเวอร์ macvlan ของ Docker ไม่อนุญาตให้คอนเทนเนอร์สื่อสารกับโฮสต์ของตัวเองผ่านอินเทอร์เฟซแม่ การรับส่งระหว่างสแตกของโฮสต์กับคอนเทนเนอร์จึงไม่มีเส้นทาง ไม่ว่าจะเขียนเส้นทางบนโฮสต์อย่างไร
ข้อจำกัดที่สองคือเส้นทางขากลับ เกตเวย์เริ่มต้นของคอนเทนเนอร์มักเป็นเราเตอร์ของ LAN คำตอบที่ส่งถึงไคลเอนต์ VPN จึงออกผ่านเราเตอร์นั้น หากเราเตอร์ไม่มีเส้นทางกลับไปยังพูล VPN คำตอบก็ถูกทิ้งที่ใดที่หนึ่งตรงกลาง
ความล้มเหลวทั้งสองดูเหมือนกันจากมุมของไคลเอนต์ คือคำขอมาถึงแต่คำตอบไม่เคยมา ทั้งสองต้องซ่อมต่างกัน และกฎ masquerade แบบกว้างอาจบดบังว่าข้อใดยังเสียอยู่ นั่นคือเหตุผลที่การทดสอบแรกคือการดักจับฝั่งคอนเทนเนอร์ ไม่ใช่การแก้ไฟร์วอลล์อีกครั้ง หากคำขอไม่เคยปรากฏที่นั่น ข้อจำกัดด้านการแยกโดดคือสิ่งที่กำลังทำงาน
สร้าง shim ฝั่งโฮสต์อย่างแคบ
อินเทอร์เฟซ macvlan ตัวที่สองบนโฮสต์ที่ผูกกับอินเทอร์เฟซแม่เดียวกันในโหมดบริดจ์ จะให้สแตกของโฮสต์มีอัตลักษณ์ของตัวเองบนเซกเมนต์ LAN นั้น จองที่อยู่ไว้นอกพูล DHCP และนอกช่วงของคอนเทนเนอร์สำหรับอินเทอร์เฟซนี้ และส่งเฉพาะคอนเทนเนอร์ผ่านมัน
ip link add macvlan-vpn link eth0 type macvlan mode bridge ip address add 192.168.60.250/32 dev macvlan-vpn ip link set macvlan-vpn up ip route add 192.168.60.210/32 dev macvlan-vpn src 192.168.60.250
คำสั่งแรกสร้างอินเทอร์เฟซในโหมดบริดจ์ ซึ่งเป็นค่าเริ่มต้นที่มีเอกสารกำกับสำหรับไดรเวอร์นี้ และเป็นโหมดที่ต้องการสำหรับการรับส่งระหว่างโฮสต์กับคอนเทนเนอร์ คำสั่งที่สองกำหนด /32 เพื่อให้ที่อยู่ไม่ชนกับพรีฟิกซ์ของ LAN และคำสั่งที่สี่ส่งเฉพาะที่อยู่ของคอนเทนเนอร์ผ่าน shim
อย่าเปลี่ยนเส้นทาง LAN ทั้งหมดผ่านอินเทอร์เฟซใหม่ เส้นทางที่เชื่อมต่อตามปกติยังคงอยู่เหมือนเดิม และเส้นทางของโฮสต์ถูกจำกัดให้แคบโดยตั้งใจ เพื่อให้คอนเทนเนอร์หนึ่งตัวได้เส้นทางโดยที่ส่วนที่เหลือของเซกเมนต์ไม่เปลี่ยนพฤติกรรม เส้นทางเริ่มต้นที่ชี้ไปยัง shim จะเป็นการออกแบบอื่นที่มีผลลัพธ์อื่น ถ้าเจาะจงไว้รัศมีของความเสียหายก็ยังเล็ก
คำสั่งเหล่านี้ เปลี่ยนการตั้งค่าเครือข่ายบนโฮสต์ที่กำลังทำงาน บนอุปกรณ์หรือแพลตฟอร์มที่จัดการ ควรใช้กลไกการกำหนดค่าที่แพลตฟอร์มนั้นรองรับ เพราะการเปลี่ยนแปลงแบบโต้ตอบใน root shell อาจไม่คงอยู่หลังการอัปเกรด
ตัดสินใจว่าจะรักษาต้นทางอย่างไร
เมื่อมี shim อยู่ คอนเทนเนอร์ก็เข้าถึงโฮสต์ได้ และคำถามก็กลายเป็นว่าคำตอบบรรทุกที่อยู่ต้นทางใด การรักษาต้นทาง VPN เดิมเป็นโมเดลที่สะอาดกว่า เพราะคอนเทนเนอร์เห็นที่อยู่จริงของไคลเอนต์ และบันทึกกับกฎการเข้าถึงของเซอร์วิสก็ใช้ที่อยู่นั้นได้
เพื่อให้ทำงานได้ การกำหนดค่าเครือข่ายของคอนเทนเนอร์ต้องส่งการรับส่งสำหรับ 10.95.0.0/24 ไปยัง 192.168.60.250 เป็นเกตเวย์สำหรับเส้นทางนี้ นี่คือการตั้งค่าเส้นทางหรือเกตเวย์ที่รองรับอยู่ในการกำหนดค่าของคอนเทนเนอร์เอง ไม่ใช่กลเม็ดฝั่งโฮสต์ ควรยืนยันจากภายในคอนเทนเนอร์แทนการสมมติว่าการตั้งค่ามีผลแล้ว เกตเวย์เริ่มต้นที่ยังชี้ไปยังเราเตอร์ของ LAN จะยังคงส่งคำตอบลงหลุมเดิม
หากการออกแบบที่ได้รับอนุมัติซ่อนต้นทาง VPN แทน ให้จำกัดการแปลอย่างแคบ คือ พูล VPN จริง, shim เป็นอินเทอร์เฟซขาออก และคอนเทนเนอร์กับเซอร์วิสที่ระบุ
iptables -t nat -A POSTROUTING -s 10.95.0.0/24 -o macvlan-vpn -j SNAT --to-source 192.168.60.250
จับคู่ shim เป็นอินเทอร์เฟซขาออก ไม่ใช่ทันเนล กฎที่ระบุทันเนลเป็นอินเทอร์เฟซขาออกจะไม่ตรงกับแพ็กเก็ตที่มุ่งไปยังคอนเทนเนอร์ และนั่นคือข้อผิดพลาดที่การตั้งค่าแบบนี้มักมีอยู่ ให้กฎแคบเท่ากับประโยคข้างต้น และทบทวนเมื่อพูลเปลี่ยน
ตรวจสอบจากไคลเอนต์ ไม่ใช่จากโฮสต์
คำขอจากโฮสต์เองที่ผูกกับที่อยู่ของ shim พิสูจน์ว่า shim เข้าถึงคอนเทนเนอร์ได้ แต่ไม่ได้พิสูจน์ว่าคอนเทนเนอร์ส่งการรับส่งกลับไปยังต้นทาง 10.95.0.x ที่รักษาไว้ได้ เพราะที่อยู่ต้นทางในการทดสอบนั้นเป็นของ shim เอง
curl -sS --interface 192.168.60.250 -o /dev/null -w '%{http_code}\n' http://192.168.60.210:8080/health
sudo tcpdump -ni macvlan-vpn host 192.168.60.210คำสั่งแรกทดสอบช่วงจากโฮสต์ไปยังคอนเทนเนอร์ และคำสั่งที่สองแสดงสิ่งที่ผ่าน shim จริง ๆ ขณะที่ไคลเอนต์ VPN ตัวจริงส่งคำขอ ดูที่อยู่ต้นทางบนเส้นทางขาเข้า และดูว่ามีคำตอบที่ใช้โฟลว์เดียวกันปรากฏขึ้นหรือไม่
ต้องเปิดการส่งต่อบนโฮสต์ และเส้นทางส่งต่อต้องการอนุญาตสำหรับพูล VPN ไปยัง shim บนพอร์ตของเซอร์วิส พร้อมคำตอบที่สร้างขึ้นแล้ว กฎ INPUT บนโฮสต์ไม่ได้อนุญาตการรับส่งที่ถูกส่งต่อ ซึ่งเป็นสาเหตุทั่วไปที่แพ็กเก็ตถูกทิ้งหลังจากเราเตอร์ดูถูกต้อง
เรียกใช้แอปพลิเคชันจริงด้วยข้อมูลรับรองจริง แล้วตรวจสอบว่าเซอร์วิสที่สองที่ไม่เกี่ยวข้องบนคอนเทนเนอร์เดียวกันยังคงปฏิเสธคุณอยู่หรือไม่ หากวิธีเดียวที่จะทำให้ทำงานได้คือเปิดกฎการส่งต่อทุกข้อ การออกแบบยังไม่เสร็จ บันทึกด้วยว่าเซอร์วิสบันทึกที่อยู่ต้นทางใด เพราะนั่นคือสิ่งที่กฎการเข้าถึงของมันเองจะใช้ต่อไป