iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0

要把家裡的服務暴露到公網,最直覺的做法是 port forwarding——在路由器上開個洞,把外部流量導進來。

我一開始就是這樣做的,而且做得很完整:

公網 → 中華電信固定 IP → 家裡的 WiFi 路由器 → NAS

為了這條路,我還特地辦了固定 IP。它確實能動、也確實好用——手機在外面打得到家裡的服務,Webhook 也進得來。

但用了一陣子之後,兩件事讓我改掉了。

**第一件是安全。**公網上無時無刻有人在掃 port,你開的那個 8080、9527,幾分鐘內就會出現在別人的掃描清單裡。而固定 IP 讓這件事更糟:**它讓我家在網路上有了一個永久地址。**動態 IP 至少每次重連會換一個,固定 IP 則是被掃到一次,就一直在那份清單上。

**第二件是每多一個服務,就要多開一個洞。**當時三個服務就是三個 port。每開一個,攻擊面就再擴大一次,而我得自己記得每一個洞後面掛的是什麼、有沒有做認證。

現在的狀態是:對外的服務比當時多了兩倍,但路由器上一個 port 都沒開、固定 IP 也不再是必要的。靠的是 Cloudflare Tunnel。


Tunnel 的原理:只出不進

傳統 port forwarding 的方向是由外向內——外部主動連進你家。Tunnel 把這個方向反過來。

NAS 上跑一個 cloudflared 守護程式,它主動向 Cloudflare 建立一條 outbound 長連線。之後所有對外請求都先到 Cloudflare 邊緣,再透過這條既有的連線回送到內網:

https://ithelp.ithome.com.tw/upload/images/20260801/20182865kRApd7RBqu.png

關鍵差別:你家防火牆只有一條對外的連線,沒有任何對內開放的 port。 外面的人掃你家 IP,掃不到任何東西——因為根本沒有監聽中的對外 port。攻擊面從「整個公網都能敲門」縮小到「只有 Cloudflare 那條既有通道」。

附帶一個好處:Cloudflare 邊緣自帶 HTTPS。我不用自己管憑證簽發與續期,子網域天生就有有效的 TLS。


一條隧道,八個服務

一條 Tunnel 可以掛很多服務,用子網域區分。設定方式是一份 ingress 規則:把「哪個 hostname」對應到「內網哪個位址:埠」。cloudflared 收到請求後在內網把流量轉過去——這些服務本身完全不需要對外開放,它們只在內網監聽。

寫這篇時我以為自己只開了三個,實際去看設定才發現已經長到八個

子網域 後端 用途
md.example.dev 主 NAS :9527 Workspace 的 Markdown 瀏覽器
mcp.example.dev 主 NAS :3001 MCP 工具層 API
bot.example.dev 主 NAS :18789 Bot API / 控制介面
tg.example.dev 主 NAS :8788 Telegram Webhook 入口(Day 5 講的「收」那一側)
ha.example.dev 另一台主機 :8123 Home Assistant(跑在 VM 上,不同的內網位址)
ds723.example.dev 主 NAS :5000 NAS 本身的管理介面
ds220.example.dev 第二台 NAS :5000 另一台舊 NAS 的管理介面
ssh.example.dev 主 NAS :22 這條是 TCP,不是 HTTP

⚠️ 真實網域一律用 example.dev 示意,內網位址也已省略。

這張表列出來之後,有三件事是我原本沒想清楚的。

① 一條隧道可以跨多台主機。cloudflared 跑在主 NAS 上,但它轉發的目標不限於本機——Home Assistant 在另一個內網位址、第二台 NAS 又是另一個。這三台都沒有任何對外曝露,全部藉那一條既有連線出去。這比「每台機器各自想辦法對外」乾淨太多。

**② Tunnel 不只能轉 HTTP。**上面七條是 http://,但 ssh 那條是 tcp://——Cloudflare Tunnel 也能代理原始 TCP。這代表我不用在路由器開 22 埠,也能從外面 SSH 回家。這條的風險等級跟其他七條不同,等一下單獨講。

