diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index c21f25dd..2459d621 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -19045,6 +19045,59 @@ "expectedMin": "預期下限" } }, + "wazuhReleaseGate": { + "eyebrow": "Wazuh 正式釋出閘門", + "title": "分清功能分支已完成與正式環境尚未釋出", + "subtitle": "這張卡把 Wazuh 只讀路由的 source-side、功能分支、正式主線、正式部署與讀回拆成不同狀態;目前功能分支已完成,但正式主線、部署、讀回與即時中繼資料仍維持 0。", + "checkLabel": "檢核", + "stateLabel": "狀態", + "boundaryTitle": "正式釋出邊界", + "boundaryIntro": "以下鍵值固定:功能分支可讀不等於正式主線已合併,也不等於正式站已部署、Wazuh 即時查詢已啟用或主機操作已授權。", + "summary": { + "source": { + "label": "原始碼", + "detail": "只讀 API、guard 與文件已完成。" + }, + "branch": { + "label": "功能分支", + "detail": "已可由版本庫讀回。" + }, + "main": { + "label": "正式主線", + "detail": "尚未由正式釋出流程合併。" + }, + "deploy": { + "label": "部署讀回", + "detail": "正式站仍是部署前邊界。" + } + }, + "items": { + "sourceGuard": { + "title": "Source-side guard 已完成", + "body": "相容路由、只讀邊界與 no-false-green 檢查已能在 repo 層阻擋誤判。" + }, + "featureBranch": { + "title": "功能分支已可交接", + "body": "分支可讀只代表 patch 可交接,不代表正式主線、部署或即時查詢已完成。" + }, + "formalMain": { + "title": "正式主線仍待釋出", + "body": "必須由合規釋出流程合併到正式主線或套用等效 patch,不得強推或用明文憑證繞過。" + }, + "productionReadback": { + "title": "正式站讀回仍未通過", + "body": "部署後要用正式讀回指令驗證不再 404;部署前 404 只能當邊界證據。" + }, + "ownerGate": { + "title": "負責人證據仍待收件", + "body": "live metadata、事件參照、主機鑑識與回復證明仍需負責人脫敏證據。" + }, + "runtimeBoundary": { + "title": "不開執行期動作", + "body": "不啟用主動回應、主機寫入、掃描、重啟、機密輪替、Nginx 或防火牆繞路。" + } + } + }, "wazuhOwnerEvidencePreflight": { "eyebrow": "Wazuh 代理清單證據收件預檢", "title": "先把負責人證據收成可驗收的脫敏封包", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index c21f25dd..2459d621 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -19045,6 +19045,59 @@ "expectedMin": "預期下限" } }, + "wazuhReleaseGate": { + "eyebrow": "Wazuh 正式釋出閘門", + "title": "分清功能分支已完成與正式環境尚未釋出", + "subtitle": "這張卡把 Wazuh 只讀路由的 source-side、功能分支、正式主線、正式部署與讀回拆成不同狀態;目前功能分支已完成,但正式主線、部署、讀回與即時中繼資料仍維持 0。", + "checkLabel": "檢核", + "stateLabel": "狀態", + "boundaryTitle": "正式釋出邊界", + "boundaryIntro": "以下鍵值固定:功能分支可讀不等於正式主線已合併,也不等於正式站已部署、Wazuh 即時查詢已啟用或主機操作已授權。", + "summary": { + "source": { + "label": "原始碼", + "detail": "只讀 API、guard 與文件已完成。" + }, + "branch": { + "label": "功能分支", + "detail": "已可由版本庫讀回。" + }, + "main": { + "label": "正式主線", + "detail": "尚未由正式釋出流程合併。" + }, + "deploy": { + "label": "部署讀回", + "detail": "正式站仍是部署前邊界。" + } + }, + "items": { + "sourceGuard": { + "title": "Source-side guard 已完成", + "body": "相容路由、只讀邊界與 no-false-green 檢查已能在 repo 層阻擋誤判。" + }, + "featureBranch": { + "title": "功能分支已可交接", + "body": "分支可讀只代表 patch 可交接,不代表正式主線、部署或即時查詢已完成。" + }, + "formalMain": { + "title": "正式主線仍待釋出", + "body": "必須由合規釋出流程合併到正式主線或套用等效 patch,不得強推或用明文憑證繞過。" + }, + "productionReadback": { + "title": "正式站讀回仍未通過", + "body": "部署後要用正式讀回指令驗證不再 404;部署前 404 只能當邊界證據。" + }, + "ownerGate": { + "title": "負責人證據仍待收件", + "body": "live metadata、事件參照、主機鑑識與回復證明仍需負責人脫敏證據。" + }, + "runtimeBoundary": { + "title": "不開執行期動作", + "body": "不啟用主動回應、主機寫入、掃描、重啟、機密輪替、Nginx 或防火牆繞路。" + } + } + }, "wazuhOwnerEvidencePreflight": { "eyebrow": "Wazuh 代理清單證據收件預檢", "title": "先把負責人證據收成可驗收的脫敏封包", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index 773ce593..009023fe 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -279,6 +279,14 @@ type WazuhLiveMetadataEnvGateItem = { tone: 'steady' | 'warn' | 'locked' } +type WazuhReleaseGateItem = { + key: string + check: string + state: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type WazuhOwnerEvidencePreflightItem = { key: string check: string @@ -2221,6 +2229,40 @@ const wazuhLiveMetadataEnvGateSummary = [ { key: 'liveQuery', value: '0', icon: Radar, tone: 'locked' }, ] as const +const wazuhReleaseGateSummary = [ + { key: 'source', value: '1', icon: CheckCircle2, tone: 'steady' }, + { key: 'branch', value: '1', icon: GitBranch, tone: 'steady' }, + { key: 'main', value: '0', icon: Lock, tone: 'locked' }, + { key: 'deploy', value: '0', icon: Route, tone: 'locked' }, +] as const + +const wazuhReleaseGateItems: WazuhReleaseGateItem[] = [ + { key: 'sourceGuard', check: 'REL-1', state: '已完成', icon: CheckCircle2, tone: 'steady' }, + { key: 'featureBranch', check: 'REL-2', state: '已推送', icon: GitBranch, tone: 'steady' }, + { key: 'formalMain', check: 'REL-3', state: '待正式釋出', icon: Lock, tone: 'locked' }, + { key: 'productionReadback', check: 'REL-4', state: '仍為部署前', icon: Route, tone: 'locked' }, + { key: 'ownerGate', check: 'REL-5', state: '待負責人', icon: ClipboardCheck, tone: 'warn' }, + { key: 'runtimeBoundary', check: 'REL-6', state: '0 / false', icon: FileWarning, tone: 'locked' }, +] as const + +const wazuhReleaseGateBoundaries = [ + 'wazuh_readonly_release_gate_visible=true', + 'wazuh_readonly_release_gate_source_side_fix_complete_count=1', + 'wazuh_readonly_release_gate_gitea_feature_branch_push_complete_count=1', + 'wazuh_readonly_release_gate_formal_main_release_complete_count=0', + 'wazuh_readonly_release_gate_production_deploy_complete_count=0', + 'wazuh_readonly_release_gate_production_readback_passed_count=0', + 'wazuh_readonly_release_gate_predeploy_404_observed_count=1', + 'wazuh_readonly_release_gate_wazuh_live_metadata_env_enabled_count=0', + 'wazuh_readonly_release_gate_runtime_gate_count=0', + 'force_push_allowed=false', + 'runtime_execution_authorized=false', + 'host_write_authorized=false', + 'wazuh_active_response_authorized=false', + 'secret_value_collection_allowed=false', + 'not_authorization=true', +] as const + const wazuhLiveMetadataEnvGateItems: WazuhLiveMetadataEnvGateItem[] = [ { key: 'releaseReadback', check: 'ENV-1', state: '待部署驗證', icon: Route, tone: 'locked' }, { key: 'serverEnv', check: 'ENV-2', state: '待負責人', icon: Server, tone: 'warn' }, @@ -7958,6 +8000,137 @@ function IwoooSWazuhLiveRouteReadbackBoard() { ) } +function IwoooSWazuhReleaseGateBoard() { + const t = useTranslations('iwooos.wazuhReleaseGate') + const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } + + return ( + + + + + + + {t('eyebrow')} + + {t('title')} + + {t('subtitle')} + + + + + {wazuhReleaseGateSummary.map(item => { + const Icon = item.icon + return ( + + + {t(`summary.${item.key}.label` as never)} + + + + {item.value} + + + {t(`summary.${item.key}.detail` as never)} + + + ) + })} + + + + + {wazuhReleaseGateItems.map(item => { + const Icon = item.icon + return ( + + + + {t('checkLabel')} {item.check} + + + + + + {t(`items.${item.key}.title` as never)} + + + {t('stateLabel')}:{item.state} + + + + {t(`items.${item.key}.body` as never)} + + + ) + })} + + + + + {t('boundaryTitle')} + + + {t('boundaryIntro')} + + + {wazuhReleaseGateBoundaries.map(item => ( + + {item} + + ))} + + + + + ) +} + function IwoooSWazuhLiveMetadataEnvGateBoard() { const t = useTranslations('iwooos.wazuhLiveMetadataEnvGate') const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } @@ -21144,6 +21317,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { + diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 0d35a2f2..62ae4bcd 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,35 @@ +## 2026-06-25|Wazuh agent 消失事故二次只讀定位與 no-false-green 加嚴 + +**背景**:使用者追問 Wazuh 為什麼仍未把所有主機納回監控、原本已納管為何又不見,以及為什麼尚未修復。本輪暫停前台 release gate 收尾,優先做 112 / Wazuh runtime 只讀定位與 repo guard 加嚴。 + +**只讀證據**: +- Production `/api/iwooos/wazuh` 仍為 `404`、`runtime_gate_count=0`,IwoooS 正式站尚未讀到 Wazuh live metadata。 +- 112 `wazuh-manager`、`wazuh-indexer`、`wazuh-dashboard` 均為 active,`NRestarts=0`。 +- 受管節點 A 的 `wazuh-agent.service` active,並有到 manager 的 transport 連線。 +- 112 manager 端 `1514` 只讀觀察到 `6` 條 established transport;這代表傳輸層有 agent 連線,但不能替代 manager registry 驗收。 +- 目前使用者 `kali` 可讀服務狀態與 journal,但不能直接讀 manager registry CLI;Wazuh manager 受限目錄需要更高權限,`sudo -n` 也需要密碼。 +- Dashboard journal 仍觀察到 stored API / API check / login 退化:`400 / 429 / 500`、stored API unreachable、run_as / internal user 權限錯誤與 TLS client trust 錯誤。 + +**判定**: +- 目前不是「所有 agent 服務都停了」的單純故障;至少 manager transport 層仍有多條連線。 +- 真正卡住的是三層:manager agent registry 沒有可驗收只讀讀回、Dashboard stored API / RBAC / run_as / rate-limit / TLS trust 退化、IwoooS production Wazuh route 尚未部署。 +- 在 registry truth 前不得宣稱所有主機已恢復、不得重新註冊 agent、不得重啟 Wazuh、不得改 stored API / TLS / RBAC / secret,也不得把 TCP 連線數當成 agent 清單恢復。 + +**完成**: +- `docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json` 更新為 2026-06-25 live read-only 狀態,新增 `manager_transport_established_connection_count=6` 與 Dashboard API 退化細項。 +- `scripts/security/wazuh-agent-visibility-runtime-gate.py` 加嚴驗證:必須保留 transport count、stored API unreachable、login 500、rate limited、run_as permission、TLS client trust 與 registry CLI permission blocked 訊號。 +- `docs/security/WAZUH-AGENT-DISAPPEARANCE-INCIDENT-READBACK-2026-06-24.md` 同步更新結論與證據表。 + +**完成度同步**: +- Wazuh agent transport 只讀確認:`65%`。 +- Dashboard API 故障定位:`70%`。 +- Manager agent registry 驗收:`0%`。 +- IwoooS production live metadata route:`0%`。 +- Dashboard stored API / RBAC / TLS 修復:`0%`。 +- Active response / host write / agent re-enroll / Wazuh restart:`0%`,維持禁止。 + +**邊界**:本輪沒有讀 Wazuh secret、沒有保存 raw log、沒有重新註冊 agent、沒有重啟 Wazuh、沒有修改 Dashboard stored API、RBAC、TLS、Nginx、Docker、K8s、firewall 或 host config,也沒有 active response 或 Kali active scan。 + ## 2026-06-25|Wazuh release gate feature branch / formal release 分層校正 **背景**:Wazuh release gate snapshot 仍把 `gitea_push_complete_count` 留在 `0`,但 feature branch 已可由 Gitea 讀回。若不拆層,另一個工作視窗會把「feature branch 已推送」與「formal main release / production deploy 尚未完成」混在一起判讀。 diff --git a/docs/security/WAZUH-AGENT-DISAPPEARANCE-INCIDENT-READBACK-2026-06-24.md b/docs/security/WAZUH-AGENT-DISAPPEARANCE-INCIDENT-READBACK-2026-06-24.md index 00c3fdf8..b6e3d3de 100644 --- a/docs/security/WAZUH-AGENT-DISAPPEARANCE-INCIDENT-READBACK-2026-06-24.md +++ b/docs/security/WAZUH-AGENT-DISAPPEARANCE-INCIDENT-READBACK-2026-06-24.md @@ -11,9 +11,9 @@ 這次不能再用「Wazuh 已建置」或「Dashboard 可開」當成資安機制有效。現有 IwoooS 機制仍停在 source-side 只讀框架、前台邊界與 owner gate,production `/api/iwooos/wazuh` 尚未部署,因此沒有真正讀到 Wazuh manager 的 agent count、agent status、last seen 或 event refs。 -本輪只讀證據顯示:110 與 188 的 Wazuh agent 服務仍在執行,且都與 112 的 Wazuh manager port `1514` 保持連線;112 的 Wazuh manager、indexer、dashboard 也都在執行。Dashboard 顯示用戶端消失的高機率原因,不是 110 / 188 agent 全部停止,而是 112 Dashboard 到 Wazuh API 的 stored API / 認證 / rate-limit / TLS trust 檢查層在 2026-06-24 23:14 CST 左右出現 `429 / 500`。 +2026-06-25 10:43 CST 再次只讀確認:112 的 Wazuh manager、indexer、dashboard 都仍為 active;manager 端 `1514` 觀察到多條 agent transport 連線;受管節點 A 的 agent service active 且 transport 已建立。但 Dashboard stored API / API check 仍在出錯,已觀察到 `400 / 429 / 500`、stored API unreachable、login 500、run_as / internal user 權限錯誤與 TLS client trust 錯誤。這表示「Dashboard 用戶端消失」目前不能簡化成所有 agent 都停了;更精準的故障層是 Dashboard 到 Wazuh API 的 stored API、RBAC / run_as、rate-limit 與 TLS trust。 -但 manager 端 agent registry 的真實清單尚未被獨立驗收,因為本視窗沒有 Wazuh API 只讀帳號,也沒有 112 root read 權限;因此 `wazuh_live_agent_registry_readback` 必須維持 `0`。 +但 manager 端 agent registry 的真實清單尚未被獨立驗收,因為本視窗沒有 Wazuh API 只讀帳號,也沒有 112 root read 權限;`kali` 使用者不屬於 `wazuh` 群組,不能直接讀 Wazuh manager 受限目錄或 registry CLI;因此 `wazuh_live_agent_registry_readback` 必須維持 `0`。 ## 2. 只讀證據摘要 @@ -21,15 +21,15 @@ |--------|------|------| | IwoooS production route | `https://awoooi.wooo.work/api/iwooos/wazuh` 與 `/api/v1/iwooos/wazuh` 皆為 `404` | IwoooS 尚未讀到 Wazuh live metadata | | 公開 Wazuh domain | `wazuh.wooo.work` 未形成可用公開入口 | 不可用公開 domain 代表 Wazuh runtime 狀態 | -| 110 agent | `wazuh-agent.service` active running;啟動於 2026-06-23 14:50 CST;`/var/ossec/etc/client.keys` 存在且為 1 筆本機 key | 110 agent 端未消失 | +| 110 agent | `wazuh-agent.service` active running;啟動於 2026-06-23 14:50 CST;本機 agent key 存在 | 110 agent 端未消失 | | 110 manager target | `ossec.conf` manager address 指向 `192.168.0.112` | 112 是 manager control plane | | 188 agent | `wazuh-agent.service` active running;啟動於 2026-06-23 14:50 CST | 188 agent 端未消失 | | agent 到 manager | 110 / 188 到 `192.168.0.112:1514` 皆有 ESTABLISHED 連線;`1514 / 1515 / 55000` targeted reachability 成功 | agent network path 正常 | | 112 control plane | `wazuh-manager`、`wazuh-indexer`、`wazuh-dashboard` 均 active running;啟動於 2026-06-23 14:48-14:51 CST;`NRestarts=0` | 112 服務沒有整體掛掉 | | 112 API endpoint | `https://192.168.0.112:55000/` 從本機、110、188 皆回 `401` | API 活著但需要認證 | -| Dashboard 讀取層 | 2026-06-24 23:14 CST 左右 `/api/check-stored-api`、`/api/check-api` 出現 `429 / 500`,並記錄 Wazuh API check 異常 | Dashboard stored API / rate-limit / 認證 / TLS trust 檢查需維修 | -| 23:20 後狀態 | 23:20 CST 後 Dashboard journal 無新增 429/500 | 可能是短時間檢查/登入造成的節流,但仍未驗收 agent registry | -| manager registry 讀取權限 | `kali` 使用者不能直接執行 manager registry 工具;`sudo -n` 讀取需要密碼;本輪不收密碼、不升權 | agent registry truth 仍未驗收,不能結案 | +| 112 agent transport | 2026-06-25 10:43 CST manager 端只讀觀察到 `6` 條 `1514` established transport | 傳輸層有 agent 連線,但不能替代 registry 驗收 | +| Dashboard 讀取層 | 2026-06-25 10:40-10:41 CST `/api/check-stored-api`、`/api/check-api`、`/api/login` 觀察到 `400 / 429 / 500`、stored API unreachable、run_as / internal user 權限錯誤與 TLS client trust 錯誤 | Dashboard stored API / RBAC / run_as / rate-limit / TLS trust 需維修 | +| manager registry 讀取權限 | `kali` 使用者不能直接執行 manager registry 工具;`sudo -n` 讀取需要密碼;一般權限也無法讀 Wazuh manager 受限目錄 | agent registry truth 仍未驗收,不能結案 | ## 3. 為什麼前一版沒有擋住 @@ -49,6 +49,13 @@ - `manager_agent_registry_readback_passed=false` - `iwooos_live_route_readback_passed=false` - `dashboard_agent_list_recovered=false` +- `manager_transport_established_connection_count=6` +- `dashboard_stored_api_unreachable_observed=true` +- `dashboard_api_login_500_observed=true` +- `dashboard_api_rate_limited_observed=true` +- `dashboard_api_run_as_permission_error_observed=true` +- `dashboard_api_tls_client_cert_unknown_observed=true` +- `manager_registry_cli_permission_blocked=true` - `runtime_gate_count=0` - `active_response_authorized=false` - `host_write_authorized=false` diff --git a/docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json b/docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json index 09203f32..ed25a62e 100644 --- a/docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json +++ b/docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json @@ -1,6 +1,6 @@ { "schema_version": "wazuh_agent_visibility_runtime_gate_v1", - "generated_at": "2026-06-24T23:35:00+08:00", + "generated_at": "2026-06-25T10:45:00+08:00", "status": "blocked_waiting_manager_agent_registry_readback", "mode": "snapshot_only_no_runtime_no_secret_collection", "incident_id": "wazuh-agent-visibility-20260624", @@ -13,9 +13,17 @@ "secret_value_collection_allowed": false, "manager_services_active_observed": true, "agent_transport_connected_observed": true, + "manager_transport_established_connection_count": 6, "dashboard_api_degraded_observed": true, + "dashboard_stored_api_unreachable_observed": true, + "dashboard_api_login_500_observed": true, + "dashboard_api_rate_limited_observed": true, + "dashboard_api_run_as_permission_error_observed": true, + "dashboard_api_tls_client_cert_unknown_observed": true, + "manager_registry_cli_permission_blocked": true, + "manager_registry_cli_requires_privilege": true, "production_route_http_status": 404, - "observed_at_taipei": "2026-06-24T23:29:22+08:00", + "observed_at_taipei": "2026-06-25T10:43:16+08:00", "observed_layers": { "iwooos_production_route": { "status": "blocked", @@ -29,18 +37,23 @@ }, "host_agent_transport": { "status": "observed_connected", - "evidence": "110 與 188 agent 已只讀觀察為 active,且到 112 的 1514 transport 已建立", - "completion_percent": 75 + "evidence": "manager 端只讀觀察到多條 agent transport 已建立;這只代表傳輸層存在,不代表 registry 已驗收", + "completion_percent": 65 + }, + "direct_agent_host_readback": { + "status": "partial_current_readback", + "evidence": "受管節點 A agent service active 且 transport 已建立;受管節點 B 本輪 SSH 只讀連線未通過,需另補合法讀回", + "completion_percent": 45 }, "manager_agent_registry": { "status": "blocked_no_readonly_registry_access", - "evidence": "kali 使用者無法以一般權限讀 manager registry;Wazuh API 需要正式只讀認證", + "evidence": "kali 使用者無法以一般權限讀 manager registry;CLI 需要更高權限,Wazuh API 需要正式只讀認證", "completion_percent": 0 }, "dashboard_api_check": { - "status": "degraded_observed", - "evidence": "dashboard plugin 在 stored API 與 API check 期間觀察到 429 或 500", - "completion_percent": 35 + "status": "degraded_current_observed", + "evidence": "dashboard plugin 在 stored API、login、API check 與 TLS client trust 路徑觀察到 400、429、500 與權限錯誤", + "completion_percent": 70 } }, "registry_counts": { @@ -51,9 +64,17 @@ "last_seen_window_verified": false }, "dashboard_error_codes_observed": [ + 400, 429, 500 ], + "current_root_cause_hypotheses": [ + "dashboard_stored_api_target_or_health_check_degraded", + "wazuh_api_run_as_or_internal_user_permission_mismatch", + "dashboard_api_check_rate_limited", + "dashboard_tls_client_trust_mismatch", + "manager_registry_read_permission_missing_for_current_readonly_user" + ], "required_evidence_before_green": [ { "evidence_id": "manager_agent_registry_counts", diff --git a/scripts/security/wazuh-agent-visibility-runtime-gate.py b/scripts/security/wazuh-agent-visibility-runtime-gate.py index 37418fc5..18e35341 100644 --- a/scripts/security/wazuh-agent-visibility-runtime-gate.py +++ b/scripts/security/wazuh-agent-visibility-runtime-gate.py @@ -50,6 +50,11 @@ def assert_zero(label: str, actual: Any) -> None: assert_equal(label, actual, 0) +def assert_at_least(label: str, actual: Any, minimum: int) -> None: + if not isinstance(actual, int) or actual < minimum: + raise SystemExit(f"BLOCKED {label}: expected >= {minimum!r}, got {actual!r}") + + def collect_string_values(value: Any) -> list[str]: if isinstance(value, str): return [value] @@ -139,15 +144,35 @@ def validate(root: Path) -> None: "wazuh_agent_visibility_runtime_gate.agent_transport_connected_observed", snapshot.get("agent_transport_connected_observed"), ) + assert_at_least( + "wazuh_agent_visibility_runtime_gate.manager_transport_established_connection_count", + snapshot.get("manager_transport_established_connection_count"), + 1, + ) assert_true( "wazuh_agent_visibility_runtime_gate.dashboard_api_degraded_observed", snapshot.get("dashboard_api_degraded_observed"), ) + for key in [ + "dashboard_stored_api_unreachable_observed", + "dashboard_api_login_500_observed", + "dashboard_api_rate_limited_observed", + "dashboard_api_run_as_permission_error_observed", + "dashboard_api_tls_client_cert_unknown_observed", + "manager_registry_cli_permission_blocked", + "manager_registry_cli_requires_privilege", + ]: + assert_true(f"wazuh_agent_visibility_runtime_gate.{key}", snapshot.get(key)) assert_equal( "wazuh_agent_visibility_runtime_gate.production_route_http_status", snapshot.get("production_route_http_status"), 404, ) + expected_error_codes = {400, 429, 500} + actual_error_codes = set(snapshot.get("dashboard_error_codes_observed", [])) + missing_error_codes = sorted(expected_error_codes - actual_error_codes) + if missing_error_codes: + raise SystemExit(f"BLOCKED wazuh_agent_visibility_runtime_gate.dashboard_error_codes_observed: missing {missing_error_codes!r}") validate_required_evidence(snapshot) validate_no_secret_values(snapshot) @@ -166,6 +191,7 @@ def main() -> None: print( "WAZUH_AGENT_VISIBILITY_RUNTIME_GATE_OK " f"registry=0 route={snapshot['production_route_http_status']} " + f"transport={snapshot['manager_transport_established_connection_count']} " f"dashboard_degraded={int(snapshot['dashboard_api_degraded_observed'])} " f"runtime_gate={snapshot['runtime_gate_count']}" )
+ {t('subtitle')} +
+ {t(`summary.${item.key}.detail` as never)} +
+ {t(`items.${item.key}.body` as never)} +
+ {t('boundaryIntro')} +
+ {item} +