最近有一个现实需求要将处于两个不同地理位置的两个内网互联,实现两个内网中客户端的互相访问。想使用Wireguard而不是传统的Openvpn或L2TP,但是和大多数现实场景一样,存在种种限制,比如不是每个内网都有公网IP。
现实中的各种限制都存在:
- 两个内网只有一个有公网IP。(这在家庭和很多商用环境中常见)
- 路由器设备限制为ROS,不能使用openwrt
- 不是每个设备都可以安装tailscale客户端
- 希望使用统一的内部域名系统
如果在一般家用环境中,两个内网都可以使用openwrt实现tailscale互联(也可以用虚拟机软路由)。但openwrt不是一个便于维护和管理的系统,在很多场合不可取。而且目前运行的ROS系统中很多功能也不能被替代。
ROS可以通过容器方式实现tailscale,但性能损耗很大,如果不是顶级产品,在开启容器后负载能力下降过多。
因为安全和兼容性等因素,不是每个设备都可以安装tailscale客户端。虽然在两个内网互联的应用中,某一个内网如果有advertise-route的主机,另一侧内网中主机可以直接连接其内网的其他地址,但是要求发起请求的主机必须安装tailscale客户端,不然就要在路由上安装。这又回到了openwrt或RouterOS-容器的模式,都不符合所有的要求。
希望两个内网可以使用同一套统一的内部域名系统,来跨网访问内部服务,而tailscale内部维护的域名表(magic dns hosts)很不方便管理。
经过反复考察网上的一些方案,我倾向于在单公网IP环境下,只使用RouterOS系统的Wireguard方案,采用tailscale做另一方向的连接。整个过程不需要在客户端安装任何软件,不需要openwrt、容器等不可靠系统。
唯一需要的是两个内网中各有一个linux服务器,也可以是虚拟机。*如果你有两个公网IP,或是只要求单向访问则不需要。
连接到公网IP一侧
我们还是按照他这个图,假设内网A(ROS-A) 内网IP 10.1.0.0/24,无公网IP。内网B(ROS-B)内网IP 10.2.0.0/24,有公网IP。
首先先在内网B(有IP 123.1.1.1)中的ROS路由器进行设置。 新建Wireguard接口(Interface),假设名字叫做 WireguardB。默认端口 12345 (123.1.1.1:12345)。
新建IP地址 地址段与内网A和内网B都不重复。比如 10.0.0.0/24。 新建一个单独的地址 10.0.0.1。指派到Interface Wireguard B 中。
这样 ROS-B 设备拥有3个地址,分别是公网 123.1.1.1(其中Wireguard挂在123.1.1.1:12345),本地内网B的网关 10.2.0.1, 在Wireguard 虚拟网络中的地址 10.0.0.1。
回到ROS-A设备(无公网IP这一侧)
同样新建Wireguard接口,接口名称 WireguardA。新建IP地址 10.0.0.2 指派给 Interface Wireguard A。
此时,ROS-A 设备具有本地内网地址 10.1.0.1, Wireguard虚拟网络地址 10.0.0.2 (与ROS-B 在Wireguard网络中的地址在同一段),其没有公网地址。
在ROS-A 上新建Peers,Peer对端:公钥为ROS-B的公钥,Endpoint为ROS-B网络的公网Wireguard端口,即123.1.1.1:12345。Allowed Address 为 10.2.0.0/24(ROS-B端内网IP), 10.0.0.0/24 (Wireguard 虚拟内网)
在ROS-B 上新建Peers,Peer对端:公钥为ROS-A的公钥,Allowed IP 为 10.1.0.0/24 (ROS-A的内网IP段),10.0.0.0/24 (Wireguard 虚拟内网),如果ROS-A端没有公网IP,Endpoint先不填。
这里有个坑!在ROS 7.1 以上系统里,这样设置完还需要设置一下路由。站在ROS-A的内网里,需要把去往ROS-B网段的请求路由到WireguardA接口去,才能经由Allowed Address 为 10.2.0.0/24 发往ROS-B的 Endpoint,实现互联。
即设置路由 10.2.0.0/24 到Gateway WireguardA。
这样从ROS-A 内网的任意一台机器 ping ROS-B 内网的任意一台机器(比如 10.2.0.3)可以ping通,也可以实现访问。
如果ROS-A 本身也具有公网IP,那么ROS-B 这一端只要把Peer中的Endpoint 填写为ROS-A的IP和端口,再把去ROS-A网段的路由给到WireguardB,就算完成了。此时内网之间可以随意访问。
连接到没有公网IP的一侧
×如果在家庭环境中,公网IP是动态的,Endpoint使用动态域名的话,ROS不支持刷新动态域名,需要写一个脚本来刷新,可以参考这里:
下面假设ROS-A 没有公网IP或没有公网路由器的管理权的情况下,ROS-B如何连回到ROS-A的内网中。需要双方内网中各有一个任意类型的linux服务器。
在双方内网中的linux服务器中都安装tailscale客户端,并加入同一张tailscale网络。假定IP分别为 10.1.0.A 和 10.2.0.B,他们在tailscale网络中的IP为 100.64.0.A, 100.64.0.B。
此时确保 100.64.0.A 和 100.64.0.B 能打洞建立tailscale链接。互相ping的延迟如果是两个内网实际位置之间的延迟,说明已经通过tailscale打洞成功。A与B两台机器可以在tailscale环境下直接互联。
A与B之间建立UDP(通过tailscale network)转发关系。具体是由B向A转发UDP,A向ROS-A 网关转发。
sudo iptables -t nat -A PREROUTING -d 10.2.0.B -p udp --dport 12345 -j DNAT --to-destination 100.64.0.A:12345
#在10.2.0.B 上建立通过tailscale向 10.1.0.A的UDP转发隧道
sudo iptables -t nat -A PREROUTING -d 100.64.0.A -p udp --dport 12345 -j DNAT --to-destination 10.1.0.1:12345
#在10.1.0.A 上接收并转发给ROS-A的ROS路由器
ROS-B 路由器网络中设置的Peer 对A,现在Endpoint 要写成ROS-B 内网中的tailscale转发服务器,即Endpoint 为 10.2.0.B:12345。这台主机通过tailscale链接,转发了B到A的Wireguard数据包。A网中的tailscale节点接收到数据后,再次转发给ROS-A路由器,实现B到A的连接。
这里Wireguard被包裹在tailscale的Wireguard里,会有一些性能损失,但在不引入其他的内网穿透手段的前提下,不失为一种办法。
最后注意在ROS-B上也要添加到ROS-A的路由,路由必须经过WireguardB接口。
使用共同的内部DNS服务
确保两端路由都能正确运行后,此时两个内网实际上是一个内网,把DNS服务器放在哪里都一样。但考虑到跨物理网络调用DNS会造成性能影响,这里只把内部的DNS映射即可。
使用一些DNS管理软件,可以将指定后缀 ,比如 *.mycompany.com 发到指定的DNS服务器处理。在任意一个内网中设立一个DNS服务器来管理 mycompany.com 下的内部域名。两个内网里ROS的DNS都指派给DNS管理/缓存工具(比如smartdns),这样两个内网中的DNS查询*.mycompany.com 都会到内部DNS中处理,而其他DNS不受影响。
这样的好处是双边所有连接都集中在ROS的Wireguard接口,方便管理。
外部连接加入
在两个内网之外的设备(如:手机)可以通过Wireguard同时加入两个内网吗?——答案是肯定的。
当然,如果使用tailscale客户端也是可以的,然后利用在两个内网中的tailscale节点advtise route出去。
这里只说怎么使用原生Wireguard实现。
手机下载Wireguard客户端,新建一个连接。Endpoint为公网IP入口, 123.1.2.3:12345。Peer 公钥为该公网IP入口服务器(ROS-B)的公钥。
手机的IP地址指定为与内网中其他Wireguard同一个网段,在这个案例里是10.0.0.0/24,随便指定一个,不重复的,比如10.0.0.100/32。*如果需要解析内部域名,DNS需要设置为内网中有效的DNS服务器。
Allowed Address 要包含全部的内网IP段,包括10.0.0.0/24(Wireguard),10.1.0.0/24 (ROS-A),10.2.0.0/24 (ROS-B)。如果希望全部流量进入内网,并由公网入口的ROS-B控制转发,也可以写0.0.0.0。
ROS-B 端新增Peer,Peer公钥为手机的公钥(生成时记录并发送过来),Allowed Address 为手机上给自己设置的IP地址,10.0.0.100/32。 这里RouterOS的Wireguard不支持DHCP,需要自己指定IP,互相不能冲突。
这时候手机再连接Wireguard, 就与ROS-B的公网入口建立了隧道。此时如果手机访问ROS-A网段的内容,也会被ROS-B 通过 ROS-A和ROS-B之间的WG隧道路由去ROS-A网段。这样手机等外部设备就能同时访问A和B网段了。
其实到这里,加入多个内网网段实现互联也就很容易了,但是每一边都要添加路由。
参考文件
两个内网只要各有一台能安装tailsacle即可,可以互相route 内网subnet