Container đường hầm của bạn hoạt động, docker compose exec vpn curl -s https://ifconfig.me trả lời bằng địa chỉ của nhà cung cấp, vậy mà trình duyệt trên máy tính để bàn vẫn ra ngoài qua ISP của bạn. Khoảng trống đó là điều dễ hiểu. Một container chạy ứng dụng VPN giữ một không gian tên mạng, các tuyến và một giao diện đường hầm, và không có gì nói HTTP hay SOCKS; công bố một cổng trên đó không thể tạo ra trình lắng nghe chưa từng tồn tại.

Nơi ứng dụng khách chạy quyết định thiết kế. Một container anh em có thể chuyển vào không gian tên của đường hầm, ứng dụng để bàn thì không, và bộ định tuyến cần các tuyến chứ không phải một trường proxy. Ánh xạ sai thì triệu chứng vẫn vậy: kết nối bị từ chối, hoặc lưu lượng lặng lẽ đi theo đường cũ.

Khớp cách bố trí với nơi ứng dụng khách chạy

Hỏi một câu trước khi sửa Compose: ứng dụng khách có thể khởi động bên trong không gian tên mạng của container VPN không? Bất cứ thứ gì đã chạy như container Linux đều có thể. Trình duyệt, IDE hay CLI trên bàn của bạn thì không, vì cài đặt proxy của nó chờ một dịch vụ giao thức, không phải không gian tên.

ClientArrangementWhat must already exist
Another Compose serviceShares the vpn namespace via network_mode: service:vpnA working tunnel and correct start order
Host browser, IDE or CLIProxy listener inside the namespace, published to host loopbackA configured SOCKS or HTTP proxy and its access rules
Router, VM or phoneThe container as a layer-3 gatewayForwarding, NAT and a return route

Ba thứ này không trộn lẫn. Một trường proxy HTTP trỏ đến địa chỉ cầu của container không chạm tới gì trừ khi có proxy lắng nghe ở đó, và một trình lắng nghe proxy không định tuyến lưu lượng của bộ định tuyến. Quyết định trước, rồi theo phần tương ứng.

Chia sẻ không gian tên của đường hầm với container anh em

Đây là lựa chọn rẻ nhất vì không có giao thức proxy nào tham gia. Ứng dụng vào không gian tên của dịch vụ vpn và thừa hưởng các giao diện, tuyến và không gian cổng của nó:

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

Ba hạn chế nảy sinh từ việc không gian tên thuộc về một dịch vụ khác. Ứng dụng không nhận khóa networks riêng, vì nó không có không gian tên để gắn vào. Việc công bố cổng thuộc về vpn, vì không gian tên sống ở đó. Và việc tạo lại vpn phá hủy không gian tên mà ứng dụng đã vào, nên docker compose up -d --force-recreate vpn để bạn tạo lại cả app; khởi động lại đơn thuần không phải lúc nào cũng đủ. Tài liệu tham chiếu dịch vụ Compose đề cập cách bố trí này và hạn chế công bố cổng của nó.

Đừng cho rằng trình phân giải đi kèm. /etc/resolv.conf là tệp cho từng container, không phải một phần của không gian tên mạng, nên ứng dụng vẫn có thể hỏi trình phân giải của máy chủ trong khi các gói của nó ra ngoài qua đường hầm. Đọc nó từ bên trong container và đặt dns: trên ứng dụng khi các tên riêng phải được phân giải qua đường hầm.

docker compose exec app ip route
docker compose exec app cat /etc/resolv.conf
docker compose exec app curl -s https://ifconfig.me

Nếu danh sách tuyến có giao diện đường hầm và địa chỉ là của nhà cung cấp, ứng dụng đang ở trong đường hầm. Nếu thiếu giao diện, app đã khởi động trước khi vpn có đường hầm, nên tạo lại app ngay khi vpn báo khỏe.

Đặt một trình lắng nghe proxy thật trong không gian tên cho các ứng dụng khách của máy chủ

Trình duyệt để bàn không thể vào không gian tên, nên đường hầm phải được mở như một dịch vụ. Chạy proxy SOCKS hoặc HTTP thật bên trong không gian tên vpn và chỉ công bố cổng của nó:

