使用 tinc 搭建虚拟局域网
自从用上了移动宽带,就一直处于大局域网中,无法直接连接家里的设备,只能通过 frp 进行内网穿透。
后来移动普及了 IPv6,终于可以通过 IPv6 地址连接家里的设备。但过完年后,从国外到家里的 IPv6 连接一直被阻断在骨干网的出口节点,而国内到家里又能正常连接。
在这种奇葩的情况下,我本想继续用回 frp 方案,但群友建议组个虚拟局域网使用起来会更加方便,于是就有了这次的折腾。
tinc or ZeroTier?
在开始之前,我先了解了一下使用比较多的两种组网工具:tinc 和 ZeroTier。
tinc
- 网状网络,任意两个节点之间可以直连,如果无法连接,则自动通过一个或多个中间节点转发;
- 开源,私有部署比较方便;
- 无图形化配置界面,需要手动编辑配置文件,且需要手动分发配置;
- Switch 模式下工作在 L2 层,Router 模式下工作在 L3 层;
- 支持通过 TCP 或 UDP 进行连接;
- 单线程运行,性能比较一般,有测试表明传输速度约为裸 HTTP 传输速度的 1/10。
ZeroTier
- 任意两个节点之间需要通过根节点来建立直连,如果无法连接,则自动通过根节点转发;
- 开源,可自建 planet 节点以实现私有部署;
- 图形化配置界面,可以通过网页进行配置,并自动分发配置;
- 仅支持通过 UDP 进行连接;
- 有免费的官方 planet 节点,但是连通性和速度都不太理想;
- 官方免费版最多可接入 25 个节点;
- 名词解释:
- planet:行星节点,官方的根服务器;
- moon:卫星节点,用户自建的私有根服务器,可中转加速行星节点;
- leaf:叶子节点,每台连接到该网络的设备。
因为 ZeroTier 在配置上的便利性,我最开始考虑使用它来组网,并且考虑到免费版限制节点数量,外加手里有一台国内的公网 VPS,所以我打算私有部署 ZeroTier。
但在实际部署中,我发现了一个最致命的问题:配置 planet 节点时不支持使用域名,必须使用 IP 地址,而我那台 VPS 却是动态 IP 的。这直接导致我不得不放弃使用 ZeroTier 而改用 tinc。
网络规划
网络拓扑图如上,本次组网将在各个 VLAN 节点上部署 tinc,最终实现效果如下:
- 所有 VLAN 和 LAN 节点之间互通且尽可能直连,如果无法直连,则通过 Cloud_A 或 Cloud_B 转发;
- VLAN 节点可通过 192.168.1.0/24 地址连接 LAN 节点;
- LAN 节点可通过 10.0.0.0/24 地址连接 VLAN 节点。
安装
各大 Linux 发行版基本都有 tinc 包,可以直接使用包管理器安装。目前 tinc 的最新稳定版是 1.0.36,另有 1.1pre18 的预发行版可选,以下步骤默认使用 1.0.36 版本。
bash
pacman -S tinc # Arch Linux
yum install tinc # CentOS/RHEL
apt install tinc # Debian/Ubuntu
opkg install tinc # OpenWrt
配置
tinc 默认配置文件目录为 /etc/tinc
,典型的目录结构如下:
bash
/etc/tinc
├── <net_1> # 网络名称,用于 tinc 的启动参数
│ ├── hosts # 节点配置文件列表,每个节点一个文件
│ │ ├── <host_1>
│ │ ├── <host_2>
│ │ ├── ...
│ │ └── <host_n>
│ ├── <private_key> # 本节点的私钥
│ ├── tinc.conf # 本节点的网络配置文件
│ ├── tinc-down # tinc 启动时调用该脚本
│ └── tinc-up # tinc 停止时调用该脚本
├── <net_2>
├── ...
└── <net_n>
以下步骤中,配置文件仅列出本次组网所需的配置项,完整的配置项可参考:
OpenWrt 节点
-
tinc.conf
ini
Name = OpenWrt # 本节点名称 Interface = tinc # 网卡名称 Mode = router # 工作模式,Android 版本不支持 switch 模式,将会无法连接 ConnectTo = Cloud_A # 本节点将尝试主动连接到 Cloud_A 节点 ConnectTo = Cloud_B
-
tinc-up
bash
#!/bin/sh ip link set $INTERFACE up # 本节点的 IP 地址 ip addr add 10.0.0.1/24 dev $INTERFACE # 来自 192.168.1.0/24 段、且去往 10.0.0.0/24 段的包,修改其源地址为 10.0.0.1(OpenWrt) iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 10.0.0.0/24 -j SNAT --to-source 10.0.0.1
-
tinc-down
bash
#!/bin/sh iptables -t nat -D POSTROUTING -s 192.168.1.0/24 -d 10.0.0.0/24 -j SNAT --to-source 10.0.0.1 ip addr del 10.0.0.1/24 dev $INTERFACE ip link set $INTERFACE down
-
hosts/OpenWrt
ini
Subnet = 10.0.0.1/32 # 分配 10.0.0.1 给本节点 Subnet = 192.168.1.0/24 # 开放 192.168.1.0/24 段给所有节点
Cloud_A、Cloud_B 节点
Cloud_A 和 Cloud_B 节点的配置类似,以下是 Cloud_A 节点的配置。
-
tinc.conf
ini
Name = Cloud_A # 本节点名称 Interface = tinc # 网卡名称 Mode = router # 工作模式,Android 版本不支持 switch 模式,将会无法连接
-
tinc-up
bash
#!/bin/sh ip link set $INTERFACE up # 本节点的 IP 地址 ip addr add 10.0.0.2/24 dev $INTERFACE # 去往 192.168.1.0/24 段的包发往 10.0.0.1(OpenWrt) 处理 ip route add 192.168.1.0/24 via 10.0.0.1 dev tinc
-
tinc-down
bash
#!/bin/sh ip route del 192.168.1.0/24 via 10.0.0.1 dev tinc ip addr del 10.0.0.2/24 dev $INTERFACE ip link set $INTERFACE up
-
hosts/Cloud_A
ini
Address = <ip_or_domain> # 本节点的公网地址,用于被其他节点连接 Port = <port_number> # 本节点的监听端口,默认为 655 Subnet = 10.0.0.2/32 # 分配 10.0.0.2 给本节点
创建密钥
配置完成后,需要在各个节点上分别创建密钥,用于连接时鉴权。创建过程中会提示公钥和私钥的保存路径,保持默认直接回车即可。
bash
tincd -n VLAN -K # 生成 VLAN 网络的密钥,使用 RSA 算法
# 如果是 1.1pre18 版本
tinc -n VLAN generate-rsa-keys # 使用 RSA 算法
# 或
tinc -n VLAN generate-ed25519-keys # 使用 Ed25519 算法
创建完成后,/etc/tinc/vlan
路径下会生成 rsa_key.priv
私钥文件,并且在 /etc/tinc/vlan/hosts
目录下的本节点配置文件中会附加公钥信息。
ini
Address = <ip_or_domain>
Port = <port_number>
Subnet = <subnet>
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA2QmhvEzTnZmRsAVGwwvSFI/ib84tRIGwg7Nh8sudanS4HuIUnkm7
g5osJ44TDMnYu5D+gmsZnukDdUAp/sNzDj6p/uE1RKLYDjEaLs9MOkCI2NcxJ9t5
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
elzSGrotqIxGde4qSC6zUm9rsqogeiR1OqeV5h8S33/gHgjG0C0fPuAEeHdum8Ea
mTvujtRMbyht+zMUcJhcuiDA/w2UJ2B6uwIDAQAB
-----END RSA PUBLIC KEY-----
分发配置
在本次组网中,Cloud_A 和 Cloud_B 节点作为中间节点,需要在其他节点无法互相直连时接受连接,因此需要将这两个节点的配置文件分发到其他节点。
同时,OpenWrt 节点在无法与其他节点直连时,需要通过 Cloud_A 和 Cloud_B 节点转发,因此也需要将 OpenWrt 节点的配置文件分发到这两个节点。
最终各节点的配置文件目录结构如下:
bash
/etc/tinc
└── VLAN
├── hosts
│ ├── OpenWrt
│ ├── Cloud_A
│ └── Cloud_B
├── rsa_key.priv
├── tinc.conf
├── tinc-down
└── tinc-up
最后需要给 tinc-up
和 tinc-down
脚本添加可执行权限。
bash
chmod +x /etc/tinc/VLAN/tinc-up
chmod +x /etc/tinc/VLAN/tinc-down
至此,所有 Linux 节点的配置已经完成。
启动和测试
启动 tinc 服务:
bash
systemctl start tinc@VLAN
如果需要开机自动启动,必须启用 tinc 全局服务和 tinc@VLAN 服务:
bash
systemctl enable tinc
systemctl enable tinc@VLAN
测试:
bash
# 在 OpenWrt 节点上
ping 10.0.0.2 # 测试 Cloud_A 节点
ping 10.0.0.3 # 测试 Cloud_B 节点
# 在 Cloud_A 节点上
ping 10.0.0.1 # 测试 OpenWrt 节点
ping 192.168.1.2 # 测试 OpenWrt 节点下的 PC 节点
# 在 Cloud_B 节点上
ping 10.0.0.1 # 测试 OpenWrt 节点
ping 192.168.1.2 # 测试 OpenWrt 节点下的 PC 节点
如果都能正常 ping 通,说明组网成功。如果有节点无法 ping 通,可以停止 tinc 服务后,使用 tincd -n VLAN -d 5 -D
命令以调试模式启动 tinc,查看日志信息。
报错的具体原因和说明可参见:Error messages。
Android 节点
Android 版本的 tinc 可以在 Tinc App 上下载安装。
打开 Tinc App 后,点击右上角“配置”按钮,选择“生成节点配置和密钥”,填写网络名称和节点名称。
创建完成后,点击上方的“FTP access”开启 FTP 服务,然后用任何可用的 FTP 客户端连接并下载 files
目录,该目录结构如下:
bash
files
└── networks
└── VLAN # 网络名称
├── ed25519_key.priv # 本节点的私钥
├── hosts # 节点配置文件列表,每个节点一个文件
│ └── Phone # 本节点的配置文件
├── network.conf # 本节点的网卡配置文件
├── rsa_key.priv # 本节点的私钥
└── tinc.conf # 本节点的网络配置文件
接下来修改各文件:
-
tinc.conf
ini
Name = Phone # 本节点名称 ConnectTo = Cloud_A # 本节点将尝试主动连接到 Cloud_A 节点 ConnectTo = Cloud_B
-
hosts/Phone
ini
-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAwMchKIonLqZnlTxvPZjRqzQIZPu4bgFveYEnuBPj4uJH0L44NYOp 8KRGiNc+/JrUXeEPsLGe7hXw4cq2OkM7REUKGurXhY0utBRU+xHUsZQq3UOPOgWz xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx udN2aP01cUUPP2oG+kRJVdQlqDhzvJSckDga7qFEiqu/9dNIOJ0RWQozTfM5Yp8/ hh2WVjMSrvrvqlUiEaGkLDWUV61mLAVKGwIDAQAB -----END RSA PUBLIC KEY----- Ed25519PublicKey = 1JpfoY/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxd2+K Port = <port_number> # 本节点的监听端口,创建节点配置时已自动生成 Subnet = 10.0.0.4/32 # 分配 10.0.0.4 给本节点
-
network.conf
ini
# network.conf: Tinc App network interface configuration file. # Keys can be repeated to specify multiple values. # Doc: https://tincapp.pacien.org/doc.html#network-interface # IP address of this node Address = 10.0.0.4/32 # Traffic to route through the VPN Route = 10.0.0.0/24 # 效果类似于 `ip route add 192.168.1.0/24 via 10.0.0.1` Route = 192.168.1.0/24 # or, to route everything: #Route = 0.0.0.0/0 # DNS server(s) #DNSServer = 10.0.0.1 # Search domain(s) to associate to the DNS resolver(s) #SearchDomain = mynet # Applications allowed or disallowed to use the VPN connection #AllowApplication = com.example.app # or #DisallowApplication = com.example.app # Allow applications to bypass the VPN connection #AllowBypass = false # Automatic reconnection on network change #ReconnectOnNetworkChange = true
与 Linux 平台下分发配置一样,需要将 Cloud_A 和 Cloud_B 节点的配置文件分发到本节点,也需要将本节点的配置文件分发到 Cloud_A 和 Cloud_B 节点。
最终 files
目录结构如下:
bash
files
└── networks
└── VLAN
├── ed25519_key.priv
├── hosts
│ ├── Phone
│ ├── Cloud_A
│ └── Cloud_B
├── network.conf
├── rsa_key.priv
└── tinc.conf
通过 FTP 将 files
目录上传并覆盖原文件夹,返回主界面后就可以看到“连接到网络”列表下列出了 VLAN 网络,点击该网络即可连接。连接后可以查看网络和节点的状态信息,也可点击右上角“显示日志”按钮查看具体的日志信息。
后记
切换到虚拟局域网后,各节点之间互连方便了很多。尤其对于那台国内的公网 VPS,再也不用担心服务商限制的可用端口数量不够。
后续可以设置若干个域名解析到 10.0.0.0/24 段的各节点 IP 地址,这样就可以通过域名访问各节点了,更加方便。
二〇二三年二月十二日