落地版對外連線盤點與停用方式

落地版對外連線盤點與停用方式

用途:客戶手冊(T-4.1)的「網路需求/封閉網路部署」章節素材,以及裝機前的 防火牆規劃依據。

盤點範圍:BE 主專案(api/ app/ common/ config/ core/ infra/ domain/)、 FE 全部原始碼與 index.html、jedi-* 套件層(jedi_*/ 下所有 .py)。 掃描方式:grep 全域 URL 字面值 + 逐項開檔確認是「執行期真的會連」還是「字串常數」。

§1

結論一句話

落地版(air-gap)唯一「不處理就會壞掉」的對外連線是 Cloudflare Turnstile 人機驗證, 本卡已將其做成 BE/FE 一對的開關並在落地版預設停用。 其餘對外連線全部屬於 「功能不啟用就不會連」或「本來就是 best-effort、連不到只是少一項紀錄」。

§2

一、必須處理的(不處理就有功能壞掉)

連線目標 用途 不處理的後果 本卡處置
challenges.cloudflare.com 登入頁人機驗證(Turnstile)。FE 載 script、BE 打 siteverify 🔴 全站沒有人登入得了。FE 端 script 載不到 → onload 不觸發 → 拿不到 token;BE 端連不到 siteverify 時 jedi-captcha 的處理是 raise UnauthorizedError不是放行 ✅ 已做 BE/FE 一對開關,落地版預設停用(見下方「停用方式」)

為什麼這一項特別危險

它的失效方式是登入直接失敗而不是「驗證被跳過」。而且症狀出現在客戶裝完機、第一次 要登入的那一刻——最沒有除錯線索的時間點。BE log 會看到 CAPTCHA_400001 驗證碼錯誤, 但真因是「連不到 Cloudflare」,訊息完全指向錯誤的方向。

§3

二、功能不啟用就不會連(客戶不設定即不外連)

連線目標 用途 觸發條件 不啟用時的行為
api.anthropic.com(Anthropic SDK) AI 助理/證據分類等 AI 功能 使用者主動操作才觸發(送出對話、按分類);需設 ANTHROPIC_API_KEY 沒人點就不會連。⚠️ 但未設 key 時並非不初始化 client——AiBotService 是拿到請求才建 client、直接呼叫,失敗被 except Exception 接住回「系統錯誤,請稍後再試。」。即 air-gap 下按了 AI 功能會等到 timeout 才回一句籠統錯誤(不會壞掉別的功能,但體驗差)。建議客戶手冊註明「未購買/未設定 AI 功能者請勿使用該入口」,或後續另開卡在無 key 時直接回明確訊息
oauth2.googleapis.comwww.googleapis.comdrive.google.com Google Drive 證據同步(OAuth 換 token、讀檔) 需在後台完成 Drive 整合授權(要 OAuth client id/secret) 未整合即無任何 Drive 同步 job,不發連線
api.telegram.org(jedi-notification) Telegram 通知管道 需在通知設定填 bot token 未設定即不使用該管道
SMTP 伺服器(客戶自填) 系統寄信(密碼重設、到期通知) 需在後台填 SMTP 設定 未設定即不寄信。⚠️ 這通常是客戶內網的郵件伺服器,不算外網
檢測工具目標主機(SSH/WinRM) 組態檢測 客戶自行新增檢測目標 ⚠️ 這是對客戶自己內網主機的連線,不是對外網
§4

三、best-effort,連不到不影響功能

