主頁 >  其他 > WireGuard 教程:使用 DNS-SD 進行 NAT-to-NAT 穿透

WireGuard 教程:使用 DNS-SD 進行 NAT-to-NAT 穿透

2021-02-02 06:45:11 其他

原文鏈接:https://fuckcloudnative.io/posts/wireguard-endpoint-discovery-nat-traversal/

WireGuard 是由 Jason A. Donenfeld 等人創建的下一代開源 VPN 協議,旨在解決許多困擾 IPSec/IKEv2OpenVPNL2TP 等其他 VPN 協議的問題,2020 年 1 月 29 日,WireGuard 正式合并進入 Linux 5.6 內核主線,

利用 WireGuard 我們可以實作很多非常奇妙的功能,比如跨公有云組建 Kubernetes 集群,本地直接訪問公有云 Kubernetes 集群中的 Pod IP 和 Service IP,在家中沒有公網 IP 的情況下直連家中的設備,等等,

如果你是第一次聽說 WireGuard,建議你花點時間看看我之前寫的 WireGuard 作業原理,然后可以參考下面兩篇文章來快速上手:

  • WireGuard 快速安裝教程
  • WireGuard 配置教程:使用 wg-gen-web 來管理 WireGuard 的配置

如果遇到某些細節不太明白的,再去參考 WireGuard 配置詳解,

本文將探討 WireGuard 使用程序中遇到的一個重大難題:如何使兩個位于 NAT 后面(且沒有指定公網出口)的客戶端之間直接建立連接,

WireGuard 不區分服務端和客戶端,大家都是客戶端,與自己連接的所有客戶端都被稱之為 Peer

1. IP 不固定的 Peer

WireGuard 的核心部分是加密密鑰路由(Cryptokey Routing),它的作業原理是將公鑰和 IP 地址串列(AllowedIPs)關聯起來,每一個網路介面都有一個私鑰和一個 Peer 串列,每一個 Peer 都有一個公鑰和 IP 地址串列,發送資料時,可以把 IP 地址串列看成路由表;接收資料時,可以把 IP 地址串列看成訪問控制串列,

公鑰和 IP 地址串列的關聯組成了 Peer 的必要配置,從隧道驗證的角度看,根本不需要 Peer 具備靜態 IP 地址,理論上,如果 Peer 的 IP 地址不同時發生變化,WireGuard 是可以實作 IP 漫游的,

現在回到最初的問題:假設兩個 Peer 都在 NAT 后面,且這個 NAT 不受我們控制,無法配置 UDP 埠轉發,即無法指定公網出口,要想建立連接,不僅要動態發現 Peer 的 IP 地址,還要發現 Peer 的埠,

找了一圈下來,現有的工具根本無法實作這個需求,本文將致力于不對 WireGuard 原始碼做任何改動的情況下實作上述需求,

2. 中心輻射型網路拓撲

你可能會問我為什么不使用中心輻射型(hub-and-spoke)網路拓撲?中心輻射型網路有一個 VPN 網關,這個網關通常都有一個靜態 IP 地址,其他所有的客戶端都需要連接這個 VPN 網關,再由網關將流量轉發到其他的客戶端,假設 AliceBob 都位于 NAT 后面,那么 AliceBob 都要和網關建立隧道,然后 AliceBob 之間就可以通過 VPN 網關轉發流量來實作相互通信,

其實這個方法是如今大家都在用的方法,已經沒什么可說的了,缺點相當明顯:

  • 當 Peer 越來越多時,VPN 網關就會變成垂直擴展的瓶頸,
  • 通過 VPN 網關轉發流量的成本很高,畢竟云服務器的流量很貴,
  • 通過 VPN 網關轉發流量會帶來很高的延遲,

本文想探討的是 AliceBob 之間直接建立隧道,中心輻射型(hub-and-spoke)網路拓撲是無法做到的,

3. NAT 穿透

要想在 AliceBob 之間直接建立一個 WireGuard 隧道,就需要它們能夠穿過擋在它們面前的 NAT,由于 WireGuard 是通過 UDP 來相互通信的,所以理論上 UDP 打洞(UDP hole punching) 是最佳選擇,

UDP 打洞(UDP hole punching)利用了這樣一個事實:大多數 NAT 在將入站資料包與現有的連接進行匹配時都很寬松,這樣就可以重復使用埠狀態來打洞,因為 NAT 路由器不會限制只接收來自原始目的地址(信使服務器)的流量,其他客戶端的流量也可以接收,

