这篇文章介绍手搓 Site-to-Site 组网的方法。

1 场景简述

假设我们有两个机器 A 与 B,其中 A 和 B 在各自的局域网中的网段分别是 192.168.2.0/24 和 192.168.3.0/24。A 和 B 都接入了一个 VPN 网络,其内网地址是 10.0.0.0/16。那么,如何让 A 访问 B 所在局域网内的另一台机器 C 呢?注意 C 是没有接入到 VPN 私有网络的。一个办法是直接让 C 也接入到 VPN 网络中,但是如果 B 的局域网中有众多设备需要被 A 访问时,将这些节点都加入 VPN 网络配置会非常麻烦。在一些场景下,VPN 的连接数量有限制(例如 Zerotier)。因此我们需要新的方案。这篇文章我们介绍通过巧妙地配置路由表和防火墙来实现。

我们假定 A 的地址是 192.168.2.2/24 和 10.0.0.2/16,B的地址是 192.168.3.2/24 和 10.0.0.3/16,C 的地址是 192.168.3.3/24。我们这里介绍的是让 A 访问 B 所在的子网。反过来让 B访问 A的子网的设置方法是类似的。进行双向设置以后就可以实现完整的 Site-to-Site 组网。这里我们假设 A 和 B 都安装了 Ubuntu 操作系统。

2 路由配置

首先我们需要进行路由表的配置,让 A 知道应该以 B 为网关来来访问 192.168.3.0/24 子网。在 A 上运行

1
sudo ip route add 192.168.3.0/24 via 10.0.0.3

然后,我们需要在 B 上启用 IP 转发。在Linux系统中开启IP转发功能,可以通过以下步骤:

  1. 临时开启(系统重启后失效)
    • 对于基于Debian或Ubuntu的系统,可以使用以下命令:
      • 查看当前IP转发状态:cat /proc/sys/net/ipv4/ip_forward,如果输出为0,则表示IP转发功能是关闭的;如果输出为1,则表示IP转发功能是开启的。
      • 开启IP转发:sudo sysctl -w net.ipv4.ip_forward=1。这条命令会在运行时修改内核参数,使系统能够进行IP转发。
    • 对于基于Red Hat或CentOS的系统,操作如下:
      • 查看IP转发状态:cat /proc/sys/net/ipv4/ip_forward
      • 开启IP转发:sudo sysctl -w net.ipv4.ip_forward=1。这会立即生效,但是在系统重启后会恢复到原来的设置。
  2. 永久开启(系统重启后依然有效)
    • 在Debian或Ubuntu系统中:
      • 编辑/etc/sysctl.conf文件:sudo nano /etc/sysctl.conf
      • 在文件中找到#net.ipv4.ip_forward=1这一行(如果没有这一行,可以添加),将前面的#去掉,使这一行变为net.ipv4.ip_forward = 1。保存并退出文件。
      • 使配置生效:sudo sysctl -p。这条命令会重新加载sysctl.conf文件中的配置,使IP转发功能在系统重启后依然有效。
    • 在Red Hat或CentOS系统中:
      • 编辑/etc/sysctl.conf文件:sudo vi /etc/sysctl.conf
      • 找到net.ipv4.ip_forward = 0这一行,将0改为1。保存并退出文件。
      • 执行sudo sysctl -p命令,让配置生效,这样在系统重启后IP转发功能也会开启。

3 防火墙配置

完成路由配置之后,A 机器能够知道到达 192.168.3.0/24 子网的路由路径,但是此时让 A 直接访问 C 的地址会失败,因为他们之间的通信会被 B 的防火墙拦截。因此我们需要对防火墙进行进一步配置。防火墙的配置分为两个主要的步骤:首先我们需要允许经过 B进行路由的数据包经过防火墙。在 B 上运行下面的代码:

1
2
sudo iptables -A FORWARD -i tun0 -o enp2s0 -s 10.0.0.0/16 -d 192.168.3.0/24 -j ACCEPT
sudo iptables -A FORWARD -i enp2s0 -o tun0 -s 192.168.3.0/24 -d 10.0.0.0/16 -j ACCEPT

注意其中的 -i -o 指定的接口名称需要针对你的服务器的实际情况调整。这里我们进行的往返路由路径的分别配置,将路由策略都设置为 ACCEPT 使得数据包能够通过 B 的防火墙。