連線目標 用途 連不到時
License Center(LICENSE_ACTIVATION_SERVER_URL ① 線上開通(序號換照)② 防竄改事件回報 ① 已有離線開通路徑(機器碼 → 原廠簽照 → 匯入),FE 明確引導改走離線
common/integrity/lc_report.py 是 best-effort:短 timeout + 全域 except絕不阻塞也不影響鎖定流程(D9 紅線)

🔴 不要為了「讓回報更可靠」把 ② 改成硬依賴或加長 timeout——那會讓防竄改鎖定流程 在 air-gap 環境卡住。這是 FR-064 D9 明確定下的紅線,本次盤點確認維持原狀未動

§5

四、看起來像對外連線、實際不是(避免誤判成待處理項)

字面值 實際是什麼
www.omg.org/spec/BPMN/...camunda.org/schema/...bpmn.io/schema/... BPMN XML 的 namespace 宣告字串。XML namespace 只是唯一識別碼,解析器不會去連
csrc.nist.gov/ns/oscalietf.org/rfc/rfc4122 同上,OSCAL/RFC 的 namespace 與識別碼字串
drive.google.com/drive/folders/<id> 給使用者點的顯示連結(新視窗開啟),由使用者的瀏覽器發出,不是伺服器連線。air-gap 環境下點了打不開屬預期
omnitruck.cinc.sh/install.sh build 期安裝 CINC 檢測引擎到 image 內用的,只出現在文件與 Dockerfile;客戶執行期不連
developers.cloudflare.com/... 註解裡的說明文件連結
§6

五、外部資源載入(字型/CDN)

零項。FE 全部字型與 JS 皆打包進 image,index.html 無任何 CDN/Google Fonts 引用。實測掃描 fonts.googleapisfonts.gstaticcdn.unpkgjsdelivr 皆無命中。

這點對 air-gap 很關鍵:外部字型的失效方式是「頁面能開但排版跑掉或載入卡住」, 屬於難以歸因的問題。

§7

停用方式(Turnstile)

這是 BE/FE 一對的開關,只關一邊都會壞:

  • 只關 FE、BE 還在驗 → BE 永遠收到空 token → 一樣登不進去
  • 只關 BE、FE 還在載 script → air-gap 下前端卡在載入
變數 落地版值 設定位置
BE TURNSTILE_ENABLED false guidant.env由 install.sh 自動寫入(不依賴人記得)
FE VITE_TURNSTILE_ENABLED false .env.onprembuild 期烙進產物npm run build:ONPREM

兩者預設值皆為啟用,這是刻意的:SaaS 版靠它擋自動化攻擊,若預設關閉,任何忘記 設定的雲端部署都會靜默失去防護——失效方向必須朝向「保留防護」而不是「靜默放行」。

若某客戶環境其實有外網、想啟用

  1. guidant.envTURNSTILE_ENABLED=true,並填入真實的 TURNSTILE_SECRET_KEY
  2. FE 需以 VITE_TURNSTILE_ENABLED=true + 真實 site key 重新 build image

(FE 的值是 build 期烙印,不能只改執行期環境變數。)

§8

防火牆規劃建議(給客戶手冊)

完全封閉網路(air-gap)可正常運作,前提是:

  1. Turnstile 已停用(installer 自動處理)
  2. 不使用 AI 功能、Google Drive 同步、Telegram 通知
  3. 授權走離線開通(機器碼 → 原廠簽照 → 匯入)

若客戶要開放部分外網,按需求逐項開通即可;除 Turnstile 外沒有任何「必須開通否則 系統不能用」的對外連線。

§9

驗證紀錄(2026-08-17)

項目 結果
TURNSTILE_ENABLED=false + 請求完全不帶 cf_turnstile_token ✅ 登入成功、取得 JWT
未設 TURNSTILE_ENABLED(預設啟用)+ 不帶 token ✅ 被擋 401 CAPTCHA_400001(防護未被弱化)
未設 TURNSTILE_ENABLED + 帶測試 token ✅ 登入成功
FE .env.onprem build VITE_TURNSTILE_ENABLED=false,停用時 script 不注入、容器不渲染

⚠️ 實作時發現的一個連帶問題:cf_turnstile_token 原本在 request schema 是 required=True,停用時 FE 不會帶這個欄位 → marshmallow 會在進到 route 之前 就回 422,route 內的停用判斷永遠執行不到(症狀是「明明關掉了還是登不進去」)。 已改為選填;啟用時仍會把 None 交給驗證函式,空 token 一樣驗不過——擋人機驗證 的是那道驗證,不是 schema 的 required 旗標