Ping 誕生於 1983 年,當時網際網路需要一種簡單有效的方法來測量連線和距離。簡而言之,ping(以及後來的 traceroute 和 MTR 之類的公用程式)為使用者提供了一種快速的方法,來驗證一台電腦能否與另一台電腦進行通訊。快進到今天,這些網路公用程式工具已經變得無處不在。現在,它們不僅是排解連線和網路效能問題的事實標準,也是常見的工具套件,幾乎所有網際網路使用者都可以在日常角色和職責中使用它們來改善我們的整體生活質量。
讓網路公用程式工具按預期工作對我們而言非常重要,尤其是現在,有越來越多的客戶在 Cloudflare 上構建私人網路。目前,在 Cloudflare 上運作私人網路的團隊已經超過了 10,000 個。在這些團隊中,有些是全球大型企業,有些是小型工作組,還有一些是業餘愛好者,但他們都想問同一個問題——我能到達嗎?
正因為如此,我們今天才會興奮地宣佈將對這些公用程式的支援納入到我們原本就已經非常全面的 Cloudflare Zero Trust 疑難排解工具組中。若要開始使用,請註冊以接收測試版,並開始使用人人皆知且喜愛的熟悉偵錯工具(例如 ping、traceroute 和 MTR)來測試與在 Tunnel 後方執行之私人網路目的地的連線。
Cloudflare Zero Trust
藉助 Cloudflare Zero Trust,在 Cloudflare 上構建私人網路將變得簡單至極。實際上,只需三個步驟即可開始使用。首先,下載 Cloudflare 的裝置用戶端 WARP,將您的使用者連線到 Cloudflare。然後,建立身分識別和裝置感知原則,以判斷哪些人員可以在您的網路內存取哪些內容。最後,直接從 Zero Trust 儀表板使用 Tunnel 將您的網路連線至 Cloudflare。
我們將 Cloudflare Zero Trust 設計為您組織的單一窗格。這意味著在您部署了_任何_一部分 Zero Trust 解決方案(無論是 ZTNA 還是 SWG)之後,都只需按幾下滑鼠,而不是幾個月,即可部署瀏覽器隔離、資料丟失預防、雲端存取安全性代理程式和電子郵件安全。而市面上的其他解決方案可能需要不同的實作或在其服務組合中具有有限的互通性,與此形成了鮮明的對比。
就是這麼簡單,但是如果您正在尋找更規範的指導,請觀看下面的示範以開始使用:
若要開始使用,請註冊以搶先體驗封閉測試版。如果您有興趣深入瞭解其工作原理以及我們未來將推出的其他功能,請繼續瀏覽下文。
那麼,這些網路公用程式實際如何工作呢?
Ping、traceroute 和 MTR 都由相同的基礎通訊協定 ICMP 提供支援。每條 ICMP 訊息有 8 位元類型和程式碼欄位,這些欄位會定義訊息的用途和語意。雖然 ICMP 有許多類型的訊息,但上面提到的網路診斷工具使用的是回應要求和回應回覆類型。
每條 ICMP 訊息包含類型、程式碼以及總和檢查碼。正如您可能已經透過名稱猜到的那樣,針對收到的回應要求,會產生一個回應回覆,而至關重要的是,要求和回覆具有相符的識別碼和序號。請記住這件事,因為它將有助於您理解本篇部落格文章後面的內容。
有關 ping、traceroute 和 MTR 的速成課
如您所料,每一個公用程式都有自己獨特的細微差別,但不要擔心。在深入實質性細節之前,我們將逐一進行快速複習。
Ping
Ping 的工作原理是向目的地傳送一系列回應要求封包。寄件者與目的地之間的每個路由器躍點都會遞減包含 ICMP 訊息之 IP 封包的 TTL 欄位,並將封包轉寄至下一個躍點。如果一個躍點在到達目的地之前將 TTL 遞減到 0,或者沒有下一個要轉寄的躍點,則會將 ICMP 錯誤訊息(分別為「TTL 超過」或「目的地主機無法連線」)傳回給寄件者。支援 ICMP 的目的地將接收這些回應要求封包,並將相符的回應回覆傳回給寄件者。在回程中也會發生同樣的周遊路由器和 TTL 遞減流程。在寄件者的電腦上,ping 會報告這些回覆的最終 TTL,以及傳送 ICMP 訊息到目的地和從中接收訊息的來回延遲。據此資訊,使用者可以確定自己與原始伺服器之間的距離(網路躍點數和時間)。
Traceroute 和 MTR
正如我們剛剛概述的那樣,ping 提供的輸出雖然很有幫助,但相對簡單。它確實提供了一些有用的資訊,但我們通常希望使用 traceroute 來追蹤此要求,以深入瞭解通往指定目的地的特定路徑。與 ping 類似,traceroute 是從傳送 ICMP 回應要求開始的。但是,它在處理 TTL 時略有不同。您可以深入瞭解其中的原因(請前往我們的學習中心),但重要的是,這就是 traceroute 能夠對應並擷取網路路徑上每個唯一躍點的 IP 位址的原因。這樣的輸出使得 traceroute 成為一個超級強大的工具,不僅能夠瞭解電腦_是否_可以連線到另一台電腦,還能瞭解它將_如何_到達那裡!最後,我們來談談 MTR。我們暫時將 traceroute 和 MTR 分組在一起,因為它們的運作方式極為相似。簡而言之,MTR 的輸出除了包含 traceroute 可以提供的一切資訊以外,但還會針對每個唯一躍點提供一些額外的彙總統計資料。MTR 也會一直執行,直到明確停止,讓使用者可以接收路徑上每個躍點的統計平均值。
檢查與原始伺服器的連線
現在我們已經快速複習了一遍,那我們來假設一下,我無法連線到我的私人應用程式伺服器。由於的 Zero Trust 帳戶啟用了 ICMP 支援,我可以執行一個 traceroute 來查看伺服器是否還在線上。
以下是我們一個實驗室環境的簡單示例:
那麼,如果我的伺服器仍在線上,則 traceroute 應該輸出如下內容:
我們來更深入地研究一下。在這裡,第一個躍點是 Cloudflare 資料中心,其中我的 Cloudflare WARP 裝置透過 Anycast 網路連線。請記住,此 IP 可能會根據您的位置而有所不同。第二個躍點將是執行 cloudflared 的伺服器。最後一個躍點是我的應用程式伺服器。
traceroute -I 172.16.10.120
traceroute to 172.16.10.120 (172.16.10.120), 64 hops max, 72 byte packets
1 172.68.101.57 (172.68.101.57) 20.782 ms 12.070 ms 15.888 ms
2 172.16.10.100 (172.16.10.100) 31.508 ms 30.657 ms 29.478 ms
3 172.16.10.120 (172.16.10.120) 40.158 ms 55.719 ms 27.603 ms
相反,如果我無法連線到我的應用程式伺服器,traceroute 則會輸出以下內容:
在上面的例子中,這意味著 ICMP 回應要求沒有到達 cloudflared。為了進行疑難排解,首先我會檢查 ZeroTrust 儀表板中 Tunnel 的狀態,以確保 cloudflared 正在執行。然後,我將檢查 Tunnel 是否有通往目的地 IP 的路由。此項可以在儀表板中 Tunnels 表格的 Routes 欄中查看。如果沒有,我會向我的 Tunnel 新增一個路由,看看這是否會改變我的 traceroute 的輸出。
traceroute -I 172.16.10.120
traceroute to 172.16.10.120 (172.16.10.120), 64 hops max, 72 byte packets
1 172.68.101.57 (172.68.101.57) 20.782 ms 12.070 ms 15.888 ms
2 * * *
3 * * *
在我確認 cloudflared 正在執行,並且 Tunnel 具有通往我的應用程式伺服器的路由後,traceroute 將顯示以下內容:
但是,我們似乎仍然無法完全連線到應用程式伺服器。這意味著 ICMP 回應要求到達了 cloudflared,但我的應用程式伺服器沒有傳回回應回覆。現在,我可以將問題的範圍縮小到我的應用程式伺服器,或 cloudflared 和應用程式伺服器之間的通訊。也許電腦需要重新啟動或部署一個防火牆規則,但無論哪種方式,我們都必須先對最後一個躍點進行疑難排解。有了 ICMP 支援,我們現在可以使用許多網路工具來排解端對端連線的疑難問題。
raceroute -I 172.16.10.120
traceroute to 172.16.10.120 (172.16.10.120), 64 hops max, 72 byte packets
1 172.68.101.57 (172.68.101.57) 20.782 ms 12.070 ms 15.888 ms
2 172.16.10.100 (172.16.10.100) 31.508 ms 30.657 ms 29.478 ms
3 * * *
請注意,即使 cloudflared 至原始伺服器有一或多個路由器,兩者之間的路由也一律會顯示為單一躍點。這是因為 cloudflared 建立了自己的回應要求以傳回到原始伺服器,而不是轉寄原始的封包。在下一節中,我們將解釋其背後的技術原因。
是什麼讓 ICMP 流量與眾不同?
幾個季度前,Cloudflare Zero Trust 還延伸了對 UDP 的端對端支援。由於 UDP 和 ICMP 都是基於資料包的通訊協定,因此在 Cloudflare 網路中,我們可以重複使用相同的基礎結構來代理 UDP 和 ICMP 流量。為此,我們使用Cloudflare 和您網路內的 cloudflared 執行個體之間的 QUIC 資料包,透過 QUIC 連線傳送任一通訊協定的個別資料包。
對於 UDP,我們為每組用戶端/目的地建立並維護一個_工作階段_,以便我們只能在資料包中傳送 UDP 承載和工作階段識別碼。這樣我們就不需要在每個單一封包中傳送 UDP 承載應轉寄到的 IP 和連接埠。
然而,對於 ICMP,我們認為建立這樣一個工作階段是一筆太大的額外開銷,因為通常只有少數的 ICMP 封包在端點之間交換。相反,我們將整個 IP 封包(內部包含 ICMP 承載)作為單一資料包傳送。
這意味著 cloudflared 可以從它接收的 IP 標頭讀取 ICMP 封包的目的地。雖然這會將封包的最終目的地傳達給 cloudflared,但若要實際傳送封包,還有一些工作要做。Cloudflared 不能在接收 IP 封包後原樣傳送出去,因為封包中的來源 IP 仍然是_原始的_用戶端 IP,而不是可路由到 Cloudflared 執行個體本身的來源。
若要接收針對其轉寄的 ICMP 封包的 ICMP 回應回覆,cloudflared 必須將一個來源 NAT 套用至封包。這意味著當 cloudflared 收到一個 IP 封包時,必須完成以下作業:
讀取封包的目的地 IP 位址
刪除 IP 標頭以取得 ICMP 承載
將 ICMP 承載傳送至目的地,表示 ICMP 封包的來源位址將是 cloudflared 可繫結之網路介面的 IP
Cloudflared 收到此位址的回覆時,必須將所接收封包的目的地位址(目的地,因為封包方向已反轉)重新寫入原始用戶端來源位址
對於 TCP 和 UDP,像這樣的網路位址轉譯一直都在進行,但在這些情況下要容易得多,因為可以使用連接埠來釐清來源和目的地 IP 相同的情況。由於 ICMP 封包沒有與其相關聯的連接埠,因此我們需要想辦法將從上游接收的封包對應回向 cloudflared 傳送這些封包的原始來源。
例如,假設兩個用戶端 192.0.2.1 和 192.0.2.2 都會傳送 ICMP 回應要求至目的地 10.0.0.8。如前所述,cloudflared 必須將這些封包的來源 IP 重寫為它可繫結的來源位址。在這種情況下,當回應回覆傳回時,IP 標頭將是相同的:來源 = 10.0.0.8 目的地 =<cloudflared 的 IP>。那麼,cloudflared 如何確定哪個封包需要將其目的地重寫為 192.0.2.1,哪個封包需要重寫為 192.0.2.2 呢?
為了解決這個問題,我們使用 ICMP 封包的欄位來追蹤封包流,方式與在 TCP/UDP NAT 中使用連接埠相同。我們將用於此目的的欄位是「回應識別碼」。當收到回應要求時,符合要求的 ICMP 端點將傳回識別碼與要求中所接收相同的回應回覆。這意味著我們可以使用識別碼 23 從 192.0.2.1 傳送封包,並使用識別碼 45 從 192.0.2.2 傳送封包,當我們收到識別碼為 23 和 45 的回覆時,我們就會知道對應於每個原始來源的是哪一個回覆。
當然,此策略僅適用於 ICMP 回應要求,它們在可用的 ICMP 訊息類型中所佔百分比相對較小。然而,出於安全原因,再加上這些訊息類型足以實作我們所追求的無處不在的 ping 和 traceroute 功能,因此,這些是我們目前唯一支援的訊息類型。我們將在下一節中討論這個選擇的安全原因。
如何在沒有提升權限的情況下代理 ICMP
通常,應用程式需要透過原始通訊端傳送 ICMP 封包。應用程式可以使用此通訊端控制 IP 標頭,因此需要提升權限才能開啟。而 TCP 和 UDP 封包的 IP 標頭在傳送時添加並在作業系統接收時刪除。為了遵守安全性最佳做法,我們並不想以額外的權限執行 cloudflared。我們需要更好的解決方案。為了解決這個問題,我們在 ping 公用程式中找到了靈感,您會注意到它可以由_任何_使用者來執行,而_無需_提升權限。那麼,作為一般使用者程式,ping 是如何傳送 ICMP 回應要求並監聽回應回覆的?嗯,答案不太令人滿意:這要視情況而定(取決於平台)。由於 cloudflared 支援以下所有平台,因此,我們需要針對每個平台分別回答這個問題。
Linux
在 Linux 上,ping 會透過系統呼叫 socket(PF_INET, SOCK_DGRAM, PROT_ICMP) 開啟 ICMP 通訊協定的資料包通訊端。只有在執行程式的使用者的群組識別碼位於 /proc/sys/net/ipv4/ping_group_range 中時,才能開啟這種類型的通訊端,但至關重要的是,使用者不需要是 root 使用者。這個通訊端比較「特殊」,因為它只能傳送 ICMP 回應要求並接收回應回覆。太好了!它還有一個與之相關聯的概念性「連接埠」,儘管 ICMP 實際上並不使用連接埠。在這種情況下,透過此通訊端傳送的回應要求的識別碼欄位會被重寫為指派給通訊端的「連接埠」。反過來,由具有相同識別碼的核心接收的回應回覆會被傳送至傳送要求的通訊端。
因此,在 Linux 上,cloudflared 能夠為 ICMP 封包執行來源 NAT,只需透過為每個來源 IP 位址開啟一個唯一的通訊端即可。這會重新寫入要求的識別碼欄位和來源位址。回覆會被傳遞到這個相同的通訊端,這意味著 cloudflared 可以輕鬆地將目的地 IP 位址(目的地,因為封包流_向_用戶端)和回應識別碼重寫為從用戶端接收的原始值。
Darwin
在 Darwin(組成 macOS 的一組基於 UNIX 的核心元件)上,情況相似,因為我們可以使用相同的系統呼叫 socket(PF_INET, SOCK_DGRAM, PROT_ICMP) 開啟一個無權限的 ICMP 通訊端。但是,有一個很重要的區別。在 Darwin 中,核心不會為這個通訊端配置概念性「連接埠」,因此,在傳送 ICMP 回應要求時,核心不會像在 Linux 上那樣重寫回應識別碼。此外,就我們的目的而言,更重要的是,核心不會取消對使用回應識別碼傳送對應要求的通訊端的多個 ICMP 回應回覆。這意味著在 macOS 上,我們實際上需要手動執行回應識別碼重寫。在實踐中,這意味著當 cloudflared 在 macOS 上收到回應要求時,它必須選擇對於目的地唯一的回應識別碼。然後,Cloudflared 會將一個(選擇的回應識別碼,目的地 IP)的金鑰添加到它隨後維護的對應,其值為(原始回應識別碼,原始來源 IP)。Cloudflared 會將回應要求封包中的回應識別碼重寫為其選取的識別碼,並將其轉寄至目的地。當它收到回覆時,它可以使用來源 IP 位址和回應識別碼來查詢用戶端位址和原始回應識別碼,並在回覆封包中重寫回應識別碼和目的地位址,然後再將其轉寄回用戶端。
Windows
最後,我們要談的是 Windows,它可以方便地提供一個 Win32 API IcmpSendEcho 來傳送回應要求並傳回回應回覆、逾時或錯誤。對於 ICMPv6,我們必須使用 Icmp6SendEcho。這些 API 以 C 語言撰寫,但是 cloudflared 可以透過 CGO 呼叫它們而毫無問題。如果您還需要在 Go 程式中呼叫這些 API,請查看我們的包裝函式以汲取靈感。
好了!這就是我們自 1983 年以來構建最令人興奮的 ping 版本的過程。總體而言,我們滿懷激動地推出了這項新功能,迫不及待地想聽到您的反饋,從而瞭解我們還可以採取哪些方式繼續改進我們的實作。
下一步驟
支援這些以 ICMP 為基礎的公用程式只是一個開始,我們將繼續探索如何改進 Zero Trust 管理員體驗。我們的目標是不斷地提供工具,幫助客戶輕鬆找出網路中影響連線和效能的問題。
展望未來,我們計劃在 Zero Trust 平台上新增更多轉盤和旋鈕,以提高數位體驗監控等公告的可觀察性,從而協助使用者主動監控不斷變化的網路狀況並保持警覺。同時,立即註冊即可免費試用,嘗試將 Zero Trust 控制套用至您的私人網路。