เบราว์เซอร์บนโฮสต์แสดงที่อยู่ขาออกของ VPN คอนเทนเนอร์รายงานการเชื่อมต่อที่บ้านของคุณ และมีคนเรียกสิ่งนั้นว่าการรั่วไหล ไม่มีอะไรรั่วไหล ทันเนลของโฮสต์คือชุดของอินเทอร์เฟซ เส้นทาง และกฎที่อยู่ในเนมสเปซเครือข่ายของโฮสต์ ส่วนคอนเทนเนอร์บนบริดจ์เป็นเนมสเปซอีกอันที่มีเส้นทางเริ่มต้นของตัวเอง ผมเคยนั่งอยู่บนสแตก Compose ที่ไคลเอนต์ VPN เชื่อมต่อมาหลายสัปดาห์และไม่มีแพ็กเก็ตของคอนเทนเนอร์แม้แต่แพ็กเก็ตเดียวที่เคยเข้าไปในนั้น

ตรวจสอบเนมสเปซก่อนไอคอนในถาดระบบ

เนมสเปซคือข้อโต้แย้งทั้งหมด เมื่อ Docker เริ่มคอนเทนเนอร์บนเครือข่ายบริดจ์ มันจะสร้างเนมสเปซเครือข่าย เชื่อมปลายด้านหนึ่งของคู่ veth เข้าไปข้างใน และให้ที่อยู่กับเส้นทางเริ่มต้นแก่เนมสเปซนั้น อินเทอร์เฟซของทันเนล ไม่ว่าจะเรียกชื่ออะไร ก็อาศัยอยู่ในเนมสเปซของโฮสต์ ทราฟฟิกในคอนเทนเนอร์มองไม่เห็นมัน และไม่มีการตั้งค่าไคลเอนต์ใดเปลี่ยนสิ่งนี้จากภายนอก

มีสองวิธีที่รองรับเพื่อพาคุณออกจากการแยกตัว --network host วางคอนเทนเนอร์ไว้ในเนมสเปซของโฮสต์ จึงสืบทอดเส้นทางของโฮสต์และทันเนลตามมา --network container:vpn แชร์เนมสเปซของคอนเทนเนอร์อื่น และนั่นคือวิธีที่การออกแบบ sidecar ส่วนใหญ่ส่งเวิร์กโหลดผ่านคอนเทนเนอร์ VPN ทั้งสองเป็นทางเลือกที่ชัดเจน โดยปกติมีเพียงหนึ่งเดียวที่เป็นสิ่งที่ผู้ติดตั้งตั้งใจไว้

โหมดโฮสต์ไม่ใช่การอัปเกรดฟรี คอนเทนเนอร์หมดที่อยู่ของตัวเอง การเผยแพร่พอร์ตใช้ไม่ได้อีก และโปรเซสที่ถือ NET_ADMIN อยู่ที่นั่นสามารถเขียนเส้นทางของโฮสต์ใหม่ได้ การยอมรับสิ่งนี้อาจเป็นการตัดสินใจที่ถูกต้อง การสันนิษฐานว่ามันเปิดอยู่แล้วคือสาเหตุที่ผู้คนใช้เวลาทั้งเย็นแก้ปัญหาทันเนลที่ไม่เคยอยู่ในเส้นทางเลย

ตามแพ็กเก็ตออกจากบริดจ์

คอนเทนเนอร์บนบริดจ์ส่งแพ็กเก็ตไปยังเกตเวย์เริ่มต้น ซึ่งก็คืออินเทอร์เฟซบริดจ์บนโฮสต์ จากนั้นโฮสต์จะส่งต่อแพ็กเก็ตและในการกำหนดค่าเริ่มต้นจะแปลที่อยู่ต้นทางเป็นของตัวเองก่อนที่แพ็กเก็ตจะออกไป ขั้นตอนการแปลนั้นเองที่ทำให้แนวนโยบายพ่ายแพ้อย่างเงียบ ๆ

