Linux:基于域名的路由

Twi*_*fty 7 networking linux dns vpn routing

在 ubuntu 16.04 上,我想根据在浏览器中输入的域名通过直接互联网eth0或我的 VPN路由我的流量tun0。本地站点的原因要么速度慢,要么取决于位置。

我意识到内核路由表是基于 IP 的,域名通常在软件层解析,但由于 linux 是一个脚本友好的平台,我希望有一个解决方法。虽然,我不知道如何编写这样的脚本。

到目前为止,我发现该dig example.com +short @8.8.8.8命令将列出与域关联的 IP,并且我发现该sudo route add -net 8.8.8.8 netmask 255.255.255.255 gw 192.168.2.1命令将绕过给定 IP 的 VPN(其中 192.168.2.1 是我的默认值eth0)。有人会友好地为脚本模板化,该脚本读取包含域名的文件并在系统启动时输入路由规则。允许屏蔽子域的奖励积分*.example.com

如果这种疯狂有更简单的方法,我会接受它作为解决方案。

注意:我可以很容易地将 IP 硬编码到其中,/etc/network/interfaces但随后它们变得难以管理。我还尝试将我所在国家/地区的所有已知 IP 硬编码到此文件中,但它非常受欢迎,并且启动时间延迟。

Pos*_*sum 14

基于目标域的路由并非不可能,而且,使用正确的工具,也不是那么难。

我将介绍一些需要很少或不需要特殊客户端配置的方法。这些都假设您使用 OpenVPN 进行连接。这应该可以通过其他 VPN 实现,但在 VPN 启动后可能需要更多的手动配置。

出于示例目的,我将使用域“example.com”、“us1.example.com”、“us2.example.com”和“geoblocked.com”作为我们要通过非 VPN 路由的域界面。

所有命令都应以 root 身份运行。

方法 1 - OpenVPN

如果您确定要路由的 IP 地址具有永不更改的静态 IP,我只会推荐此方法。

优点:

  • 极其简单

缺点:

  • 仅对 IP永不改变的域可靠
  • 每个域和子域都需要一个明确的条目

方法:

将以下行添加到您的 OpenVPN 配置中:

route example.com     255.255.255.255 net_gateway
route us1.example.com 255.255.255.255 net_gateway
route us2.example.com 255.255.255.255 net_gateway
route geoblocked.com  255.255.255.255 net_gateway
Run Code Online (Sandbox Code Playgroud)

重新启动 OpenVPN。

就是这样,但是如果这些 IP 地址发生变化,您将不得不再次重新启动 VPN。

注意:一些消息来源说您还需要指定allow-pull-fqdn,但根据我的经验,情况似乎并非如此。天啊。

方法 2 - 基于策略的路由

基于策略的路由是基于特定标准进行路由的能力;通常是源地址或协议,但在这种情况下,我们将在路由之前检查目标域名并使用标记的数据包(“fwmark”)。

所以我们首先需要做的是为你的 VPN 路由数据包创建一个单独的表,以便我们可以标记那些通过 VPN 的数据包,并通过非 VPN 接口传递标记的数据包。(请记住,这是一种方法,还有许多其他方法可以解决这个问题,例如让 VPN 通过主表正常进行路由,并为非 VPN 流量创建一个单独的表。)

您的内核必须足够新并具有适当的模块,尽管现代系统可能在其默认内核中包含它们。

名称“vpn_table”(路由表名称)、数字“201”(路由表ID)和“3”(fwmark)是任意选择的。

创建新的路由表(以 root 身份):

echo 201 vpn_table >> /etc/iproute2/rt_tables
Run Code Online (Sandbox Code Playgroud)

配置 OpenVPN:

在某处创建以下脚本(我称之为“/etc/openvpn/client/setup-routing”)并使其可执行:

#!/bin/bash
ip route add 0.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
ip route add 128.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
sysctl -w net.ipv4.conf.$dev.rp_filter=2

# You can optionally leave the next two lines out but run the `ip rule add`
# command at each boot instead
ip rule del fwmark 3 table vpn_table &>/dev/null # This might fail but that's ok
ip rule add fwmark 3 table vpn_table
Run Code Online (Sandbox Code Playgroud)