services:
  vpn:
    ports:
      - "127.0.0.1:1080:1080"
  proxy:
    image: your-proxy
    network_mode: service:vpn
    command: ["--socks", "0.0.0.0:1080"]

Địa chỉ phía máy chủ là nửa quan trọng của ánh xạ đó. 127.0.0.1:1080 bên trái giữ trình lắng nghe ngoài LAN của bạn; một "1080:1080" trần công bố proxy mở cho mọi thiết bị có thể chạm tới máy của bạn.

Phía container có cái bẫy riêng. Cổng được công bố được giao đến địa chỉ của container trên cầu, không phải loopback của nó, nên proxy khởi động với --socks 127.0.0.1:1080 bên trong không gian tên chấp nhận kết nối từ các container anh em và từ chối mọi thứ đến qua cổng đã công bố. Gắn nó vào 0.0.0.0 và dựa vào việc gắn loopback phía máy chủ để cô lập. Quy tắc tương tự áp dụng cho địa chỉ lắng nghe trong tệp cấu hình proxy.

Sau đó quyết định nơi tên được phân giải. Trong curl, --socks5 phân giải tên máy chủ cục bộ và --socks5-hostname gửi tên đến proxy; trình duyệt hiển thị cùng công tắc dưới nhãn khác. Phân giải cục bộ làm lộ truy vấn ngay cả khi trang tải qua đường hầm.

Chỉ coi container là cổng khi có tuyến quay về

Bộ định tuyến, VM và điện thoại sẽ không dùng trường proxy HTTP, và một số ứng dụng bỏ qua hoàn toàn các biến proxy. Định tuyến lưu lượng của chúng nghĩa là lớp 3: container chuyển tiếp gói và dịch địa chỉ trên giao diện đường hầm.

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

Hai điều dễ bị bỏ sót. Ứng dụng khách cần một tuyến trỏ cổng mặc định của nó đến máy chủ này, và các phản hồi phải tìm được đường về, hoặc qua tuyến tĩnh trên bộ định tuyến phía trên hoặc qua NAT trên giao diện hướng về phía ứng dụng khách. Không có một trong hai, đường hầm mang yêu cầu ra ngoài và phản hồi biến mất.

Nó cũng có bán kính ảnh hưởng rộng nhất. Một máy chủ làm cổng cho toàn bộ LAN sẽ gửi mọi ứng dụng khách cấu hình sai qua tài khoản VPN của bạn, và đường hầm sụp sẽ hoặc làm tối LAN hoặc quay về ISP tùy theo kill switch.

Kiểm tra đường ra, rồi hoàn nguyên theo thứ tự

Kiểm tra theo thứ tự này và dừng ở lỗi đầu tiên: địa chỉ ra bên trong không gian tên, địa chỉ ra từ máy chủ qua proxy, rồi phân giải tên cho một máy chủ riêng.

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

Hai địa chỉ đầu phải khớp với nhà cung cấp, và địa chỉ thứ ba phải phân giải qua proxy thay vì thất bại với lỗi tên. Dòng đầu khớp và dòng thứ hai thất bại nghĩa là trình lắng nghe hoặc cổng đã công bố sai, không phải đường hầm.

Hoàn nguyên là ngược lại của thiết lập. Xóa các biến môi trường proxy khỏi hồ sơ shell và khỏi mọi unit systemd, rồi hạ ngăn xếp để không gian tên dùng chung biến mất cùng chủ của nó. Xóa cổng đã công bố trước khi xóa dịch vụ proxy, nếu không máy chủ sẽ giữ một trình lắng nghe loopback mà không ai đọc.

Trước khi tiếp tục, xác nhận bốn điều: ứng dụng khách bạn quan tâm thực sự tới được đường hầm, chỉ chủ không gian tên công bố cổng, các tên riêng được phân giải ở nơi bạn định, và không có gì lắng nghe trên địa chỉ máy chủ không phải loopback. Mỗi điều trong số đó hỏng âm thầm trong môi trường thật, và mỗi điều là một dòng kiểm tra ở trên.

Bài viết liên quan