Hãy hình dung một máy chủ Linux vừa kết thúc VPN vừa chạy một container trên mạng macvlan. LAN là 192.168.60.0/24, container giữ 192.168.60.210, và các máy khách VPN đến từ 10.95.0.0/24. Máy khách tới được dịch vụ, câu trả lời không bao giờ quay về, và mọi quy tắc tường lửa trên máy chủ đều có vẻ đúng. Hai ràng buộc riêng biệt tạo ra triệu chứng đó, và sửa một cái thì cái kia vẫn nằm nguyên chỗ cũ.

Hai ràng buộc, một triệu chứng

Ràng buộc thứ nhất là cách ly: trình điều khiển macvlan của Docker không cho container nói chuyện với máy chủ của chính nó qua giao diện cha. Lưu lượng giữa ngăn xếp máy chủ và container không có đường đi, dù các tuyến trên máy chủ được viết thế nào.

Ràng buộc thứ hai là đường về. Cổng mặc định của container thường là bộ định tuyến LAN, nên câu trả lời gửi cho máy khách VPN đi ra qua bộ định tuyến đó. Nếu bộ định tuyến không có tuyến quay lại nhóm VPN, câu trả lời bị bỏ ở đâu đó giữa đường.

Hai lỗi này trông giống hệt nhau từ phía máy khách: một yêu cầu đến và một câu trả lời không bao giờ đến. Chúng cần cách sửa khác nhau, và một quy tắc masquerade rộng có thể che mất cái nào vẫn còn hỏng. Vì vậy phép thử đầu tiên là bắt gói ở phía container chứ không phải một thay đổi tường lửa nữa. Nếu yêu cầu không bao giờ xuất hiện ở đó, thì ràng buộc cách ly đang có hiệu lực.

Dựng shim phía máy chủ, hẹp

Một giao diện macvlan thứ hai trên máy chủ, gắn vào cùng giao diện cha ở chế độ cầu, cho ngăn xếp máy chủ danh tính riêng trên phân đoạn LAN đó. Hãy dành cho nó một địa chỉ ngoài nhóm DHCP và ngoài dải container, và chỉ cho container đi qua nó.

ip link add macvlan-vpn link eth0 type macvlan mode bridge
ip address add 192.168.60.250/32 dev macvlan-vpn
ip link set macvlan-vpn up
ip route add 192.168.60.210/32 dev macvlan-vpn src 192.168.60.250

Lệnh đầu tiên tạo giao diện ở chế độ cầu, đây là mặc định được ghi trong tài liệu cho trình điều khiển này và là chế độ bạn muốn cho lưu lượng giữa máy chủ và container. Lệnh thứ hai gán một /32 để địa chỉ không đụng tiền tố LAN, và lệnh thứ tư chỉ gửi địa chỉ của container qua shim.

Đừng chuyển hướng toàn bộ LAN qua giao diện mới. Tuyến kết nối thông thường vẫn giữ nguyên, và tuyến của máy chủ cố ý hẹp để một container có được đường đi mà phần còn lại của phân đoạn không đổi hành vi. Một tuyến mặc định trỏ tới shim sẽ là một thiết kế khác với hậu quả khác. Hãy giữ nó cụ thể và bán kính ảnh hưởng vẫn nhỏ.

Những lệnh này thay đổi cấu hình mạng trên một máy chủ đang chạy. Trên thiết bị hoặc nền tảng được quản lý, hãy dùng cơ chế cấu hình được nền tảng đó hỗ trợ, vì một thay đổi tương tác trong root shell có thể không sống sót qua bản nâng cấp.

Quyết định cách giữ nguồn

Khi shim đã vào chỗ, container có thể tới được máy chủ, và câu hỏi trở thành câu trả lời mang địa chỉ nguồn nào. Giữ nguyên nguồn VPN gốc là mô hình sạch hơn: container thấy địa chỉ thật của máy khách, và nhật ký cùng quy tắc truy cập của dịch vụ có thể dùng nó.

Để điều đó hoạt động, cấu hình mạng của container phải gửi lưu lượng cho 10.95.0.0/24 tới 192.168.60.250 làm cổng cho đường này. Đó là một thiết lập tuyến hoặc cổng được hỗ trợ bên trong cấu hình của chính container, không phải mẹo ở phía máy chủ. Hãy xác nhận từ bên trong container thay vì cho rằng thiết lập đã có hiệu lực. Một cổng mặc định vẫn trỏ tới bộ định tuyến LAN sẽ tiếp tục gửi câu trả lời vào cùng cái lỗ đó.

Nếu thiết kế được duyệt thay vào đó lại giấu nguồn VPN, hãy giới hạn bản dịch cho hẹp: nhóm VPN thật, shim là giao diện ra, và container cùng dịch vụ cụ thể.

iptables -t nat -A POSTROUTING -s 10.95.0.0/24 -o macvlan-vpn -j SNAT --to-source 192.168.60.250

Khớp shim như giao diện ra, không phải đường hầm. Một quy tắc gọi đường hầm là giao diện ra sẽ không khớp với gói tin đi về phía container, và đó chính là lỗi mà kiểu lắp đặt này thường chứa. Hãy giữ quy tắc hẹp như câu trên và xem lại khi nhóm thay đổi.

Kiểm chứng từ máy khách, không phải từ máy chủ

Một yêu cầu từ chính máy chủ, gắn với địa chỉ của shim, chứng minh shim tới được container. Nó không chứng minh container gửi lưu lượng trở lại nguồn đã giữ 10.95.0.x, vì địa chỉ nguồn trong phép thử đó là của chính shim.

curl -sS --interface 192.168.60.250 -o /dev/null -w '%{http_code}\n' http://192.168.60.210:8080/health
sudo tcpdump -ni macvlan-vpn host 192.168.60.210

Lệnh đầu kiểm tra đoạn từ máy chủ tới container, và lệnh thứ hai cho thấy điều gì thực sự đi qua shim khi một máy khách VPN thật gửi yêu cầu. Hãy xem địa chỉ nguồn trên đường vào và liệu có câu trả lời xuất hiện với cùng luồng hay không.

Chuyển tiếp phải được bật trên máy chủ, và đường chuyển tiếp cần một sự cho phép cho nhóm VPN tới shim trên cổng dịch vụ cùng các câu trả lời đã thiết lập. Một quy tắc INPUT trên máy chủ không cho phép lưu lượng được chuyển tiếp, và đó là lý do phổ biến khiến gói bị bỏ sau khi định tuyến có vẻ đúng.

Hãy chạy ứng dụng thật với thông tin đăng nhập thật, rồi kiểm tra rằng dịch vụ thứ hai, không liên quan trên cùng container vẫn từ chối bạn. Nếu cách duy nhất để nó chạy là mở mọi quy tắc chuyển tiếp, thiết kế chưa xong. Cũng ghi lại dịch vụ đã ghi nhận địa chỉ nguồn nào, vì đó là thứ mà các quy tắc truy cập của chính nó sẽ dùng từ nay.

Bài viết liên quan