上述脚本中的变量将被 OpenVPN 填充为环境变量。另请注意,这将通过“vpn_table”路由表中的 VPN 网关设置到所有地址的路由。如果您的 VPN 设置需要更复杂的路由,请参阅 OpenVPN 文档并进行相应调整。

将以下内容添加到您的 OpenVPN 配置中:

## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
Run Code Online (Sandbox Code Playgroud)

“route-noexec”行允许 OpenVPN 从服务器获取路由,但阻止它实际填充路由。而是调用路由脚本。“script-security 2”是调用用户定义脚本所必需的。

这是路由标记数据包的所有必要设置,但我们需要设置一种方法来实际标记数据包。两个选项是将 dnsmasq 与 ipset 一起使用,或者设置一个 squid 代理。

方法 2a - 使用 ipset 和 dnsmasq 的基于策略的路由

如果您已经在基于 dnsmasq 的路由器上运行此方法,或者您的客户端不支持代理配置,我会推荐此方法。这实际上与在查找域名时更新路由表的缓存 DNS 相同。

优点:

  • 处理子域
  • 适用于无法访问代理的设备(是否存在?)

缺点:

  • 不处理引用字段(参见方法 2b)
  • 需要复杂的 ipset 和 iptables 配置
  • 需要将 VPN 连接系统设置为路由器(需要专用接口)
  • 我不知道 ipset 的可扩展性如何(我的用例是针对整个 ccTLD)

这假设您已经配置和设置了 dnsmasq,并充当连接到专用接口“eth1”的客户端的网关和 DNS 服务器。

创建ipset:

ipset create SKIP_VPN_IPSET iphash
Run Code Online (Sandbox Code Playgroud)

告诉 iptables 标记 ipset 数据包(注意,这必须创建 ipset 列表完成):

# Mark ALL packets coming in on eth1 - change this to the interface dnsmasq listens on
iptables -A PREROUTING -i eth1 -t mangle -j MARK --set-mark 3

# REMOVE mark on any addresses that match our ipset
iptables -A PREROUTING -t mangle -m set --match-set SKIP_VPN_IPSET dst -j MARK --set-mark 0/3
Run Code Online (Sandbox Code Playgroud)

注意:必须在每次启动时运行上述命令(ipsetiptables)。或者,您的操作系统可能会提供一些用于保存/恢复 iptable 规则和 ipsets 的选项。

NOTE2:有记录是相反的,! --match-set但是当我尝试它时导致所有数据包都消失了。

将以下内容添加到您的 dnsmasq.conf 中:

ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
Run Code Online (Sandbox Code Playgroud)

显然,无论您想要路由哪个域名,都要调整该行。这也会将所有子域添加到 ipset,因此您无需明确指定它们。即使使用 TLD 也能工作。

重新启动 dnsmasq 并设置您的客户端以将 VPN 连接的系统用作网关和 DNS(如果它被设置为 DHCP 服务器,则应该暗示它)。

方法 2b -使用鱿鱼的基于策略的路由

这是我最喜欢的方法,适用于我的 PS4 和我用来连接的其他设备。

优点:

  • 处理子域
  • 处理引用字段
  • 不需要更换您现有的路由器
  • 客户端(浏览器)可以选择使用或不使用它

缺点:

  • 客户端必须支持代理连接

这假设您有一个有效的鱿鱼设置和鱿鱼配置的基本知识。

将以下行添加到 squid.conf:

# redirect example domains
acl domain_to_remote_proxy dstdomain .example.com
acl ref_to_remote_proxy referer_regex [^.]*\.example.com.*

# redirect geoblocked domain
acl domain_to_remote_proxy dstdomain .geoblocked.com
acl ref_to_remote_proxy referer_regex [^.]*\.geoblocked.com.*

# mark packets that we want routed through the VPN
tcp_outgoing_mark 0x03 !ref_to_remote_proxy !domain_to_remote_proxy
Run Code Online (Sandbox Code Playgroud)

请注意,每个域有 2 行,并且子域是匹配的。第一行检查目标域,第二行匹配“Referer”标头。这很有用,因为浏览器在获取网页上的内容(如图像、CSS 或 javascript)时会发送引用;这意味着站点请求的内容也将通过非 VPN 地址路由,即使它托管在不同的域(例如 example-cdn.com)上。

在客户端上,像往常一样设置连接,但设置代理设置以使用该系统的代理服务器和端口。大多数设备(包括游戏机)允许系统范围的配置。在 PC 上,大多数浏览器都可以配置为使用独立于系统设置的代理。

