Docker container เข้าถึงไม่ได้ผ่าน VPN? เทียบ subnet ทั้งสอง
Docker แจก subnet ที่ VPN ก็อ้างสิทธิ์เช่นกัน เทียบแผนที่อยู่ทั้งสอง แล้วเลือกวิธีซ่อมที่อยู่รอดหลังเชื่อมต่อใหม่
container ตอบสนองใน LAN ของออฟฟิศ แล้วหยุดตอบสนองทันทีที่ VPN ของบริษัทเชื่อมต่อ ไม่มีอะไรเปลี่ยนใน container: ไม่รีสตาร์ต ไม่อัปเดตอิมเมจ ไม่แก้ไขคอนฟิก สิ่งที่เปลี่ยนคือตารางเส้นทางของโฮสต์ ซึ่งเส้นทางที่อุโมงค์เพิ่มเข้าไปครอบที่อยู่ที่ Docker แจกไปแล้ว ก่อนจะแก้ subnet หรือขอข้อยกเว้นจาก VPN ให้วางแผนที่อยู่ทั้งสองไว้ข้างกัน เพราะแต่ละวิธีซ่อมใช้เวลาต่างกันมาก
อ่านเส้นทางก่อนแก้คอนฟิก
เริ่มจากเครือข่ายที่ Docker สร้างจริง บล็อก IPAM คือแผนที่อยู่ และ subnet คือบรรทัดที่สำคัญ:
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]โดยค่าเริ่มต้น Compose จัดสรร /16 ให้ทุกโปรเจกต์ใหม่ ทำให้สองบริการอยู่ในช่วง 65,536 ที่อยู่ได้ ความกว้างนี้เองทำให้การทับซ้อนพบได้บ่อย: /16 ที่สุ่มมาอาจใหญ่พอจะกลืนไซต์ที่คุณเข้าถึงผ่านอุโมงค์ และไม่มีใครสังเกตจนกว่าอุโมงค์จะขึ้น
จากนั้นเทียบการเลือกเส้นทางสำหรับที่อยู่ container หนึ่งรายการ ครั้งหนึ่งตอนอุโมงค์ปิด และอีกครั้งตอนเปิด:
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1คำตอบสองแบบนำไปคนละที่ ถ้าเอาต์พุตระบุ tun0, wg0 หรืออุปกรณ์อุโมงค์อื่น คำนำหน้า VPN ก็เฉพาะเจาะจงอย่างน้อยเท่ากับเส้นทางบริดจ์ และ แพ็กเก็ตไม่เคยไปถึง container ถ้ายังระบุบริดจ์อยู่ โฮสต์ยังเข้าถึง container ได้ และปัญหาของคุณคือการกรอง DNS หรือเส้นทางขากลับที่ล้มเหลวเพียงทิศทางเดียว
ทำไมการชนกันของ /16 จึงเกิดขึ้นง่าย
Docker ดึงเครือข่ายบริดจ์จากพูลส่วนตัว และพูลค่าเริ่มต้นมาจากบล็อก 172.16.0.0/12 เป็นชิ้น /16 เครือข่ายองค์กรมักใช้พื้นที่ส่วนตัวเดียวกัน และ VPN สำหรับการเข้าถึงระยะไกลก็ผลักคำนำหน้าเหล่านั้นไปยังไคลเอนต์ทุกเครื่องที่เชื่อมต่อ เมื่อทั้งสองฝ่ายเชื่อว่าตนเป็นเจ้าของ 172.18.0.0/16 ตารางเส้นทางเดียวจึงมีการอ้างสิทธิ์ที่ขัดแย้งกันสองรายการ
ความยาวคำนำหน้าเป็นตัวตัดสินผู้ชนะ ไม่ใช่ลำดับของเส้นทาง อุโมงค์ที่ผลัก 10.0.0.0/8 ไม่ชนะเส้นทางบริดจ์ 172.18.0.0/16 เพราะ /8 เจาะจงน้อยกว่า อุโมงค์ที่ผลัก 172.18.0.0/16 หรือ 172.18.5.0/24 ที่อยู่ข้างในชนะ เพราะเหตุนี้แล็ปท็อปเครื่องหนึ่งจึงใช้ได้บนเครือข่ายออฟฟิศแต่ล้มเหลวบน Wi-Fi โรงแรมด้วย VPN เดียวกัน: ไคลเอนต์ได้รับคำนำหน้าที่ต่างกัน
ตารางเดียวกันควบคุมทิศทางกลับด้วย container ที่ต้องการโฮสต์ภายในที่ 10.8.4.20 จะส่งแพ็กเก็ตไปยังเกตเวย์บริดจ์ของตน แล้วโฮสต์จึงกำหนดเส้นทางเข้าอุโมงค์ เส้นทางนั้นมักใช้ได้เพราะโฮสต์ทำ NAT แต่จะล้มเหลวเมื่อฝั่งองค์กรยอมรับเฉพาะที่อยู่ VPN ของแล็ปท็อป หรือเมื่อการกรองเส้นทางย้อนกลับทิ้งคำตอบที่ไม่สมมาตร
เทียบวิธีซ่อมทั้งสาม
มีทางออกเพียงสามทาง และต่างกันที่ใครต้องอนุมัติและอะไรพังระหว่างที่คุณใช้วิธีหนึ่ง
| Repair | Who owns it | What it costs |
|---|---|---|
| Renumber the container network | You | Recreates the network, restarts services, invalidates literal addresses in configs and scripts |
| Exclude your range from the tunnel | Network team | Changes what every laptop sees, needs a change request and a client profile update |
| Reach the service through a published port | You and the client | One port only, no direct container-to-container routing across the tunnel |
การเปลี่ยนเลขที่อยู่ใหม่เป็นวิธีเดียวที่ทำเสร็จได้โดยไม่ต้องออกตั๋ว จึงเป็นที่ที่บทความส่วนใหญ่หยุดอยู่ วิธีนี้ถูกในโปรเจกต์ใหม่และแพงเมื่อเครื่องมือย้ายข้อมูลหรือสตริงการเชื่อมต่อที่บันทึกไว้ชี้ไปที่ที่อยู่แบบตรงตัว นับการอ้างอิงเหล่านั้นก่อนแก้ YAML ไม่ใช่หลัง
ข้อยกเว้นจากอุโมงค์เป็นคำตอบที่สะอาดที่สุดเมื่อการทับซ้อนอยู่ในช่วงที่บริษัทของคุณเป็นเจ้าของจริง แต่มันก็ช้าที่สุดด้วย และไม่มีประโยชน์กับผู้รับเหมาที่เปลี่ยนโปรไฟล์ไคลเอนต์ไม่ได้ พอร์ตที่เผยแพร่หรือรีเวิร์สพร็อกซีขนาดเล็กหลีกเลี่ยงการโต้เถียงนี้: ไคลเอนต์คุยกับที่อยู่ที่อุโมงค์กำหนดเส้นทางถูกต้องอยู่แล้ว และมีเพียงพอร์ตนั้นที่ต้องเข้าถึงได้
กำหนดช่วงด้วยความตั้งใจ
สำหรับโปรเจกต์เดียว ให้ประกาศ subnet ในไฟล์ compose เพื่อให้โปรเจกต์เลิกดึงจากพูลที่ใช้ร่วมกัน:
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24เอาช่วงนั้นจากทะเบียนที่ทีมของคุณดูแล ไม่ใช่จากตัวอย่างที่คุณเจอตอนค้นหา ตรวจสอบกับคำนำหน้าที่ VPN ผลักดัน เครือข่ายบ้านที่ผู้คนทำงาน และเครือข่าย container อื่นบนโฮสต์เดียวกัน จากนั้นวางแผนสร้างใหม่ เพราะเครือข่ายที่มีอยู่เก็บ subnet เดิมไว้: docker compose down แล้ว up -d คำสั่ง docker network rm จะปฏิเสธขณะยังมีปลายทางเชื่อมอยู่ และรายการ ipv4_address ใดในไฟล์ต้องอยู่ในช่วงใหม่
สำหรับเครือข่ายที่ Docker สร้างเอง ให้ตั้งพูลของดีมอนใน /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}พูลนี้ใช้กับเครือข่ายที่สร้างหลังดีมอนรีสตาร์ต และบริดจ์ที่มีอยู่แล้วยังคงที่อยู่เดิม สิ่งนี้สะดวกและลืมง่าย จึงควรมองโปรเจกต์เก่าว่ายังทับซ้อนอยู่จนกว่าจะมีคนสร้างเครือข่ายของมันใหม่ เอกสาร เครือข่ายของเอนจิน ของ Docker อธิบายกฎการจัดสรรไว้ หากคุณต้องปกป้องพูลในการรีวิว
บริดจ์บน Linux และ Docker Desktop ล้มเหลวต่างกัน
บนโฮสต์ Linux container อยู่บนบริดจ์ในเนมสเปซของโฮสต์ ตารางเดียวตัดสินทุกอย่าง การเปรียบเทียบข้างต้นจึงเป็นเรื่องทั้งหมด: อ่านเส้นทางบริดจ์ อ่านเส้นทางอุโมงค์ แล้วดูว่าอันไหนชนะสำหรับที่อยู่ container จริง
Docker Desktop วาง container ไว้หลัง VM ที่มี NAT ของตัวเอง โฮสต์ไม่มีเส้นทางไปยังที่อยู่ container เลย มันเข้าถึงบริการผ่านพอร์ตที่เผยแพร่ ซึ่งมักผูกกับ 127.0.0.1 ไคลเอนต์ VPN จึงทำให้บริการเสียได้โดยไม่มีการทับซ้อนของ subnet เลย: ไม่ว่าจะติดตั้งเส้นทางสำหรับช่วงภายในของ VM หรือบล็อกทราฟฟิก loopback ขณะอุโมงค์เชื่อมต่อ ถ้า ip route get บนโฮสต์รายงานว่าไม่มีเส้นทางสำหรับที่อยู่ container นั่นคือสิ่งที่คาดไว้ ไม่ใช่บั๊ก
ผลในทางปฏิบัติคือคุณควรดูที่ใดก่อน บน Linux เริ่มที่ตารางเส้นทาง บน Desktop เริ่มที่พอร์ตที่เผยแพร่และกฎไฟร์วอลล์ของไคลเอนต์ VPN แล้วตรวจว่าช่วง subnet ภายในของ VM ชนกับสิ่งที่อุโมงค์ผลักดันหรือไม่
ตรวจสอบขณะอุโมงค์เปิดอยู่
เชื่อมต่อ VPN ใหม่สองครั้งก่อนจะบอกว่าแก้แล้ว เพราะโปรไฟล์ที่เพิ่มเส้นทางตอนเชื่อมต่อและถอนกลับตอนตัดการเชื่อมต่ออาจดูถูกต้องหลังความพยายามครั้งแรก จากนั้นรันการตรวจสอบที่ไคลเอนต์ทำจริง:
ip route getจากโฮสต์ โดยใช้ที่อยู่จริงของ container- คำขอจาก container อื่น เพื่อพิสูจน์ว่าบริการนั้นแข็งแรงดี
curl -sv http://127.0.0.1:8080/จากไคลเอนต์ขณะอุโมงค์เปิดอยู่ แล้วตามด้วยtraceroute -nไปยังที่อยู่ container จากเครื่องใน LANsysctl net.ipv4.conf.all.rp_filterถ้าคำขอไปถึงโฮสต์แต่คำตอบหายไป
สุดท้าย ให้เขียนช่วงที่เลือกไว้ในทะเบียนเดิมที่คุณใช้เลือก พร้อมวันที่และโปรเจกต์ที่เป็นเจ้าของ การทับซ้อนครั้งต่อไปจะมาจากโปรเจกต์ที่ไม่มีใครบันทึกไว้ และการป้องกันเดียวคือช่วงนั้นถูกจองไว้โดยเจตนา ไม่ใช่สุ่มมา