想像你在宿舍裡開了一間很小的甜點店,只賣給認識的人。問題來了:你不想把宿舍地址公開在網路上,也不想讓陌生人直接敲門,但你又想讓人買得到。
傳統的解法是在門口掛個牌子(路由器開埠轉發),順便租一個地址(動態 DNS),然後祈禱來的都是好人。cloudflared 走另一條路:你請一個外送平台幫你收單。客人只會看到平台的門市,下單之後平台派人來你這裡拿,你的地址從頭到尾沒有出現在任何地方,而且是你主動去平台報到,不是平台來找你。
它實際上在做什麼
機器上跑一支叫 cloudflared 的程式,主動對 Cloudflare 建立長連線。訪客連到 Cloudflare 的邊緣節點,Cloudflare 再把請求沿著這條連線送進來。你的機器沒有任何對外開放的埠,IP 也不會出現在 DNS 裡。cloudflared 本身是開源的,Apache-2.0,Cloudflare 提供官方容器映像,用 digest 釘死版本很方便。
第一次設定成功的時候我有點不敢相信。路由器的設定頁面一次都沒開過,防火牆一條規則都沒加,網站就這樣從外面連得到了。那種感覺像是發現原來可以不用排隊就進去。
路由表
一條隧道可以掛很多主機名,每個主機名對應「本機的哪個服務」,例如 blog.example.com 送到 http://127.0.0.1:8080。這張表叫 ingress。遠端管理模式下它存在 Cloudflare 那邊,連接器只拿一個 token 啟動;本機管理模式下它是連接器旁邊的一個 YAML 檔。
有一件事要想清楚,而且最好在半夜出事之前想清楚:表裡的「本機」指的是連接器所在的那台機器。連接器搬到另一台機器,整張表的目的地就跟著搬,表裡每一個服務都得在新機器上存在,否則對應的主機名會回 502。
用外送比喻就是:外送員記的是「到這棟樓拿」,不是「到某某人那裡拿」。你搬到隔壁棟,外送員還是會去原本那棟,然後回報說店關了。連接器屬於隧道,不屬於任何一個站;逐站遷移的計畫要先問「哪些站共用這條隧道」。這句話我是付了學費才學會的,學費是一個早上的 502。
不想公開的東西,就不要放進路由表
後台管理介面這類東西,最乾淨的保護不是密碼,是根本沒有路由。讓它只聽本機迴路位址,不寫進 ingress,網際網路上就找不到它;需要時從自己的電腦開一條 SSH 隧道進去。掃描器來了只會看到公開的那幾條。
這跟「把後門鎖好」不一樣,這是「根本沒有後門」。你不用擔心鎖夠不夠好,因為沒有東西可以撬。我覺得這是整個做法裡最讓人安心的一部分。
一條隧道、多個連接器是什麼意思
同一條隧道可以同時跑多個連接器,Cloudflare 會把流量分配給它們。這是為了高可用:一台機器掛了,另一台還在。它不是「兩台機器各服務一部分主機名」的機制。
我一開始就誤會了這一點,以為可以在兩台機器上各跑一個連接器,各自負責自己的站。結果是請求隨機落到沒有那個服務的那一台,一半的請求成功、一半失敗,比全部壞掉更難查,因為你重新整理三次可能有兩次是好的,你會以為是網路不穩。要高可用,兩台機器要能服務同一張表,就像兩個外送員都要知道所有店的位置,而不是一個人只認得東邊、一個人只認得西邊。
跟嚴格 CSP 的衝突
Cloudflare 的網站分析功能預設會在邊緣把一段 JavaScript 注入每個頁面。如果你的站送的 Content-Security-Policy 是 script-src 'self'、沒有 'unsafe-inline',瀏覽器會擋掉這段注入,每個頁面的主控台多一條錯誤,分析資料也收不到。
這件事的荒謬之處在於兩邊都沒有錯:你的 CSP 在做它該做的事,Cloudflare 的分析也在做它該做的事,只是它們互相看不順眼。解法是把邊緣注入關掉,改在需要統計的頁面手動放官方的程式碼片段,並且只在那些頁面把 CSP 放寬到分析腳本的來源。不需要統計的頁面維持原本的政策。
小劇場:這樣安全嗎
「所以你的網站流量全部經過 Cloudflare?」
「對,它們看得到所有請求。」
「這樣你不會怕嗎?」
「會,但另一個選項是我自己在路由器上開洞,讓全世界直接敲我家的門。兩個都要信任某個東西,我選擇信任一家有幾千個工程師的公司,而不是我自己半夜設的防火牆規則。」
「聽起來像是把鑰匙交給管理室。」
「差不多。管理室也可能出事,但比把鑰匙插在門上好。」
它的代價
單一隧道就是單一依賴。Cloudflare 那邊出狀況,連接器掛掉,所有掛在這條隧道上的服務一起消失。連接器的指標端點只聽本機,外部監控要自己另外做。我目前沒有做,這是我欠的,寫在這裡提醒自己。有時候寫部落格最大的用途就是公開地欠一筆債,這樣比較有壓力還。
適合誰
沒有固定 IP、不想碰路由器、想把 TLS 和 DDoS 防護交給邊緣的人。它免費層就夠用,設定比開埠轉發簡單,而且「機器上沒有對外開放的埠」這件事本身就少掉一大類問題。
回到甜點店。外送平台會抽成、會有規則、會在你最忙的時候當機。但比起把宿舍地址貼在網路上,我還是選外送。
一點背景
這個產品在 2018 年推出的時候叫 Argo Tunnel,是付費功能。2021 年改名 Cloudflare Tunnel,並且對所有人免費。免費這件事改變了很多個人開發者的部署方式:以前把家裡的機器對外服務是一件需要研究路由器、動態 DNS、憑證的苦差事,現在是裝一個程式、貼一個 token。
cloudflared 這個連接器本身從一開始就是開源的,Apache-2.0,你可以去讀它怎麼建立連線、怎麼重連、怎麼處理多個邊緣節點。它跟 Cloudflare 之間的連線會自動選擇最近的幾個資料中心並保持多條連線,其中一條斷了其他的還在,這是為什麼它比自己開埠轉發穩定:你的家用網路可能不穩,但它同時連著好幾個地方。
常見的三個誤會
第一個誤會是「隧道會讓網站變慢」。訪客連到離他最近的 Cloudflare 節點,Cloudflare 再走自己的骨幹網路到離你最近的節點,最後一段才是你的家用網路。對大部分訪客來說,這比直接連到你家的路由器快,因為 Cloudflare 的網路比你家的網路好。
第二個誤會是「用了隧道就不用管安全」。隧道保護的是「進來的路」,你的應用程式本身該有的驗證、輸入檢查、CSP 一樣都不能少。它擋掉的是直接對你 IP 的掃描,不是對你網站邏輯的攻擊。
第三個誤會是「一個連接器只能服務一個服務」。一條隧道可以掛很多主機名,每個指向本機不同的埠,一個連接器全部搞定。你不需要每個站開一條隧道,那樣管理起來才累。
一個週末可以試的小練習
註冊一個 Cloudflare 帳號,把一個你有的網域指過去,免費方案就夠。在本機用 python -m http.server 8080 開一個最陽春的靜態伺服器,隨便放一個 HTML。在 Cloudflare 的儀表板建一條隧道,選遠端管理,它會給你一行安裝指令和一個 token,跑起來。加一條路由,某個子網域指到 http://localhost:8080。
然後用手機關掉 Wi-Fi、用行動網路打開那個子網域。你會看到你電腦上那個 HTML,而你的路由器一個設定都沒動、你的 IP 沒有出現在任何地方。這個瞬間值得體驗一次。體驗完記得把隧道刪掉,或者至少把路由拿掉,不要讓一個 http.server 一直掛在網路上。
後記
那間宿舍甜點店的比喻其實有一個沒講的部分:外送平台會知道你賣了什麼、賣給誰。Cloudflare 也一樣,所有流量經過它。這是這個做法的價格,不是隱藏費用,是明碼標價的那種。我付了,因為另一個選項對我來說更貴。每個人的算法不一樣,但至少要知道自己在付什麼。
留言(0)
還沒有留言。讀完有想法,歡迎留下第一則。