最后一点 - 我的用例实际上是通过 VPN 路由特定域,通过非 VPN 路由其他所有域。方法与上述类似,但颠倒了。


小智 4

我建议您避免基于域名管理路由(顺便说一句,解析通配符子域也是不可能的,无论它是否是奖励点:D)

\n

为了更具描述性,您不应该这样做,因为:

\n
    \n
  1. 有些域名会不时更改其 IP,

    \n
  2. \n
  3. 无法匹配子域中的通配符

    \n
  4. \n
  5. 不可能知道/获取任何域的所有子域

    \n
  6. \n
  7. 任何随机子域都可以有任何随机 IP 地址。

    \n
  8. \n
\n

因此,作为浏览器插件(和/或自定义本地代理,如鱿鱼)的解决方案是解决您的问题的最佳选择。

\n

但是,我想,“FoxyProxy”插件(它最初是 Firefox 插件,但 AFAIRC,也存在 Chrome 版本)正是您想要的。

\n

并且,另外,回答您的通知“FoxyProxy 是付费服务,您已经拥有您的 VPN”:

\n

FoxyProxy Plus是付费服务,但不是 FoxyProxy。

\n

FoxyProxy 是插件,可用于主要浏览器:

\n

标准版(火狐) | 基础版(火狐浏览器)

\n

标准版 (Chrom{e,ium}) | 基础版(Chrom{e,ium})

\n

因此,如果您想通过 VPN 访问某些域,您应该:

\n
    \n
  1. 为 Foxyproxy 编写规则以通过您的鱿鱼实例获取域列表

    \n
  2. \n
  3. 和/或编写鱿鱼的规则列表

    \n
  4. \n
  5. 使用 iptables 捕获不属于鱿鱼的http/https 流量,并按如下规则将其指向鱿鱼:

    \n
  6. \n
\n
iptables -m owner -m multiport -t nat -A OUTPUT ! -o lo ! --uid-owner $squid_user_id -p tcp --dports 80,443,8080,... -j REDIRECT --to-ports $SQUID_PORT\n
Run Code Online (Sandbox Code Playgroud)\n

--syn可能需要选项-p tcp

\n
    \n
  1. 捕获squid拥有的http/https流量,并将其标记为下次将其路由到VPN,规则如下:
  2. \n
\n
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. \n
\n
echo 11 forcevpn >> /etc/iproute2/rt_tables\nip rule add fwmark 11 table forcevpn\nip route add default via 10.0.0.1 table forcevpn\n
Run Code Online (Sandbox Code Playgroud)\n

10.0.0.1你的 VPN 内网关在哪里?或者,如果您没有网关并且只想将所有流量推送到 VPN 接口中,则可以使用dev $VPN_IF代替。via 10.0.0.1

\n
    \n
  1. 或者,您可能需要运行sudo sysctl ipv4.conf.all.rp_filter =0
  2. \n
\n

===

\n

还有一件事情:

\n

如果您想对非 http(s) TCP 流量执行相同的魔法,您将需要诸如代理链之类的东西,并执行类似的捕获魔法。

\n

而且,如果你想用 UDP 实现这个魔法,我有一个坏消息:我不知道有任何代理能够代理 UDP(由于该协议的性质):)

\n

\xe2\x87\x93\xe2\x87\x93\xe2\x87\x93 编辑 \xe2\x87\x93\xe2\x87\x93\xe2\x87\x93

\n

如果您想要相反的东西(默认 gw = vpn,并直接通过 ISP 规则某些域),它可以是:

\n
    \n
  1. 为 Foxyproxy 编写规则以通过您的鱿鱼实例获取域列表

    \n
  2. \n
  3. 捕获鱿鱼拥有的流量,并将其标记为下一次路由,规则如下:

    \n
  4. \n
\n
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. \n
\n
echo 11 novpn >> /etc/iproute2/rt_tables\nip rule add fwmark 11 table novpn\nip route add default via ${ISP_GW} table novpn\n
Run Code Online (Sandbox Code Playgroud)\n

其中ISP_GW是将流量路由到 VPN 服务器的网关。某些用户可能希望使用dev ppp0(或ppp1, ..., pppN),而不是via ${ISP_GW}使用 pptp 连接到互联网。

\n