网上关于透明网关,旁路由的文章很多,大多数基于OpenWrt,硬件方面使用R2s,刷路由固件等等,它们的优点是简单快捷易上手。
而我的需求是,开源,低依赖,兼顾安全和使用。最好能设备之间获取相同的体验,一次(一处)配置,重复利用,减少配置。
根据"V2Ray 白话文指南 - 透明代理"的指引进行了简单的改进,使Wireguard接入的client都可以访问内部的DNS,自带透明代理。
2023.10.21,更新V2Ray与Wireguard的nftables共存实验 (终结篇)
WG proto ┌────────┐ port forward ┌───────┐ dokodemo-door ┌───────────┐
Client───────────►│ Router ├────────────────►│ V2Ray ├─────────────────►│ Wireguard │
└────────┘ └───────┘ └───────────┘
主要步骤:
拓扑情况:
10.12.0.0/24
10.12.0.200
10.12.0.100
10.16.0.0/24
用于nftable对国内IP的进行直连。sed
指令用于移除数据源中IPv6的数据。
创建 chnroute.nft,保存到 /etc/nftables/chnroute.nft
curl -sL https://github.com/Loyalsoldier/geoip/raw/release/text/cn.txt -o china_ip_list.txt
sed -i -r -e '/([^:]+\:){1,}.*$/d' china_ip_list.txt
echo "define chnroute_ipv4 = {" > chnroute.nft
cat china_ip_list.txt | awk -F\| '{ printf("%s,\n", $1) }' >> chnroute.nft
echo "}" >> chnroute.nft
用于nftable对局域网IP的进行直连。
创建 private-ipv4.nft,保存到 /etc/nftables/private-ipv4.nft
define private_ipv4 = {
0.0.0.0/8 ,
10.0.0.0/8 ,
100.64.0.0/10 ,
127.0.0.0/8 ,
169.254.0.0/16 ,
172.16.0.0/12 ,
192.0.0.0/24 ,
192.0.2.0/24 ,
192.88.99.0/24 ,
192.168.0.0/16 ,
198.18.0.0/15 ,
198.51.100.0/24 ,
203.0.113.0/24 ,
224.0.0.0/4 ,
233.252.0.0/24 ,
240.0.0.0/4 ,
255.255.255.255/32,
}
注意,private_ipv4
这里的IP地址,记得把服务器的地址加进去,就是直连远端的服务器。当然,也可以加一些DNS服务商的地址,令DNS查询直连。
顾名思义白名单,主要用途为服务器地址,ntp服务,dns服务等等的直连操作。
保存为v2ray.nft
本部分主要兼顾透明代理,假如单单网关或代理模式,则无需本部分
include "/etc/nftables/ipv4-whitelist.nft"
include "/etc/nftables/ipv4-private.nft"
include "/etc/nftables/ipv4-chnroute.nft"
define DIRECT-IPV4 = {
$whitelist_ipv4,
$private_ipv4,
$chnroute_ipv4,
}
table ip v2ray {
chain PREROUTING {
type filter hook prerouting priority filter; policy accept;
ip daddr $DIRECT-IPV4 return
meta l4proto { tcp, udp } ip daddr 10.12.0.0-10.12.0.255 return
meta mark 0x000000ff return
meta l4proto { tcp, udp } meta mark set 0x00000001 tproxy to 127.0.0.1:12345 accept
}
chain OUTPUT {
type route hook output priority filter; policy accept;
ip daddr $DIRECT-IPV4 return
meta l4proto { tcp, udp } ip daddr 10.12.0.0-10.12.0.255 return
meta mark 0x000000ff return
meta l4proto { tcp, udp } meta mark set 0x00000001 accept
}
}
table ip v2ray-filter {
chain DIVERT {
type filter hook prerouting priority mangle; policy accept;
meta l4proto tcp socket transparent 1 meta mark set 0x00000001 accept
}
}
保存为wg.sh
nft add table wg2nat
nft add chain wg2nat PREROUTING { type nat hook prerouting priority -100 \; }
nft add rule wg2nat PREROUTING meta nftrace set 1
nft add chain wg2nat INPUT { type nat hook input priority 100 \; }
nft add chain wg2nat OUTPUT { type nat hook output priority -100 \; }
nft add chain wg2nat POSTROUTING { type nat hook postrouting priority 100 \; }
nft add rule wg2nat POSTROUTING oifname "eth0" ip saddr 10.16.0.0/24 counter masquerade comment "wireguard-nat-rule"
或者使用nftable
格式,下面的格式并没有counter
,意思为不会统计包的数量。下面内容追加到nftables部分 —— V2Ray(V2Fly)
之后。
table ip wg2nat {
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
}
chain INPUT {
type nat hook input priority 100; policy accept;
}
chain OUTPUT {
type nat hook output priority -100; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
oifname "eth0" ip saddr 10.16.0.0/24 masquerade comment "wireguard-nat-rule"
}
}
主要部分routing,inbounds。PORT-ROUTE-FORWARD-PORT
为路由映射的端口,PORT-WG-REAL-WORKING-PORT
为Wireguard实际运行的端口。
routing需要配置国内geoip的规则,因为Wireguard的流量判断不被nftable处理。
特别,routing的规则(rules)可做2条,1允许进入的IP白名单,2拒绝其他的IP,以提高安全。
特别2,wg接入的客户端访问内网设备的时候,需要在inbounds添加dokodemo-door做转发,通过访问网关实现。
后续,思考如何不通过V2Ray进行透明代理,添加 meta l4proto { tcp, udp } ip daddr 10.16.0.0-10.16.0.255 return
使wg的IP穿过?有经验的朋友,可以在v2ex上留言讨论。
{
"log": {},
"dns": {},
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "mph",
"rules": [
{
"outboundTag": "Direct",
"port": "123",
"network": "udp",
"type": "field"
},
{
"outboundTag": "Direct",
"port": "{PORT-WG-FORWARD-PORT}",
"network": "udp",
"type": "field"
}
]
},
"inbounds": [
{
"tag": "TSP",
"protocol": "dokodemo-door",
"listen": "127.0.0.1",
"port": 12345,
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy",
"mark": 255
}
}
},
{
"tag": "WG",
"protocol": "dokodemo-door",
"listen": "0.0.0.0",
"port": {PORT-ROUTE-FORWARD-PORT},
"settings": {
"address": "127.0.0.1",
"port": {PORT-WG-REAL-WORKING-PORT},
"network": "udp",
"followRedirect": false
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy",
"mark": 255
}
}
}
]
}
没有什么要注意的,根据上面规划来划分网段即可。PiVPN已经提供了很完备的安装流程。
用tcpdump去debug数据包时的指令:
tcpdump -i wg0 -i eth0 -nn not icmp and 'port !443 && port !80 && port !53 && port !33 ' -w wg2.cap
以服务方式运行v2ray,储存为/lib/systemd/system/net.service
。
[Unit]
Description=Net Service
After=network.target nss-lookup.target
[Service]
User=nobody
WorkingDirectory=/{工作目录}
Environment="V2RAY_CONF_GEOLOADER=memconservative"
Environment="V2RAY_BUF_READV=auto"
Environment="V2RAY_LOCATION_ASSET=/{V2RAY的资源目录}"
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStartPre=/usr/sbin/sysctl -w net.core.rmem_max=2500000
ExecStart=/usr/local/bin/net -c /{配置目录}/config.json
# ExecStopPost=/usr/bin/systemctl stop coredns.service
Restart=on-failure
RestartPreventExitStatus=23
LimitNPROC=500
LimitNOFILE=1000000
RestartSec=3
TimeoutStopSec=10
StandardOutput=append:/var/log/net.log
StandardError=append:/var/log/net.log
[Install]
WantedBy=multi-user.target
以服务方式运行透明网关,储存为/etc/systemd/system/tproxyrule.service
。
[Unit]
Description=Tproxy rule
After=network.target
Wants=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/sbin/ip rule add fwmark 1 table 100 ; /sbin/ip route add local 0.0.0.0/0 dev lo table 100
ExecStart=/sbin/nft -f /etc/nftables/tproxy-rules.nft
ExecStop=/sbin/ip rule del fwmark 1 table 100 ; /sbin/ip route del local 0.0.0.0/0 dev lo table 100 ; /sbin/nft flush ruleset
[Install]
WantedBy=multi-user.target