Gói của container không bao giờ đến tun0 , và các quyền năng bổ sung trên container sẽ không thay đổi điều đó. Giữa khách và đường hầm có hai lần tra cứu tuyến: một trong không gian tên mạng của khách, một trên máy chủ sau khi khung đáp xuống cầu, và lần thứ hai hỏi một bảng khác với quy tắc khớp khác. Bố cục tham chiếu của tôi là một máy chủ Linux chạy OpenVPN client giữ 10.8.0.10 trên tun0 , một cầu Docker trên 172.18.0.0/16 , và một địa chỉ riêng mà chỉ phía đường hầm mới chạm tới. Mục tiêu vẫn hẹp: subnet khách đó đi ra qua đường hầm trong khi máy chủ giữ uplink thường của nó.

Chạy client VPN bên trong container là một thiết kế khác , không phải đường tắt. Khi đó container sở hữu đường hầm và thông tin đăng nhập của riêng nó, và công việc chuyển vào Dockerfile thay vì ở lại trong iptables.

Chuyển tiếp chọn tuyến theo đích, sau đó NAT ghi lại nguồn

Khi đã đặt net.ipv4.ip_forward=1 , gói được chuyển tiếp chỉ được định tuyến theo đích, trong bảng chính. Nguồn không đóng vai trò gì trong quyết định đó. Chỉ sau khi chọn tuyến, nhân mới chạy POSTROUTING , và đó là nơi quy tắc riêng của Docker nằm: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE . Mọi thứ rời cầu sang giao diện khác đều được dịch, và địa chỉ mà nó trở thành phụ thuộc vào giao diện mà tuyến đã gửi nó tới.

Quy tắc đó giải thích phần lớn các triệu chứng tôi được hỏi. Nếu bảng chính không có tuyến cho đích riêng, gói rời uplink mang địa chỉ vật lý của máy chủ. Nhà cung cấp của bạn bỏ đích, hoặc trang từ xa bỏ một nguồn RFC 1918 mà nó không bao giờ chờ đợi, và container hết thời gian trong khi đường hầm vẫn dựng và khỏe. Bắt gói trên tun0 không cho thấy gì, và đó là dấu hiệu. Nếu tuyến thực sự trỏ tới tun0 , MASQUERADE ghi lại nguồn thành 10.8.0.10 , và cùng container đó hoạt động mà bạn không chạm vào gì.

Hãy yêu cầu tra cứu nêu tên nguồn của khách. Không có from và iif máy chủ trả lời cho socket của chính nó và báo một đường mà gói được chuyển tiếp sẽ không bao giờ đi:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Nếu câu trả lời đó nêu tên uplink, đường hầm không có trong bức tranh và không cấu hình nào phía đường hầm giúp được.

Đưa khách sang địa chỉ riêng của đường hầm

Thay đổi nhỏ nhất hoạt động được là ghi lại nguồn một cách rõ ràng khi đi ra. Chèn nó vào thay vì thêm vào cuối: Docker đã ngồi ở đầu POSTROUTING , nên một quy tắc được thêm bằng -A được đánh giá sau quy tắc của Docker và không bao giờ kích hoạt.

iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10

Hai điều kiện quyết định việc này có giữ được không. Địa chỉ phải là địa chỉ mà client thực sự có, nên hãy ghim nó bằng một mục cho mỗi client chứa ifconfig-push 10.8.0.10 255.255.255.0 ; một quy tắc nêu tên địa chỉ chưa bao giờ được cấp sẽ tạo ra việc âm thầm bỏ gói, không phải lỗi. Và phía xa phải chấp nhận nguồn đó và định tuyến các trả lời về, vì thế danh sách cho phép ở đó nêu tên địa chỉ đường hầm chứ không phải subnet khách. Khi địa chỉ client động, -j MASQUERADE với -o tun0 đi theo thứ mà giao diện có, với cái giá là chính sách từ xa mà bạn không thể viết giá trị cố định chống lại.

Lọc là nửa còn lại. Docker đặt chính sách FORWARD thành drop và xóa các chuỗi nó sở hữu mỗi lần daemon khởi động lại, nên hãy đặt chính sách cục bộ trong DOCKER-USER , thứ tồn tại qua các lần khởi động:

iptables -I DOCKER-USER -i br-appnet -o tun0 -j ACCEPT
iptables -I DOCKER-USER -i tun0 -o br-appnet -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Quy tắc trả về quan trọng: một trả lời đến trên tun0 cho kết nối do cầu mở không được chấp nhận chỉ vì hướng đi ra đã được.