对防火墙进行配置的第二个步骤是在 B 上开启地址伪装,即 MASQUERADE 机制。注意到我们在 A 上配置了路由表,但是没有作为访问目标的 C 则并不知道通向 A 的路由路径(A 和 B 之间在的 VPN 私有网络中是直通的,不需要额外路由)。因此我们需要配置进行一个额外的配置,即在 B 中开启地址伪装,此时 B 会将来自 A 的,以 C 为目标的数据包的源地址修改为自身的地址,那么 C 会认为这个包来自 B,响应也会被发送给 B,B 再将包转发回到 A。要开启地址伪装,需要在 B 上开启 iptables 配置:

1
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/16 -o enp2s0 -j MASQUERADE

注意上面的命令结果会在重启后失效。要持久化 iptables 命令的配置,可以采用下面的方法:

  1. 使用iptables - save和iptables - restore命令(适用于大多数Linux发行版)
    • 保存配置
      • 首先,使用iptables - save命令可以将当前的iptables规则保存到一个文件中。例如,可以将规则保存到/etc/iptables.rules文件中,命令如下:sudo iptables - save > /etc/iptables.rules
      • 这个命令会将当前活动的iptables规则以文本格式输出并保存到指定的文件。输出的内容包含了表(如filternatmangle)以及每个表中的链(如INPUTOUTPUTFORWARD)的规则。
    • 恢复配置
      • 要在系统启动时自动恢复这些规则,可以在合适的启动脚本中添加iptables - restore命令。例如,在基于Debian或Ubuntu的系统中,可以创建一个脚本文件(如/etc/network/if - post - up.d/iptables - restore),并在其中添加以下内容:
        1
        2
        #!/bin/sh
        iptables - restore < /etc/iptables.rules
        然后给这个脚本文件添加可执行权限:sudo chmod +x /etc/network/if - post - up.d/iptables - restore
      • 在基于Red Hat或CentOS的系统中,可以将iptables - restore命令添加到/etc/rc.d/rc.local文件中(不过要确保rc.local文件有可执行权限),如:iptables - restore < /etc/iptables.rules。这样,在系统启动后,iptables规则就会根据保存的文件进行恢复。
  2. 使用iptables - persistent工具(适用于Debian和Ubuntu系统)
    • 安装iptables - persistent
      • 在Debian或Ubuntu系统中,可以使用以下命令安装iptables - persistent工具:sudo apt - get install iptables - persistent
      • 这个工具会自动在安装过程中保存当前的iptables规则,并且会创建一些系统服务相关的配置来确保规则在系统启动时自动加载。
    • 保存和恢复规则
      • 安装完成后,iptables规则会被自动保存到/etc/iptables目录下的文件中(/etc/iptables/rules.v4用于IPv4规则,/etc/iptables/rules.v6用于IPv6规则)。
      • 在系统启动时,iptables - persistent服务会自动读取这些文件中的规则并恢复iptables配置。如果后续修改了iptables规则,想保存新规则,可以使用sudo iptables - save > /etc/iptables/rules.v4(对于IPv4)和sudo iptables - save > /etc/iptables/rules.v6(对于IPv6)命令手动保存,或者使用dpkg - reconfigure iptables - persistent命令重新配置并保存规则。
  3. 使用systemd服务(适用于有systemd的Linux发行版)
    • 创建systemd服务单元文件
      • 可以创建一个自定义的systemd服务单元文件来保存和恢复iptables规则。例如,创建一个名为iptables - save - service.service的文件,内容如下:
        1
        2
        3
        4
        5
        6
        7
        8
        [Unit]
        Description=Save iptables rules
        After=network.target
        [Service]
        Type=oneshot
        ExecStart=/usr/bin/iptables - save > /etc/iptables.rules
        [Install]
        WantedBy=multi - user.target
      • 将这个文件保存到/etc/systemd/system/目录下,然后使用systemd命令来管理这个服务。
    • 启用和启动服务
      • 执行sudo systemd - enable iptables - save - service.service命令来设置服务在系统启动时自动运行。
      • 可以使用sudo systemd - start iptables - save - service.service命令来手动启动服务,保存当前的iptables规则。
      • 同时,还需要创建一个用于恢复规则的服务单元文件,例如iptables - restore - service.service,内容如下:
        1
        2
        3
        4
        5
        6
        7
        8
        [Unit]
        Description=Restore iptables rules
        After=network.target
        [Service]
        Type=oneshot
        ExecStart=/usr/bin/iptables - restore < /etc/iptables.rules
        [Install]
        WantedBy=multi - user.target
      • 同样将这个文件保存到/etc/systemd/system/目录下,然后使用systemd - enable iptables - restore - service.service命令来设置在系统启动时自动恢复规则,并且可以使用systemd - start iptables - restore - service.service命令手动启动恢复规则的服务。