Ứng dụng cục bộ, quy tắc VPN: vì sao chuỗi FORWARD của bạn không bao giờ kích hoạt
Một tiến trình trên máy dùng OUTPUT, không phải FORWARD. Chọn hook trong namespace đúng, rồi quyết định xem một dấu hay một tuyến đơn giản đã đủ chưa.
Bạn thêm một quy tắc cho lưu lượng VPN vào chuỗi FORWARD, chạy yêu cầu từ chính máy Linux đó, và bộ đếm vẫn đứng ở số không. Quy tắc có lẽ là đúng. Gói tin chưa bao giờ đi theo đường đó, vì một yêu cầu sinh ra trên chính máy thì máy không chuyển tiếp nó. Việc lần theo vòng đời của gói tin từ socket đến dây cáp giải thích vì sao, và quy tắc nên nằm ở đâu.
Tìm nơi gói tin ra đời
Thứ tự hook của Netfilter tách ba trường hợp, và chỉ một trong số đó liên quan đến FORWARD:
| Origin | Path | Hook that sees it |
|---|---|---|
| Local process | OUTPUT then POSTROUTING | OUTPUT, POSTROUTING |
| Addressed to a local service | PREROUTING then INPUT | PREROUTING, INPUT |
| Received for another host | PREROUTING, FORWARD, POSTROUTING | FORWARD |
Một ứng dụng cục bộ bắt đầu trên đường OUTPUT, vì nhân đã xác định rằng gói tin phát sinh ở đây và không bao giờ là ứng viên để chuyển tiếp. Việc tạo một tunnel không thay đổi cách phân loại đó; tunnel là thiết bị mà gói tin có thể đi ra, không phải giai đoạn biến đầu ra cục bộ thành lưu lượng đi qua.
Hai gói tin, một câu chuyện
Khi tunnel đã lên, gói tin bên trong của ứng dụng và gói tin bên ngoài đã mã hóa là hai đối tượng khác nhau. Gói bên trong mang đích đến và tải trọng mà ứng dụng đã chọn. Gói bên ngoài mang bản mã đến địa chỉ và cổng của đối tác, và chính nó mới thực sự đi qua giao diện vật lý.
Chính sách của bạn liên quan đến một trong hai, và chọn sai là lý do phổ biến khiến một quy tắc có vẻ không làm gì cả. Một quy tắc khớp với địa chỉ bên ngoài của đối tác nhìn mọi gói tin của tunnel như nhau, bất kể ứng dụng bên trong làm gì. Một quy tắc khớp với đích bên trong nhìn thấy lưu lượng bạn quan tâm, và nó chỉ kích hoạt nếu gói tin đó đến được hook nơi bạn đã đặt quy tắc.
Namespace dịch chuyển ranh giới
Một container làm việc này khó hơn mà không đổi nguyên tắc. Yêu cầu của ứng dụng là đầu ra cục bộ bên trong namespace mạng của container. Máy chủ nhận lưu lượng đó trên một giao diện Ethernet ảo và có thể chuyển tiếp nó, nên một quy tắc FORWARD trên máy chủ có thể có ý nghĩa dù tiến trình tạo ra yêu cầu chưa bao giờ chạm vào chuỗi OUTPUT của máy chủ.
Trước khi chọn hook, hãy xác định nơi bộ quy tắc và tunnel thực sự sống. Tunnel bên trong container, tunnel trên máy chủ với container được định tuyến qua nó, và tunnel trong một namespace riêng là ba cấu trúc liên kết khác nhau với ba vị trí quy tắc khác nhau. Hãy đọc các quy tắc bên trong namespace sở hữu thiết bị, không chỉ trong namespace mặc định.
Hỏi xem bạn có thực sự cần một dấu không
Nếu mục tiêu là gửi một tiền tố đích qua tunnel, một tuyến cho tiền tố đó có thể là đủ. Hãy kiểm tra các tuyến hiệu lực trước, kể cả mọi thứ máy chủ đã đẩy xuống: một tuyến mặc định bị chuyển hướng gửi nhiều lưu lượng qua VPN hơn hẳn chính sách hẹp bạn đã nghĩ, và không lượng đánh dấu nào khiến kết quả khớp với ý định.
Phân loại là cần thiết khi quyết định phụ thuộc vào thứ mà tuyến không thể diễn đạt, như nguồn, người dùng hoặc tiến trình. Với lưu lượng nhận vào, đánh dấu trong PREROUTING hoạt động. Với lưu lượng cục bộ, việc đánh dấu phải diễn ra ở giai đoạn đầu ra có thể kích hoạt một lần tra tuyến mới sau khi dấu được đặt. Với nftables đó là một chuỗi kiểu route trên hook đầu ra; một chuỗi lọc đầu ra thông thường sẽ đặt giá trị và để nguyên tuyến.
Với iptables, tương đương nằm trong cách bố trí mangle OUTPUT, và yêu cầu tương tự cũng áp dụng: quyết định định tuyến phải được xem lại sau khi dấu tồn tại. Sau đó một quy tắc chính sách phải tiêu thụ dấu và trỏ tới một bảng có tuyến dùng được. Đặt dấu conntrack là thao tác thứ ba, riêng biệt; nó lưu một giá trị trên luồng và tự nó không đặt giá trị đó lên gói hiện tại để định tuyến đọc được.
Kiểm chứng tra cứu và bộ đếm cùng lúc
nft list ruleset
ip route get 198.51.100.25
ip route get 198.51.100.25 mark 0x1
Địa chỉ và dấu chỉ là ví dụ, và hai truy vấn tuyến trả lời hai câu hỏi khác nhau. Truy vấn thứ hai hỏi nhân sẽ làm gì nếu gói tin mang dấu đó; nó không cho thấy bộ quy tắc của bạn gán một dấu. Hãy coi đó là phép thử giả thuyết, không phải bằng chứng rằng chính sách hoạt động.
Sau đó chạy một yêu cầu vô hại và quan sát hai thứ cùng lúc: bộ đếm trên hook bạn đã chọn và lối ra mà gói tin thực sự đi. Nếu bộ đếm tăng mà gói tin đi ra chỗ khác, dấu hoặc bảng bị sai. Nếu cả hai đều không nhúc nhích, gói tin hoàn toàn không đến được hook đó, và câu hỏi tiếp theo là namespace hoặc đường đi.
Cuối cùng, hãy giới hạn phép thử trong những gì cơ chế có thể diễn đạt. Một tuyến cho một địa chỉ không trở thành chính sách lâu dài cho một dịch vụ có địa chỉ thay đổi. Hãy quyết định phạm vi gói tin trước, rồi kiểm tra xem thiết kế định tuyến và đánh dấu có giữ được phạm vi đó sau khi kết nối lại hay không.