第 21 章

Credentials、備份還原、Projects 與資安硬化

把 flow、credentials(認證資料)、設定、套件清冊與解密材料分層保護,並以停用所有外部副作用的隔離還原演練,證明備份真的能用。本章只做離線檢查,不連線、不部署。

為何需要本章

備份不是把 flows.json 複製出去就完成,資安也不是只替編輯器加一道登入。這個 Add-on 同時持有 flow、加密後的 credentials、Home Assistant 與 Supervisor 能力,還能存取多個映射目錄與主機網路。任何一層外洩、誤刪或錯配,都可能讓自動化停擺或把原本受控的能力交給不受信任的節點。

本章以 Add-on 22.0.1、內嵌 Node-RED 5.0.2 與 HA WebSocket nodes 0.80.3 為固定邊界。你會建立「資料、金鑰、版本、暴露面、權限」五層威脅模型,並用唯讀清冊與離線還原演練確認可復原性。涉及網路、Home Assistant、檔案寫入、套件安裝或硬體的範例都只作辨識,不會在本章執行。

先決條件:先完成第 2 章的 Add-on 與 Ingress 邊界、第 17 章的 Flow 架構及第 18 章的安全測試。若你尚未能辨認有副作用的節點,先不要做還原後 Deploy。

核心觀念

五層威脅模型

層級主要資產典型失效最低控制
資料flows、credentials、settings、context遺失、損毀、錯版覆蓋加密備份、保留週期、還原演練
金鑰credential_secret、Project credential secret、TLS 私鑰與資料一起外洩,或遺失而無法解密分離保管、不可任意輪替、雙人取用紀錄
版本Add-on、Node-RED、節點與套件版本還原後出現 unknown node 或 schema 不相容保存固定版本清冊與套件來源
暴露面Ingress、direct port、HTTP、Dashboard、MQTT把編輯器登入錯當成所有 endpoint 的驗證逐 route 驗證 auth、TLS 與來源限制
權限Supervisor、HA API、host network、UART、映射目錄第三方節點取得超出用途的能力最少節點、最少目的地、最少可寫路徑

資料路徑要連同語意一起備份

Add-on wrapper 固定 userDir=/config/、nodesDir=/config/nodes、flowFile=flows.json,後端只聽 127.0.0.1:46836,flow HTTP root 固定為 /endpoint。因此「設定目錄」至少涵蓋 /config/flows.json、同目錄的 credentials 檔、/config/settings.js、/config/package.json、/config/nodes/ 與可能存在的 context storage。實際 credentials 檔名由 flow 檔名推導,不應只靠猜檔名挑選備份。

addon_config:rw 將這些持久資料映射到 /config。homeassistant_config:rw、media:rw、share:rw 是額外能力,不代表 Node-RED 必然會寫入;ssl 掛載供 direct TLS 讀取,內含私鑰時更不能複製到 flow、log 或一般共享區。

建立可還原且不觸發動作的流程

  1. 建立唯讀資產清冊。

    記錄 Add-on 22.0.1、Node-RED 5.0.2、HA WebSocket 0.80.3、每個額外套件的固定版本、Projects 是否啟用、context store,以及哪些映射目錄真的被 flow 使用。不要在清冊放 token、密碼、私鑰、真實 URL、entity/device/area ID、MQTT broker、client ID 或 topic。

  2. 分開封存資料與解密材料。

    使用受控的 Home Assistant 備份機制保存 Add-on 資料;另在核准的秘密管理處保存 credential_secret。Project 若啟用,另保存該 Project 的 credential secret。兩份材料不可只存在同一部主機,也不可提交到 Git。

  3. 確認備份排除與重建來源。

    Add-on manifest 明確以 backup_exclude 排除 node_modules。所以要保存 package.json、自訂 npm_packages 與 system_packages 的固定版本/名稱清冊;不要宣稱備份含完整 dependency tree。

  4. 先在隔離環境做還原演練。

    隔離環境不得連到正式 HA、MQTT、HTTP、TCP、UDP、WebSocket、資料庫或 UART。所有 Action、API、Fire Event、Update Config、檔案輸出、email、Cast、Modbus、serial 與 network 節點保持停用;自動排程也先停用。

  5. 以唯讀檢查驗證。

    比對 flow 數量、節點 type、套件版本、credentials 是否能解密,以及 settings 與 context store 是否存在。只使用手動 Inject 接純資料處理鏈,Debug 限定必要欄位;不 Deploy 任何對外或寫入節點。

  6. 記錄演練結果與復原點。

    記下備份時間、還原版本、缺少的 dependency、驗證者及回復方式,不記秘密值。只有隔離驗證通過,才另行排定正式變更窗口;本章不執行正式還原、網路連線或 Deploy。

