跳至主要内容

ADR-032: Actuator 維運面由應用集中分類授權

ADR 編號: 032 狀態: 已接受 (Accepted) 決策日期: 2026-08-29 決策者: Development Team 取代: ADR-026 中 Actuator authority、 AuthorityContributor 與 SecurityContributor 跟隨 endpoint owner 的決策,以及 platform-info public system handshake matcher 跟隨 Feature contributor 的決策 被取代: 無


摘要​

Feature 繼續擁有固定的 @WebEndpoint、HealthIndicator、InfoContributor 與 system handshake adapter;但 Actuator 的認證與授權 policy(預設路徑為 /actuator/**),以及固定 GET /api/v1/system/info public handshake 的 path classification,改由應用唯一的 security chain 集中擁有。通用 Actuator 讀取使用固定 actuator:read,異動使用固定 actuator:write;Public 與單純 Authenticated 端點必須由應用明確列出。Feature 不再為每個 維運端點建立自己的 Authority/AuthorityContributor/SecurityContributor。

本 ADR 取代 ADR-026 的 Actuator 授權 ownership,並涵蓋刪除 PlatformInfoSecurityContributor 時一併上收的 system handshake public matcher。固定 adapter 仍屬 Feature core,產品資源 API 仍屬 reference implementation。

背景 (Context)​

ADR-026 正確把不依賴產品模型、契約固定的 Actuator/health/info adapter 留在 Feature core, 使其能透過 Feature 同步管道維護。但它同時要求每個 endpoint 攜帶 feature-specific authority 與兩個 contributor,形成 mail:ops、audit:ops、platform_info:ops 等維運詞彙。

這個模型帶來三個問題:

  1. 每新增一個維運 endpoint,Authority catalog、Role seed、授權類別與測試矩陣都要成長。
  2. 這些 authority 的真正 consumer 都是同一類平台維運者,feature 粒度沒有形成有效的職責隔離。
  3. Spring Boot 已提供可解析實際 Actuator mapping 的完整安全邊界;把共同 policy 分散回各 Feature,反而增加 漏掉 Spring Boot 內建 endpoint 的風險。

同時,應用仍可能因風險、法遵或部署拓樸,需要把個別端點改成 Public、Authenticated,或使用 專屬 {resource}:{operation}。集中預設不得剝奪這個覆寫能力。

考量的方案 (Options Considered)​

方案 A:維持每個 Feature 的 {feature}:ops​

endpoint 與授權元件同進退,但 Authority catalog 與樣板程式碼隨 Feature 數量成長,且內建 Actuator endpoint 容易因沒有明確 owner 而漏掉授權。

方案 B:所有 Actuator endpoint 只要求 authenticated​

規則最少,但任一登入者都能讀取稽核事件、組態、metrics 或執行維運異動,無法作為安全預設。

方案 C:應用集中分類,提供穩定 read/write capability(採用)​

由 application security chain 依 Spring Boot 的實際 endpoint mapping 完整覆蓋 Actuator,使用 固定的讀寫 capability 作為 fail-safe 預設;應用可在通用 matcher 前對特定端點明確覆寫。

決策 (Decision)​

1. Adapter ownership 與 authorization ownership 分離​

ConcernOwner
@WebEndpoint、HealthIndicator、InfoContributor、固定 system handshake提供該整合面的 Feature core
Actuator 實際 endpoint mapping 的認證與授權分類application security chain
固定 GET /api/v1/system/info public path classificationapplication security chain
誰取得維運 capabilityapplication Role seed/外部身分映射
產品 /api/** 與其領域 capabilitystructural/reference domain

Feature 移除後 endpoint 會消失;應用的通用 matcher 留存並不產生 dead surface,也不需要跟著 每個 Feature 增減。

2. Actuator 統一分成三類​

所有 matcher 必須在 anyRequest() 前完成,順序由具體到通用:

  1. Public:應用以精確 method+path 明列 permitAll()。
  2. Authenticated:應用以精確 matcher 明列 authenticated(),並完成風險與 Audit 決策。
  3. Capability protected:其餘 Actuator GET/HEAD 要求 actuator:read;其他 method 要求 actuator:write。

Actuator links endpoint(預設 /actuator 與 /actuator/)亦遵守同一 method 分類。Spring Boot toAnyEndpoint().withHttpMethod(...) 不會把 method 限制套到 links matcher,因此實作必須先對 links 明確分流,其他 endpoint 則使用 excludingLinks().withHttpMethod(...);呼叫順序不得反轉, 因為 excludingLinks() 會清除先前的 method 限制。

actuator:read 與 actuator:write 是應用維運面的固定詞彙,不因 Feature 增減而改變。寫入權限 不自動蘊含讀取權限;需要讀寫的維運角色應同時取得兩者。

3. 應用保留端點級覆寫權,但 Feature 不得宣告 Actuator matcher​

參考 security chain 先依 getOrder() 排序並套用全部 SecurityContributor,之後才註冊 application 自己的 public 與通用 Actuator matcher。因此:

  • getOrder() 只決定 contributors 彼此的順序,不能把 contributor 排到 application fallback 之後;
  • 任何 contributor 若與 Actuator mapping 重疊,都會因 first-match-wins 先於集中政策命中;
  • 所有非 application-owned SecurityContributor 都禁止用 /actuator 字面或 EndpointRequest 認領 Actuator,並由掃描整個非 config/ application source 的架構測試阻擋;
  • 個別端點確需不同政策時,只能由 application-owned contributor 或 application chain 的等價 matcher 明確覆寫為 Public、Authenticated 或專屬 {resource}:{operation}。

覆寫必須同步測試、Role/身分映射,以及適用的 Audit 決策。

4. 固定 system handshake 的 resource-plane 例外​

SystemInfoController 與其 DTO 仍由 platform-info Feature core 擁有,但刪除 PlatformInfoSecurityContributor 後,精確的 GET /api/v1/system/info permitAll() matcher 改由 application chain 宣告。這是固定啟動握手的 application-wide path classification,不是產品領域 authority,也不屬 Actuator capability fallback。

actuator:read/actuator:write 僅匹配 Spring Boot 實際發布的 Actuator endpoints(預設位於 /actuator);持有它們不會授予任何 /api/** 權限。

5. Exposure 與 authorization 分離​

management.endpoints.web.exposure.include 只決定 endpoint 是否發布,不構成認證或授權。新增 endpoint 即使已加入 exposure,也會自動落入通用 actuator:read/actuator:write policy。

兩者是不同 concern,但實作 matcher 集合會刻意從 Spring Boot PathMappedEndpoints 取得實際已 發布的 endpoints。因此未 exposure 的路徑(例如 /actuator/env)不會進入 capability matcher: 匿名請求由一般 fallback 回 401,已認證請求到達不存在的 mapping 後回 404,而非先回 403。 這不會曝露 endpoint 內容;測試與監控不得把「未發布路徑一定先回 403」當成契約。

management.endpoints.web.base-path 與 endpoint path mapping 是合法部署旋鈕。集中 policy 必須以 Spring Boot EndpointRequest 解析實際 mappings,不得硬編 /actuator/**;變更 base path 後仍須 保持相同授權分類。

參考實作只支援 Actuator 與 application 共用同一個 server port。若 management.server.port 形成不同 port,主 application chain 的 EndpointRequest 依 Spring Boot 設計不會匹配 MANAGEMENT namespace;因此參考實作會在啟動時 fail-fast。需要分離 management port 的應用必須先提供 management context 專用的完整 security chain,再明確替換此 same-port guard。

6. Health 公開範圍採精確路徑​

預設只公開 GET/HEAD /actuator/health 與 /actuator/info。/actuator/health/** 的 component 或 group 子路徑仍落入 actuator:read,避免以 wildcard 公開額外診斷細節。

management.endpoint.health.show-details 維持 when_authorized,並明確指定 management.endpoint.health.roles=actuator:read。因此匿名或一般已認證使用者在 public health root 只看到整體狀態;只有具 actuator:read 的呼叫者可看到 components/details。Spring Boot 4.0 會把這個設定值直接和 GrantedAuthority 比對,不會要求 ROLE_ 前綴。

目前參考部署、Docker health check 與 Kubernetes probe 都使用精確的 /actuator/health。下游若 使用 /actuator/health/liveness 或 /actuator/health/readiness,必須在升級時二選一:

  • 讓探針攜帶具 actuator:read 的身分;或
  • 經風險確認後,在應用明確公開所需的精確 group 路徑。

不得只為探針方便而直接公開 /actuator/health/**。

7. Authority 與既有 Role 的遷移​

應用以單一 AuthorityContributor 宣告 actuator:read、actuator:write,並由 Role seed 或外部 身分映射授予維運角色。既有 audit:ops、mail:ops、platform_info:ops role link 以 revokedAuthorities 顯式撤回;孤兒 authority 依 ADR-031 保留並告警,不在 seed 階段刪除。

revokedAuthorities 只治理 seed 擁有的 SYSTEM role,不會靜默修改人工建立的 CUSTOM role。 升級時必須人工盤點 CUSTOM role 是否仍持有 audit:ops、mail:ops 或 platform_info:ops,移除 這些失效授權,並依實際維運職責改授 actuator:read、actuator:write 或兩者。

對 ADR-026 的取代範圍​

保留 ADR-026 的下列決策:

  • 固定 Actuator/health/info/system handshake adapter 屬 Feature core。
  • Feature core 與 reference implementation 仍以「是否預期由消費端重塑」區分。
  • MailEndpoint、MailHealthIndicator、ConfigCheckEndpoint、SystemInfoController 繼續留在 各自 Feature core。

取代 ADR-026 的下列決策:

  • 「授權跟隨 surface owner」不再適用於 Actuator 維運面。
  • MailAuthority、AuditAuthority、PlatformInfoAuthority 及其 AuthorityContributor/ SecurityContributor 不再屬 Feature core。
  • PlatformInfoSecurityContributor 原有的 GET /api/v1/system/info public matcher 一併移到 application chain;controller ownership 不變。
  • ADR-026 遷移章節中把 mail:ops 與 platform-info 授權三件套移回 Feature 的部分失效。

除上述固定 system handshake 的 path classification 外,產品 /api/** 的領域 authority 不受 影響,例如 mail_setting:* 仍隨 mail reference domain 進退。

權衡分析 (Trade-offs)​

我們獲得什麼​

  • 新增 Feature endpoint 不再增加 Authority catalog、Role seed 與授權樣板類別。
  • Spring Boot 內建與自訂 endpoint 都有同一個 fail-safe policy。
  • application 仍可依實際風險做端點級覆寫。

我們放棄什麼​

  • Actuator endpoint 與其預設 authority 不再具有完全相同的原始碼生命週期。
  • 只持有舊 {feature}:ops 的外部身分必須遷移到 actuator:read/actuator:write。
  • read/write 是較粗粒度的維運能力;真正需要職責隔離的端點必須由應用明確覆寫。

實作與驗證​

  • Security chain 必須以 EndpointRequest 覆蓋 Spring Boot 實際 Actuator mappings,且位於一般 authenticated fallback 前;不得假設 base path 永遠是 /actuator。
  • 測試至少涵蓋 Public、匿名 401、已認證但無 capability 403、read 可讀不可寫、write 可異動、 health root 明細只對 read authority 顯示、health component 子路徑仍受保護,以及舊 {feature}:ops 不再授權。
  • 架構測試必須掃描非 config/ 的 application source,阻擋非 application-owned SecurityContributor 使用 /actuator 字面或 EndpointRequest。
  • 授權整合測試必須以非預設 base path 驗證 Public、401、403 與 capability success 仍成立。
  • links endpoint 必須測試 GET/HEAD 走 read、其他 method 走 write,避免 Spring Boot links matcher 未繼承 withHttpMethod 的行為放寬 policy。
  • same-port guard 必須驗證不同 management.server.port 會 fail-fast;分離 port 不得靜默改由 Spring Boot 預設 management security 承接。
  • Role seed 必須驗證 grants 與 revokedAuthorities 無重疊,並涵蓋既有資料庫的冪等 reconcile。
  • exposure allowlist、Bruno/API 測試與部署探針文件必須和實際 endpoint 分類一致。

相關文檔 (References)​

變更歷史 (Change Log)​

日期變更內容變更者
2026-08-29接受集中式 Actuator 授權與固定 read/write capabilityDevelopment Team + AI Assistant

文檔維護者: Development Team + AI Assistant 最後審閱: 2026-08-29