路綫圖Network map

Joe 屋企網絡R3S · Cloudflare · 自架服務

一部 NanoPi R3S、一個 Cloudflare 帳戶 · joenyc.net 所有網站同自架服務點樣連埋一齊 · ← 返 Joe's Line

全圖

Overview

左邊係街外嘅用戶,中間係 Cloudflare 喺香港機房做嘅嘢,右邊係屋企。每種顏色代表一種連線方式。重點:屋企冇開任何入站 TCP port,網站全部靠 R3S 主動連出去嘅 Tunnel;唯一開咗嘅入站 port 係 WireGuard。

播放路徑
Joe 屋企網絡結構圖 訪客經 Cloudflare DNS 同 edge 去 Workers 或 Tunnel;Tunnel 由 R3S 上嘅 cloudflared 用 QUIC 主動連出;WireGuard 經 UDP 入屋企;R3S 經 br-lan 連屋企電腦同 WiFi 裝置。 街外 · 用戶 Cloudflare 邊緣 香港機房 hkg09 / hkg12 · Anycast 屋企 內網 + VPN 443 WireGuard(UDP)→ 屋企 WAN QUIC UDP 7844 WoL · SSH 訪客瀏覽器 HTTPS · TLS 1.3 HTTP/2 或 HTTP/3 Joe 嘅瀏覽器 dash.joenyc.net Access 登入 → JWT cookie 外部服務 / 自動化 第三方 API Notion · GitHub 等 捷徑 POST(Bearer token) 出街 iPhone / Mac WireGuard client · full tunnel 手機 / 手提電腦 DNS 權威 DNS · joenyc.net 橙雲 proxied 記錄 104.21.74.126 · 172.67.158.96 EDGE 反向代理 · TLS 終結 Edge 憑證 · WAF · cache HTTP/2 · HTTP/3 (QUIC) ZERO TRUST Cloudflare Access dash:Email 身份登入 pc:只認 Service token WORKERS / PAGES · 喺 EDGE 行,唔經屋企 joenyc.net Worker · 靜態 drink Worker · 靜態 links Pages · 靜態 blogs Worker + KV dash Worker + KV + Cron sublens Worker + D1 KV VIEWS KV joe-dashboard-data D1 · SQLITE sublens-db CLOUDFLARE TUNNEL Tunnel「r3s」· ingress 規則 gym → openGym drop → PsiTransfer PC 控制 → CGI 其他 → 404 Edge 揀一條 R3S 已經連好嘅連線,將 request 送落去 DNS ONLY · 灰雲 VPN 入口 CNAME 指去 DDNS 名,直達屋企,唔經 Cloudflare proxy ISP 寬頻 · WAN eth0(DHCP) 真公網 IP(唔係 CGNAT),DHCP 派,會變 NanoPi R3S · iStoreOS 24.10.6(OpenWrt) fw4 防火牆 + OpenClash(fake-ip) WAN 入站只開 WireGuard · 擋 QUIC dnsmasq DNS + DHCP → OpenClash DNS ddns.DYNU 每 10 分鐘報 WAN IP Dynu DDNS WireGuard UDP · 公鑰認證 VPN 網段 uhttpd(pc 實例) 只 listen 本機 PC 控制 CGI(sh) DOCKER · EMMC opengym-web nginx 前端 psitransfer 檔案分享 opengym-api API + 資料 Compose bridge 網絡 container 之間用 cloudflared · --network host 主動連出 edge,收返 request 再轉去本機 port br-lan 屋企內網 · DHCP 屋企 WiFi / LAN 裝置 DNS 指 R3S → OpenClash 分流 屋企電腦(Windows) 固定 DHCP lease 有線 · WoL · OpenSSH

← 左右拖動睇成張圖 →

▶ 撳上面 A–E 播放一個 request 點樣行;撳下面圖例只睇一種線。

一個 request 點樣行

Request paths

五種最常見嘅情況,逐步列出經過邊度、用咩協議。每條綫嘅顏色同上面全圖一樣。

