p2p vpn的部署方法
p2p vpn的基本概念
p2p vpn这个概念的提出,是因为openvpn在数据传输上的一个特性——虚拟链路都是从拨入端到服务器的。例如vpn网关在北美,上海电信的两个人要通讯,数据就要从北美绕一圈。这个特性在多节点打通上无疑很扯,于是催生了很多p2p vpn。他们的基本理念是——尽力从端到端,不成再绕。而且为了解决端到端,顺便得解决NAT问题——也就是带有STUN打洞。
tun模式的三层转发表
先说明一点,大部分p2p vpn都是tun模式。这也很正常,tunnel用的么。但是大家在配置openvpn的时候,不知是否注意过iroute这个指令。为什么会有iroute指令的存在?
tun模式是三层模式,相信大家都有数。也就是说,报文传递的时候只带有三层地址,openvpn也凭借三层地址来找到要转发给谁。这里和普通的网络就显示出区别了——普通网络使用ARP协议来自管理转发规则,而openvpn则是凭借内部写好的转发表。
例如vpn gateway的虚地址是192.168.100.1,节点1是100.5,节点2是100.10。那么节点2发送报文给节点1时,报文大约长这个样子。
192.168.100.10 -> 192.168.100.5
在普通网络中,第一步会查路由表,确定是eth0(虚拟网络是tun0)。然后在上面广播ARP请求,获得MAC地址。最后填写MAC地址,发送报文。但是在tun虚拟网络中,仔细看你的路由表,是不是整个虚网络都被交给了一个叫做100.4之类的奇怪gateway转发?甚至如果你没有打开client-to-client,整个虚网络只有一台可见,这台还是交给这个gateway转发的。
这是因为你到这个奇怪的IP之间还是走ARP过程,但是这个奇怪的IP收到你的报文后,就可以是纯三层过程了。你可以把这个IP视为本地openvpn的化身。openvpn会把你的报文发送到openvpn gateway,然后openvpn gateway再转发给正确的机器。也就是说,openvpn gateway必须知道某个目标IP需要转给哪个节点,物理地址多少,对吧?
作为纯虚网络,知道节点的IP很容易——毕竟是openvpn gateway管理的地址分配过程。但是作为tunnel和多地址打通,这里就有点困难了。例如节点1还有个网段是192.168.80.0/24,节点2还有个网段是192.168.60.0/24,那么如下一个报文从节点2中出去,你让openvpn gateway怎么办?
192.168.60.15 -> 192.168.80.15
你也许会说,我当然有配节点1的网关转给openvpn的拨入端。问题是,这个动作,openvpn的拨入端尚且不知,何况openvpn gateway?于是我们派生了route/iroute这两个指令。
route/iroute表示这个地址段归属于这个节点所有,区别在于route同时修改路由表,而iroute不修改路由表。配合push,可以由服务器端下发指令修改客户端路由表。
p2p vpn也有类似的问题。甚至,由于没有统一配置端,因此连每个节点的虚IP都不能很容易的得到。在配置中必须注意这点。
n2n
n2n的模式比较简单,也比较有局限性。基本分为两个端,supernode和edge。supernode类似于hub,把所有edge拉到一起。edge都是对等的。
supernode:
supernode -l port
edge:
edge -a [address] -c [name] -k [password] -l [supernode:port] -u [userid] -g [groupid]
解释一下。开一个supernode,不用做任何设定。反正supernode也不会持有edge别的信息。edge要设定supernode的ip和端口,然后提交name和password。name和password相同的,就进入同一个虚拟网络。然后自己的虚拟ip是address。最后的userid和groupid必须是数字。主要是因为开tun需要root权限,因此可以在获得完权限后退化成普通用户,以防权限太高。
这里好玩的就是,理论上address可以天南海北,完全不用管路由怎么走。甚至172.16.0.1可以和10.0.0.1通讯(我没实际确认)。因为大家都是看彼此的IP是否经过注册,而不是计算路由表。
但是这里就有个缺点,我看到man文档中只提到address,没提到可以提交一个网段。所以无论我怎么设定,使用三层方案做隧道的时候,n2n是转不过去的。因为他不知道这个网段归哪个节点管。
所以,n2n的p2p模式很便利,但是没法打tunnel(至少我不知道怎么玩)。
tinc
tinc和n2n一样,也是一种p2p vpn。不过好处在于,tinc允许你在一个节点上配置多个网络,因此可以打tunnel。
在配置之前,我先约定两个词。“配置名”和“节点名”。一个配置是接入同一个网络的多个节点,还有他们如何拓扑。节点名就是一个节点的名字。所以,和配置有关的有以下两个。
/etc/tinc/nets.boot: 这里写上想自动启用的配置的名字
/etc/tinc/[configname]: 配置的根路径,以下路径全是相对路径。
配置
下面就是某个配置中的一堆文件。注意这些配置都是配置自己节点的属性信息。
tinc.conf:
Name = [nodename]
Device = /dev/net/tun
ConnectTo = xxx
tinc-up:
ip link set $INTERFACE up
ip addr add [address/32] dev $INTERFACE
ip route add [network/mask] dev $INTERFACE
tinc-down:
ip route del [network/mask] dev $INTERFACE
ip addr del [address/32] dev $INTERFACE
ip link set $INTERFACE down
其中tinc-up会在tinc启动时执行,tinc-down会在tinc关闭时执行,所以两者必须是+x权限。有趣的是,tinc并不管理你的整个启动过程,tinc-up和tinc-down甚至要亲自来管理接口的up和down过程。但是这也给你足够的额权力去执行任何复杂的路由逻辑指令。
hosts
除去上部分外,还有一个很大的hosts目录。里面放满了节点群信息。
hosts/[nodename]:
Address = external ip address
Port = external port
Subnet = network/mask
其中Address和Port如果没有可以不写。Subnet可以彼此重叠,路由决定逻辑是从小子网到大子网(普通路由表逻辑)
初始化
在配置hosts的时候,我建议每个节点就配置自己的hosts/[nodename]文件,然后执行下面的指令:
tincd -n [name] -K
这个指令会在/etc/tinc/[configname]/rsa_key.priv下生成一个私钥,并且把公钥添加到hosts/[nodename]文件中。最后,在多个node间把彼此的hosts/下的文件一复制,让每个node都拥有其他node的hosts文件,整个网络就完成配置了。
如果你的配置名已经写入nets.boot文件的话,可以用/etc/init.d/tinc restart
来重启启动整个网络。