每多一個服務就是一條規則、一個要記得「它後面擋著什麼」的東西。而規則會累積、後端會下線,清單卻不會自己更新。服務清單本身就該定期盤點——不是為了整潔,是為了讓「我以為的樣子」跟「實際的樣子」對得上。


別忘了認證層:Tunnel 不等於安全

這是最容易出包的地方:Tunnel 解決的是「不開 port」,不是「誰都能用」。

把服務掛上 Tunnel、設好子網域之後,它就是一個公網可達的 URL。如果沒加認證,任何拿到網址的人都能直接打開你的 md-server、看你的 workspace。所以每個對外服務前面都要有一道自己的認證:

  • md-server:自己實作的密碼保護,密碼走環境變數(MD_PASSWORD),不寫死在程式碼裡——未通過驗證一律回 401(Fail-Closed 設計)。
  • MCP server:API key 驗證(MCP_API_KEY),同樣走環境變數。
  • 憑證原則:所有密鑰只進 .env,不進 git;.env 本身在 .gitignore 裡。

「對外一律過認證層」這條,是踩過坑才刻進系統的鐵則。**能對外連到,跟該不該讓人用,是兩件事。**Tunnel 給你前者,認證層才管後者。

⚠️ 但有一條,應用層的認證保護不到

上面那些認證是應用程式自己實作的——請求先到達程式,程式檢查密碼或 key,不對就回 401。

ssh 那條不是這樣。它是 TCP 隧道,直接把流量丟到 22 埠,中間沒有任何「我的程式」可以檢查任何東西。擋在後面的只剩 SSH 自己的認證。

而它的風險等級跟其他七條完全不同:

  • md-server 被看到,最壞的情況是我的筆記外流,尷尬但可控
  • SSH 被進去,是整台機器淪陷——所有容器、所有資料、所有憑證

我是列完上面那張表才意識到這件事的,而且當時那條什麼額外保護都沒有。

寫這段的當下我就去補了:在那條隧道前面單獨掛一層 Cloudflare Access 存取政策。現在連過去會先跳出登入頁,通過才碰得到 SSH。加上 SSH 本身的金鑰認證,變成兩層。

如果你也要開 SSH 這條,至少確認兩件事:

  1. SSH 關掉密碼登入,只留金鑰——這是最低標準
  2. 在這條隧道上單獨加一層存取政策——就算其他服務沒加,這條要加

**當服務從三個長到八個,你很容易忘記其中某一條的權限跟其他七條不在同一個量級。**這也是為什麼我後來把「列出所有對外服務、逐條問它後面擋著什麼」變成定期要做的事——你保護不了你不知道存在的東西,也保護不了你以為已經保護好的東西。


這樣要花多少錢?只有網域

講到這裡該把帳講清楚,因為這是我當初猶豫的點。

Cloudflare Tunnel 本身免費,Access 的基本存取政策在免費方案裡也有。個人規模用不到任何付費方案——我這幾個服務、每天幾十次請求,離免費方案的天花板遠得很。

唯一的固定支出是網域,一年繳一次。

而挑哪個頂級域名(TLD),我不是挑最便宜的——這裡有兩個考量值得說。

.dev 是強制 HTTPS 的。這個 TLD 被列在瀏覽器的 HSTS preload 清單裡,意思是瀏覽器根本不讓你用 HTTP 連上去,不管你伺服器怎麼設定。對一個把家裡服務放上公網的人來說,這很划算:我不可能不小心用明文開一個服務出去——因為那在協定層就走不通。

安全設計裡最可靠的一種,是「錯誤的做法根本做不到」,而不是「我記得不要那樣做」。這個 TLD 幫我把一整類的疏忽變成不可能。

**② 我想知道公司內網連不連得到。**我白天在金融業,公司網路對外的管制不算寬鬆。既然這套系統的價值有一部分是「我在哪都看得到家裡的狀況」,那能不能穿過辦公室的網路就是個實際考量——冷門或分類不明的 TLD 比較容易被企業代理擋掉,選一個由大廠營運、用途明確的 TLD,通過的機會高一些。

我那時候說服自己的理由很簡單:**都已經在付 AI 訂閱了,一年多這一筆網域費,比例上根本看不出來。**而它換掉的是固定 IP 的月租、加上路由器那幾個洞。

