我正在尝试使用 PrivateInternetAccess 在 ArchLinux 上设置 OpenVPN 客户端。默认情况下,PIA 会推送以下路由:全部流量通过 VPN。我只想要一些应用程序使用 VPN。
为此,我使用了“基于策略的路由”。我创建了一个名为“vpn”的新路由表,并将有选择地将用户发送到该路由表。
执行这些命令后,“media”用户将被路由到“vpn”路由表:
$ echo 100 vpn >> /etc/iproute2/rt_tables
$ iptables -t mangle -I OUTPUT -m owner --uid-owner media -j MARK --set-mark 0x1
$ ip rule add fwmark 0x1 table vpn
我还修改了我的 OpenVPN 客户端配置,以便我的路由表能够正确填充:
$ cat /etc/openvpn/client.conf
client
dev tun
proto udp
remote us-newyorkcity.privateinternetaccess.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/ca.crt
tls-client
remote-cert-tls server
auth-user-pass /etc/openvpn/login.conf
comp-lzo
verb 1
reneg-sec 0
crl-verify /etc/openvpn/crl.pem
# This will override PIA so that traffic will route through our normal gateway
route 0.0.0.0 192.0.0.0 net_gateway
route 64.0.0.0 192.0.0.0 net_gateway
route 128.0.0.0 192.0.0.0 net_gateway
route 192.0.0.0 192.0.0.0 net_gateway
# Calling these scripts will add the PIA routes to the vpn table
script-security 2
up /etc/openvpn/up.sh
down /etc/openvpn/down.sh
$ cat /etc/openvpn/up.sh
#!/bin/sh
ip route add table vpn default via $ifconfig_remote
$ cat /etc/openvpn/down.sh
#!/bin/sh
ip route flush table vpn
运行后的路由表如下openvpn /etc/openvpn/client.conf
:
$ ip route show table main
0.0.0.0/2 via 192.168.1.1 dev eth0
0.0.0.0/1 via 10.197.1.5 dev tun0
default via 192.168.1.1 dev eth0 src 192.168.1.124 metric 202
10.197.1.1 via 10.197.1.5 dev tun0
10.197.1.5 dev tun0 proto kernel scope link src 10.197.1.6
64.0.0.0/2 via 192.168.1.1 dev eth0
128.0.0.0/2 via 192.168.1.1 dev eth0
128.0.0.0/1 via 10.197.1.5 dev tun0
192.0.0.0/2 via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.124 metric 202
209.95.50.86 via 192.168.1.1 dev eth0
$ ip route show table vpn
default via 10.197.1.5 dev tun0
作为未被路由到“vpn”表的用户,一切正常运行:
$ whoami
jordan
$ ping -c 2 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=46 time=38.9 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=39.0 ms
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 38.952/38.999/39.047/0.203 ms
但是,对于确实被路由到“vpn”表的用户来说,事情却失败了:
$ whoami
media
$ ping -c 2 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 999ms
我查看了一下tcpdump -i tun0
发生了什么。看来 icmp 请求是通过 tun0 发出的,但是不会回来?
$ tcpdump -i tun0
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
15:37:30.134399 IP keep > google-public-dns-a.google.com: ICMP echo request, id 10256, seq 1, length 64
15:37:31.143217 IP keep > google-public-dns-a.google.com: ICMP echo request, id 10256, seq 2, length 64
有什么想法吗?:(
==== 编辑 #1 ====
作为健全性检查,如果我们发送全部通过 VPN 的流量(通过删除 /etc/openvpn/client.conf 中的“route xxxx 192.0.0.0 net_gateway”行),我们可以正常获得 icmp 响应:
$ tcpdump -i tun0
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
16:26:54.401732 IP keep > google-public-dns-a.google.com: ICMP echo request, id 10480, seq 1, length 64
16:26:54.483122 IP google-public-dns-a.google.com > keep: ICMP echo reply, id 10480, seq 1, length 64
16:26:55.403465 IP keep > google-public-dns-a.google.com: ICMP echo request, id 10480, seq 2, length 64
16:26:55.485068 IP google-public-dns-a.google.com > keep: ICMP echo reply, id 10480, seq 2, length 64
==== 编辑 #2 ====
根据 MariusMatutiae 的建议,我尝试使用--route-noexec
标志在 中手动设置路线/etc/openvpn/up.sh
。我们还在 中禁用了反向路径过滤/etc/openvpn/up.sh
,并在 中重新启用了它/etc/openvpn/down.sh
:
$ cat /etc/openvpn/up.sh
#!/bin/sh
ip route add table vpn 0.0.0.0/1 via $ifconfig_remote
ip route add table vpn 128.0.0.0/1 via $ifconfig_remote
ip route add table vpn $route_network_1 via $ifconfig_remote
ip route add table vpn $trusted_ip via $route_net_gateway
ip route add table vpn $ifconfig_remote dev tun0 proto kernel src $ifconfig_local
ip route add table vpn 192.168.1.0/24 dev eth0 proto kernel src 192.168.1.124 metric 202
ip route del table main $ifconfig_remote
# Disable reverse path filtering
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 0 > $f;
done
$ cat /etc/openvpn/down.sh
#!/bin/sh
ip route flush table vpn
# Re-enable reverse path filtering
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 1 > $f;
done
之后,我的路由表如下所示:
$ ip route show table main
default via 192.168.1.1 dev eth0 src 192.168.1.124 metric 202
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.124 metric 202
$ ip route show table vpn
0.0.0.0/1 via 10.173.1.5 dev tun0
10.173.1.1 via 10.173.1.5 dev tun0
10.173.1.5 dev tun0 proto kernel scope link src 10.173.1.6
128.0.0.0/1 via 10.173.1.5 dev tun0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.124 metric 202
209.95.50.133 via 192.168.1.1 dev eth0
然而,用户“media”仍然无法ping 8.8.8.8
。tcpdump -i tun0
仍然报告说没有回复:(
答案1
您错误地通过两个路由表划分了路由。main
表中出现的以下路由属于该vpn
表:
0.0.0.0/1 via 10.197.1.5 dev tun0
128.0.0.0/1 via 10.197.1.5 dev tun0
10.197.1.1 via 10.197.1.5 dev tun0
10.197.1.5 dev tun0 proto kernel scope link src 10.197.1.6
209.95.50.86 via 192.168.1.1 dev eth0
下面的路由属于这两个表:
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.124 metric 202
原因在于:前四条规则涉及接口tun0
,即 OpenVPN 启动的虚拟 NIC,这显然与main
表无关。第五条规则教您的内核如何访问您的 PIA 服务器。最后一条规则允许两个表访问本地 PC、打印机、NAS 等。
至于如何自动设置,由于您的 OpenVPN 服务器tun0
每次可能都会为您分配一个不同的 IP 地址,因此这有点复杂:由于您无法控制服务器,因此您可能无法将其配置为为您的tun0
接口分配静态地址。
因此,您需要做的是首先学会使用选项--route-no-exec
。手册状态:
–route-no-exec(路线无执行)
不要自动添加或删除路由。而是使用环境变量将路由传递给 --route-up 脚本。
然后你应该学习如何使用环境变量(上面链接的手册末尾有一整节内容),尤其是那些被称作ifconfig--
某物的东西。
此外,你会发现有必要禁用内核的反向路径过滤,
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
我会把这个命令放在你的up
脚本中,然后反向命令
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 1 > $f; done
在你的down
脚本中。这是必要的,因为你在主表上有一个默认网关,这意味着,当内核检查是否存在欺骗时,任何数据包被标记。
另外,更简单的是,您可以考虑切换两个表的角色:让 OpenVPN 自动执行其操作,并设置main
路由表的路由,并设置一个名为的新路由表,non-vpn
其中包含您通常的配置,
# ip route show
default via 192.168.0.1 dev eth0 proto static
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.74 metric 1
(或类似的东西)对于数据包不是与上面的标记匹配。这更容易设置。
答案2
我放弃了让基于策略的路由发挥作用。相反,我最终使用网络命名空间来实现我的目标。可以在此处找到一个不错的入门指南https://schnouki.net/posts/2014/12/12/openvpn-for-a-single-application-on-linux/
特别感谢 MariusMatutiae 的所有帮助!