PBR với netifd trên OpenWrt: tách đường VPN, WAN và các ngoại lệ
- Mục tiêu và giới hạn
- Nguyên tắc
- Hướng dẫn
- Route LAN vào VPN và DMZ vào WAN
- Route LAN của VPN server qua VPN client
- Route LAN vào VPN, rớt VPN thì quay về WAN
- Route LAN vào VPN theo IP set
- Route LAN vào VPN nhưng giữ port WAN forwarding
- Route LAN sang OpenVPN
- Route LAN sang Tailscale
- Prohibitive routes
- Prohibitive rules
- Kiểm tra và rollback
- Nguồn
- Footnotes
Bài này dùng netifd để tách các bảng định tuyến và lần lượt trình bày các tình huống IPv4/IPv6, VPN và ngoại lệ.1
Mục tiêu và giới hạn
PBR là cách chọn đường đi dựa trên chính sách của quản trị viên. Với pbr_netifd, mỗi interface được gán một routing table riêng rồi các rule có priority cụ thể sẽ quyết định traffic đi vào table nào. Cách này phù hợp với interface, subnet, địa chỉ IP và port; nếu cần match theo domain thì nên xem PBR app. mwan3 lại dành cho load-balancing hoặc failover nhiều WAN, không phải thay thế trực tiếp cho từng rule bên dưới.1
Trước khi làm, mình cần có interface lan, vpn, wan hoặc wan6 đã hoạt động; có quyền SSH; biết rõ tuyến tới endpoint VPN; và có một bản sao /etc/config/network cùng /etc/config/firewall. Mỗi đoạn dưới đây đều kết thúc bằng commit và restart, nên nên áp dụng từng mẫu một, kiểm tra rồi mới chuyển sang mẫu tiếp theo.
Nguyên tắc
- Gán mỗi interface vào routing table riêng; để
netifdtự tạo các route nền tảng. - Giữ default route trên từng upstream interface, nhưng không đặt các route tĩnh giống nhau vào nhiều table.
- Đưa tunnel endpoint và mạng nội bộ vào các rule cụ thể trước khi đưa default route vào rule tổng quát.
- Priority khoảng
30000thường đứng trước main table; khoảng40000–80000đứng sau main table. Hãy kiểm traip rulethực tế sau khi reload. - Nếu dùng IPv6, hãy cân nhắc NAT/NPT và source filter theo đúng topology; đừng coi cấu hình IPv4 là đủ để ngăn rò rỉ IPv6.1
Hướng dẫn
Các lệnh UCI dưới đây dùng đúng tên section của ví dụ; hãy đổi tên interface và table theo router thực tế. Các tên lan, vpn, dmz, wan, wan6, wgclient và các routing table ID chỉ là các tên mà mẫu nguồn yêu cầu; hãy đổi chúng theo cấu hình thật của router, không đổi nửa chừng giữa các đoạn.
Route LAN vào VPN và DMZ vào WAN
Mẫu đầu tiên đặt lan vào table 1, vpn vào table 2, dmz vào table 3, rồi đưa traffic vào từ LAN tới lookup 2 với priority 30000. Default route của DMZ vẫn nằm trong table WAN theo setup đã có.1
for IPV in 4 6douci set network.lan.ip${IPV}table="1"uci set network.vpn.ip${IPV}table="2"uci set network.dmz.ip${IPV}table="3"uci -q delete network.lan_vpn${IPV%4}uci set network.lan_vpn${IPV%4}="rule${IPV%4}"uci set network.lan_vpn${IPV%4}.in="lan"uci set network.lan_vpn${IPV%4}.lookup="2"uci set network.lan_vpn${IPV%4}.priority="30000"doneuci commit networkservice network restart
Route LAN của VPN server qua VPN client
Mẫu này giả định WireGuard client đã có peer. Hai dòng allowed_ips để peer nhận default route IPv4 và IPv6, sau đó LAN được đưa vào table VPN.1
uci add_list network.wgclient.allowed_ips="0.0.0.0/0"uci add_list network.wgclient.allowed_ips="::/0"for IPV in 4 6douci set network.lan.ip${IPV}table="1"uci set network.vpn.ip${IPV}table="2"uci -q delete network.lan_vpn${IPV%4}uci set network.lan_vpn${IPV%4}="rule${IPV%4}"uci set network.lan_vpn${IPV%4}.in="lan"uci set network.lan_vpn${IPV%4}.lookup="2"uci set network.lan_vpn${IPV%4}.priority="30000"doneuci commit networkservice network restart
Route LAN vào VPN, rớt VPN thì quay về WAN
Ở đây table 2 được gán cho wan/wan6 và priority 40000, nên có thể dùng WAN làm đường dự phòng sau khi rule VPN không còn áp dụng. Mình vẫn phải kiểm tra trạng thái tunnel và bảng ip rule thay vì mặc định rằng failover đã hoạt động.1
for IPV in 4 6douci set network.lan.ip${IPV}table="1"uci set network.wan${IPV%4}.ip${IPV}table="2"uci -q delete network.lan_wan${IPV%4}uci set network.lan_wan${IPV%4}="rule${IPV%4}"uci set network.lan_wan${IPV%4}.in="lan"uci set network.lan_wan${IPV%4}.lookup="2"uci set network.lan_wan${IPV%4}.priority="40000"doneuci commit networkservice network restart
Route LAN vào VPN theo IP set
Nguồn tạo ipset chứa các mạng của WAN, đánh dấu traffic LAN không khớp các mạng đó bằng 0x1, rồi lookup vào table VPN. Đây là nhánh dùng khi cần giữ một số đích ngoài VPN.1
for IPV in 4 6douci -q delete firewall.wan_set${IPV%4}uci set firewall.wan_set${IPV%4}="ipset"uci set firewall.wan_set${IPV%4}.name="wan${IPV%4}"uci set firewall.wan_set${IPV%4}.family="ipv${IPV}"uci set firewall.wan_set${IPV%4}.match="net"uci -q delete firewall.lan_mark${IPV%4}uci set firewall.lan_mark${IPV%4}="rule"uci set firewall.lan_mark${IPV%4}.name="Mark-LAN-VPN"uci set firewall.lan_mark${IPV%4}.src="lan"uci set firewall.lan_mark${IPV%4}.dest="*"uci set firewall.lan_mark${IPV%4}.ipset="!wan${IPV%4} dest"uci set firewall.lan_mark${IPV%4}.proto="all"uci set firewall.lan_mark${IPV%4}.family="ipv${IPV}"uci set firewall.lan_mark${IPV%4}.set_mark="0x1"uci set firewall.lan_mark${IPV%4}.target="MARK"uci set network.lan.ip${IPV}table="1"uci set network.vpn.ip${IPV}table="2"uci -q delete network.lan_vpn${IPV%4}uci set network.lan_vpn${IPV%4}="rule${IPV%4}"uci set network.lan_vpn${IPV%4}.in="lan"uci set network.lan_vpn${IPV%4}.mark="1"uci set network.lan_vpn${IPV%4}.lookup="2"uci set network.lan_vpn${IPV%4}.priority="30000"doneuci commit firewalluci commit networkservice firewall restartservice network restart
Route LAN vào VPN nhưng giữ port WAN forwarding
Mẫu này đánh dấu traffic HTTPS của một máy LAN theo MAC 00:11:22:33:44:55 và port 443, sau đó đẩy phần đã đánh dấu vào table WAN. MAC và port trong nguồn là ví dụ kỹ thuật; mình phải thay bằng máy chủ thật, đồng thời kiểm tra rule forward/redirect riêng của firewall.1
uci -q delete firewall.lan_webuci set firewall.lan_web="rule"uci set firewall.lan_web.name="Mark-HTTPS"uci set firewall.lan_web.src="lan"uci set firewall.lan_web.src_mac="00:11:22:33:44:55"uci set firewall.lan_web.src_port="443"uci set firewall.lan_web.dest="*"uci set firewall.lan_web.proto="tcp"uci set firewall.lan_web.set_mark="0x1"uci set firewall.lan_web.target="MARK"for IPV in 4 6douci set network.lan.ip${IPV}table="1"uci set network.wan${IPV%4}.ip${IPV}table="2"uci -q delete network.lan_web${IPV%4}uci set network.lan_web${IPV%4}="rule${IPV%4}"uci set network.lan_web${IPV%4}.in="lan"uci set network.lan_web${IPV%4}.mark="1"uci set network.lan_web${IPV%4}.lookup="2"uci set network.lan_web${IPV%4}.priority="30000"doneuci commit firewalluci commit networkservice firewall restartservice network restart
Route LAN sang OpenVPN
Nguồn giả định interface OpenVPN tên vpn. Default route 0.0.0.0/0 và ::/0 được đặt trong table 2, rồi LAN lookup table đó.1
for IPV in 4 6douci set network.lan.ip${IPV}table="1"uci set network.vpn.ip${IPV}table="2"uci -q delete network.vpn_rt${IPV%4}uci set network.vpn_rt${IPV%4}="route${IPV%4}"uci set network.vpn_rt${IPV%4}.interface="vpn"uci -q delete network.lan_vpn${IPV%4}uci set network.lan_vpn${IPV%4}="rule${IPV%4}"uci set network.lan_vpn${IPV%4}.in="lan"uci set network.lan_vpn${IPV%4}.lookup="2"uci set network.lan_vpn${IPV%4}.priority="30000"doneuci set network.vpn_rt.target="0.0.0.0/0"uci set network.vpn_rt6.target="::/0"uci commit networkservice network restart
Route LAN sang Tailscale
Tailscale có thể tự tạo rule. Mẫu dưới đây tạo rule riêng ở priority 30000 và dùng goto="10000" để override rule tích hợp, vì vậy phải kiểm tra ip rule sau khi Tailscale khởi động.1
for IPV in 4 6douci set network.lan.ip${IPV}table="1"uci -q delete network.lan_vpn${IPV%4}uci set network.lan_vpn${IPV%4}="rule"uci set network.lan_vpn${IPV%4}.in="lan"uci set network.lan_vpn${IPV%4}.lookup="52"uci set network.lan_vpn${IPV%4}.priority="30000"uci -q delete network.pbr${IPV%4}uci set network.pbr${IPV%4}="rule"uci set network.pbr${IPV%4}.goto="10000"uci set network.pbr${IPV%4}.priority="1"doneuci commit networkservice network restart
Prohibitive routes
Thay vì để traffic rơi xuống một default route không mong muốn, mẫu này tạo route kiểu prohibit trong table VPN. Interface loopback được dùng làm interface luôn up theo giả định của nguồn; hãy kiểm tra kết quả với ip route trước khi áp dụng trên router đang phục vụ người dùng.1
for IPV in 4 6douci set network.vpn.ip${IPV}table="2"uci -q delete network.vpn_ks${IPV%4}uci set network.vpn_ks${IPV%4}="route${IPV%4}"uci set network.vpn_ks${IPV%4}.interface="loopback"uci set network.vpn_ks${IPV%4}.type="prohibit"uci set network.vpn_ks${IPV%4}.metric="9000"uci set network.vpn_ks${IPV%4}.table="2"doneuci set network.vpn_ks.target="0.0.0.0/0"uci set network.vpn_ks6.target="::/0"uci commit networkservice network restart
Prohibitive rules
Nhánh cuối tạo rule action="prohibit" cho traffic từ LAN ở priority 32000. Đây là ranh giới an toàn để tránh leak khi VPN không có tuyến; nhưng nếu đặt priority sai, rule có thể chặn cả các exception hợp lệ.1
Kiểm tra và rollback
Sau mỗi mẫu, chạy ip rule show, ip -6 rule show, ip route show table all, ip -6 route show table all và kiểm tra endpoint VPN. Test một client LAN, một đích phải đi VPN và một đích phải đi WAN. Nếu mất mạng, khôi phục bản sao /etc/config/network hoặc /etc/config/firewall qua console/SSH đã chuẩn bị rồi restart dịch vụ; không trộn nhiều mẫu trước khi biết mẫu nào gây lỗi.
for IPV in 4 6douci -q delete network.lan_ks${IPV%4}uci set network.lan_ks${IPV%4}="rule${IPV%4}"uci set network.lan_ks${IPV%4}.in="lan"uci set network.lan_ks${IPV%4}.action="prohibit"uci set network.lan_ks${IPV%4}.priority="32000"doneuci commit networkservice network restart
Bình Luận & Thảo Luận
Góp ý, hỏi đáp và chia sẻ suy nghĩ của bạn