章末離線驗收

產物預期內容失敗即停止
復原清冊Add-on/Node-RED/HA WebSocket 與額外套件版本、flow/node type 數量、備份 hash、三個秘密管理參照欄位、缺少的 dependencies,以及外部與寫入 I/O 啟用數為 0版本或數量無法比對、hash/參照缺失、credentials 無法解密、unknown node 未釐清,或任何正式 HA/MQTT/HTTP/檔案/硬體 I/O 可作用

Credentials、匯出與金鑰生命週期

credential_secret 必須可恢復且維持不變

Node-RED 以秘密金鑰加密儲存 credentials。Add-on 的 credential_secret 會映射成 runtime 的 credentialSecret。官方文件明確要求安全保存;設定後不可任意更換,否則既有 credentials 將無法解密。這不代表加密檔可以公開:密文與金鑰都屬敏感資產,必須分開控管。

復原參照清冊(不是 Add-on 設定,不可執行)
Add-on credential secret reference = PLACEHOLDER_SECRET_RECORD_ID
Project credential secret reference = PLACEHOLDER_PROJECT_SECRET_RECORD_ID
Backup reference = PLACEHOLDER_BACKUP_ID

清冊只保存參照 ID;實際 secret 值只能存在核准的 secret manager,不得寫進這份清冊、Flow、Git、備份說明或工單。

Projects 是不同的金鑰邊界:Add-on 文件指出,若你手動啟用 Node-RED Projects,Add-on 的 credential_secret 雖然仍為必要選項,Node-RED 會忽略它;Project credentials 使用 Project 自己的 secret。兩者不能互相替代。

匯出前做內容清理,而不是相信 credentials 面板

Flow JSON 匯出、Library 項目與 Subflow 可以帶出伺服器設定參照、環境識別資訊或節點屬性;Subflow instance properties、environment 與 parent flow context 也要列入檢查。分享前逐欄檢查並替換為完整 placeholder,例如 YOUR_HA_SERVER、YOUR_ENTITY_ID、YOUR_DEVICE_ID、YOUR_AREA_ID、YOUR_MQTT_BROKER 與 YOUR_MQTT_TOPIC。同時移除 credentials block、Authorization、Cookie、webhook ID、zone/tag、位置資料、檔案路徑和 Debug 擷取內容。

Render Template、API、Webhook、Zone、deprecated Entity,以及 Server、Device Config、Entity Config、Update Config 等 HA 節點都可能接觸敏感設定或環境 ID。尤其 Update Config 會更新 companion entity 的設定/狀態中繼資料;匯出範例時保持節點停用並只留 placeholder。更多 HA 節點角色見第 8 章,HTTP/Webhook 邊界見第 19 章。

備份內容、不可變性與還原演練

項目固定位置或來源備份/還原注意事項
Flow/config/flows.json先離線比對;還原後所有副作用節點仍停用。
Credentials與 flow 檔配對的 credentials storage密文與正確 secret 缺一不可;不得貼到工單。
Runtime 設定/config/settings.jswrapper 會覆寫 backend、paths、auth、TLS 等特定鍵,不可把 upstream 預設直接套用。
節點宣告/config/package.json、Add-on options 清冊node_modules 不在備份內,應由可信固定來源重建。
本機節點/config/nodes檢查來源與程式碼;本機模組以 Node-RED process 權限執行。
Context設定的 memory/localfilesystem storememory 不具跨重啟持久性;磁碟 store 也不能假設每次 assignment 都立即 durable。
外部狀態HA、MQTT、InfluxDB、裝置端不屬 Node-RED 備份;還原不得重播命令或假設外部狀態同步。

備份不可變性(immutability)

至少保留一份無法由 Node-RED 執行帳號改寫的版本,並使用保存期限、完整性雜湊與取用紀錄。不可變不是「永遠不刪」;它是讓遭入侵的 runtime 無法同時破壞所有復原點。定期以新備份取代到期版本,並依資料分類安全銷毀。

事故後不是立刻 Deploy

先隔離、保存時間線與已去識別的 log,再從已知良好備份重建。若 credentials、Project secret、HA token、MQTT 帳密、HTTP Basic Auth、TLS private key 或 webhook ID 可能外洩,依各系統機制撤銷並輪替。先輪替外部系統憑證,再更新 Node-RED credentials,最後以停用 flow 驗證;不要把更換 credential_secret 當成一般 token rotation,因為它會影響既有密文解密。