A

外人開 gym.joenyc.net / drop.joenyc.net

自架服務,經 Cloudflare Tunnel 入屋企

  1. DNS 查詢:hostname 喺 Cloudflare 係 proxied(橙雲)CNAME,指去 tunnel 嘅 <id>.cfargotunnel.com,所以 resolver 收到嘅係 Cloudflare 嘅 anycast IP,唔係屋企 IP。DNS
  2. 連去 edge:瀏覽器同最近嘅 Cloudflare 機房(香港)做 TLS 握手,用 Cloudflare 簽發嘅 edge 憑證。TLS 1.3HTTP/2 · HTTP/3
  3. Edge 搵 tunnel:edge 睇 hostname 對上 tunnel「r3s」,揀一條 R3S 早已連好嘅連線,將成個 HTTP request 包落去。QUIC
  4. cloudflared 轉發:R3S 上嘅 cloudflared(--network host)按 ingress 規則轉去本機嘅 port。呢一段係純 HTTP,但只喺 R3S 本機 loopback 入面行。HTTP
  5. 入 container:opengym-web 嘅 nginx 收到;再將 API 交去 opengym-api(Compose bridge 網絡)。訪客真 IP 靠 CF-Connecting-IP header 帶落嚟。
  6. 原路返回:container → cloudflared → tunnel → edge → 瀏覽器。全程屋企冇開過入站 TCP port。
B

喺 dash.joenyc.net 遙距開 / 關屋企電腦

兩重 Access:人用 Email 登入,Worker 用 Service token

  1. Access 攔截:未登入會 302 去 Access 登入頁;驗證完 Access 發一個 CF_Authorization cookie(JWT)。OIDC 式登入
  2. Edge 加 header:之後每個 request,Access 喺 edge 加 Cf-Access-Jwt-Assertion 先交畀 dashboard Worker。
  3. Worker 自己再驗:攞 team 公鑰驗 RS256 簽名,再核 aud、iss、exp 同 email 白名單。JWT RS256
  4. 撳開機:Worker 用 fetch 打一個私人控制 endpoint,header 帶 Access Service token(Client ID + Secret,存喺 Worker secret)。HTTPS
  5. 第二重 Access:嗰個 endpoint 有自己嘅 Access app,冇 service token 直接 401;過到就經 tunnel「r3s」落 R3S。QUIC tunnel
  6. R3S 執行:uhttpd 只 listen 本機,行一個 shell CGI。開機用 etherwake 喺 LAN 廣播 magic packet;睡眠 / 關機用 key SSH 入部電腦行 Windows 指令;查狀態睇 ARP 表(Windows 擋 ping)。WoLSSH
  7. 回覆:CGI 回 JSON → tunnel → Worker → 前端顯示。
C

喺屋企 WiFi 開自己啲網站

多咗 OpenClash 呢一關

  1. 攞 DNS:裝置經 DHCP 攞到 IP 同 DNS R3S。DHCP
  2. Fake-IP:dnsmasq 將查詢轉去 OpenClash DNS,fake-ip 模式即刻答一個 198.18.x.x 假 IP,記低「假 IP ↔ 域名」。DNS
  3. 分流:裝置連去假 IP,OpenClash 截住、查返真域名,撞中自訂規則 DOMAIN-SUFFIX,joenyc.net,DIRECT,於是直接由 WAN 出去 Cloudflare,唔經代理節點。
  4. QUIC 被拒:OpenClash 會擋 UDP 443,瀏覽器退返 TCP,用 HTTP/2。TCP
  5. 之後同 A 或 E 一樣。冇咗嗰條 DIRECT 規則,屋企開 gym.joenyc.net 會 connection reset。
  6. R3S 自己都受影響:cloudflared log 見到 edge IP 都係 198.18.x 假 IP,即係 tunnel 連線本身都係經 OpenClash 嘅 fake-ip 轉出去。
D

出街用 WireGuard 連返屋企

