วิธีเส้นทางทราฟฟิกของโฮสต์ผ่านคอนเทนเนอร์ Docker VPN
คอนเทนเนอร์ VPN คือเนมสเปซเครือข่าย ไม่ใช่พร็อกซี เลือก netns ที่ใช้ร่วมกัน ตัวรับฟัง SOCKS จริง หรือเกตเวย์ที่กำหนดเส้นทาง แล้วตรวจสอบเส้นทางออก
คอนเทนเนอร์อุโมงค์ของคุณทำงาน docker compose exec vpn curl -s https://ifconfig.me ตอบกลับด้วยที่อยู่ของผู้ให้บริการ แต่เบราว์เซอร์บนเดสก์ท็อปของคุณยังออกผ่าน ISP ช่องว่างนี้เป็นสิ่งที่คาดไว้ คอนเทนเนอร์ที่รันไคลเอนต์ VPN ถือเนมสเปซเครือข่าย เส้นทาง และอินเทอร์เฟซอุโมงค์ ไม่มีอะไรที่พูด HTTP หรือ SOCKS การเผยแพร่พอร์ตบนนั้นไม่สามารถสร้างตัวรับฟังที่ไม่เคยมีได้
ตำแหน่งที่ไคลเอนต์รันเป็นตัวกำหนดการออกแบบ คอนเทนเนอร์ข้างเคียงย้ายเข้าเนมสเปซของอุโมงค์ได้ แอปเดสก์ท็อปทำไม่ได้ และเราเตอร์ต้องการเส้นทางไม่ใช่ช่องพร็อกซี ถ้าจับคู่ผิด อาการก็เหมือนกัน: การเชื่อมต่อถูกปฏิเสธ หรือทราฟฟิกที่แอบใช้เส้นทางเดิม
ปรับการจัดวางให้ตรงกับที่ที่ไคลเอนต์รัน
ถามหนึ่งคำถามก่อนแก้ Compose: เริ่มไคลเอนต์ภายในเนมสเปซเครือข่ายของคอนเทนเนอร์ VPN ได้หรือไม่ อะไรก็ตามที่รันเป็นคอนเทนเนอร์ Linux อยู่แล้วทำได้ เบราว์เซอร์ IDE หรือ CLI บนโต๊ะของคุณทำไม่ได้ เพราะการตั้งค่าพร็อกซีคาดหวังบริการโปรโตคอล ไม่ใช่เนมสเปซ
| Client | Arrangement | What must already exist |
|---|---|---|
| Another Compose service | Shares the vpn namespace via network_mode: service:vpn | A working tunnel and correct start order |
| Host browser, IDE or CLI | Proxy listener inside the namespace, published to host loopback | A configured SOCKS or HTTP proxy and its access rules |
| Router, VM or phone | The container as a layer-3 gateway | Forwarding, NAT and a return route |
สามอย่างนี้ไม่ผสมกัน ช่องพร็อกซี HTTP ที่ชี้ไปที่ที่อยู่บริดจ์ของคอนเทนเนอร์ไม่ถึงสิ่งใดเลยเว้นแต่มีพร็อกซีฟังอยู่ที่นั่น และตัวรับฟังพร็อกซีไม่ได้กำหนดเส้นทางทราฟฟิกของเราเตอร์ ตัดสินใจก่อนแล้วทำตามหัวข้อที่ตรงกัน
แบ่งปันเนมสเปซของอุโมงค์กับคอนเทนเนอร์ข้างเคียง
นี่คือตัวเลือกที่ถูกที่สุดเพราะไม่มีโปรโตคอลพร็อกซีเข้ามาเกี่ยวข้อง แอปเข้าร่วมเนมสเปซของบริการ vpn และสืบทอดอินเทอร์เฟซ เส้นทาง และพื้นที่พอร์ตของบริการนั้น:
services:
vpn:
image: your-vpn-client
cap_add:
- NET_ADMIN
healthcheck:
test: ["CMD-SHELL", "ip link show tun0 >/dev/null"]
interval: 10s
retries: 5
app:
image: your-app
network_mode: service:vpn
depends_on:
vpn:
condition: service_healthyมีข้อจำกัดสามข้อที่ตามมาจากการที่เนมสเปซเป็นของบริการอื่น แอปไม่ได้คีย์ networks ของตัวเองเพราะไม่มีเนมสเปซให้ยึด การเผยแพร่พอร์ตเป็นของ vpn เพราะเนมสเปซอาศัยอยู่ที่นั่น และการสร้าง vpn ใหม่จะทำลายเนมสเปซที่แอปเข้าร่วม ดังนั้น docker compose up -d --force-recreate vpn จึงปล่อยให้คุณสร้าง app ใหม่ด้วย การรีสตาร์ตธรรมดาไม่เพียงพอเสมอไป เอกสารอ้างอิงบริการ Compose ครอบคลุมการจัดวางนี้และข้อจำกัดการเผยแพร่พอร์ต
อย่าสันนิษฐานว่าตัวแก้ชื่อมาด้วย /etc/resolv.conf เป็นไฟล์ต่อคอนเทนเนอร์ ไม่ใช่ส่วนหนึ่งของเนมสเปซเครือข่าย ดังนั้นแอปยังถามตัวแก้ชื่อของโฮสต์ได้ขณะที่แพ็กเก็ตออกผ่านอุโมงค์ อ่านจากภายในคอนเทนเนอร์และตั้ง dns: บนแอปเมื่อชื่อส่วนตัวต้องแก้ผ่านอุโมงค์
docker compose exec app ip route
docker compose exec app cat /etc/resolv.conf
docker compose exec app curl -s https://ifconfig.meถ้ารายการเส้นทางมีอินเทอร์เฟซอุโมงค์และที่อยู่เป็นของผู้ให้บริการ แอปอยู่ภายในอุโมงค์ ถ้าอินเทอร์เฟซหายไป app เริ่มก่อนที่ vpn จะมีอุโมงค์ ดังนั้นสร้าง app ใหม่เมื่อ vpn รายงานว่าสุขภาพดี
วางตัวรับฟังพร็อกซีจริงในเนมสเปซสำหรับไคลเอนต์โฮสต์
เบราว์เซอร์เดสก์ท็อปเข้าร่วมเนมสเปซไม่ได้ ดังนั้นอุโมงค์ต้องถูกเปิดเป็นบริการ รันพร็อกซี SOCKS หรือ HTTP จริงภายในเนมสเปซ vpn และเผยแพร่เฉพาะพอร์ตของมัน:
services:
vpn:
ports:
- "127.0.0.1:1080:1080"
proxy:
image: your-proxy
network_mode: service:vpn
command: ["--socks", "0.0.0.0:1080"]ที่อยู่ฝั่งโฮสต์คือครึ่งสำคัญของการจับคู่นั้น 127.0.0.1:1080 ทางซ้ายเก็บตัวรับฟังไว้นอก LAN ของคุณ "1080:1080" เปลือยเผยแพร่พร็อกซีเปิดให้ทุกอุปกรณ์ที่เข้าถึงเครื่องของคุณได้
ฝั่งคอนเทนเนอร์มีกับดักของตัวเอง พอร์ตที่เผยแพร่ถูกส่งไปยังที่อยู่ของคอนเทนเนอร์บนบริดจ์ ไม่ใช่ loopback ของมัน ดังนั้นพร็อกซีที่เริ่มด้วย --socks 127.0.0.1:1080 ภายในเนมสเปซจะรับการเชื่อมต่อจากข้างเคียงและปฏิเสธทุกอย่างที่มาทางพอร์ตที่เผยแพร่ ผูกมันกับ 0.0.0.0 และพึ่งการผูก loopback ฝั่งโฮสต์เพื่อแยกส่วน กฎเดียวกันใช้กับที่อยู่รับฟังในไฟล์กำหนดค่าพร็อกซี
จากนั้นตัดสินใจว่าจะแก้ชื่อที่ไหน ใน curl --socks5 แก้ชื่อโฮสต์ในเครื่อง และ --socks5-hostname ส่งชื่อไปยังพร็อกซี เบราว์เซอร์แสดงสวิตช์เดียวกันภายใต้ป้ายอื่น การแก้ในเครื่องรั่วการสอบถามแม้หน้าเว็บโหลดผ่านอุโมงค์
ถือว่าคอนเทนเนอร์เป็นเกตเวย์เฉพาะเมื่อมีเส้นทางกลับ
เราเตอร์ VM และโทรศัพท์จะไม่ใช้ช่องพร็อกซี HTTP และบางแอปเพิกเฉยต่อตัวแปรพร็อกซีทั้งหมด การกำหนดเส้นทางทราฟฟิกของพวกมันหมายถึงเลเยอร์ 3: คอนเทนเนอร์ส่งต่อแพ็กเก็ตและแปลที่อยู่บนอินเทอร์เฟซอุโมงค์
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADEสองสิ่งถูกละเลยได้ง่าย ไคลเอนต์ต้องการเส้นทางที่ชี้เกตเวย์เริ่มต้นมาที่โฮสต์นี้ และคำตอบต้องหาเส้นทางกลับ ไม่ว่าจะผ่านเส้นทางคงที่บนเราเตอร์ต้นน้ำหรือผ่าน NAT บนอินเทอร์เฟซที่หันไปทางไคลเอนต์ ถ้าไม่มีอย่างใดอย่างหนึ่ง อุโมงค์จะพาคำขอออกไปและคำตอบก็หายไป
ยังมีรัศมีผลกระทบกว้างที่สุด โฮสต์ที่ทำหน้าที่เกตเวย์ให้ทั้ง LAN ส่งไคลเอนต์ที่ตั้งค่าผิดทุกตัวผ่านบัญชี VPN ของคุณ และเมื่ออุโมงค์ล่มก็ทำให้ LAN มืดหรือถอยกลับไปใช้ ISP ขึ้นอยู่กับ kill switch
ตรวจสอบเส้นทางออก แล้วย้อนกลับตามลำดับ
ตรวจตามลำดับนี้และหยุดที่ความล้มเหลวแรก: ที่อยู่ขาออกภายในเนมสเปซ ที่อยู่ขาออกจากโฮสต์ผ่านพร็อกซี แล้วจึงแก้ชื่อสำหรับโฮสต์ส่วนตัว
docker compose exec vpn curl -s https://ifconfig.me
curl -s -x socks5h://127.0.0.1:1080 https://ifconfig.me
curl -s -x socks5h://127.0.0.1:1080 http://internal.example/healthสองที่อยู่แรกควรตรงกับผู้ให้บริการ และที่สามควรแก้ผ่านพร็อกซีแทนที่จะล้มเหลวด้วยข้อผิดพลาดชื่อ บรรทัดแรกตรงและบรรทัดที่สองล้มเหลวหมายความว่าตัวรับฟังหรือพอร์ตที่เผยแพร่ผิด ไม่ใช่อุโมงค์
การย้อนกลับคือสิ่งที่ตรงข้ามกับการตั้งค่า ลบตัวแปรสภาพแวดล้อมพร็อกซีออกจากโปรไฟล์เชลล์และจากยูนิต systemd ใด ๆ แล้วลดสแตกลงเพื่อให้เนมสเปซที่ใช้ร่วมกันหายไปพร้อมเจ้าของ ลบพอร์ตที่เผยแพร่ก่อนลบบริการพร็อกซี มิฉะนั้นโฮสต์จะเก็บตัวรับฟัง loopback ที่ไม่มีใครอ่าน
ก่อนไปต่อ ยืนยันสี่อย่าง: ไคลเอนต์ที่คุณสนใจเข้าถึงอุโมงค์จริง มีเพียงเจ้าของเนมสเปซที่เผยแพร่พอร์ต ชื่อส่วนตัวถูกแก้ในที่ที่คุณตั้งใจ และไม่มีอะไรฟังบนที่อยู่โฮสต์ที่ไม่ใช่ loopback แต่ละข้อล้มเหลวเงียบ ๆ ในการใช้งานจริง และแต่ละข้อคือการตรวจหนึ่งบรรทัดด้านบน