舉個例子,假設 Alice 向新主機 Carol 發送一個 UDP 資料包,而 Bob 此時通過某種方法獲取到了 Alice 的 NAT 在地址轉換程序中使用的出站源 IP:PortBob 就可以向這個 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 打洞?

RFC5389 關于 STUNSession Traversal Utilities for NAT,NAT會話穿越應用程式)的詳細描述中定義了一個協議回答了上面的一部分問題,這是一篇內容很長的 RFC,所以我將盡我所能對其進行總結,先提醒一下,STUN 并不能直接解決上面的問題,它只是個扳手,你還得拿他去打造一個稱手的工具:

STUN 本身并不是 NAT 穿透問題的解決方案,它只是定義了一個機制,你可以用這個機制來組建實際的解決方案,

— RFC5389

STUNSession Traversal Utilities for NAT,NAT會話穿越應用程式)是一種網路協議,它允許位于NAT(或多重NAT)后的客戶端找出自己的公網地址,查出自己位于哪種型別的 NAT 之后以及 NAT 為某一個本地埠所系結的公網埠,這些資訊被用來在兩個同時處于 NAT 路由器之后的主機之間建立 UDP 通信,該協議由 RFC 5389 定義,

STUN 是一個客戶端-服務端協議,在上圖的例子中,Alice 是客戶端,Carol 是服務端,AliceCarol 發送一個 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 的創建者就在 WireGuard 郵件串列上分享了一個 NAT 穿透示例,Jason 的示例包含了客戶端應用和服務端應用,其中客戶端應用于 WireGuard 一起運行,服務端運行在擁有靜態地址的主機上用來發現各個 Peer 的 IP:Port,客戶端使用原始套接字(raw socket)與服務端進行通信,

