我想为我的 Linux 服务器添加辅助备份互联网连接。我计划为此使用 USB LTE 调制解调器。
由于此蜂窝连接将被计量,因此我想将可使用的数据量限制在必要的绝对最小值。
我有一个自定义服务器应用程序,可以对其进行任何更改。它有一些任务需要不间断的连接,而其他任务则不需要停机。
我正在想象这样的事情:
- 服务器需要发出外部 HTTP API 请求。第一次尝试通过系统的默认路由(即 eth0,主互联网连接)进行。
- 如果请求失败或超时,请通过 LTE 接口重试该请求。
只有我的服务器进程明确希望通过 LTE 发送的流量才应通过 LTE 发送。系统任何部分的其他流量都不应通过 LTE 发送。
- 具体来说,我将使用节点的
localAddress
套接字选项指定请求应通过 LTE 进行。 - 我如何确保其他流量不会最终通过 LTE 接口进行路由(即使 eth0 已关闭)?
- DNS 解析怎么样?
答案1
我最终通过配置一个替代路由表和一个路由策略规则为备份接口的源地址。
我的 USB LTE 调制解调器显示为 NDIS 设备,因此它仅显示eth1
IP 为 192.168.0.190,并且它在内部进行 NAT 路由。我已配置eth1
静态 IP 并手动配置路由。
默认配置使用 DHCP,因此关闭接口并确保删除任何自动添加的路由。
为接口添加静态 IP 配置并启动它。
1
向子网和默认网关的备用路由表(我已选择)添加条目。# ip route add 192.168.0.0/24 dev eth1 src 192.168.1.190 table 1 # ip route add default via 192.168.0.1 table 1
设置路由策略规则,以便明确使用 192.168.1.190 作为源地址的应用程序将使用路由表 1 而不是默认值。
# ip rule add from 192.168.0.190/32 table 1 # ip rule add to 192.168.0.190/32 table 1
此时,您应该能够测试您的连接。
$ curl https://wtfismyip.com/text
1.2.3.4 # primary ISP external IP
$ curl --interface 192.168.0.190 https://wtfismyip.com/text
5.6.7.8 # backup LTE external IP
如果一切看起来不错,请使配置永久化。我添加到/etc/network/interfaces
:
iface eth1 inet static
address 192.168.0.190
netmask 255.255.255.0
post-up ip route add 192.168.0.0/24 dev eth1 src 192.168.0.190 table 1
post-up ip route add default via 192.168.0.1 table 1
post-up ip rule add from 192.168.0.190/32 table 1
post-up ip rule add to 192.168.0.190/32 table 1
现在,只有在进行传出连接时明确绑定到 192.168.0.190 的应用程序才会通过备用连接进行路由。所有其他流量都会通过该连接进行路由eth0
(或根据main
[默认] 路由表中的配置进行路由)。
这是可能的您有某种东西可以枚举所有可用的 IP 并尝试从这些 IP 发送流量,这可能会导致备份连接上出现意外流量,但这种情况不太可能发生。我还没有观察到任何此类流量。
请注意,这不涉及 DNS 解析。在主连接处于离线状态的情况下,您可能会很幸运地从缓存中进行查找,但这并不值得依赖。我也不会将系统范围的解析器配置为通过 LTE 接口发送请求。相反,您的应用可以在发出备份请求时手动处理 DNS 解析。
使用 node,从特定源地址发出 HTTP 请求(或任何 TCP 连接)很容易。只需指定localAddress
选项,例如:
https.get('https://wtfismyip.com/text', { localAddress: '192.168.0.190' }, …);
解决 DNS 查找稍微有点棘手。lookup
还有一个选项,允许您覆盖默认的 DNS 解析过程。您可以使用自定义dns.Resolver
进行查找。不幸的是,node 没有办法指定 DNS 查找的源地址,所以我添加了它. 有了这些,您就可以将各个部分组合在一起:
const resolver = new dns.Resolver();
resolver.setServers(['8.8.8.8']);
resolver.setLocalAddress('192.168.0.190'); // requires node > v15.0.0
https.get('https://wtfismyip.com/text', {
localAddress: '192.168.0.190',
lookup: function(hostname, opts, cb) {
resolver.resolve(hostname, function(err, records) {
if (err) cb(err);
else if (!records[0]) cb(new Error('Missing DNS record'));
else cb(null, records[0], 4);
});
}
}, function(res) { … });