Yêu cầu luôn là một biến thể của cùng một câu: giữ VPN để duyệt web, nhưng để SSH hoặc một tác vụ sao lưu dùng kết nối thường. Lời khuyên đang lan truyền về việc này, chuyển tiếp cổng bằng các quy tắc NAT của iptables, không làm được điều đó. Một quy tắc NAT ghi lại địa chỉ và cổng bên trong một luồng; nó không bao giờ chọn giao diện mà gói tin đi ra. Quyết định đó thuộc về tra cứu tuyến, và tra cứu tuyến cần được giúp để nhìn thấy một cổng.

Vì sao một tuyến thường không khớp được với cổng

Linux chọn tuyến bằng cách tra địa chỉ đích trong một bảng, sau khi lọc các bảng ứng viên qua các quy tắc chính sách. Những quy tắc đó có thể khớp với dấu tường lửa, địa chỉ nguồn, giao diện vào hoặc UID. Bản thân mục tuyến không có trường cổng, vì tuyến được chọn trước khi phần đầu giao vận trở nên quan trọng.

iproute2 và nhân hiện nay mở rộng bộ chọn quy tắc: ipproto, sport và dport được chấp nhận, nên ip rule add ipproto tcp dport 22 table 100 diễn đạt ý định trong một dòng. Chạy ip rule add help trên máy thật để xem bản dựng của bạn có liệt kê dport không. Nếu có, bạn xong với một lệnh; nếu không, đường đi qua dấu bên dưới là tương đương.

Dù thế nào, quy tắc chỉ là con trỏ. Bảng 100 vẫn cần một tuyến, và đó là phần người ta quên.

Ba mảnh, hay một

Dạng hiện đại là quy tắc đơn trên cùng một tuyến trong bảng thay thế. Dạng cổ điển thêm netfilter vào giữa: đánh dấu gói, rồi khớp dấu trong một quy tắc. Cả hai kết thúc ở cùng một lần tra cứu; chúng khác nhau ở chỗ đọc cổng.

Cách đánh dấu tồn tại vì netfilter thấy phần đầu giao vận còn mã định tuyến thì trước đây không thấy. Đó là toàn bộ cơ chế, và nó giải thích vì sao đường dự phòng là ba mảnh phối hợp chứ không phải một lệnh khéo léo.

Ba mảnh theo thứ tự

Đầu tiên, đánh dấu các luồng muốn chuyển hướng, trong một chuỗi chạy đủ sớm để dấu tồn tại trước quyết định tuyến:

nft add table inet vpnexempt
nft 'add chain inet vpnexempt out { type route hook output priority mangle; }'
nft add rule inet vpnexempt out tcp dport 22 meta mark set 0x1

Thứ hai, gửi các gói đã đánh dấu tới một bảng riêng:

ip rule add fwmark 0x1/0x1 table 100

Thứ ba, cho bảng đó một tuyến mặc định qua đường liên kết vật lý. Các địa chỉ chỉ là minh họa; hãy thay bằng của bạn:

ip route add default via 192.168.1.1 dev wlan0 table 100

Bỏ qua bước thứ ba và gói đã đánh dấu không tìm thấy tuyến trong bảng 100, nên nó thất bại. Triệu chứng trông như quy tắc đã làm hỏng mạng, trong khi thực ra nó đã bỏ đi con đường duy nhất còn lại của gói.

Điều gì giữ nó hoạt động sau gói đầu tiên

Dấu áp dụng cho gói đi ra. Phản hồi đến trên giao diện vật lý với địa chỉ mà liên kết đã cấp, và bảng chính có thể vẫn ưu tiên đường hầm cho đích đó. Hai cơ chế giữ đường phản hồi nhất quán: một quy tắc dựa trên nguồn cho địa chỉ liên kết, và connection tracking giữ quyết định suốt vòng đời luồng. Lưu dấu vào conntrack và khôi phục khi phản hồi là cách thường làm để một ngoại lệ có trạng thái ổn định.

Lọc đường ngược là kẻ giết người thầm lặng thứ hai. Nếu nhân kiểm tra nghiêm ngặt đường về và phản hồi đến trên giao diện mà bảng chính không chọn, gói bị bỏ mà không có thông báo. rp_filter giá trị mặc định khác nhau theo bản phân phối và theo việc giao diện có được chuyển tiếp hay không, nên hãy đọc giá trị hiệu lực thay vì cho rằng nó đang tắt.

Đừng đánh dấu chính giao vận của đường hầm. Đường hầm WireGuard hay UDP OpenVPN không phải TCP 22, nên công thức trên an toàn, nhưng một ngoại lệ viết là mọi thứ trừ đích đường hầm có thể bắt các gói đã mã hóa và phá đường hầm theo cách trông như vòng lặp định tuyến.

Kiểm tra thay vì cho rằng

Hỏi nhân xem nó sẽ đi đường nào, cho đích và cho cổng, rồi chạy truyền thật:

ip route get 203.0.113.10
ip rule show
nft list ruleset

Nếu dấu tồn tại mà bộ đếm không bao giờ nhích, quy tắc không khớp. Bộ đếm là khác biệt giữa cấu hình bạn viết và chính sách nhân đang áp dụng.

Khi đây là công cụ sai

Ngoại lệ theo cổng vốn dễ vỡ. Dịch vụ đổi cổng, HTTP/3 đến dưới dạng UDP 443, và một ứng dụng có thể mở nhiều luồng khác loại. Nếu yêu cầu thật là chương trình này dùng liên kết thường, lớp định tuyến là chỗ sai; không gian tên mạng hoặc proxy theo ứng dụng phù hợp hơn.

Nếu yêu cầu là mạng con này dùng liên kết thường, hãy bỏ hẳn các dấu và thêm một tuyến cụ thể hơn. Nó đơn giản hơn, nhìn thấy trong một bảng, và người sau có thể kiểm toán mà không cần đọc bộ quy tắc.

Hãy ghi ra bạn thực sự có yêu cầu nào trong ba yêu cầu trước khi chạm vào tường lửa. Cơ chế trên là chính xác, và sự chính xác áp vào sai lớp chỉ là thời gian ngừng lâu hơn.

Bài viết liên quan