feat(iwooos): expose Wazuh live metadata gate readback
This commit is contained in:
@@ -23,6 +23,9 @@ from src.services.iwooos_security_control_coverage import (
|
||||
from src.services.iwooos_wazuh_readonly_status import (
|
||||
load_iwooos_wazuh_readonly_status,
|
||||
)
|
||||
from src.services.iwooos_wazuh_live_metadata_gate import (
|
||||
load_latest_iwooos_wazuh_live_metadata_gate,
|
||||
)
|
||||
from src.services.public_redaction import redact_public_lan_topology
|
||||
|
||||
|
||||
@@ -44,6 +47,38 @@ async def get_iwooos_wazuh_readonly_status_v1() -> JSONResponse:
|
||||
return await _wazuh_readonly_status()
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/iwooos/wazuh-live-metadata-gate",
|
||||
response_model=dict[str, Any],
|
||||
summary="取得 Wazuh 即時中繼資料負責人閘門讀回",
|
||||
description=(
|
||||
"讀取已提交的 Wazuh 即時中繼資料負責人閘門,並附上 Wazuh 正式只讀路由的"
|
||||
"公開安全彙總。此端點不讀機密明文、不查主機、不保存原始 Wazuh 載荷、"
|
||||
"不啟用主動回應、不改 K8s / ArgoCD / Docker / Nginx / firewall。"
|
||||
),
|
||||
)
|
||||
async def get_iwooos_wazuh_live_metadata_gate() -> dict[str, Any]:
|
||||
"""回傳 Wazuh 即時中繼資料啟用前負責人閘門只讀狀態。"""
|
||||
try:
|
||||
wazuh_result = await load_iwooos_wazuh_readonly_status()
|
||||
payload = await asyncio.to_thread(
|
||||
load_latest_iwooos_wazuh_live_metadata_gate,
|
||||
wazuh_live_status=wazuh_result.payload,
|
||||
wazuh_live_http_status=wazuh_result.http_status,
|
||||
)
|
||||
return redact_public_lan_topology(payload)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
except (json.JSONDecodeError, ValueError) as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"IwoooS Wazuh 即時中繼資料閘門無效:{exc}",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/iwooos/runtime-security-readback",
|
||||
response_model=dict[str, Any],
|
||||
|
||||
@@ -20,6 +20,7 @@ _SNAPSHOT_FILES = {
|
||||
"owner_gap": "s4-9-owner-response-gap-audit.snapshot.json",
|
||||
"wazuh_coverage": "wazuh-managed-host-coverage-gate.snapshot.json",
|
||||
"wazuh_runtime": "wazuh-agent-visibility-runtime-gate.snapshot.json",
|
||||
"wazuh_live_metadata_gate": "wazuh-readonly-live-metadata-env-gate.snapshot.json",
|
||||
"kali_status": "kali-integration-status.snapshot.json",
|
||||
"soc_control": "soc-siem-kali-wazuh-integration-control.snapshot.json",
|
||||
"alert_readability": "telegram-alert-readability-guard.snapshot.json",
|
||||
@@ -31,6 +32,7 @@ _EXPECTED_SCHEMAS = {
|
||||
"owner_gap": "s4_9_owner_response_gap_audit_v1",
|
||||
"wazuh_coverage": "wazuh_managed_host_coverage_gate_v1",
|
||||
"wazuh_runtime": "wazuh_agent_visibility_runtime_gate_v1",
|
||||
"wazuh_live_metadata_gate": "iwooos_wazuh_readonly_live_metadata_env_gate_v1",
|
||||
"kali_status": "kali_integration_status_v1",
|
||||
"soc_control": "soc_siem_kali_wazuh_integration_control_v1",
|
||||
"alert_readability": "telegram_alert_readability_guard_v1",
|
||||
@@ -70,6 +72,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
|
||||
owner_gap_summary = _summary(snapshots["owner_gap"])
|
||||
wazuh_summary = _summary(snapshots["wazuh_coverage"])
|
||||
live_metadata_gate_summary = _summary(snapshots["wazuh_live_metadata_gate"])
|
||||
soc_summary = _summary(snapshots["soc_control"])
|
||||
alert_summary = _summary(snapshots["alert_readability"])
|
||||
dispatch_summary = _summary(snapshots["owner_dispatch"])
|
||||
@@ -92,7 +95,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
"source_refs": source_refs,
|
||||
"summary": {
|
||||
"source_snapshot_count": len(source_refs),
|
||||
"p0_lane_count": 7,
|
||||
"p0_lane_count": 8,
|
||||
"control_plane_visibility_percent": _average_percent(
|
||||
soc_summary.get("coverage_percent_after_soc_integration_control"),
|
||||
intrusion_summary.get("coverage_percent_after_prevention_control"),
|
||||
@@ -118,6 +121,24 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
"wazuh_live_below_expected_count": live_wazuh["agent_below_expected_minimum_count"],
|
||||
"wazuh_live_metadata_available_count": live_wazuh["metadata_available_count"],
|
||||
"wazuh_live_status": live_wazuh["status"],
|
||||
"wazuh_live_metadata_gate_owner_accepted_count": _int(
|
||||
live_metadata_gate_summary.get("live_metadata_owner_response_accepted_count")
|
||||
),
|
||||
"wazuh_live_metadata_gate_secret_source_accepted_count": _int(
|
||||
live_metadata_gate_summary.get("secret_source_metadata_accepted_count")
|
||||
),
|
||||
"wazuh_live_metadata_gate_manager_health_accepted_count": _int(
|
||||
live_metadata_gate_summary.get("wazuh_manager_health_ref_accepted_count")
|
||||
),
|
||||
"wazuh_live_metadata_gate_readonly_scope_accepted_count": _int(
|
||||
live_metadata_gate_summary.get("readonly_account_scope_accepted_count")
|
||||
),
|
||||
"wazuh_live_metadata_gate_post_enable_readback_count": _int(
|
||||
live_metadata_gate_summary.get("post_enable_readback_passed_count")
|
||||
),
|
||||
"wazuh_live_metadata_gate_live_query_authorized_count": _int(
|
||||
live_metadata_gate_summary.get("wazuh_api_live_query_authorized_count")
|
||||
),
|
||||
"kali_active_scan_authorized_count": _int(soc_summary.get("kali_active_scan_authorized_count")),
|
||||
"kali_execute_authorized_count": _int(soc_summary.get("kali_execute_authorized_count")),
|
||||
"kali_finding_envelope_accepted_count": _int(soc_summary.get("kali_finding_envelope_accepted_count")),
|
||||
@@ -132,7 +153,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
"blocked_waiting_manager_registry",
|
||||
0,
|
||||
"locked",
|
||||
"manager_registry_cross_check",
|
||||
"管理器清單交叉驗收",
|
||||
{
|
||||
"expected_hosts": wazuh_summary.get("expected_host_scope_count", 0),
|
||||
"transport_observed": wazuh_summary.get("manager_transport_established_connection_count", 0),
|
||||
@@ -145,7 +166,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
live_wazuh["status"],
|
||||
0 if live_wazuh["degraded_count"] else 30,
|
||||
"steady" if live_wazuh["metadata_available_count"] else "warn",
|
||||
"enable_readonly_metadata_owner_gate_and_manager_registry_cross_check",
|
||||
"先通過唯讀中繼資料負責人閘門與管理器清單交叉驗收",
|
||||
{
|
||||
"http_status": live_wazuh["http_status"],
|
||||
"readonly_enabled": live_wazuh["readonly_api_enabled_count"],
|
||||
@@ -155,12 +176,42 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
},
|
||||
["GET /api/iwooos/wazuh"],
|
||||
),
|
||||
_lane(
|
||||
"wazuh_live_metadata_gate",
|
||||
snapshots["wazuh_live_metadata_gate"].get("status", "blocked_waiting_live_metadata_owner_response"),
|
||||
0,
|
||||
"locked",
|
||||
"補齊負責人回覆、機密中繼資料、管理節點健康、唯讀範圍與啟用後讀回",
|
||||
{
|
||||
"route_readback": live_metadata_gate_summary.get("production_route_readback_passed_count", 0),
|
||||
"owner_accepted": live_metadata_gate_summary.get("live_metadata_owner_response_accepted_count", 0),
|
||||
"secret_metadata_accepted": live_metadata_gate_summary.get(
|
||||
"secret_source_metadata_accepted_count",
|
||||
0,
|
||||
),
|
||||
"manager_health_accepted": live_metadata_gate_summary.get(
|
||||
"wazuh_manager_health_ref_accepted_count",
|
||||
0,
|
||||
),
|
||||
"readonly_scope_accepted": live_metadata_gate_summary.get(
|
||||
"readonly_account_scope_accepted_count",
|
||||
0,
|
||||
),
|
||||
"post_enable_readback": live_metadata_gate_summary.get("post_enable_readback_passed_count", 0),
|
||||
"live_query_authorized": live_metadata_gate_summary.get(
|
||||
"wazuh_api_live_query_authorized_count",
|
||||
0,
|
||||
),
|
||||
"runtime_gate": live_metadata_gate_summary.get("runtime_gate_count", 0),
|
||||
},
|
||||
["docs/security/wazuh-readonly-live-metadata-env-gate.snapshot.json"],
|
||||
),
|
||||
_lane(
|
||||
"wazuh_dashboard_api",
|
||||
"degraded_api_connection_not_green",
|
||||
0,
|
||||
"warn",
|
||||
"dashboard_api_rbac_tls_repair_readback",
|
||||
"儀表板 API、RBAC 與 TLS 修復後重新讀回",
|
||||
{
|
||||
"dashboard_api_degraded": wazuh_summary.get("dashboard_api_degraded_observed_count", 0),
|
||||
"runtime_gate": wazuh_summary.get("runtime_gate_count", 0),
|
||||
@@ -176,7 +227,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
snapshots["kali_status"].get("status", "blocked_waiting_kali_scope"),
|
||||
0,
|
||||
"locked",
|
||||
"kali_scope_and_finding_envelope_accepted",
|
||||
"資安觀測範圍與 finding envelope 先被接受",
|
||||
{
|
||||
"active_scan_authorized": soc_summary.get("kali_active_scan_authorized_count", 0),
|
||||
"execute_authorized": soc_summary.get("kali_execute_authorized_count", 0),
|
||||
@@ -192,7 +243,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
"contract_ready_no_send_receipt",
|
||||
_alert_contract_percent(alert_summary),
|
||||
"warn",
|
||||
"alert_route_receipt_available",
|
||||
"補齊告警路由 receipt 與實發驗證",
|
||||
{
|
||||
"formatter_markers": alert_summary.get("source_formatter_marker_count", 0),
|
||||
"required_markers": alert_summary.get("required_output_marker_count", 0),
|
||||
@@ -205,7 +256,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
snapshots["owner_dispatch"].get("status", "owner_request_draft_ready_not_dispatched"),
|
||||
0,
|
||||
"locked",
|
||||
"owner_response_packet_delivery",
|
||||
"正式負責人回覆封包送達與接受",
|
||||
{
|
||||
"request_drafts": dispatch_summary.get("request_draft_count", 0),
|
||||
"request_sent": dispatch_summary.get("request_sent_count", 0),
|
||||
@@ -218,7 +269,7 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
"candidate_only_no_runtime_containment",
|
||||
_int(intrusion_summary.get("coverage_percent_after_prevention_control")),
|
||||
"warn",
|
||||
"redacted_evidence_refs_and_maintenance_window",
|
||||
"補脫敏證據參照與維護窗口",
|
||||
{
|
||||
"urgent_candidates": intrusion_summary.get("urgent_prevention_candidate_count", 0),
|
||||
"evidence_received": intrusion_summary.get("evidence_ref_received_count", 0),
|
||||
@@ -244,13 +295,14 @@ def load_latest_iwooos_runtime_security_readback(
|
||||
"not_authorization": True,
|
||||
},
|
||||
"no_false_green_rules": [
|
||||
"dashboard_route_200_is_not_wazuh_registry_recovery",
|
||||
"transport_count_is_not_full_host_management",
|
||||
"ui_visible_is_not_runtime_authorization",
|
||||
"owner_request_draft_is_not_owner_acceptance",
|
||||
"kali_health_is_not_active_scan_authorization",
|
||||
"alert_format_contract_is_not_telegram_send_receipt",
|
||||
"wazuh_live_route_disabled_or_degraded_is_p0_not_green",
|
||||
"儀表板路由 200 不代表 Wazuh 清單已恢復",
|
||||
"傳輸連線數不代表所有主機都已納管",
|
||||
"前台可見不代表執行期已授權",
|
||||
"負責人送件草案不代表負責人已接受",
|
||||
"資安觀測節點健康不代表主動掃描已授權",
|
||||
"告警格式合約不代表通知已實發或已取得 receipt",
|
||||
"Wazuh 正式只讀路由 disabled 或退化時仍是 P0 紅燈",
|
||||
"Wazuh 即時中繼資料必須先通過負責人、機密中繼資料、唯讀範圍與啟用後讀回",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -367,6 +419,9 @@ def _require_runtime_boundaries(snapshots: dict[str, dict[str, Any]]) -> None:
|
||||
"telegram_send_authorized_count",
|
||||
"host_write_authorized_count",
|
||||
"secret_value_collection_allowed_count",
|
||||
"wazuh_api_live_query_authorized_count",
|
||||
"wazuh_active_response_authorized_count",
|
||||
"post_enable_readback_passed_count",
|
||||
):
|
||||
if key in summary and _int(summary.get(key)) != 0:
|
||||
raise ValueError(f"{name}: {key} must remain 0")
|
||||
|
||||
280
apps/api/src/services/iwooos_wazuh_live_metadata_gate.py
Normal file
280
apps/api/src/services/iwooos_wazuh_live_metadata_gate.py
Normal file
@@ -0,0 +1,280 @@
|
||||
"""
|
||||
IwoooS Wazuh live metadata owner gate readback.
|
||||
|
||||
This module only exposes committed gate metadata plus public-safe Wazuh route
|
||||
aggregate status. It never reads secret values, never queries hosts, and never
|
||||
authorizes Wazuh active response, scans, restarts, reloads, or host writes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from src.services.snapshot_paths import default_security_dir
|
||||
|
||||
_DEFAULT_SECURITY_DIR = default_security_dir(Path(__file__))
|
||||
_SNAPSHOT_FILE = "wazuh-readonly-live-metadata-env-gate.snapshot.json"
|
||||
_EXPECTED_SCHEMA = "iwooos_wazuh_readonly_live_metadata_env_gate_v1"
|
||||
|
||||
_REQUIRED_FALSE_BOUNDARIES = {
|
||||
"argocd_sync_authorized",
|
||||
"docker_restart_authorized",
|
||||
"firewall_change_authorized",
|
||||
"host_write_authorized",
|
||||
"k8s_secret_patch_authorized",
|
||||
"kali_active_scan_authorized",
|
||||
"nginx_gateway_workaround_authorized",
|
||||
"production_deploy_authorized",
|
||||
"raw_wazuh_payload_storage_allowed",
|
||||
"repo_write_authorized",
|
||||
"runtime_execution_authorized",
|
||||
"secret_value_collection_allowed",
|
||||
"wazuh_active_response_authorized",
|
||||
"wazuh_api_live_query_authorized",
|
||||
}
|
||||
|
||||
|
||||
def load_latest_iwooos_wazuh_live_metadata_gate(
|
||||
security_dir: Path | None = None,
|
||||
wazuh_live_status: dict[str, Any] | None = None,
|
||||
wazuh_live_http_status: int = 0,
|
||||
) -> dict[str, Any]:
|
||||
"""Load the committed Wazuh live metadata owner gate as a public-safe payload."""
|
||||
directory = security_dir or _DEFAULT_SECURITY_DIR
|
||||
snapshot = _load_snapshot(directory)
|
||||
_require_boundaries(snapshot)
|
||||
|
||||
summary = _summary(snapshot)
|
||||
live_route = _live_route_summary(wazuh_live_status, wazuh_live_http_status)
|
||||
merged_summary = {
|
||||
"server_side_env_key_count": _int(summary.get("server_side_env_key_count")),
|
||||
"required_owner_field_count": _int(summary.get("required_owner_field_count")),
|
||||
"reviewer_check_count": _int(summary.get("reviewer_check_count")),
|
||||
"outcome_lane_count": _int(summary.get("outcome_lane_count")),
|
||||
"blocked_action_count": _int(summary.get("blocked_action_count")),
|
||||
"production_route_readback_passed_count": _int(summary.get("production_route_readback_passed_count")),
|
||||
"live_metadata_owner_response_received_count": _int(
|
||||
summary.get("live_metadata_owner_response_received_count")
|
||||
),
|
||||
"live_metadata_owner_response_accepted_count": _int(
|
||||
summary.get("live_metadata_owner_response_accepted_count")
|
||||
),
|
||||
"secret_source_metadata_accepted_count": _int(summary.get("secret_source_metadata_accepted_count")),
|
||||
"wazuh_manager_health_ref_accepted_count": _int(
|
||||
summary.get("wazuh_manager_health_ref_accepted_count")
|
||||
),
|
||||
"readonly_account_scope_accepted_count": _int(summary.get("readonly_account_scope_accepted_count")),
|
||||
"post_enable_readback_passed_count": _int(summary.get("post_enable_readback_passed_count")),
|
||||
"wazuh_api_live_query_authorized_count": _int(summary.get("wazuh_api_live_query_authorized_count")),
|
||||
"wazuh_active_response_authorized_count": _int(summary.get("wazuh_active_response_authorized_count")),
|
||||
"host_write_authorized_count": _int(summary.get("host_write_authorized_count")),
|
||||
"runtime_gate_count": _int(summary.get("runtime_gate_count")),
|
||||
"wazuh_live_route_http_status": live_route["http_status"],
|
||||
"wazuh_live_route_degraded_count": live_route["degraded_count"],
|
||||
"wazuh_live_readonly_api_enabled_count": live_route["readonly_api_enabled_count"],
|
||||
"wazuh_live_agent_total": live_route["agent_total"],
|
||||
"wazuh_live_metadata_available_count": live_route["metadata_available_count"],
|
||||
"wazuh_live_status": live_route["status"],
|
||||
}
|
||||
|
||||
return {
|
||||
"schema_version": "iwooos_wazuh_live_metadata_gate_readback_v1",
|
||||
"status": snapshot.get("status", "blocked_waiting_live_metadata_owner_response"),
|
||||
"mode": "committed_snapshot_readback_with_public_safe_wazuh_route_metadata",
|
||||
"source_refs": [f"docs/security/{_SNAPSHOT_FILE}", "GET /api/iwooos/wazuh"],
|
||||
"summary": merged_summary,
|
||||
"items": _items(merged_summary),
|
||||
"boundary_markers": _boundary_markers(merged_summary),
|
||||
"boundaries": {
|
||||
"runtime_execution_authorized": False,
|
||||
"secret_value_collection_allowed": False,
|
||||
"raw_wazuh_payload_storage_allowed": False,
|
||||
"wazuh_api_live_query_authorized": False,
|
||||
"wazuh_active_response_authorized": False,
|
||||
"host_write_authorized": False,
|
||||
"kali_active_scan_authorized": False,
|
||||
"k8s_secret_patch_authorized": False,
|
||||
"argocd_sync_authorized": False,
|
||||
"docker_restart_authorized": False,
|
||||
"nginx_gateway_workaround_authorized": False,
|
||||
"firewall_change_authorized": False,
|
||||
"not_authorization": True,
|
||||
},
|
||||
"no_false_green_rules": [
|
||||
"正式路由 200 不是即時查詢授權",
|
||||
"Wazuh 儀表板可見不是 manager registry 已驗收",
|
||||
"機密來源只能交付中繼資料,不得交付明文、片段或雜湊",
|
||||
"live metadata query、active response、host write 與 Kali active scan 是不同閘門",
|
||||
"owner response accepted、post-enable readback 與 runtime gate 仍全部維持 0",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _load_snapshot(directory: Path) -> dict[str, Any]:
|
||||
path = directory / _SNAPSHOT_FILE
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"{path}: Wazuh 即時中繼資料閘門快照不存在")
|
||||
with path.open(encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError(f"{path}: expected JSON object")
|
||||
if payload.get("schema_version") != _EXPECTED_SCHEMA:
|
||||
raise ValueError(f"{path}: expected schema_version={_EXPECTED_SCHEMA}")
|
||||
return payload
|
||||
|
||||
|
||||
def _summary(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
summary = payload.get("summary")
|
||||
return summary if isinstance(summary, dict) else {}
|
||||
|
||||
|
||||
def _int(value: Any) -> int:
|
||||
return value if isinstance(value, int) else 0
|
||||
|
||||
|
||||
def _live_route_summary(payload: dict[str, Any] | None, http_status: int) -> dict[str, Any]:
|
||||
if not isinstance(payload, dict):
|
||||
return {
|
||||
"status": "not_checked_by_snapshot_loader",
|
||||
"http_status": 0,
|
||||
"degraded_count": 1,
|
||||
"readonly_api_enabled_count": 0,
|
||||
"agent_total": 0,
|
||||
"metadata_available_count": 0,
|
||||
}
|
||||
summary = _summary(payload)
|
||||
status_text = str(payload.get("status") or "unknown")
|
||||
metadata_available = status_text == "readonly_metadata_available"
|
||||
return {
|
||||
"status": status_text,
|
||||
"http_status": http_status if isinstance(http_status, int) else 0,
|
||||
"degraded_count": 0 if metadata_available else 1,
|
||||
"readonly_api_enabled_count": _int(summary.get("readonly_api_enabled_count")),
|
||||
"agent_total": _int(summary.get("agent_total")),
|
||||
"metadata_available_count": 1 if metadata_available else 0,
|
||||
}
|
||||
|
||||
|
||||
def _items(summary: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
return [
|
||||
_item(
|
||||
"release_readback",
|
||||
"ENV-1",
|
||||
"route_readback_passed",
|
||||
"steady" if summary["production_route_readback_passed_count"] == 1 else "warn",
|
||||
{"route_readback": summary["production_route_readback_passed_count"]},
|
||||
),
|
||||
_item(
|
||||
"server_env_owner",
|
||||
"ENV-2",
|
||||
"waiting_owner_response",
|
||||
"locked",
|
||||
{
|
||||
"owner_received": summary["live_metadata_owner_response_received_count"],
|
||||
"owner_accepted": summary["live_metadata_owner_response_accepted_count"],
|
||||
},
|
||||
),
|
||||
_item(
|
||||
"secret_metadata",
|
||||
"ENV-3",
|
||||
"metadata_only_waiting_acceptance",
|
||||
"locked",
|
||||
{"secret_metadata_accepted": summary["secret_source_metadata_accepted_count"]},
|
||||
),
|
||||
_item(
|
||||
"manager_health",
|
||||
"ENV-4",
|
||||
"waiting_manager_health_ref",
|
||||
"warn",
|
||||
{
|
||||
"manager_health_accepted": summary["wazuh_manager_health_ref_accepted_count"],
|
||||
"live_route_degraded": summary["wazuh_live_route_degraded_count"],
|
||||
},
|
||||
),
|
||||
_item(
|
||||
"readonly_scope",
|
||||
"ENV-5",
|
||||
"waiting_readonly_scope_ref",
|
||||
"warn",
|
||||
{"readonly_scope_accepted": summary["readonly_account_scope_accepted_count"]},
|
||||
),
|
||||
_item(
|
||||
"post_enable_readback",
|
||||
"ENV-6",
|
||||
"waiting_post_enable_readback",
|
||||
"locked",
|
||||
{
|
||||
"post_enable_readback": summary["post_enable_readback_passed_count"],
|
||||
"live_query_authorized": summary["wazuh_api_live_query_authorized_count"],
|
||||
"runtime_gate": summary["runtime_gate_count"],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def _item(
|
||||
item_id: str,
|
||||
check: str,
|
||||
state_key: str,
|
||||
tone: str,
|
||||
metrics: dict[str, int],
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"item_id": item_id,
|
||||
"check": check,
|
||||
"state_key": state_key,
|
||||
"tone": tone,
|
||||
"metrics": metrics,
|
||||
}
|
||||
|
||||
|
||||
def _boundary_markers(summary: dict[str, Any]) -> list[str]:
|
||||
return [
|
||||
f"正式路由讀回={summary['production_route_readback_passed_count']}",
|
||||
f"負責人回覆接受={summary['live_metadata_owner_response_accepted_count']}",
|
||||
f"機密來源中繼資料接受={summary['secret_source_metadata_accepted_count']}",
|
||||
f"管理節點健康參照接受={summary['wazuh_manager_health_ref_accepted_count']}",
|
||||
f"唯讀帳號範圍接受={summary['readonly_account_scope_accepted_count']}",
|
||||
f"啟用後讀回={summary['post_enable_readback_passed_count']}",
|
||||
f"Wazuh 即時查詢授權={summary['wazuh_api_live_query_authorized_count']}",
|
||||
f"Wazuh 主動回應授權={summary['wazuh_active_response_authorized_count']}",
|
||||
f"主機寫入授權={summary['host_write_authorized_count']}",
|
||||
f"執行期閘門={summary['runtime_gate_count']}",
|
||||
"機密明文收集=false",
|
||||
"原始 Wazuh 載荷保存=false",
|
||||
"K8s secret 手動修改=false",
|
||||
"ArgoCD 手動同步=false",
|
||||
"Docker 重啟=false",
|
||||
"Nginx 或 gateway 繞路=false",
|
||||
"防火牆變更=false",
|
||||
"資安觀測節點主動掃描=false",
|
||||
"不是執行授權=true",
|
||||
]
|
||||
|
||||
|
||||
def _require_boundaries(payload: dict[str, Any]) -> None:
|
||||
summary = _summary(payload)
|
||||
for key in (
|
||||
"live_metadata_owner_response_accepted_count",
|
||||
"secret_source_metadata_accepted_count",
|
||||
"wazuh_manager_health_ref_accepted_count",
|
||||
"readonly_account_scope_accepted_count",
|
||||
"post_enable_readback_passed_count",
|
||||
"wazuh_api_live_query_authorized_count",
|
||||
"wazuh_active_response_authorized_count",
|
||||
"host_write_authorized_count",
|
||||
"runtime_gate_count",
|
||||
):
|
||||
if _int(summary.get(key)) != 0:
|
||||
raise ValueError(f"Wazuh 即時中繼資料閘門 summary.{key} 必須維持 0")
|
||||
|
||||
boundaries = payload.get("execution_boundaries")
|
||||
if not isinstance(boundaries, dict):
|
||||
raise ValueError("Wazuh 即時中繼資料閘門 execution_boundaries 缺失")
|
||||
for key in _REQUIRED_FALSE_BOUNDARIES:
|
||||
if boundaries.get(key) is not False:
|
||||
raise ValueError(f"Wazuh 即時中繼資料閘門 execution_boundaries.{key} 必須維持 false")
|
||||
if boundaries.get("not_authorization") is not True:
|
||||
raise ValueError("Wazuh 即時中繼資料閘門 not_authorization 必須維持 true")
|
||||
Reference in New Issue
Block a user