在 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 硬编码到这个文件中,但效果很差,而且启动时间也比较延迟。
答案1
我建议您避免基于域名管理路由(顺便说一句,也不可能解析通配符子域名,不管这是否是加分项:D)
更详细地描述一下,你不应该这样做,因为:
有些域名会不时更改其 IP,
子域名中无法匹配通配符
不可能知道/获取任何域的所有子域
任何随机子域名都可以有任意随机 IP 地址。
因此,浏览器插件(和/或自定义本地代理,如 squid)的解决方案是解决您的问题的最佳选择。
但是,我猜,“FoxyProxy”插件(它最初是 Firefox 插件,但据我所知,也有一个 Chrome 版本)正是您想要的。
另外,回答您关于“FoxyProxy 是付费服务并且您已经拥有 vpn”的通知:
福克斯代理加是付费服务,但 FoxyProxy 不是。
FoxyProxy 是一个插件,适用于主流浏览器:
标准版 (Chrom{e,ium})|基础版 (Chrom{e,ium})
因此,如果您想通过 VPN 访问某些域,您应该:
为 foxyproxy 编写规则,以便通过 squid 实例获取域列表
和/或编写 squid 的规则列表
捕获 http/https 流量不拥有通过 squid 使用 iptables 并通过规则将其指向 squid,如下所示:
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
(--syn
可能需要选项-p tcp
)
- 捕获 http/https 流量拥有由 squid 处理,并使用如下规则将其标记为下次路由到 VPN:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
echo 11 forcevpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table forcevpn
ip route add default via 10.0.0.1 table forcevpn
您的 VPN 网关在哪里。或者,如果您没有网关,并且只想将所有流量推送到 VPN 接口,10.0.0.1
则可以使用dev $VPN_IF
而不是。via 10.0.0.1
- 可选地,你可能需要运行
sudo sysctl ipv4.conf.all.rp_filter =0
===
还有一件事情:
如果您想对非 http(s) TCP 流量执行相同的魔法,您将需要类似代理链之类的东西,并执行类似的捕获魔法。
而且,如果你想用 UDP 来实现这个魔法,我有一个坏消息:我不知道有任何代理能够代理 UDP(由于这个协议的性质):)
⇓⇓⇓ 编辑 ⇓⇓⇓
如果您想要相反的操作(默认 gw = vpn,并直接通过 ISP 统治一些域),则可以:
为 foxyproxy 编写规则,以便通过 squid 实例获取域列表
捕获流量拥有由 squid 处理,并使用如下规则将其标记为下次以另一种方式路由:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
echo 11 novpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table novpn
ip route add default via ${ISP_GW} table novpn
您用来将流量路由到 VPN 服务器的网关在哪里。如果某些用户使用 pptp 连接到互联网,他们ISP_GW
可能希望使用dev ppp0
(或ppp1
, ..., pppN
) 而不是 。via ${ISP_GW}
答案2
基于目标域的路由并非不可能,而且,使用正确的工具,也不是那么难。
我将介绍几种几乎不需要特殊客户端配置的方法。这些方法都假设您使用 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
重新启动 OpenVPN。
就是这样,但是如果这些 IP 地址发生变化,您将必须重新启动 VPN。
笔记:有些消息来源说您还需要指定allow-pull-fqdn
,但根据我的经验,情况似乎并非如此。YMMV。
方法 2 - 基于策略的路由
基于策略的路由是根据某些标准进行路由的能力;通常是源地址或协议,但在这种情况下,我们将在路由之前检查目标域名并使用标记的数据包(“fwmark”)。
因此,我们首先需要做的是为您的 VPN 路由数据包创建一个单独的表,以便我们可以标记那些通过 VPN 的数据包,并通过非 VPN 接口传递标记的数据包。(请记住,这只是一种方法,还有许多其他方法可以实现这一点,例如让 VPN 通过主表正常进行路由,并为非 VPN 流量创建一个单独的表。)
您的内核必须足够新并且具有适当的模块,尽管现代系统可能在其默认内核中已有它们。
名称“vpn_table”(路由表名称)以及数字“201”(路由表ID)和“3”(fwmark)是任意选择的。
创建新的路由表(以 root 身份):
echo 201 vpn_table >> /etc/iproute2/rt_tables
配置 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
OpenVPN 将把上述脚本中的变量填充为环境变量。另请注意,这会将路由设置为全部通过“vpn_table”路由表中的 VPN 网关获取地址。如果您的 VPN 设置需要更复杂的路由,请参阅 OpenVPN 文档并进行相应调整。
将以下内容添加到您的 OpenVPN 配置:
## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
“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
告诉 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
笔记:每次启动时都必须运行上述命令(ipset
和)。或者,您的操作系统可能会提供一些用于保存/恢复 iptable 规则和 ipset 的选项。iptables
笔记2:有记录表明存在相反的情况! --match-set
,但当我尝试时,所有数据包都消失了。
将以下内容添加到您的 dnsmasq.conf:
ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
显然,无论你想要路由哪个域名,都要调整该行。这还将添加全部子域名添加到 ipset,因此您无需明确指定它们。即使使用 TLD 也可以。
重新启动 dnsmasq 并设置您的客户端以使用 VPN 连接系统作为网关和 DNS(如果它设置为 DHCP 服务器,则应该是隐含的)。
方法 2b - 基于策略的路由使用 squid
这是我最喜欢的方法,并且适用于我的 PS4 和我用来连接的其他设备。
优点:
- 处理子域名
- 处理引荐来源字段
- 不需要更换现有的路由器
- 客户端(浏览器)可以选择使用或不使用
缺点:
- 客户端必须支持代理连接
这假设您有一个可运行的 Squid 设置并且具备 Squid 配置的基本知识。
将以下行添加到 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
请注意,每个域名有 2 行,并且子域名是匹配的。第一行检查目标域名,第二行匹配“Referer”标头。这很有用,因为浏览器在获取网页上的内容(例如图像、CSS 或 javascript)时会发送 referer;这意味着即使网站请求的内容托管在不同的域名上(例如 example-cdn.com),它也会通过非 VPN 地址路由。
在客户端上,像平常一样设置连接,但设置代理设置以使用此系统的代理服务器和端口。大多数设备(包括游戏机)允许系统范围的配置。在 PC 上,大多数浏览器都可以配置为使用独立于系统设置的代理。
最后说明 - 我的用例实际上是通过 VPN 路由特定域,而通过非 VPN 路由其他所有域。方法与上述方法类似,但反过来。
答案3
Squid 不支持 socks(比如 ssh 隧道)... 有一个选项可以构建带有 socks 支持的 squid,但很难让它工作。
私有网络可以胜任
- 支持父母袜子
- 支持http/https代理
- 支持推荐人
- ETC。
Privoxy 设置:
安装 privoxy
编辑配置文件(删除所有内容
/etc/privoxy
并添加/etc/privoxy/config
)user-manual /usr/share/doc/privoxy/webserver/user-manual confdir /etc/privoxy logdir /var/log/privoxy actionsfile default.action filterfile default.filter logfile logfile toggle 1 enable-remote-toggle 0 enable-remote-http-toggle 0 enable-edit-actions 0 enforce-blocks 0 buffer-limit 4096 enable-proxy-authentication-forwarding 0 forwarded-connect-retries 0 accept-intercepted-requests 0 allow-cgi-request-crunching 0 split-large-forms 0 keep-alive-timeout 5 tolerate-pipelining 1 socket-timeout 300 listen-address 127.0.0.1:8888 forward-socks5 .whatismyipaddress.com 127.0.0.1:8080 . forward-socks5 .whatismyip.com 127.0.0.1:8080 .
重启服务
systemctl start privoxy
在客户端应用程序上设置 privoxy 代理
如果你想要路由 referrer,请添加 default.action 和 default.filter,你可以使用以下命令进行测试http://www.play-hookey.com/htmltest/带有 html 代码
<a href="http://amibehindaproxy.com/">test-ip</a></br> <a href="http://www.stardrifter.org/cgi-bin/ref.cgi">test-referrer</a>
default.action 和 default.filter
默认操作
{+client-header-tagger{referer}} / {+forward-override{forward-socks5 127.0.0.1:8080 .}} TAG:.*?hookey.com
默认过滤器
CLIENT-HEADER-TAGGER: referer s@^Referer:.*?$@$0@i
重启 privoxy 服务
答案4
作为配置 iptables 和浏览器扩展的替代方案,有一个工具解决方案v2ray. 适用于所有主流操作系统。
v2ray
旨在用作绕过 GFW 的代理工具。它支持多种 VPN 协议,包括 ShadowSocks、SOCKS5、HTTPS……此外,它还可以配置为基于域名路由流量。
自从问了这个问题。我托管了自己的 VPN,并在所有端点使用 v2ray。我的本地系统配置为将所有网络流量重定向(使用 iptables)到本地 v2ray 服务,然后将其转发到我的 VPN 服务器。我还配置了一个 SOCKS5 侦听器并使用名为 的浏览器扩展SmartProxy
来过滤特定域。v2ray 能够过滤域,但我发现浏览器方法更容易配置。