Projects、Git 與私人儲存庫邊界

Add-on 的 settings.js 預設 editorTheme.projects.enabled=false;Projects 不是開箱即用的備份。手動啟用後,它提供 Git-backed project 工作方式,但 Git commit、remote 可用性與完整 Add-on 備份是三件不同的事。

Projects 要區分三項資產:實際 plaintext credentials、由 Project secret 加密後且可由 Git 追蹤的 Project credential file,以及不在 repository 內的 Project secret。標準工作方式可把密文檔提交到限制成員與權限的私人 repository;加密不代表密文可以公開,而解密 secret 必須在核准的秘密管理處分開保護。

  • 私人儲存庫仍不等於秘密管理:最小化成員、deploy key 和 automation token 權限;永不提交 plaintext credentials、Project secret、TLS key、備份檔、真實 endpoint 或環境 ID。
  • Project secret 獨立保存:它不由 Add-on 的 credential_secret 取代,也不隨 encrypted Project credential file 進 Git。還原 Project 前,先確認你持有匹配的 secret,且不要用「重設金鑰」嘗試碰運氣。
  • 明確選擇密文政策:若組織允許追蹤 encrypted Project credential file,僅提交到受限私人 repository;若政策連密文也排除,Git 就無法還原 Project credentials,必須另備一份受保護且已驗證可還原的 credential-file backup。
  • 提交前看差異:檢查 flow JSON、Project metadata、套件版本、密文檔政策及刪除項。匯出 scrub 規則同樣適用於 Git。
  • 遠端只是一層:另保留 Add-on 備份,因為 settings、非 Project flow、context、Add-on options 與 credentials 生命週期不一定都由 Git 涵蓋。

如果 remote 無法使用,先保存本機 Project 目錄的唯讀副本與狀態,不要 force push、不要刪除 .git、不要重新初始化覆蓋歷史。待身分驗證與目標 remote 都確認後,再由有權限的維護者處理。

暴露面、能力與最小權限

Ingress、direct 與 endpoint 不是同一層

表面22.0.1 行為必要控制
Ingressingress=true、動態 ingress_port=0、ingress_stream=true;NGINX listener 只允許 Supervisor ingress source。仍要以 HA 帳號最小權限管理;不可推論 direct endpoint 也受相同保護。
Direct editor容器 80/tcp 可映射 host 1880;/ 預設透過 Supervisor auth API。只在必要時映射;leave_front_door_open 保持 false/省略。
TLSssl 預設 true,只影響 direct listener;certfile 與 keyfile 從 /ssl 讀取。驗證配對、期限與權限;這些選項不會簽發或自動續期,Ingress 也不受它控制。
Flow HTTP/endpoint/ 不走 direct editor 的 Supervisor auth;http_node 提供 Basic Auth。使用獨立強密碼、TLS、輸入大小/schema 限制;不要與 editor auth 混淆。
Statichttp_static 只保護 static content。不能外推成 HTTP nodes、editor、WebSocket 或其他 route 的保護。
DashboardFlowFuse Dashboard 2 @flowfuse/[email protected] 是選裝且非內建,宣告 Node >=14、Node-RED >=3.0.0;route 由 Node-RED 提供。相容 metadata 不取代目標環境測試;逐 route 驗證 HTTP/WebSocket auth。legacy node-red-dashboard 與 deprecated @flowforge scope 只做遷移,不新裝。
MQTT/其他網路MQTT、HTTP、WebSocket、TCP、UDP 與 TLS config 節點都可產生外部 I/O。使用 broker ACL、獨立 client、TLS 驗證、topic 最小權限;範例維持 placeholder 且節點停用。

Manifest grant 是風險上限,不是使用需求

hassio_api=true 搭配 hassio_role=manager 授予高權限 Supervisor 能力,homeassistant_api=true 授予 HA Core API,auth_api=true 讓 wrapper 使用 Supervisor auth。host_network=true 擴大本機網路可達面,uart=true 允許序列硬體能力。不要把 Supervisor token 或 HA token 寫入 flow/Debug;限制額外節點、外連目的地與可寫資料夾,並讓 File、File In、Watch、HTTP In/Response/Request/Proxy、MQTT、TLS config、TCP、UDP、WebSocket、serial 等節點只取得工作所需範圍。