เมื่อแพ็กเก็ตไปถึงปลายทางของทันเนล มันพาที่อยู่ของโฮสต์ไปด้วย กฎใด ๆ เส้นทางนโยบาย หรือการตัดสินใจเรื่องทันเนลแยกส่วนที่อิงกับที่อยู่เดิมของคอนเทนเนอร์จะไม่ตรงกันอีกต่อไป และคอนเทนเนอร์สองตัวที่ตั้งใจให้ออกผ่านทางออกต่างกันก็แยกจากกันด้วยต้นทางเพียงอย่างเดียวไม่ได้ หากคุณต้องการกำหนดเส้นทางต่อคอนเทนเนอร์ การตัดสินใจต้องเกิดขึ้นก่อนการแปลหรือภายในเนมสเปซที่คุณควบคุม

Docker ดูแลกฎเหล่านี้เอง ผ่านแบ็กเอนด์ไฟร์วอลล์ใดก็ตามที่เดมอนถูกกำหนดค่าไว้ กฎที่เขียนด้วยมือภายนอกฮุกที่รองรับสำหรับทราฟฟิกที่ส่งต่อ DOCKER-USER อาจถูกจัดเรียงใหม่หรือถูกทิ้งเมื่อเดมอนรีสตาร์ตหรือโฮสต์อัปเกรด เอกสารของ Docker เกี่ยวกับการกรองแพ็กเก็ต แสดงลำดับของเชน ประเด็นในทางปฏิบัติคือทุกกฎต้องมีเจ้าของและมีเส้นทางการโหลดใหม่ที่คุณทดสอบจริง ๆ แล้ว

ทดสอบสองระบบแยกกัน

หลักฐานจากเนมสเปซหนึ่งไม่ได้บอกอะไรเกี่ยวกับอีกเนมสเปซหนึ่ง คำขอที่รันภายในคอนเทนเนอร์ VPN พิสูจน์ว่าทันเนลทำงานสำหรับคอนเทนเนอร์นั้น ไม่ใช่หลักฐานเกี่ยวกับโฮสต์ และคำสั่งเดียวกันที่รันจากคอนเทนเนอร์ --network host ก็ไม่ใช่หลักฐานเกี่ยวกับบริดจ์

# host namespace
ip route get 1.1.1.1
ip -brief addr show

# a fresh container on the same bridge network
docker run --rm --network appnet alpine sh -c 'ip route; cat /etc/resolv.conf; ip -6 route'

จากนั้นทำการทดสอบซ้ำภายในคอนเทนเนอร์แอปพลิเคชันจริง ไม่ใช่ในอิมเมจที่คล้ายกัน แอปอาจมีเส้นทางเพิ่มเติม ตัวแปรสภาพแวดล้อมพร็อกซี หรือตัวแก้ไขชื่ออื่น และแต่ละอย่างก็เปลี่ยนคำตอบ ตรวจสอบการแก้ชื่อและตระกูลที่อยู่ที่สองทุกครั้ง คอนเทนเนอร์บนบริดจ์ที่ผู้ใช้กำหนดจะถูกแก้ผ่านตัวแก้ไขในตัวของ Docker ที่ 127.0.0.11 และเส้นทางเริ่มต้น IPv6 บนบริดจ์สามารถพาทราฟฟิกออกไปได้ในขณะที่คุณกำลังยุ่งอยู่กับการทดสอบทันเนลที่เป็น IPv4 เท่านั้น

อ่านรูปแบบก่อนจะแตะข้อมูลรับรอง

เมื่อโฮสต์ทำงานแต่คอนเทนเนอร์ไม่ทำงาน สาเหตุเกือบทุกครั้งเป็นเชิงโครงสร้าง และการหมุนคีย์หรือติดตั้งไคลเอนต์ใหม่ก็เสียเวลาช่วงบ่ายไปเปล่า ๆ จัดเรียงอาการก่อนแล้วการแก้ไขมักจะบอกชื่อตัวเอง