唯一一個直接入屋企嘅入口

  1. Endpoint 寫 IP:client 直接寫 WAN IP,唔用 DNS 名,因為喺屋企開 tunnel 時 DNS 會被 OpenClash 答假 IP,帶出街就連唔到。
  2. 握手:WireGuard 用 Noise IK 握手(Curve25519 交換金鑰、ChaCha20-Poly1305 加密),每個 peer 用公鑰認證,未認證嘅封包一律唔回應。UDP
  3. 避開 443:以前用 UDP 443,撞正 OpenClash 嘅 QUIC reject,所以換咗第二個 port。
  4. Full tunnel:AllowedIPs = 0.0.0.0/0,DNS 指 R3S。一定要 full tunnel,否則 OpenClash 答嘅假 IP 會行咗手機自己網絡,上唔到網。
  5. 可以做咩:直接 SSH 入 R3S 同屋企電腦。SSH
  6. IP 會變:R3S 每 10 分鐘將 WAN IP 報去 Dynu DDNS;client 寫死 IP,IP 變咗要手動改。
E

開 joenyc.net、blogs、drink、sublens、links

純 Cloudflare,完全唔經屋企

  1. Custom domain:呢啲 hostname 係 Worker / Pages 嘅 custom domain,Cloudflare 自動建 DNS 記錄同簽 edge 憑證。DNS 一樣答 anycast IP。DNSTLS 1.3
  2. 喺 edge 行:request 落到香港機房,直接喺 V8 isolate 行 Worker;靜態檔案由 Workers Static Assets 出,唔使行 code。
  3. 有 code 嘅部分:blog 嘅 /api/* 先行 Worker(KV VIEWS 記閱讀數);sublens 用 D1(SQLite);dashboard 用 KV 存成個 state。
  4. Cron:dash Worker 有 Cron Trigger,每日做一次備份入 KV;另一個排程同步第三方資料。Cron Triggers
  5. R3S 斷線都照開:呢五個網站同屋企完全冇關係。

Cloudflare 上面嘅 projects

Workers · Pages · Tunnel · DNS

Cloudflare 帳戶入面有 5 個 Workers、1 個 Pages project、2 個 KV namespace、1 個 D1 database、1 條 Tunnel。下面每行係一個對外 hostname。

Hostname喺邊度行儲存存取控制部署方式
joenyc.netEdge · Worker
純靜態 assets
—公開wrangler deploy,custom domain
blogs.joenyc.netEdge · Worker
靜態 + /api/* 先行 code
KV(閱讀數)公開;後台要登入wrangler deploy + GitHub Actions
drink.joenyc.netEdge · Worker
純靜態 assets
—公開wrangler deploy
sublens.joenyc.netEdge · WorkerD1(SQLite)公開wrangler deploy
dash.joenyc.netEdge · Worker
靜態 + API + Cron
KV(state、undo、每日備份、cache)Access Email 登入 + Worker 再驗 JWTwrangler deploy
links.joenyc.netEdge · Pages—公開wrangler pages deploy(direct upload)
gym.joenyc.net屋企 · R3S
openGym(Docker)
R3S eMMC公開;passkey(WebAuthn)登入Tunnel + docker compose
drop.joenyc.net屋企 · R3S
PsiTransfer(Docker)
R3S eMMC上載需密碼Tunnel + docker compose

另外仲有一個 Access 保護嘅 PC 控制 endpoint(流程 B)同一個 DNS-only 嘅 VPN 記錄(流程 D),兩個都唔公開列出。

Workers 同 Pages 分別

兩樣都喺 Cloudflare 全球機房行,唔使伺服器。Workers 係「一段 JS 程式 + 可選靜態檔」,可以綁 KV / D1 / Cron;Pages 專門放靜態網站,可以接 Git 自動 build。新 project 都用 Workers + Static Assets,只有 links 仲係 Pages。

Tunnel 點解唔使開 port

cloudflared 開機就由 R3S 向外連去 Cloudflare(預設 QUIC,唔通會退去 HTTP/2),連住香港兩個機房做備援。外人嘅 request 由 Cloudflare 經呢條「已經開咗」嘅連線送返落嚟,所以路由器唔使做 port forward,WAN IP 變咗都唔受影響。路由規則喺 Dashboard 設定,cloudflared 自動收到新 config。

Access 兩種用法

人:去 Access 登入頁驗證身份,攞 JWT cookie。機器:request header 帶 Service token(Client ID + Secret),唔使登入頁。dashboard 用前者,PC 控制 endpoint 用後者,所以瀏覽器直接開會見到 401。

用咗咩協議

Protocols
協議由 → 去做咩用
HTTPS(TLS 1.3,HTTP/2、HTTP/3)瀏覽器 → Cloudflare edge所有 joenyc.net 網站。屋企 OpenClash 擋 QUIC,所以屋企只行 TCP。
QUIC(Cloudflare Tunnel)R3S cloudflared → 香港機房Tunnel 連線,由屋企主動連出,request 喺入面以 stream 形式行。後備係 HTTP/2。
HTTP(明文,本機)cloudflared → container只喺 R3S 入面行;加密由 tunnel 負責。
WireGuard出街裝置 → 屋企 WANVPN;Noise IK、Curve25519、ChaCha20-Poly1305。唯一嘅入站 port。
DNS(fake-ip)LAN 裝置 → dnsmasq → OpenClash按域名分流;joenyc.net 走 DIRECT。
DNS over HTTPSMac → cloudflare-dns.com喺屋企查真 IP(普通 DNS 會被 OpenClash 攔)。
DHCPLAN 裝置 ↔ dnsmasq派 IP;屋企電腦有固定 lease。
Wake-on-LANR3S → 屋企電腦網卡etherwake 發 magic packet(Ethernet 廣播,EtherType 0x0842,MAC 重複 16 次),唔係 IP 封包。
SSHMac → R3S、R3S → 屋企電腦全部用 ed25519 key,唔用密碼。
Dynamic DNSR3S → Dynu每 10 分鐘報 WAN IP。
JWT(RS256)Access → dashboard WorkerCf-Access-Jwt-Assertion,Worker 用 team 公鑰驗。
Access Service tokendashboard Worker → PC 控制 endpointCF-Access-Client-Id / CF-Access-Client-Secret header。
Bearer token捷徑 / script → dashboard Worker推數據入 dashboard,常數時間比較。
Cron TriggersCloudflare → dashboard Worker每日備份同定時同步第三方資料。

屋企設備

Home side

NanoPi R3S

  • iStoreOS 24.10(OpenWrt),aarch64,約 1GB RAM
  • 兼做路由器、DNS / DHCP、OpenClash、WireGuard、Docker host
  • 自架服務放喺 eMMC 分區,唔塞系統分區
  • 每月自動重啟一次,停咗一批用唔著嘅服務慳 RAM

Containers

  • cloudflared,host 網絡
  • opengym-web + opengym-api(openGym)
  • psitransfer(檔案分享)
  • 全部 restart: unless-stopped,Docker 開機自動起

DNS 兩個世界

  • 街外:DNS 答 Cloudflare anycast IP
  • 屋企 / VPN:OpenClash 答 198.18.x.x 假 IP,再按規則分流
  • 測試要分清:屋企見到嘅唔代表外人見到嘅,要用 DoH 查真 IP 再 curl --resolve

安全設計

Exposure

對外暴露面好細:WAN 只開 WireGuard 一個 UDP port,而 WireGuard 對冇正確金鑰嘅封包完全唔回應。所有網站流量都經 Cloudflare,屋企 IP 唔會喺 proxied DNS 記錄出現。

私人工具有兩層:dashboard 喺 edge 由 Access 擋,Worker 入面再驗一次 JWT 同 email 白名單;PC 控制只收 Service token,只接受幾個固定動作,SSH host key 唔啱就直接失敗。

呢頁係公開精簡版:IP、MAC、內部 port、hostname 同所有 token 都刪咗。