這也成了我後來評估任何自架工具的判準:**不是「它免不免費」,是「它把哪一類問題從我的待辦清單上永久移除了」。**憑證續期、DDNS、port 管理、被掃描的焦慮——這些一次全部消失,才是這筆網域費真正買到的東西。


免費版的天花板:兩個我還沒撞到、但你該先知道的限制

這兩點我目前的用量還沒踩到,但既然用了免費方案,先知道天花板在哪,免得日後規模長大才中招:

1. 100MB 單次上傳限制
Cloudflare 免費方案會阻擋超過 100MB 的單次 HTTP 請求(回傳 HTTP 413 Payload Too Large)。我的 md-server 只送小 Markdown,離這條線很遠;但如果未來有上傳大型日誌、高畫質圖片或影片的需求,就會在這裡被默默擋掉。

2. 長連線(SSE / WebSocket)的閒置 Timeout
持久的 SSE 或 WebSocket 連線經過 Cloudflare 邊緣,閒置太久(大約 100 秒沒有封包)可能被強制切斷。如果你的 Gateway 依賴長連線,client 端就得實作 Ping/Pong 或自動重連(Auto-Reconnect),別假設連線永遠在。


補課:那 CF Access 呢?VPN 呢?——老實說我兩個都沒想過

寫到這裡我得承認一件事,而且它可能是這篇最有價值的一段。

熟悉 Cloudflare 的人讀到上面一定會問:「既然都用 Tunnel 了,為什麼不順手把每個服務都套上 Access?」熟悉網路的人則會問:「為什麼不用 Tailscale 或 WireGuard?」

兩個問題我當初都沒有想過。

我的實際路徑很土:

固定 IP + 開 port(因為那是我當時唯一會的做法)
   ↓ 看到 Cloudflare Tunnel,覺得「這個好像可以不用開 port」
換成 Tunnel
   ↓ 服務總得有個密碼吧
自己在程式裡寫了一層密碼保護

**中間沒有比較表,沒有評估矩陣,就是撞到什麼用什麼。**上面那些看起來像架構決策的東西,多數是我寫這系列時才回頭補的功課——包括前面那條 SSH 的存取政策,也是寫到那一段才臨時去加的。

所以這一節不是我的決策紀錄,是事後查證。我把它留著,是因為它對還沒開始的人有用。

補課一:那為什麼其他七條不套 Access?

查完之後有一個發現讓我有點慶幸:如果我當初把 Access 無差別套在所有服務上,會把自己的 Telegram Webhook 擋死。

Access 的運作方式是在服務前面插一個登入頁。人可以填,但 Telegram 的伺服器不會填登入表單——它只會收到一個登入頁然後放棄。要讓機器通過得另外設 Service Token,等於多一套東西要維護。

也就是說,我因為沒想過而沒踩到一個坑。這不值得驕傲,但結論值得記:有機器要打進來的服務,前面不適合放給人看的登入牆。

那「純人看」的 md-server 後台呢?那個確實適合套 Access,比我自己寫的密碼保護嚴謹。而且不是錢的問題——基本政策免費方案就有。

那為什麼到今天還是沒套?因為會用到它的人就是我和我太太。多一層登入畫面換來的安全提升是真的,但它也意味著每次想看一眼家裡狀況都要多過一關。以目前的使用情境,我判斷不划算。

誠實的說法是:這裡我沒有做到最嚴謹,我做到的是「以現在的使用者數量,夠用」。

這裡有個個人自架很常見、卻很少被講的判斷:安全措施的成本不只是設定它的力氣,還有它每天對你造成的摩擦。該不該加,取決於使用者是兩個人還是兩百個人——以及那扇門後面是什麼。這就是為什麼 SSH 那條我加了,md-server 這條我沒加:不是標準不一致,是破壞半徑不一樣

補課二:如果我當初知道 Tailscale,會選它嗎?

有可能,而且會更省事——如果我沒有 Webhook 的話。