What you seeUsual causeFirst check
Host exits through the VPN, container shows the ISP addressThe container never entered the tunnelDefault route inside the container
Container works until the daemon restarts, then stopsRules outside the supported hook, or a changed backendDaemon's reported firewall backend and DOCKER-USER
Container loses internal hosts after the tunnel comes upVPN prefix overlaps the Docker bridge subnetip route on the host, then docker network inspect
Small replies arrive, large ones hangTunnel MTU or missing MSS clampingLink MTU on both ends of the tunnel
Container falls back to the ISP address when the tunnel dropsNothing enforces the kill switch for forwarded trafficStop the tunnel with a request in flight

แถวการทับซ้อนสมควรได้ประโยคของตัวเอง โปรไฟล์ VPN ที่ดันเส้นทางสำหรับ 172.17.0.0/16 หรือสำหรับ 10.0.0.0/8 ที่บังเอิญมีคำนำหน้าของบริดจ์ของคุณอยู่ จะทำให้ทันเนลอ้างสิทธิ์ซับเน็ตของคอนเทนเนอร์ และคำตอบที่ส่งไปยังที่อยู่ที่ถูกแปลจะไปผิดทิศหรือหายไป การเปลี่ยนเลขเครือข่าย Docker มักสะอาดกว่าการสะสมข้อยกเว้น

ทำให้ความครอบคลุมชัดเจน แล้วจงใจทำให้มันพัง

การออกแบบสองแบบทนทานต่อการตรวจทาน แชร์เนมสเปซของคอนเทนเนอร์ VPN แล้วทุกเวิร์กโหลดที่ครอบคลุมจะใช้ทันเนลเดียวโดยมีขอบเขตการรีสตาร์ตเดียว ซึ่งง่ายจนกว่าบริการหนึ่งจะต้องการทางออกอื่นหรือเส้นทาง DNS ของตัวเอง หรือ กำหนดเส้นทางเครือข่ายคอนเทนเนอร์ไปยังเกตเวย์ VPN บนโฮสต์ เปิดการส่งต่อ คำนำหน้าของบริดจ์ชี้ไปที่ทันเนล ทราฟฟิกขากลับถูกจัดการอย่างชัดเจน สิ่งนี้ซื้อการควบคุมต่อเครือข่ายและแลกกับชุดกฎที่คุณต้องดูแลตลอดการอัปเกรดเดมอนและไคลเอนต์

ไม่ว่าจะทางไหน การทดสอบที่สำคัญคือกรณีล้มเหลว หยุดทันเนลในขณะที่คำขอของคอนเทนเนอร์กำลังลอยอยู่และดูว่าแพ็กเก็ตไปที่ไหน หากมันกลับไปใช้เส้นทางตรง ความครอบคลุมไม่เคยถูกบังคับใช้ เพราะ kill switch ของไคลเอนต์ควบคุมทราฟฟิกของไคลเอนต์เอง ไม่ใช่แพ็กเก็ตที่ส่งต่อจากบริดจ์ จากนั้นรีสตาร์ตเดมอนและโฮสต์และยืนยันผลลัพธ์เป็นครั้งที่สอง นั่นคือช่วงเวลาที่กฎที่เขียนด้วยมือหายไป

ก่อนจะบอกว่าเสร็จแล้ว ให้ยืนยันสี่อย่าง อินเทอร์เฟซของทันเนลอยู่ในเนมสเปซเดียวกับทราฟฟิก การทดสอบคอนเทนเนอร์รันภายในคอนเทนเนอร์จริง มีการตรวจสอบการแก้ชื่อและตระกูลที่อยู่ทั้งสอง และการทดสอบความล้มเหลวของทันเนลยังคงล้มเหลวแบบปิดอยู่ จากนั้นบันทึกแบ็กเอนด์ไฟร์วอลล์ เวอร์ชันไคลเอนต์ และเจ้าของของแต่ละกฎ บันทึกนั้นคือสิ่งที่ป้องกันไม่ให้การอัปเกรดครั้งถัดไปทิ้งส่วนผสมของสมมติฐานเก่าที่ไม่ได้บันทึกไว้

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