Giữ máy chủ trực tiếp bằng một quy tắc nguồn

Khi toàn bộ cầu phải dùng đường hầm nhưng không gì khác trên máy chủ phải dùng, hãy bỏ qua NAT và cho nguồn đó bảng riêng. Địa chỉ khách khi đó vẫn nhìn thấy từ đầu đến cuối.

echo "200 vpnbox" >> /etc/iproute2/rt_tables
ip rule add from 172.18.0.0/16 lookup vpnbox pref 1000
ip route add default dev tun0 table vpnbox

Các quy tắc được tra trước bảng chính, và from khớp với nguồn. Các gói bên ngoài của chính đường hầm mang nguồn vật lý của máy chủ, nên chúng không bao giờ khớp với from 172.18.0.0/16 , và đường hầm không thể định tuyến chính nó quay lại vào chính nó. Chính sự tự loại trừ đó làm bố cục ổn định.

Đường về giờ cần trợ giúp. Phía xa thấy một 172.18.0.x chưa dịch và không có lý do để biết nó sống ở đâu. Trên máy chủ, một tệp cho mỗi client cần iroute 172.18.0.0 255.255.0.0 để bảng nội bộ của máy chủ trao tiền tố đó cho client này, và một route 172.18.0.0 255.255.0.0 được đẩy để các client khác biết nó tồn tại.

Hai kiểm tra trước khi tin. Chạy ip route show default : với redirect-gateway def1 bảng giữ 0.0.0.0/1 và 128.0.0.0/1 qua tun0 , và máy chủ hoàn toàn không trực tiếp, vì các nửa mặc định đó thắng mà không thay thế default . Các quy tắc thêm bằng ip rule add cũng biến mất khi khởi động lại, nên hãy mang chúng trong một script up.

Kích thước gói là thất bại thứ hai bạn gặp

Một giao diện cầu ở 1500 byte trao cho đường hầm nhiều hơn mức nó chở được khi thêm các tiêu đề UDP và IP bên ngoài. Các trao đổi nhỏ đi qua, nên DNS phân giải và bắt tay TLS hoàn tất, rồi một lần tải lên lớn hơn bị treo mà không có lỗi ở đâu cả. Bên gửi tiếp tục lặp lại cùng kích thước phân đoạn vì thông điệp ICMP về nhu cầu phân mảnh, thứ sẽ bảo nó thu nhỏ, không bao giờ đến.

Sửa ở giao diện, ở MSS hoặc cả hai. Việc kẹp không cần sự hợp tác của khách:

ip link set dev tun0 mtu 1400
iptables -t mangle -A FORWARD -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Việc kẹp chỉ ghi lại TCP. UDP, kể cả QUIC, thay vào đó cần giao diện khách thấp hơn, với --opt com.docker.network.driver.mtu=1400 khi tạo mạng hoặc khóa compose tương ứng.

DNS và các kiểm tra đáng lặp lại

Phân giải tên đi theo cùng đường. Bộ phân giải nhúng của Docker tại 127.0.0.11 chuyển tiếp tới các máy chủ đã cấu hình của máy chủ, nên container thừa hưởng thứ máy chủ dùng. Khi vùng riêng chỉ được trả lời bởi một bộ phân giải tiếp cận được qua đường hầm, hãy trỏ khách tới địa chỉ đó, và kiểm tra bằng getent hosts bên trong container chứ không phải trên máy chủ.

  • Chạy yêu cầu thất bại bên trong khách; một kiểm tra phía máy chủ dùng địa chỉ nguồn khác và che giấu vấn đề.
  • Đọc các bộ đếm trên các quy tắc NAT và lọc bạn đã thêm. Một quy tắc không khớp gì là lỗi định tuyến, không phải lỗi chính sách.
  • Xác nhận địa chỉ đường hầm trong quy tắc SNAT của bạn là địa chỉ mà client thực sự có.
  • Hạ MTU của khách hoặc kẹp MSS trước khi đổ lỗi cho máy chủ về việc treo.

Đi xuống danh sách đó và dừng ở thất bại đầu tiên. Lớp đó là lớp cần sửa; bất cứ thứ gì thêm vào bên dưới chỉ che giấu ranh giới đang vỡ.

Bài viết liên quan