Pod OpenVPN trong Kubernetes không chuyển tiếp gì? Kiểm tra ip_forward
Các máy khách ping pod VPN và không có gì vượt qua. Namespace đã tắt chuyển tiếp, và bản sửa phải sống sót qua admission, các lần khởi động lại và đường quay về.
Laptop của bạn hiện một đường hầm xanh, nhật ký máy khách báo rằng các tuyến đã được đẩy, và địa chỉ duy nhất trả lời là IP đường hầm của chính máy chủ VPN. Các ping vượt qua nó đều chết bất kể bạn nhắm vào đâu, dù là ClusterIP, địa chỉ của pod khác hay một tên máy chủ công khai. Sự kết hợp đó mang tính chẩn đoán chứ không mơ hồ: việc bóc gói hoạt động, còn chặng thứ hai thì không.
Tôi gặp điều này trên một node Debian chạy image OpenVPN được cài bằng chart, và đường hầm là phần dễ. Máy khách nhận một lease 10.8.0.x, 10.8.0.1 trả lời, và 10.96.0.10, dịch vụ DNS của cụm, hết thời gian. Không có gì trong nhật ký máy chủ trông ốm yếu, và đó chính là lý do người ta bắt đầu viết lại các quy tắc iptables mà họ chưa bao giờ cần chạm vào.
Một pod VPN là một router, và đáng nói thẳng như vậy. Các gói được mã hóa đến, được bóc trong namespace mạng của chính pod, rồi phải ra ngoài qua một giao diện khác. Linux từ chối bước thứ hai đó trừ khi chuyển tiếp được bật bên trong chính namespace ấy.
Máy khách có thể và không thể chạm tới gì
Địa chỉ trả lời thuộc về điểm cuối của đường hầm, và tiến trình VPN sở hữu nó cục bộ, nên chuyển tiếp hoàn toàn không tham gia. Mọi thứ khác, kể cả pod khác, VIP dịch vụ hay internet, cần kernel di chuyển một gói từ tun0 sang eth0. Khi việc di chuyển đó bị từ chối, triệu chứng là sự im lặng đồng đều chứ không phải từ chối, vì không có gì tạo ra lỗi ICMP ngược xuống đường hầm. Bạn nhận được timeout, không phải thông báo không thể tới, và đó là manh mối rằng không ai định tuyến cả.
Hai phút phân loại thu hẹp tầng. Nếu tên DNS thất bại trong khi ping thô tới IP dịch vụ thành công, vấn đề nằm ở hệ thống ống DNS, không phải chuyển tiếp. Nếu mọi đích vượt quá địa chỉ peer đều hết thời gian, kể cả IP trần, hãy bắt đầu từ namespace.
Đọc thiết lập bên trong namespace
Mỗi pod nhận namespace mạng riêng, và net.ipv4.ip_forward là một sysctl cho mỗi namespace. Thay đổi nó trên node không cho bạn biết gì về container, và đó là một cách đáng tin cậy để dành cả buổi chiều trên máy sai.
kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0
Số không xác nhận chẩn đoán. Giá trị cũng không được thừa hưởng từ cấu hình thời gian chạy của node: nó bắt đầu từ mặc định của image mỗi lần container được tạo. Vì vậy một bản sửa áp dụng thủ công bằng kubectl exec ... sysctl -w sẽ biến mất ở lần khởi động lại tiếp theo và quay lại như một lỗi có vẻ ngắt quãng.
Bật nó để manifest giữ lại
Lựa chọn bền vững là một init container chạy trong cùng pod sandbox và ghi giá trị trước khi container VPN khởi động. Các container trong một pod chia sẻ namespace mạng, nên thiết lập được mang vào container chính.
initContainers:
- name: enable-forwarding
image: busybox:1.36
command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
securityContext:
privileged: true
Chính privileged: true là dòng mà người đánh giá phản đối, và họ đúng. Ghi một sysctl cần hoặc đặc quyền hoặc một khả năng hẹp, và một cụm chạy Pod Security Standard hạn chế có thể từ chối thẳng một init container có đặc quyền. Nếu admission từ chối pod, bạn biết ngay lúc tạo, điều đó ít nhất cũng trung thực và dễ đọc.
Lựa chọn sạch hơn là danh sách securityContext.sysctls ở cấp pod, cho phép kubelet áp dụng sysctl có namespace mà không cấp đặc quyền cho bất kỳ container nào. Nó chỉ bao gồm các tên trong danh sách cho phép của cụm. Việc net.ipv4.ip_forward có trong danh sách đó hay không phụ thuộc vào cách kubelet được cấu hình, và một pod yêu cầu sysctl ngoài danh sách sẽ bị từ chối ở admission hoặc mắc kẹt trong CreateContainerConfigError trong khi sandbox được thử lại. Kiểm tra danh sách cho phép trước khi cho rằng trường đó bị hỏng.
Trước khi tự tay dựng một trong hai cách, hãy xem chart đã phơi bày công tắc chưa, thường là thứ gì đó như ipForwardInitContainer: true. Dùng giá trị của chart giữ tệp ghi đè của bạn nhỏ và sống sót qua lần nâng cấp tiếp theo, điều mà các chỉnh sửa Deployment vá tay không làm được.
Nửa sau: đường quay về và chính sách
Bật chuyển tiếp là cần, không đủ. Nếu máy khách dùng một pool riêng mà mạng cụm không có tuyến tới, các phản hồi không tìm được đường quay vào đường hầm, và pod phải masquerade lưu lượng đó về phía đích của nó.
Chính sách nằm trên đó. Các đối tượng NetworkPolicy và quy tắc tường lửa đám mây được đánh giá theo địa chỉ nguồn mà gói mang sau khi dịch, nên một quy tắc viết cho pool máy khách có thể lặng lẽ ngừng khớp khi masquerading được bật.
Khi máy khách chỉ cần chạm tới các dịch vụ bên trong cụm, việc để VPN kết thúc tại VIP của dịch vụ là thiết kế nhỏ hơn so với định tuyến vào mạng pod. Nó cũng giữ pool máy khách hoàn toàn ngoài bảng định tuyến của CNI.
Cô lập chặng hỏng khỏi máy khách
Làm việc hướng ra ngoài và dừng ở bước im lặng đầu tiên. Bắt đầu với peer VPN, rồi thử địa chỉ của một pod nào đó, rồi một VIP dịch vụ, và chỉ sau đó một đích công cộng. Mỗi thành công dịch chuyển ranh giới một chặng, và mỗi thất bại gọi tên tầng đáng kiểm tra. Sau bất kỳ lần triển khai nào, exec vào và đọc lại sysctl, vì pod bị thay thế bắt đầu từ mặc định của image và một bản sửa chưa bao giờ nằm trong manifest thì đã biến mất.
Tài liệu Kubernetes về việc dùng sysctl trong một cụm liệt kê những tên nào có thể đặt qua ngữ cảnh bảo mật của pod, và đó là nơi đầu tiên cần xem khi admission từ chối thay đổi.