/* 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 套接字上,

客戶端在其原始套接字上使用一個經典的 BPF 過濾器來過濾服務端發往 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);
	}
}

客戶端與服務端的通信資料都被定義在 packetreply 這兩個結構體中:

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_pubkeytheir_pubkey 欄位會被適當填充,當服務端收到來自客戶端的資料包時,它會向以公鑰為密鑰的 Peer 記憶體表中插入或更新一個 pubkey=my_pubkeyentry,然后再從該表中查找 pubkey=their_pubkeyentry,一但發現 entry 存在,就會將其中的 IP:Port 發送給客戶端,當客戶端收到回復時,會將 IP 和埠從資料包中解包,并配置 Peer 的 endpoint 地址(wg set <interface> peer <key> <options...> endpoint <ip>:<port>),

entry 結構體原始碼:

struct entry {
	uint8_t pubkey[32];
	uint32_t ip;
	uint16_t port;
};

entry 結構體中的 ipport 欄位是從客戶端收到的資料包中提取的 IP 和 UDP 頭部,每次客戶端請求 Peer 的 IP 和埠資訊時,都會在 Peer 串列中重繪自己的 IP 和埠資訊,

上面的例子展示了 WireGuard 如何實作 UDP 打洞,但還是太復雜了,因為并不是所有的 Peer 端都能打開原始套接字(raw socket),也并不是所有的 Peer 端都能利用 BPF 過濾器,而且這里還用到了自定義的 wire protocol,代碼層面的資料(鏈表、佇列、二叉樹)都是結構化的,但網路層看到的都是二進制流,所謂 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 記錄型別叫 SRV記錄(Service Record,服務定位記錄),它用來記錄服務器提供的服務,即識別服務的 IP 和埠,RFC6763 用具體的結構和查詢模式對這種記錄型別進行了擴展,用于發現給定域下的服務,我們可以直接利用這些擴展語意,

7. CoreDNS

選好了服務發現協議后,還需要一種方法來將其與 WireGuard 對接,CoreDNS 是 Golang 撰寫的一個插件式 DNS 服務器,是目前 Kubernetes 內置的默認 DNS 服務器,并且已從 CNCF 畢業,我們可以直接寫一個 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 編碼的設計是為了以一種允許使用大寫字母和小寫字母的形式來表示任意的八位位元組序列,

— RFC4648

不幸的是,DNS 的 SRV 記錄的服務名稱是不區分大小寫的:

DNS 樹中的每個節點都有一個由零個或多個標簽組成的名稱 [STD13, RFC1591, RFC2606],這些標簽不區分大小寫,

— RFC4343

Base32 雖然產生了一個稍長的字串(56 位元組),但它的表現形式允許我們在 DNS 內部表示 WireGuard 公鑰:

Base32 編碼的目的是為了表示任意八位位元組序列,其形式必須不區分大小寫,

我們可以使用 base64base32 命令來回轉換編碼格式,例如:

$ wg genkey | wg pubkey > pub.txt
$ cat pub.txt
O9rAAiO5qTejOEtFbsQhCl745ovoM9coTGiprFTaHUE=
$ cat pub.txt | base64 -D | base32
HPNMAARDXGUTPIZYJNCW5RBBBJPPRZUL5AZ5OKCMNCU2YVG2DVAQ====
$ cat pub.txt | 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 資訊,該插件名就叫 wgsd,自己撰寫的插件不屬于官方內置插件,從 CoreDNS 官方下載頁下載的可執行程式并不包括這兩個插件,所以需要自己編譯 CoreDNS,

編譯 CoreDNS 并不復雜,在沒有外部插件的情況下可以這么編譯:

$ git clone https://github.com/coredns/coredns.git
$ cd coredns
$ make

如果要加上 wgsd 插件,則在 make 前,要修改 plugin.cfg 檔案,加入以下一行:

wgsd:github.com/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 確實有一個握手機制,但它比你想象的更像是一個無連接的協議,

任何安全協議都需要保持一些狀態,所以最初的握手是非常簡單的,只是建立用于資料傳輸的對稱密鑰,這種握手每隔幾分鐘就會發生一次,以提供輪換密鑰來實作完美的前向保密,它是根據時間來完成的,而不是根據之前資料包的內容來完成的,因為它的設計是為了優雅地處理資料包丟失的問題,

— wireguard.com/protocol

現在萬事俱備,只欠東風,只需要實作 wgsd-client 就完事了,

9. 實作 wgsd-client

wgsd-client 負責使 Peer 的 endpoint 配置保持最新狀態,它會檢索配置中的 Peer 串列,查詢 CoreDNS 中與之匹配的公鑰,然后在需要時為相應的 Peer 更新 endpoint 的值,最初的實作方式是以定時任務或者類似的調度機制運行,以序列化的方式檢查所有 Peer,設定 endpoint,然后退出,目前它還不是一個守護行程,后續會繼續改進優化,

wgsd-client 的原始碼位于 wgsd 倉庫中的 cmd/wgsd-client 目錄,

下面開始進行最終的測驗,

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/wireguard/wg0.conf
[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/wireguard/wg0.conf
[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/wireguard/wg0.conf
[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]
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
  public key: syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js=
  private key: (hidden)
  listening port: 51820

peer: xScVkH3fUGUv4RrJFfmcqm8rs3SEHr41km6+yffAHw4=
  endpoint: 2.2.2.2:41424
  allowed ips: 10.0.0.1/32
  latest handshake: 22 seconds ago
  transfer: 392 B received, 9.73 KiB 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, 14 seconds ago
  transfer: 2.08 KiB received, 17.59 KiB sent
  persistent keepalive: every 5 seconds

wgsd-client 成功發現了 Peer 的 endpoint 地址并更新了 WireGuard 的配置,最終 Alice 和 Bob 之間直接建立了一條隧道!

總結

本文探討了如何在受 NAT 限制的兩個 Peer 之間直接建立一條 WireGuard 隧道,本文提供的解決方案都是使用現有的協議和服務發現技術,以及自己寫了個可插拔的插件,你可以直接使用 dignslookup 來進行除錯,不需要干擾或修改 WireGuard 本身,

當然,這個 CoreDNS 插件肯定還可以優化,wgsd-client 也需要繼續優化,比如,CoreDNS 服務器是否應該限制只在 Registry 的隧道中可用?是否應該對域進行簽名?每次查詢 DNS 時是否都需要查詢一次 WireGuard 的 Peer 資訊,還是說可以用快取來解決?這些都是值得思考的問題,

wgsd 插件的代碼是開源的,歡迎大家踴躍貢獻,


Kubernetes 1.18.2 1.17.5 1.16.9 1.15.12離線安裝包發布地址http://store.lameleg.com ,歡迎體驗, 使用了最新的sealos v3.3.6版本, 作了主機名決議配置優化,lvscare 掛載/lib/module解決開機啟動ipvs加載問題, 修復lvscare社區netlink與3.10內核不兼容問題,sealos生成百年證書等特性,更多特性 https://github.com/fanux/sealos ,歡迎掃描下方的二維碼加入釘釘群 ,釘釘群已經集成sealos的機器人實時可以看到sealos的動態,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/255507.html

標籤:其他

上一篇:Lua編程

下一篇:PowerApps畫布應用編碼規范和指南

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more