WireGuard教程:使⽤DNS-SD进⾏NAT-to-NAT穿透
WireGuard是由 Jason A. Donenfeld 等⼈创建的下⼀代开源 VPN 协议,旨在解决许多困扰IPSec/IKEv2、OpenVPN或L2TP等其他 VPN 协议的问题。2020 年 1 ⽉ 29
⽇,WireGuard 正式合并进⼊Linux 5.6内核主线。
利⽤ WireGuard 我们可以实现很多⾮常奇妙的功能,⽐如跨公有云组建 Kubernetes 集,本地直接访问公有云Kubernetes集中的 Pod IP 和 Service IP,在家中没有公⽹ IP 的情况下直连家中的设备,等等。
如果你是第⼀次听说 WireGuard,建议你花点时间看看我之前写的 WireGuard 。然后可以参考下⾯两篇⽂章来快速上⼿:
如果遇到某些细节不太明⽩的,再去参考。
本⽂将探讨 WireGuard 使⽤过程中遇到的⼀个重⼤难题:如何使两个位于 NAT 后⾯(且没有指定公⽹出⼝)的客户端之间直接建⽴连接。
WireGuard 不区分服务端和客户端,⼤家都是客户端,与⾃⼰连接的所有客户端都被称之为Peer。
1. IP 不固定的 Peer
WireGuard 的核⼼部分是,它的⼯作原理是将公钥和 IP 地址列表(AllowedIPs)关联起来。每⼀个⽹络接⼝都有⼀个私钥和⼀个 Peer 列表,每⼀个 Peer 都有⼀个公钥和 IP 地址列表。发送数据时,可以把 IP 地址列表看成路由表;接收数据时,可以把 IP 地址列表看成访问控制列表。
公钥和 IP 地址列表的关联组成了 Peer 的必要配置,从隧道验证的⾓度看,根本不需要 Peer 具备静态 IP 地址。理论上,如果 Peer 的 IP 地址不同时发⽣变化,WireGuard 是可以实现 IP 漫游的。
现在回到最初的问题:假设两个 Peer 都在 NAT 后⾯,且这个 NAT 不受我们控制,⽆法配置 UDP 端⼝转发,即⽆法指定公⽹出⼝,要想建⽴连接,不仅要动态发现 Peer 的 IP 地址,还要发现 Peer 的端⼝。
了⼀圈下来,现有的⼯具根本⽆法实现这个需求,本⽂将致⼒于不对 WireGuard 源码做任何改动的情况下实现上述需求。
2. 中⼼辐射型⽹络拓扑
你可能会问我为什么不使⽤?中⼼辐射型⽹络有⼀个 VPN ⽹关,这个⽹关通常都有⼀个静态 IP 地址,其他所有的客户端都需要连接这个 VPN ⽹关,再由⽹关将流量转发到其他的客户端。假设Alice
和Bob都位于 NAT 后⾯,那么Alice和Bob都要和⽹关建⽴隧道,然后Alice和Bob之间就可以通过 VPN ⽹关转发流量来实现相互通信。
其实这个⽅法是如今⼤家都在⽤的⽅法,已经没什么可说的了,缺点相当明显:
当 Peer 越来越多时,VPN ⽹关就会变成垂直扩展的瓶颈。
通过 VPN ⽹关转发流量的成本很⾼,毕竟云服务器的流量很贵。
通过 VPN ⽹关转发流量会带来很⾼的延迟。
本⽂想探讨的是Alice和Bob之间直接建⽴隧道,中⼼辐射型(hub-and-spoke)⽹络拓扑是⽆法做到的。
3. NAT 穿透
要想在Alice和Bob之间直接建⽴⼀个 WireGuard 隧道,就需要它们能够穿过挡在它们⾯前的 NAT。由于 WireGuard 是通过UDP来相互通信的,所以理论上是最佳选择。UDP 打洞(UDP hole punching)利⽤了这样⼀个事实:⼤多数 NAT 在将⼊站数据包与现有的连接进⾏匹配时都很宽松。这样就可以重复使⽤端⼝状态来打洞,因为 NAT 路由器不会限制只接收来⾃原始⽬的地址(信使服务器)的流量,其他客户端的流量也可以接收。
举个例⼦,假设Alice向新主机Carol发送⼀个 UDP 数据包,⽽Bob此时通过某种⽅法获取到了Alice的 NAT 在地址转换过程中使⽤的出站源IP:Port,Bob就可以向这个
IP:Port(2.2.2.2:7777)发送 UDP 数据包来和Alice建⽴联系。
其实上⾯讨论的就是完全圆锥型 NAT(Full cone NAT),即⼀对⼀(one-to-one)NAT。它具有以下特点:
⼀旦内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发⾃ iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送。
任意外部主机都能经由发送数据包给 eAddr:ePort 到达 iAddr:iPort。
⼤部分的 NAT 都是这种 NAT,对于其他少数不常见的 NAT,这种打洞⽅法有⼀定的局限性,⽆法顺利使⽤。
4. STUN
回到上⾯的例⼦,UDP 打洞过程中有⼏个问题⾄关重要:
Alice 如何才能知道⾃⼰的公⽹IP:Port?
Alice 如何与 Bob 建⽴连接?
在 WireGuard 中如何利⽤ UDP 打洞?
关于 STUN(Session Traversal Utilities for NAT,NAT会话穿越应⽤程序)的详细描述中定义了⼀个协议回答了上⾯的⼀部分问题,这是⼀篇内容很长的 RFC,所以我将尽我所能对其进⾏总结。先提醒⼀下,STUN并不能直接解决上⾯的问题,它只是个扳⼿,你还得拿他去打造⼀个称⼿的⼯具:
STUN 本⾝并不是 NAT 穿透问题的解决⽅案,它只是定义了⼀个机制,你可以⽤这个机制来组建实际的解决⽅案。
—
是⼀种⽹络协议,它允许位于NAT(或多重NAT)后的客户端出⾃⼰的公⽹地址,查出⾃⼰位于哪种类型的 NAT 之后以及 NAT 为某⼀个本地端⼝所绑定的公⽹端⼝。这些信息被⽤来在两个同时处于 NAT 路由器之后的主机之间建⽴ UDP 通信。该协议由 RFC 5389 定义。
STUN 是⼀个客户端-服务端协议,在上图的例⼦中,Alice是客户端,Carol是服务端。Alice向Carol发送⼀个STUN Binding请求,当 Binding 请求通过Alice的 NAT 时,源IP:Port 会被重写。当Carol收到 Binding 请求后,会将三层和四层的源IP:Port复制到 Binding 响应的有效载荷中,并将其发送给Alice。
Binding 响应通过 Alice 的 NAT 转发到内⽹的Alice,此时的⽬标 IP:Port 被重写成了内⽹地址,但有效载荷保持不变。Alice收到 Binding 响应后,就会意识到这个 Socket 的公⽹ IP:Port 是2.2.2.2:7777。
然⽽,STUN并不是⼀个完整的解决⽅案,它只是提供了这么⼀种机制,让应⽤程序获取到它的公⽹IP:Port,但 STUN 并没有提供具体的⽅法来向相关⽅向发出信号。如果要重头编写⼀个具有 NAT 穿透功能的应⽤,肯定要利⽤ STUN 来实现。当然,明智的做法是不修改 WireGuard 的源码,最好是借鉴 STUN 的概念来实现。总之,不管如何,都需要⼀个拥有静态公⽹地址的主机来充当信使服务器。
5. NAT 穿透⽰例
早在 2016 年 8 ⽉份,WireGuard 的创建者就在上分享了⼀个。Jason 的⽰例包含了客户端应⽤和服务端应⽤,其中客户端应⽤于 WireGuard ⼀起运⾏,服务端运⾏在拥有静态地址的主机上⽤来发现各个 Peer 的IP:Port,客户端使⽤与服务端进⾏通信。
/* We use raw sockets so that the WireGuard interface can actually own the real socket. */
sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if (sock < 0) {
perror("socket");
return errno;
}
正如评论中指出的,WireGuard 拥有“真正的套接字”。通过使⽤原始套接字(raw socket),客户端能够向服务端伪装本地 WireGuard 的源端⼝,这样就确保了在服务端返回响应经过 NAT 时⽬标IP:Port会被映射到 WireGuard 套接字上。
客户端在其原始套接字上使⽤⼀个来过滤服务端发往 WireGuard 端⼝的回复。
static void apply_bpf(int sock, uint16_t port, uint32_t ip)
{
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12 /* src ip */),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ip, 0, 5),
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 /* src port */),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PORT, 0, 3),
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22 /* dst port */),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 0, 1),
BPF_STMT(BPF_RET + BPF_K, -1),
BPF_STMT(BPF_RET + BPF_K, 0)
};
struct sock_fprog filter_prog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) {
perror("setsockopt(bpf)");
exit(errno);
}
}
客户端与服务端的通信数据都被定义在packet和reply这两个结构体中:
struct {
struct udphdr udp;
uint8_t my_pubkey[32];
uint8_t their_pubkey[32];
} __attribute__((packed)) packet = {
.udp = {
.
len = htons(sizeof(packet)),
.dest = htons(PORT)
}
};
struct {
struct iphdr iphdr;
struct udphdr udp;
uint32_t ip;
uint16_t port;
} __attribute__((packed)) reply;
客户端会遍历配置好的 WireGuard Peer(wg show <interface> peers),并为每⼀个 Peer 发送⼀个
数据包给服务端,其中my_pubkey和their_pubkey字段会被适当填充。当服务端收到来⾃客户端的数据包时,它会向以公钥为密钥的 Peer 内存表中插⼊或更新⼀个pubkey=my_pubkey的entry,然后再从该表中查pubkey=their_pubkey的entry,⼀但发现entry存在,就会将其中的IP:Port发送给客户端。当客户端收到回复时,会将 IP 和端⼝从数据包中解包,并配置 Peer 的 endpoint 地址(wg set <interface> peer <key> &> endpoint
<ip>:<port>)。
entry结构体源码:
struct entry {
uint8_t pubkey[32];
uint32_t ip;
uint16_t port;
};
entry结构体中的ip和port字段是从客户端收到的数据包中提取的 IP 和 UDP 头部,每次客户端请求 Peer 的 IP 和端⼝信息时,都会在 Peer 列表中刷新⾃⼰的 IP 和端⼝信息。
上⾯的例⼦展⽰了 WireGuard 如何实现 UDP 打洞,但还是太复杂了,因为并不是所有的 Peer 端都能打开原始套接字(raw socket),也并不是所有的 Peer 端都能利⽤ BPF 过滤器。⽽且这⾥还⽤到了⾃定义的,代码层⾯的数据(链表、队列、⼆叉树)都是结构化的,但⽹络层看到的都是⼆进制流,所谓wire protocol就是把结构化的数据序列化为⼆进制流发送出去,并且对⽅也能以同样的格式反序列化出来。这种⽅式是很难调试的,所以我们需要另辟蹊径,利⽤现有的成熟⼯具来达到⽬的。
6. WireGuard NAT 穿透的正解
其实完全没必要这么⿇烦,我们可以直接利⽤ WireGuard 本⾝的特性来实现 UDP 打洞,直接看图:
你可能会认为这是个中⼼辐射型(hub-and-spoke)⽹络拓扑,但实际上还是有些区别的,这⾥的 Registry Peer 不会充当⽹关的⾓⾊,因为它没有相应的路由,不会转发流量。Registry 的 WireGuard 接⼝地址为10.0.0.254/32,Alice 和 Bob 的AllowedIPs中只包含了10.0.0.254/32,表⽰只接收来⾃Registry的流量,所以 Alice 和 Bob 之间⽆法通过 Registry 来进⾏通信。
这⾥有⼀点⾄关重要,Registry分别和 Alice 与 Bob 建⽴了两个隧道,这就会在 Alice 和 Bob 的 NAT
上打开⼀个洞,我们需要到⼀种⽅法来从 Registry Peer 中查询这些洞的IP:Port,⾃然⽽然就想到了DNS协议。DNS 的优势很明显,它⽐较简单、成熟,还跨平台。有⼀种 DNS 记录类型叫,它⽤来记录服务器提供的服务,即识别服务的 IP 和端
⼝,⽤具体的结构和查询模式对这种记录类型进⾏了扩展,⽤于发现给定域下的服务,我们可以直接利⽤这些扩展语义。
7. CoreDNS
选好了服务发现协议后,还需要⼀种⽅法来将其与 WireGuard 对接。是 Golang 编写的⼀个插件式 DNS 服务器,是⽬前 Kubernetes 内置的默认 DNS 服务器,并且已从毕业。我们可以直接写⼀个 CoreDNS 插件,⽤来接受DNS-SD(DNS-based Service Discovery)查询并返回相关 WireGuard Peer 的信息,其中公钥作为记录名
称,fuckcloudnative.io 作为域。如果你熟悉 bind 风格的域⽂件,可以想象⼀个类似这样的域数据:
_wireguard._udp IN PTR alice._wireguard._udp.fuckcloudnative.io.
_wireguard._udp IN PTR bob._wireguard._udp.fuckcloudnative.io.
alice._wireguard._udp IN SRV 0 1 7777 alice.fuckcloudnative.io.
alice IN A 2.2.2.2
bob._wireguard._udp IN SRV 0 1 8888 bob.fuckcloudnative.io.
bob IN A 3.3.3.3
公钥使⽤ Base64 还是 Base32 ?
到⽬前为⽌,我们⼀直使⽤别名 Alice 和 Bob 来替代其对应的 WireGuard 公钥。WireGuard 公钥是Base64编码的,长度为44字节:
$ wg genkey | wg pubkey
UlVJVmPSwuG4U9BwyVILFDNlM+Gk9nQ7444HimPPgQg=
Base 64 编码的设计是为了以⼀种允许使⽤⼤写字母和⼩写字母的形式来表⽰任意的⼋位字节序列。
—
不幸的是,DNS 的 SRV 记录的服务名称是不区分⼤⼩写的:
DNS 树中的每个节点都有⼀个由零个或多个标签组成的名称 [STD13, RFC1591, RFC2606],这些标签不区分⼤⼩写。
—
Base32虽然产⽣了⼀个稍长的字符串(56字节),但它的表现形式允许我们在 DNS 内部表⽰ WireGuard 公钥:
Base32 编码的⽬的是为了表⽰任意⼋位字节序列,其形式必须不区分⼤⼩写。
我们可以使⽤base64和base32命令来回转换编码格式,例如:
$ wg genkey | wg pubkey >
$
O9rAAiO5qTejOEtFbsQhCl745ovoM9coTGiprFTaHUE=
$ | base64 -D | base32
HPNMAARDXGUTPIZYJNCW5RBBBJPPRZUL5AZ5OKCMNCU2YVG2DVAQ====
$ | base64 -D | base32 | base32 -d | base64
O9rAAiO5qTejOEtFbsQhCl745ovoM9coTGiprFTaHUE=
我们可以直接使⽤base32这种不区分⼤⼩写的公钥编码,来使其与 DNS 兼容。
编译插件
CoreDNS 提供了,插件必须要实现plugin.Handler接⼝:
type Handler interface {
ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
Name() string
}
我⾃⼰已经写好了插件,通过DNS-SD(DNS-based Service Discovery)语义来提供 WireGuard 的 Peer 信息,该插件名就叫。⾃⼰编写的插件不属于官⽅内置插件,从CoreDNS 官⽅下载页下载的可执⾏程序并不包括这两个插件,所以需要⾃⼰编译 CoreDNS。
编译 CoreDNS 并不复杂,在没有外部插件的情况下可以这么编译:
$ git clone github/coredns/coredns.git
$ cd coredns
$ make
如果要加上 wgsd 插件,则在make前,要修改plugin.cfg⽂件,加⼊以下⼀⾏:
wgsd:github/jwhited/wgsd
然后开始编译:
$ go generate
$ go build
查看编译好的⼆进制⽂件是否包含该插件:
$ ./coredns -plugins | grep wgsd
dns.wgsd
编译完成后,就可以在配置⽂件中启⽤wgsd插件了:
.:53 {
wgsd <zone> <wg device>
}
可以来测试⼀下,配置⽂件如下:
$ cat Corefile
.:53 {
debug
wgsd fuckcloudnative.io. wg0
}
运⾏ CoreDNS:
$ ./coredns -conf Corefile
.:53
CoreDNS-1.8.1
linux/amd64, go1.15,
当前节点的 WireGuard 信息:
$ sudo wg show
interface: wg0
listening port: 52022
peer: mvplwow3agnGM8G78+BiJ3tmlPf9gDtbJ2NdxqV44D8=
endpoint: 3.3.3.3:8888
allowed ips: 10.0.0.2/32
下⾯就是见证奇迹的时候,列出所有 Peer:
$ dig @127.0.0.1 _wireguard._udp.fuckcloudnative.io. PTR +noall +answer +additional
; <<>> DiG 9.10.6 <<>> @127.0.0.1 _wireguard._udp.fuckcloudnative.io. PTR +noall +answer +additional
; (1 server found)
;; global options: +cmd
_wireguard._udp.fuckcloudnative.io. 0 IN PTR TL5GLQUMG5VATRRTYG57HYDCE55WNFHX7WADWWZHMNO4NJLY4A7Q====._wireguard._udp.fuckcloudnative.io.
查询每个 Peer 的 IP 和端⼝:
$ dig @127.0.0.1 TL5GLQUMG5VATRRTYG57HYDCE55WNFHX7WADWWZHMNO4NJLY4A7Q====._wireguard._udp.fuckcloudnative.io. SRV +noall +answer +additional
; <<>> DiG 9.10.6 <<>> @127.0.0.1 TL5GLQUMG5VATRRTYG57HYDCE55WNFHX7WADWWZHMNO4NJLY4A7Q====._wireguard._udp.fuckcloudnative.io. SRV +noall +answer +additional
; (1 server found)
;; global options: +cmd
tl5glqumg5vatrrtyg57hydce55wnfhx7wadwwzhmno4njly4a7q====._wireguard._udp.fuckcloudnative.io. 0 IN SRV 0 0 8888 TL5GLQUMG5VATRRTYG57HYDCE55WNFHX7WADWWZHMNO4NJLY4A7Q====.fuckcloudnative.io. TL5GLQUMG5VATRRTYG57HYDCE55WNFHX7WADWWZHMNO4NJLY4A7Q====.fuckcloudnative.io. 0 IN A 3.3.3.3
完美!
验证公钥是否匹配:
$ wg show wg0 peers
mvplwow3agnGM8G78+BiJ3tmlPf9gDtbJ2NdxqV44D8=
$ dig @127.0.0.1 _wireguard._udp.fuckcloudnative.io. PTR +short | cut -d. -f1 | base32 -d | base64
mvplwow3agnGM8G78+BiJ3tmlPf9gDtbJ2NdxqV44D8=
8. 最终通信流程
最终实现的通信流程如下:
⼀开始,Alice 和 Bob 分别与 Registry 建⽴了隧道;接下来,Alice 上的wgsd-client向 Registry 节点上运⾏的 CoreDNS插件(wgsd)发起查询请求,该插件从 WireGuard 信息中
检索Bob的 endpoint 信息,并将其返回给wgsd-client;然后wgsd-client开始设置 Bob 的 endpoint;最后 Alice 和 Bob 之间直接建⽴了⼀条隧道。
任何提及 "建⽴隧道 "的地⽅都只是意味着发⽣了握⼿,数据包可以在 Peer 之间传输。虽然 WireGuard 确实有⼀个握⼿机制,但它⽐你想象的更像是⼀个⽆连接的协议。
任何安全协议都需要保持⼀些状态,所以最初的握⼿是⾮常简单的,只是建⽴⽤于数据传输的对称密钥。这种握⼿每隔⼏分钟就会发⽣⼀次,以提供轮换密钥来实现
完美的前向保密。它是根据时间来完成的,⽽不是根据之前数据包的内容来完成的,因为它的设计是为了优雅地处理数据包丢失的问题。
—
现在万事俱备,只⽋东风,只需要实现wgsd-client就完事了。
9. 实现 wgsd-client
wgsd-client负责使 Peer 的 endpoint 配置保持最新状态,它会检索配置中的 Peer 列表,查询 CoreDNS 中与之匹配的公钥,然后在需要时为相应的 Peer 更新 endpoint 的值。最
初的实现⽅式是以定时任务或者类似的调度机制运⾏,以序列化的⽅式检查所有 Peer,设置 endpoint,然后退出。⽬前它还不是⼀个守护进程,后续会继续改进优化。
wgsd-client的源码位于 wgsd 仓库中的⽬录。
下⾯开始进⾏最终的测试。
Alice 和 Bob 都在 NAT 后⾯,Registry 没有 NAT,且有固定的公⽹地址。这三个 Peer 的信息如下:
Peer Public Key Tunnel Address
Alice xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=10.0.0.1
Bob syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=10.0.0.2
Registry JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=10.0.0.254
它们各⾃的初始配置:
Alice
$ cat /etc/f
[Interface]
Address = 10.0.0.1/32
PrivateKey = 0CtieMOYKa2RduPbJss/Um9BiQPSjgvHW+B7Mor5OnE=
ListenPort = 51820
# Registry
[Peer]
PublicKey = JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=
Endpoint = 4.4.4.4:51820
PersistentKeepalive = 5
AllowedIPs = 10.0.0.254/32
# Bob
[Peer]
PublicKey = syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
PersistentKeepalive = 5
AllowedIPs = 10.0.0.2/32
$ wg show
interface: wg0
public key: xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
private key: (hidden)
listening port: 51820
peer: JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=
endpoint: 4.4.4.4:51820
allowed ips: 10.0.0.254/32
latest handshake: 48 seconds ago
transfer: 1.67 KiB received, 11.99 KiB sent
persistent keepalive: every 5 seconds
peer: syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
allowed ips: 10.0.0.2/32
persistent keepalive: every 5 seconds
Bob
$ cat /etc/f
[Interface]
Address = 10.0.0.2/32
PrivateKey = cIN5NqeWcbreXoaIhR/4wgrrQJGym/E7WrTttMtK8Gc=
ListenPort = 51820
# Registry
[Peer]
PublicKey = JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=
Endpoint = 4.4.4.4:51820
PersistentKeepalive = 5
AllowedIPs = 10.0.0.254/32
# Alice
[Peer]
PublicKey = xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
PersistentKeepalive = 5
AllowedIPs = 10.0.0.1/32
$ wg show
interface: wg0
public key: syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
private key: (hidden)
listening port: 51820
peer: JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=
endpoint: 4.4.4.4:51820
allowed ips: 10.0.0.254/32
latest handshake: 26 seconds ago
transfer: 1.54 KiB received, 11.75 KiB sent
persistent keepalive: every 5 seconds
peer: xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
allowed ips: 10.0.0.1/32
persistent keepalive: every 5 seconds
Registry
$ cat /etc/f
[Interface]
Address = 10.0.0.254/32
PrivateKey = wLw2ja5AapryT+3SsBiyYVNVDYABJiWfPxLzyuiy5nE=
ListenPort = 51820
# Alice
[Peer]
PublicKey = xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
AllowedIPs = 10.0.0.1/32
# Bob
peer[Peer]
PublicKey = syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
AllowedIPs = 10.0.0.2/32
$ wg show
interface: wg0
public key: JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=
private key: (hidden)
listening port: 51820
peer: xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
endpoint: 2.2.2.2:41424
allowed ips: 10.0.0.1/32
latest handshake: 6 seconds ago
transfer: 510.29 KiB received, 52.11 KiB sent
peer: syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
endpoint: 3.3.3.3:51820
allowed ips: 10.0.0.2/32
latest handshake: 1 minute, 46 seconds ago
transfer: 498.04 KiB received, 50.59 KiB sent
Registry 与 Alice 和 Bob 都建⽴了连接,可以直接查询它们的 endpoint 信息:
$ dig @4.4.4.4 -p 53 _wireguard._udp.fuckcloudnative.io. PTR +noall +answer +additional
; <<>> DiG 9.10.6 <<>> @4.4.4.4 -p 53 _wireguard._udp.fuckcloudnative.io. PTR +noall +answer +additional
; (1 server found)
;; global options: +cmd
_wireguard._udp.fuckcloudnative.io. 0 IN PTR YUTRLED535IGKL7BDLERL6M4VJXSXM3UQQPL4NMSN27MT56AD4HA====._wireguard._udp.fuckcloudnative.io.
_wireguard._udp.fuckcloudnative.io. 0 IN PTR WMRID55V4ENHXQX2JSTYOYVKICJ5PIHKB2TR7R42SMIU3T5L4I5Q====._wireguard._udp.fuckcloudnative.io.
$ dig @4.4.4.4 -p 53 YUTRLED535IGKL7BDLERL6M4VJXSXM3UQQPL4NMSN27MT56AD4HA====._wireguard._udp.fuckcloudnative.io. SRV +noall +answer +additional
; <<>> DiG 9.10.6 <<>> @4.4.4.4 -p 53 YUTRLED535IGKL7BDLERL6M4VJXSXM3UQQPL4NMSN27MT56AD4HA====._wireguard._udp.fuckcloudnative.io. SRV +noall +answer +additional
; (1 server found)
;; global options: +cmd
yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====._wireguard._udp.fuckcloudnative.io. 0 IN SRV 0 0 41424 YUTRLED535IGKL7BDLERL6M4VJXSXM3UQQPL4NMSN27MT56AD4HA====.fuckcloudnative.io. YUTRLED535IGKL7BDLERL6M4VJXSXM3UQQPL4NMSN27MT56AD4HA====.fuckcloudnative.io. 0 IN A 2.2.2.2
完美,下⾯分别在 Alice 和 Bob 上启动wgsd-client试试:
# Alice
$ ./wgsd-client -device=wg0 -dns=4.4.4.4:53 -zone=fuckcloudnative.io.
2020/05/20 13:24:02 [JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=] no SRV records found
jwhited@Alice:~$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: icmp_seq=0 ttl=64 time=173.260 ms
^C
jwhited@Alice:~$ wg show
interface: wg0
public key: xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
private key: (hidden)
listening port: 51820
peer: syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
endpoint: 3.3.3.3:51820
allowed ips: 10.0.0.2/32
latest handshake: 2 seconds ago
transfer: 252 B received, 264 B sent
persistent keepalive: every 5 seconds
peer: JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=
endpoint: 4.4.4.4:51820
allowed ips: 10.0.0.254/32
latest handshake: 1 minute, 19 seconds ago
transfer: 184 B received, 1.57 KiB sent
persistent keepalive: every 5 seconds
# Bob
$ ./wgsd-client -device=wg0 -dns=4.4.4.4:53 -zone=fuckcloudnative.io.
2020/05/20 13:24:04 [JeZlz14G8tg1Bqh6apteFCwVhNhpexJ19FDPfuxQtUY=] no SRV records found
jwhited@Bob:~$ wg show
interface: wg0
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论