方案 適合場景 我的狀況
Cloudflare Tunnel 對「公開或半公開」服務,給瀏覽器/Webhook 直接用 現在在用:免裝客戶端、自帶 HTTPS、Webhook 可直接打
Tunnel + Access 高權限入口(SSH、管理介面) SSH 這條在用:寫這篇時才補上
Tailscale / VPN 純自己用、設備數固定的私有網段 當初沒想過。若我沒有 Webhook 需求,這可能才是更省事的答案
固定 IP + port forwarding 想最快打通、不想學新東西 我的起點,後來放棄:每服務一個洞、固定 IP 等於永久地址、憑證自己管

補完功課之後,我發現判準其實只有一句:「誰要連?」

  • 只有人要連(我自己從手機看家裡狀況)→ Tailscale 那種點對點私網其實更省心,連網域都不用買。
  • 有機器要連(Telegram 的 Webhook 打進來)→ 機器不會裝你的 VPN 客戶端,所以需要一個公網可達、帶認證的 URL,這是 Tunnel 的場景。

而我剛好是後者——所以我瞎撞出來的答案,恰好是對的。這種運氣不值得學,但結論值得記:

如果你的需求裡沒有任何「機器要主動連進來」,你可能根本不需要 Tunnel,也不需要買網域。

前面才剛講完網域怎麼挑、.dev 有多好,這裡卻告訴你可能用不到——但這才是誠實的順序。真正該避開的只有一個選項:「開 port」是最危險、也最不必要的那個,而它偏偏是最多人的起點(包括我)。


小結

家用 NAS 要對外,不代表要在路由器上開洞。

  • 起點:固定 IP + port forwarding。能動,但每服務一個洞、固定 IP 等於給家裡一個永久地址。
  • 現在:一條 Tunnel 用 outbound 長連線取代所有的洞,可掛多個服務、跨多台主機,httptcp 都能轉,免費送 HTTPS。
  • 成本只有網域——換掉的是固定 IP 月租、憑證管理與 port 焦慮。挑 TLD 時 .dev 的強制 HTTPS 值得考慮。
  • Tunnel ≠ 安全:公網可達的 URL 一定要再加認證,密鑰走 .env。這條沒有例外。
  • 但認證要做到多嚴謹,看破壞半徑:SSH 這種高權限入口我加了存取政策;只有兩個人在看的後台,我停在應用層密碼。
  • ⚠️ TCP 隧道繞過應用層認證——它不是「認證比較弱」,是你的程式根本沒機會檢查
  • 服務清單會自己長,也會留下屍體,要定期盤點。
  • Tunnel 與 VPN/Tailscale 不是二選一,依「誰要連」來分流。

這週把架構層的幾個核心決策都走過一遍了。明天是第一週週回顧——談自架到底划不划算,以及為什麼我決定不給你一個月費數字


🔑 這篇的關鍵字
起點方案:ISP 固定 IP + 路由器 port forwarding(能動,但每服務一個洞、IP 成為永久地址)
一條隧道多服務cloudflared · ingress 規則 hostname → 內網 host:port · 可跨多台內網主機 · http://tcp:// 都支援
⚠️ TCP 隧道繞過應用層認證——SSH 這類高權限入口要單獨加 Cloudflare Zero Trust Access 政策 + 關閉密碼登入
Access 的取捨:純人看的後台適用;有 Webhook 打進來的服務會被登入頁擋死,要改用 Service Token
選 TLD 的兩個非價格考量.devHSTS preload 清單上=瀏覽器強制 HTTPS(明文開服務在協定層就不可能)· 大廠營運、用途明確的 TLD 比較不會被企業代理擋掉
限制:免費方案單次請求 100MB(413)· SSE/WebSocket 閒置約 100 秒斷線 → 需 Ping/Pong 或 Auto-Reconnect
替代方案Tailscale / WireGuard(純自用更省心,但第三方 Webhook 打不進來)
認證層:Fail-Closed 設計(沒設密碼就拒絕啟動,而不是放行)· 密鑰只進 .env + .gitignore


我是一名金融業資訊工程師,這是我半年來在家自架 AI Agent 系統的實錄。


上一篇
Day 5:自架 AI 的 Telegram 通訊層——從建立到測試
系列文
生活中的 AI 應用:我在家用 NAS 養了一隻 Agent,幫我看盤、顧家、盯備考——30 天自架實錄6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言