![在 Ubuntu Server 12.04 上通过 OpenVPN 拆分隧道路由特定端口](https://linux22.com/image/1366446/%E5%9C%A8%20Ubuntu%20Server%2012.04%20%E4%B8%8A%E9%80%9A%E8%BF%87%20OpenVPN%20%E6%8B%86%E5%88%86%E9%9A%A7%E9%81%93%E8%B7%AF%E7%94%B1%E7%89%B9%E5%AE%9A%E7%AB%AF%E5%8F%A3.png)
所以我知道这里还有另一个问题,我将其用作指南,因为它非常有帮助!(为新的 VPN 连接设置路由和 iptables,以仅重定向端口 80 和 443) 只是我的目标有点不同。我正在运行一个无头无 GUI 的 Ubuntu Server 12.04 安装,用于各种不同的目的...我希望所有流量都能通过我的 ISP 不受禁止地传输,除了我的传输流量。我有一个我订阅的 VPN,它允许我访问,我只想将单个端口的流量定向到该 VPN。我目前正在使用上述链接中代码的修改版本。我当前的代码如下:
#!/bin/sh
sleep 200
DEV1=eth0
IP1=`ifconfig|perl -nE'/dr:(\S+)/&&say$1'|grep 192.`
GW1=10.0.1.1
TABLE1=open
TABLE2=vpn
DEV2=tun0
IP2=`ifconfig|perl -nE'/dr:(\S+)/&&say$1'|grep 10.`
GW2=`route -n | grep 'UG[ \t]' | awk '{print $2}'`
ip route flush table $TABLE1
ip route flush table $TABLE2
ip route show table main | grep -Ev ^default | while read ROUTE ; do
ip route add table $TABLE1 $ROUTE
ip route add table $TABLE2 $ROUTE
done
ip route add table $TABLE1 $GW1 dev $DEV1 src $IP1
ip route add table $TABLE2 $GW2 dev $DEV2 src $IP2
ip route add table $TABLE1 default via $GW1
ip route add table $TABLE2 default via $GW2
echo "1" > /proc/sys/net/ipv4/ip_forward
echo "1" > /proc/sys/net/ipv4/ip_dynaddr
echo "2" > /proc/sys/net/ipv4/conf/tun0/rp_filter
ip rule add from $IP1 lookup $TABLE1
ip rule add from $IP2 lookup $TABLE2
ip rule add fwmark 1 lookup $TABLE1
ip rule add fwmark 2 lookup $TABLE2
iptables -t nat -A POSTROUTING -o $DEV1 -j SNAT --to-source $IP1
iptables -t nat -A POSTROUTING -o $DEV2 -j SNAT --to-source $IP2
iptables -t nat -A PREROUTING -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark
iptables -t nat -A PREROUTING -i $DEV1 -m state --state NEW -j CONNMARK --set-mark 1
iptables -t nat -A PREROUTING -i $DEV2 -m state --state NEW -j CONNMARK --set-mark 2
iptables -t nat -A PREROUTING -m connmark --mark 1 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -m connmark --mark 2 -j MARK --set-mark 2
iptables -t nat -A PREROUTING -m state --state NEW -m connmark ! --mark 0 -j CONNMARK --save-mark
iptables -t mangle -A PREROUTING -i $DEV2 -m state --state NEW -p tcp --dport 44447 -j CONNMARK --set-mark 2
iptables -t mangle -A PREROUTING -i $DEV2 -m state --state NEW -p udp --dport 44447 -j CONNMARK --set-mark 2
route del default
ip route del 0.0.0.0/1
ip route del 128.0.0.0/1
route add default gw $GW1 eth0
我考虑了原始发帖人自己的评论,将其修改为我的 IP 配置和端口需求……延长睡眠时间以确保 OpenVPN 配置已发生……然后还删除了两条路由,我相信它们是我的 VPN 提供商添加的,以便在默认路由失败时进行回退……现在一切就绪似乎除了几件事之外一切都还好……
- 跟踪路由彻底失败……
$ traceroute yahoo.com 跟踪路由至 yahoo.com (206.190.36.45),最多 30 个跳数,60 字节数据包 1 * * * 2 * * * 3 * * * 4 * * * 5 * * *
- ping 结果数据包丢失 100%
$ ping google.com PING google.com (173.194.43.46) 56(84) 字节数据。 ^C --- google.com ping 统计 --- 已发送 119 个数据包,已接收 0 个数据包,100% 数据包丢失,耗时 118945 毫秒
我不知道这是什么原因造成的???
$ nslookup > google.com 服务器:10.0.1.1 地址:10.0.1.1#53 非权威答案: 名称:google.com 地址: 173.194.43.46 名称:google.com 地址: 173.194.43.38 名称:google.com 地址: 173.194.43.35 名称:google.com 地址:173.194.43.41 名称:google.com 地址: 173.194.43.39 名称:google.com 地址:173.194.43.34 名称:google.com 地址: 173.194.43.36 名称:google.com 地址: 173.194.43.37 名称:google.com 地址: 173.194.43.32 名称:google.com 地址: 173.194.43.40 名称:google.com 地址: 173.194.43.33
路线表如下:
$ 路线 内核 IP 路由表 目标网关 Genmask 标志 指标参考使用 Iface 默认 Rolands-AirPort 0.0.0.0 UG 0 0 0 eth0 默认 Rolands-AirPort 0.0.0.0 UG 100 0 0 eth0 10.0.1.0 * 255.255.255.255 UH 0 0 0 eth0 10.0.1.0 * 255.255.255.0 U 0 0 0 eth0 10.4.0.1 10.4.49.21 255.255.255.255 UGH 0 0 0 tun0 10.4.49.21 * 255.255.255.255 UH 0 0 0 tun0 托管于.租赁 Rolands-AirPort 255.255.255.255 UGH 0 0 0 eth0
任何帮助将不胜感激!
答案1
假设存在一个问题,即仅重定向发往某个互联网服务器的远程端口 Y 的数据包,并且仅重定向 ID 为 100Z 的用户所拥有的数据包。我们应该正确使用 MANGLE 和 NAT 表来重定向此用户打开的特定端口,因为 MANGLE 表中的规则在 NAT 之前和重新路由(例如“ip 路由”规则)过程之前适用,而 NAT 表中的规则则在重新路由之后适用。在下面的示例中,NAT 规则仅用于发送数据包,而 MANGE 用于发送和接收,以方便使用。
可以有以下本地接口配置:用于 VPN 的“tun0”接口,本地机器地址为 10.???.???.???,对等(网关)为 10.???.???.(???+1),以及真实硬件接口 eth0,本地机器地址为 192.168.XXX.XXX,网关为 192.168.XXX.1。
我们现在可以应用以下规则来为所需用户拆分重定向流量。
第 1 部分。本地生成的(出站连接)包的输出。
- 使用特定于连接的标记来标记本地生成的传出包,以便将来使用不同的 rt_table 进行重新路由。
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p tcp --dport Y -m owner --uid-owner 100Z -j CONNMARK --set-mark 2
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p udp --dport Y -m owner --uid-owner 100Z -j CONNMARK --set-mark 2
- 将特定于连接的标记转换为特定于包的标记。
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p tcp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j MARK --set-mark 2
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p udp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j MARK --set-mark 2
- 保存标记以便为所有跟踪的连接进一步恢复它。这只能在 NAT POSTROUTING 表中实现 - 这只是在连接遍历路由表后不丢失标记的预防措施。
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p tcp -m owner --uid-owner 100Z -m tcp --dport Y -m connmark ! --mark 0 -j CONNMARK --save-mark
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p udp -m owner --uid-owner 100Z -m udp --dport Y -m connmark ! --mark 0 -j CONNMARK --save-mark
通常情况下,数据包重新路由并到达 NAT POSTROUTING 表后,应保存特定于连接的标记
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p tcp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j CONNMARK --save-mark
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p udp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j CONNMARK --save-mark
- 将 tun0 接口的源替换为适当的 VPN 相关本地地址。
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p tcp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j SNAT --to-source 10.???.???.???
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p udp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j SNAT --to-source 10.???.???.???
第 2 部分。与本地生成的(出站连接)包相关的连接的输入。
- 在输入数据包进入路由表之前恢复与连接相关的标记。
/sbin/iptables -t mangle -A PREROUTING -i tun0 -p tcp -j CONNMARK --restore-mark
/sbin/iptables -t mangle -A PREROUTING -i tun0 -p udp -j CONNMARK --restore-mark
第 3 部分。可选优化
除了上述规则外,人们强烈希望优化标记过程,以防止或尽量减少标记时的冗余和重复检查。此任务最常用的方法是区分 NEW 和 RELATED、ESTABLISHED 连接之间的规则,并使用以下模板对已标记的连接进行最佳重新标记:* 恢复旧标记(您可能不会对 NEW 连接执行此操作,因为它们显然之前没有被标记):
/sbin/iptables -A OUTPUT -t mangle -j CONNMARK --restore-mark
- 跳过已经标记的连接:
/sbin/iptables -A OUTPUT -t mangle -m mark ! --mark 0 -j ACCEPT
- 标记新的连接(通常只标记新的连接就足够了):
/sbin/iptables -A OUTPUT -m mark --mark 0 -p tcp --dport 21 -t mangle -j MARK --set-mark 1
/sbin/iptables -A OUTPUT -m mark --mark 0 -p tcp --dport 80 -t mangle -j MARK --set-mark 2
- 使用“虚拟”标记(根本不在其他地方使用)标记其他包:
/sbin/iptables -A OUTPUT -m mark --mark 0 -t mangle -p tcp -j MARK --set-mark 3
- 最后保存标记:
/sbin/iptables -A OUTPUT -t mangle -j CONNMARK --save-mark
注意:以上设置不是分割隧道的 netfilter 配置的强制部分 - 它只是一个模板(即适当的建议),用于最小化冗余过滤器检查。它也可以是 POSTROUTING 而不是 OUTPUT,但建议只在一个地方进行标记,不需要执行两次。
第 4 部分。在 rt_tables 中创建表别名。
echo 2 vpn >/etc/iproute2/rt_tables
echo 3 novpn >/etc/iproute2/rt_tables
第 5 部分。为 VPN 连接配置路由表。
这是 VPN 的标准配置,允许重定向所有本地源(0.0.0.0/1 和 128.0.0.0/1 子网包括本地地址的整个 IP 范围),并且还为返回包提供向后兼容性(即“默认”路由)。
- 具有 VPN 连接的表
ip route flush table vpn
ip route add 10.???.???.(???+1) dev tun0 src 10.???.???.??? table vpn
ip route add 0.0.0.0/1 dev tun0 via 10.???.???.(???+1) table vpn
ip route add 128.0.0.0/1 dev tun0 via 10.???.???.(???+1) table vpn
ip route add 192.168.0.0/16 src 192.168.XXX.XXX dev eth0 table vpn
ip route add default via 192.168.XXX.1 dev eth0 table vpn
- 另一个表称为“novpn”。它的目标是提供绕过VPN连接的直接路由。
ip route flush table novpn
ip route add 192.168.0.0/16 src 192.168.XXX.XXX dev eth0 table novpn
ip route add default via 192.168.XXX.1 dev eth0 table novpn
第六部分 配置路由规则(最好按照下面的规则顺序)
ip rule add from all lookup novpn
ip rule add from all fwmark 2 lookup vpn
ip rule add from 10.XXX.XXX.XXX lookup vpn
第 7 部分。最后步骤
现在是时候刷新主路由表了(分割隧道工作时不需要这样做):
ip route flush table main
配置的最后一步是在 Linux 内核中启用套接字(客户端应用程序连接)的 IP 转发和动态 IP 地址:
echo 1 >/proc/sys/net/ipv4/ip_forward
echo 1 >/proc/sys/net/ipv4/ip_dynaddr
现在我们可以使用“--route-noexec”和/或“--ifconfig-noexec”(无论您是否已经知道或仍未使用 tun0 接口配置从 VPN 服务器接收推送消息)参数启动 OpenVPN 客户端。如果 tun0 地址有问题 - 最好不要使用“--ifconfig-noexec”,让 OpenVPN 客户端为您设置 tun0 地址。之后,您只需从“ip route”和“/sbin/iptables”中删除一些旧规则,并将其替换为包含正确 tun0 地址的类似规则(请参阅上面所有包含 10.XXX.XXX.XXX 或 10.XXX.XXX.(XXX+1) 的行)。
openvpn --config ./<your_config>.ovpn --route-noexec --ifconfig-noexec --auth-nocache
这些只是出站连接的规则。入站和出站配置之间的主要区别是:入站规则应主要在“mangle”表中实现,而不是“nat”,出站规则则相反,如上所示。有一个例外 - 由于 netfilter 架构实现,对于本地生成(出站)和应答(入站连接)包的输出,规则必须放在“mangle”表中(出于某种原因,“nat”表仅在重新路由过程之后拦截包 - 请参阅 Wikipedia netfilter graph 或 netfilter 官方文档)。
答案2
好吧,我已经找到了解决办法……我不知道为什么我无法让它工作,但基本上我发现,即使我的路由以这种方式配置,bittorrent 的性质也会导致我的 ISP 提供的公共 IP 被公布……这对连接来说是一个主要问题。为了解决我的问题,我停止了 transmission-daemon 并修改了 /etc/transmission-daemon/settings.json 文件以绑定到 tun0 接口的 ipv4 地址。这不是一个理想的解决方案,因为它不是动态的。但是,我的 vpn 的 ip 地址足够静态,所以这应该不是什么大问题。如果有人有更好的答案,请告诉我!
请注意,通过手动删除 VPN 提供商推送的路由(在脚本内),可以解决 ping 时 100% 数据包丢失且没有可用的跟踪路由的问题。
强制通过 VPN 传输流量只需调整客户端中的设置以绑定到 tun0 接口的 IP 地址即可。这不是一个合适的解决方案,但仍然有效。我继续使用该脚本,以便默认接口不是 VPN。