homeassistant_config:rw、media:rw、share:rw 都可被流程寫入。檔名與路徑必須 allowlist,避免 traversal、覆寫及把秘密放進共用目錄。UART 與 Modbus 寫入可能造成實體副作用,永遠先在無硬體連線的隔離環境驗證。

自訂程式碼與預裝套件同樣在信任邊界內

啟動順序先安裝 system_packages,再以 npm install --omit=dev --omit=optional 安裝 npm_packages,最後逐行以 eval 執行 init_commands;任一步驟失敗會中止初始化。init_commands 每次啟動都會執行,必須保持空白,除非經變更審查、可重複執行且不含秘密。本章不提供也不執行任何命令。

固定套件主要風險與限制
[email protected]wrapper 用於 HTTP Basic Auth hash;hash 不是可公開的密碼替代物。
[email protected]、[email protected]可控制裝置或外寄;還原演練保持節點停用。
[email protected]資料庫 credentials、查詢範圍與 retention 分開控管。
[email protected]、[email protected]OT/序列讀寫可能有實體副作用;搭配 UART 能力時風險更高。
[email protected]持久狀態與還原後外部真實狀態可能不一致。
[email protected]、[email protected]讀取不可信內容或探測網路;限制來源、大小、頻率與目的地。
[email protected]、[email protected]Base64 不是加密,random 節點不可作密碼學秘密。

首次啟動會建立 /config;舊資料可由 /homeassistant/node-red 遷移,舊 dark theme 會遷移到 dark-modern,並嘗試移除三個衝突的舊 HA packages。這些是 wrapper 行為,不是你應手動執行的刪除指令。選裝擴充與 MQTT 的供應鏈界線見第 20 章。

安全地判斷備份與存取問題

  • 還原後 credentials 無法使用:保持所有 flow 停用,核對備份與 secret 的版本/Project 邊界。不要反覆改 credential_secret,不要把密文或 secret 貼到 log;若無匹配 secret,依事故流程重新簽發外部 credentials。
  • Ingress 可開、/endpoint/ 卻未受預期驗證:先辨認你走 Ingress 或 direct host port,再以不含真實資料的唯讀請求確認 route。檢查 http_node、TLS 與 port mapping;不要用 leave_front_door_open 當修復。
  • 還原後出現 unknown nodes:不要 Deploy 或刪除 unknown node。從清冊找出匹配的固定套件,先在隔離環境重建被排除的 node_modules,確認來源與版本後再載入。
  • Project remote 或 credentials 出錯:保留本機資料與 Git 狀態;核對 Project secret、remote 身分與最小權限。不要 force push、重設 secret 或重新初始化 Project。
  • 懷疑權杖或私鑰外洩:先隔離 Add-on、保存已去識別時間線,再從來源系統撤銷/輪替。不要只刪 Debug 訊息就恢復服務;還要檢查備份、Git、共享目錄與外部 log 的曝露範圍。

固定版本來源

本章技術邊界只以以下精確提交與同版官方文件為準:

常見問題

事故中發現 secret-manager 參照與備份時間不相符,應該先試哪一把金鑰嗎?
不要試錯或覆寫設定。先停止還原,保留備份 hash、時間戳與參照版本的唯讀證據,由 secret manager 的取用紀錄確認匹配關係;無法證明匹配就把該復原點判為未通過,改走已核准的事故與外部 credentials 重新簽發流程。
Projects 的私人 Git repo 能取代 Add-on 備份嗎?
不能。Git 不一定涵蓋 Add-on options、非 Project 資料、context、所有 credentials 與 wrapper 狀態;私人 repo 也不是秘密管理器。兩者應作不同復原層。
可以定期更換 credential_secret 嗎?
不要把它當一般 access token 定期輪替。Add-on 官方文件警告,任意變更會讓既有 credentials 無法解密。事故輪替應先處理外部 credentials,若金鑰本身受影響則依受控遷移/重建流程處理。
Ingress 登入是否自動保護 HTTP、Dashboard 與 MQTT?
不是。Ingress、direct editor、/endpoint/、static、Dashboard route、WebSocket 與 MQTT 是不同邊界。逐一配置 endpoint auth、TLS、broker ACL 與來源限制。
還原演練如何避免觸發智慧家庭動作?
使用無正式網路、無 HA/MQTT/UART 連線的隔離環境;停用所有外部 I/O、寫入、排程和 HA executable nodes,只做檔案完整性、節點 type、credentials 解密與純資料鏈的唯讀檢查。