Files
awoooi/scripts/security/wazuh-iwooos-intrusion-readback-plan.py
Your Name ac9ee65c3a
All checks were successful
Code Review / ai-code-review (push) Successful in 12s
CD Pipeline / tests (push) Successful in 1m34s
CD Pipeline / build-and-deploy (push) Successful in 5m16s
CD Pipeline / post-deploy-checks (push) Successful in 1m38s
feat(iwooos): 接入 Wazuh 入侵回讀 gate
2026-06-18 09:20:25 +08:00

449 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
IwoooS Wazuh / 主機入侵 read-only 接入計畫產生器。
本工具只產生 repo 內 snapshot將已建置的 Wazuh 平台、host-110 /
host-188 入侵跡象、外部 Agent 高風險操作宣稱與後續 containment /
recovery proof 轉成 IwoooS 可收件、可拒收、可補件的脫敏欄位。
它不連 Wazuh API、不讀主機、不 SSH、不啟動 Wazuh active response、
不隔離主機、不封鎖端口、不重啟服務、不收 secret / private key /
runner token / webhook secret也不把外部 Agent 的口頭宣稱視為已修復。
"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any
TAIPEI = timezone(timedelta(hours=8))
READBACK_FIELDS = [
"readback_candidate_id",
"scope_alias",
"label",
"control_tier",
"priority",
"source_kind",
"wazuh_platform_reported_ready",
"requires_owner_evidence",
"requires_wazuh_event_refs",
"requires_host_forensics_refs",
"requires_cross_project_sync",
"wazuh_manager_health_ref",
"wazuh_agent_status_ref",
"wazuh_alert_rule_refs",
"wazuh_event_refs",
"host_auth_log_refs",
"process_tree_refs",
"network_connection_refs",
"file_integrity_refs",
"package_inventory_refs",
"persistence_mechanism_refs",
"runner_or_gateway_config_diff_refs",
"actor_attribution_ref",
"incident_time_window_ref",
"affected_scope",
"impact_summary_ref",
"containment_decision",
"containment_reason_ref",
"recovery_proof_refs",
"postcheck_readback_ref",
"recurrence_guard_ref",
"cross_project_sync_ref",
"rollback_owner",
"followup_owner",
"reviewer_outcome",
"not_authorization",
]
REQUIRED_READBACK_FIELDS = [
"wazuh_manager_health_ref",
"wazuh_agent_status_ref",
"wazuh_event_refs",
"host_auth_log_refs",
"process_tree_refs",
"network_connection_refs",
"file_integrity_refs",
"package_inventory_refs",
"persistence_mechanism_refs",
"actor_attribution_ref",
"incident_time_window_ref",
"affected_scope",
"impact_summary_ref",
"containment_decision",
"containment_reason_ref",
"recovery_proof_refs",
"postcheck_readback_ref",
"recurrence_guard_ref",
"cross_project_sync_ref",
"rollback_owner",
"followup_owner",
"redacted_evidence_refs",
"no_secret_value_attestation",
"no_raw_log_attestation",
"no_raw_wazuh_payload_attestation",
"no_private_key_or_runner_token_attestation",
"no_false_green_attestation",
"external_agent_claim_not_accepted_attestation",
"active_response_not_enabled_attestation",
"independent_reviewer_attestation",
]
REVIEWER_CHECKS = [
{"check_id": "wazuh_platform_ready_is_metadata_only", "instruction": "Wazuh 已建置只能當作平台存在訊號,不能直接視為 IwoooS 已完成事件接入。"},
{"check_id": "manager_health_ref_present", "instruction": "必須有脫敏 manager / indexer / dashboard / API health ref。"},
{"check_id": "agent_status_ref_present", "instruction": "host-110、host-188 或其他受影響 host 必須有 agent 狀態 ref。"},
{"check_id": "event_refs_present", "instruction": "必須有 Wazuh event / alert refs不能只寫已發現或已阻擋。"},
{"check_id": "host_forensics_refs_present", "instruction": "需要 auth log、process tree、network connection、file integrity、package inventory 或 persistence refs。"},
{"check_id": "actor_attribution_present", "instruction": "需要 actor role / team / tool attribution外部 Agent 操作不能匿名通過。"},
{"check_id": "time_window_present", "instruction": "需要 incident / operation / recovery time window供事件與變更對齊。"},
{"check_id": "affected_scope_present", "instruction": "需列 host alias、服務 alias、產品 alias 與受影響配置類別,不顯示原始內部命名空間。"},
{"check_id": "containment_decision_present", "instruction": "需有 containment decision 與理由;未決定時維持 waiting_containment_decision。"},
{"check_id": "external_agent_claim_not_trusted", "instruction": "外部 Agent 宣稱已清除、已修復、已阻擋或已 Zero-Trust 不能直接 accepted。"},
{"check_id": "runner_gateway_change_reviewed", "instruction": "runner label、runner config、gateway、firewall、workflow 或 secret 相關事件需串到既有高價值配置 gate。"},
{"check_id": "secret_material_absent", "instruction": "不得收 private key、password、runner token、webhook secret、cookie、env dump、hash 或 partial token。"},
{"check_id": "raw_payload_absent", "instruction": "不得保存 raw Wazuh payload、raw syslog、raw journal、完整截圖或未脫敏 command output。"},
{"check_id": "hardcoded_wazuh_credential_rejected", "instruction": "硬編碼 Wazuh URL、使用者、密碼或關閉 TLS 驗證的程式碼必須拒收或隔離。"},
{"check_id": "readonly_api_disabled_by_default", "instruction": "IwoooS Wazuh API code path 必須預設關閉,沒有 owner gate 與 server-side env 時不得查詢。"},
{"check_id": "host_write_not_opened", "instruction": "不得因 Wazuh 告警就自動 SSH、kill process、封鎖端口、restart 或修改主機。"},
{"check_id": "active_response_not_opened", "instruction": "Wazuh active response、firewall drop、isolation 或 remediation 必須另有 break-glass 與人工批准。"},
{"check_id": "cross_project_sync_present", "instruction": "若事件影響 AWOOOI、AwoooP、IwoooS、任務媒合、股票研究、公開網站或監控需有同步 ref。"},
{"check_id": "recovery_proof_present", "instruction": "需要 recovery proof refs服務 route 200 或 dashboard 可見不能單獨當恢復證據。"},
{"check_id": "postcheck_independent", "instruction": "postcheck 必須獨立於操作人與外部 Agent。"},
{"check_id": "recurrence_guard_present", "instruction": "需要防再發 guard例如 credential hygiene、runner config control、firewall diff control、Wazuh rule coverage。"},
{"check_id": "owner_response_not_auto_accepted", "instruction": "owner response received / accepted count 只能由 reviewer record 推進。"},
{"check_id": "no_false_green", "instruction": "Wazuh online、agent active、route 200、CD success、UI 可見都不能單獨當事件驗收。"},
{"check_id": "runtime_stays_zero", "instruction": "readback plan 不得打開 active response、host write、workflow change、secret rotation 或 action button。"},
]
OUTCOME_LANES = [
{"lane_id": "waiting_wazuh_owner_evidence", "meaning": "Wazuh 已建置但尚未收到 IwoooS 可接受的脫敏事件 refs。"},
{"lane_id": "request_manager_health_supplement", "meaning": "缺 Wazuh manager / indexer / dashboard / API health ref 時要求補件。"},
{"lane_id": "request_agent_status_supplement", "meaning": "缺 host agent 狀態、agent id 對照或 last seen ref 時要求補件。"},
{"lane_id": "request_host_forensics_supplement", "meaning": "缺 auth / process / network / FIM / package / persistence refs 時要求補件。"},
{"lane_id": "request_actor_or_time_supplement", "meaning": "缺 actor attribution、工具來源或事件時間窗時要求補件。"},
{"lane_id": "request_containment_decision", "meaning": "缺 containment decision、理由、owner 或 rollback owner 時要求補件。"},
{"lane_id": "quarantine_secret_or_raw_payload", "meaning": "收到 secret、private key、runner token、raw payload、raw log 或未脫敏截圖時隔離。"},
{"lane_id": "reject_external_agent_claim_only", "meaning": "只有外部 Agent 口頭宣稱已修復、已阻擋或已清除時拒收。"},
{"lane_id": "route_to_high_value_config_gate", "meaning": "涉及 runner、workflow、secret、gateway、firewall、systemd、Docker 或 K8s 時串到高價值配置 gate。"},
{"lane_id": "ready_for_intrusion_reviewer_review", "meaning": "metadata 合格後只能進 reviewer review不自動執行。"},
{"lane_id": "recurrence_guard_backfill_required", "meaning": "需補 Wazuh rule coverage、runner/gateway/secret guard、credential hygiene 或 change freeze。"},
{"lane_id": "waiting_runtime_gate", "meaning": "即使事件 refs acceptedactive response 與 host containment 仍需獨立人工批准。"},
]
BLOCKED_ACTIONS = [
"wazuh_active_response_enable",
"wazuh_agent_install",
"wazuh_agent_restart",
"wazuh_manager_restart",
"wazuh_rule_change",
"wazuh_decoder_change",
"wazuh_api_live_query_without_owner_gate",
"hardcode_wazuh_url",
"hardcode_wazuh_username",
"hardcode_wazuh_password",
"disable_tls_verification",
"store_raw_wazuh_payload",
"store_raw_syslog",
"store_raw_journal",
"store_raw_command_output",
"store_unredacted_screenshot",
"collect_password",
"collect_private_key",
"collect_runner_token",
"collect_webhook_secret",
"collect_cookie_or_session",
"collect_env_dump",
"ssh_read",
"ssh_write",
"sudo_action",
"host_file_write",
"kill_process",
"quarantine_host",
"firewall_drop",
"port_close",
"port_open",
"docker_restart",
"docker_kill",
"systemctl_restart",
"nginx_reload",
"workflow_modification",
"runner_label_change",
"runner_config_change",
"repo_secret_change",
"secret_rotation",
"active_scan",
"production_write",
"mark_malware_removed_without_evidence",
"mark_rce_fixed_without_evidence",
"accept_external_agent_claim_without_wazuh_refs",
"accept_route_200_as_recovery",
"accept_agent_active_as_incident_closed",
"open_runtime_gate",
"add_action_button",
]
READBACK_CANDIDATES = [
{
"readback_candidate_id": "wazuh_intrusion_readback:wazuh_control_plane",
"scope_alias": "wazuh-control-plane",
"label": "Wazuh manager / indexer / dashboard / API",
"control_tier": "C0",
"priority": "P0",
"source_kind": "wazuh_platform",
"requires_wazuh_event_refs": False,
"requires_host_forensics_refs": False,
"requires_cross_project_sync": True,
},
{
"readback_candidate_id": "wazuh_intrusion_readback:host110_intrusion_signal",
"scope_alias": "host-110",
"label": "host-110 入侵 / gateway / runner / service 異常訊號",
"control_tier": "C0",
"priority": "P0",
"source_kind": "host_intrusion",
"requires_wazuh_event_refs": True,
"requires_host_forensics_refs": True,
"requires_cross_project_sync": True,
},
{
"readback_candidate_id": "wazuh_intrusion_readback:host188_intrusion_signal",
"scope_alias": "host-188",
"label": "host-188 入侵 / 惡意程式 / RCE 宣稱回讀",
"control_tier": "C0",
"priority": "P0",
"source_kind": "host_intrusion",
"requires_wazuh_event_refs": True,
"requires_host_forensics_refs": True,
"requires_cross_project_sync": True,
},
{
"readback_candidate_id": "wazuh_intrusion_readback:external_agent_live_change_claim",
"scope_alias": "external-agent-handoff",
"label": "外部 Agent live 變更與修復宣稱驗證",
"control_tier": "C0",
"priority": "P0",
"source_kind": "external_agent_claim",
"requires_wazuh_event_refs": True,
"requires_host_forensics_refs": True,
"requires_cross_project_sync": True,
},
{
"readback_candidate_id": "wazuh_intrusion_readback:runner_secret_gateway_control",
"scope_alias": "runner-secret-gateway",
"label": "Runner / secret / gateway 高價值配置連動",
"control_tier": "C0",
"priority": "P0",
"source_kind": "high_value_config_link",
"requires_wazuh_event_refs": True,
"requires_host_forensics_refs": True,
"requires_cross_project_sync": True,
},
{
"readback_candidate_id": "wazuh_intrusion_readback:containment_recovery_postcheck",
"scope_alias": "containment-recovery",
"label": "Containment / recovery proof / postcheck",
"control_tier": "C0",
"priority": "P0",
"source_kind": "incident_response",
"requires_wazuh_event_refs": True,
"requires_host_forensics_refs": True,
"requires_cross_project_sync": True,
},
]
def git_short_sha(root: Path) -> str:
try:
result = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
cwd=root,
check=True,
capture_output=True,
text=True,
)
return result.stdout.strip()
except Exception:
return "unknown"
def build_candidate(source: dict[str, Any]) -> dict[str, Any]:
return {
**source,
"status": "waiting_wazuh_owner_evidence",
"wazuh_platform_reported_ready": True,
"requires_owner_evidence": True,
"wazuh_manager_health_ref": None,
"wazuh_agent_status_ref": None,
"wazuh_alert_rule_refs": [],
"wazuh_event_refs": [],
"host_auth_log_refs": [],
"process_tree_refs": [],
"network_connection_refs": [],
"file_integrity_refs": [],
"package_inventory_refs": [],
"persistence_mechanism_refs": [],
"runner_or_gateway_config_diff_refs": [],
"actor_attribution_ref": None,
"incident_time_window_ref": None,
"affected_scope": "pending_redacted_scope",
"impact_summary_ref": None,
"containment_decision": "waiting_containment_decision",
"containment_reason_ref": None,
"recovery_proof_refs": [],
"postcheck_readback_ref": None,
"recurrence_guard_ref": None,
"cross_project_sync_ref": None,
"rollback_owner": "pending_owner_response",
"followup_owner": "pending_owner_response",
"reviewer_outcome": "waiting_wazuh_intrusion_review",
"not_approval": True,
"runtime_gate": False,
"action_buttons_allowed": False,
"active_response_authorized": False,
"host_write_authorized": False,
"secret_value_collection_allowed": False,
"blocked_actions": BLOCKED_ACTIONS,
"readback_fields": READBACK_FIELDS,
"required_readback_fields": REQUIRED_READBACK_FIELDS,
"reviewer_checks": [item["check_id"] for item in REVIEWER_CHECKS],
"outcome_lanes": [item["lane_id"] for item in OUTCOME_LANES],
}
def build_report(root: Path, generated_at: str | None) -> dict[str, Any]:
report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
candidates = [build_candidate(item) for item in READBACK_CANDIDATES]
return {
"schema_version": "wazuh_iwooos_intrusion_readback_plan_v1",
"generated_at": report_time,
"git_commit": git_short_sha(root),
"status": "wazuh_intrusion_readback_plan_ready_no_runtime_action",
"mode": "metadata_only_no_wazuh_api_no_host_read_no_active_response",
"source_context": {
"wazuh_platform_reported_ready": True,
"iwooos_runtime_ingestion_accepted": False,
"affected_host_aliases": ["host-110", "host-188"],
"external_agent_transcript_published_to_frontend": False,
"raw_secret_or_private_key_collection_allowed": False,
},
"summary": {
"wazuh_platform_reported_count": 1,
"readback_candidate_count": len(candidates),
"c0_readback_candidate_count": sum(1 for item in candidates if item["control_tier"] == "C0"),
"affected_host_alias_count": 2,
"wazuh_event_required_candidate_count": sum(1 for item in candidates if item["requires_wazuh_event_refs"]),
"host_forensics_required_candidate_count": sum(1 for item in candidates if item["requires_host_forensics_refs"]),
"cross_project_sync_required_candidate_count": sum(1 for item in candidates if item["requires_cross_project_sync"]),
"readback_field_count": len(READBACK_FIELDS),
"required_readback_field_count": len(REQUIRED_READBACK_FIELDS),
"reviewer_check_count": len(REVIEWER_CHECKS),
"outcome_lane_count": len(OUTCOME_LANES),
"blocked_action_count": len(BLOCKED_ACTIONS),
"wazuh_manager_health_ref_received_count": 0,
"wazuh_agent_status_ref_received_count": 0,
"wazuh_event_ref_received_count": 0,
"host_forensics_ref_received_count": 0,
"containment_decision_received_count": 0,
"recovery_proof_received_count": 0,
"postcheck_readback_received_count": 0,
"recurrence_guard_received_count": 0,
"cross_project_sync_received_count": 0,
"owner_response_received_count": 0,
"owner_response_accepted_count": 0,
"wazuh_event_accepted_count": 0,
"host_forensics_accepted_count": 0,
"containment_decision_accepted_count": 0,
"recovery_proof_accepted_count": 0,
"postcheck_readback_accepted_count": 0,
"recurrence_guard_accepted_count": 0,
"readonly_api_code_path_present_count": 1,
"readonly_api_enabled_count": 0,
"active_response_authorized_count": 0,
"host_write_authorized_count": 0,
"secret_value_collection_allowed_count": 0,
"runtime_gate_count": 0,
"action_button_count": 0,
"coverage_percent_after_readback_plan": 72,
},
"execution_boundaries": {
"runtime_execution_authorized": False,
"wazuh_api_live_query_authorized": False,
"wazuh_active_response_authorized": False,
"wazuh_rule_change_authorized": False,
"wazuh_agent_install_authorized": False,
"host_read_authorized": False,
"host_write_authorized": False,
"ssh_read_authorized": False,
"ssh_write_authorized": False,
"sudo_action_authorized": False,
"firewall_change_authorized": False,
"process_kill_authorized": False,
"host_isolation_authorized": False,
"runner_change_authorized": False,
"workflow_modification_authorized": False,
"repo_secret_change_authorized": False,
"secret_value_collection_allowed": False,
"raw_wazuh_payload_storage_allowed": False,
"raw_log_storage_allowed": False,
"private_key_collection_allowed": False,
"active_scan_authorized": False,
"production_write_authorized": False,
"frontend_raw_transcript_display_allowed": False,
"action_buttons_allowed": False,
"not_authorization": True,
},
"readback_candidates": candidates,
"reviewer_checks": REVIEWER_CHECKS,
"outcome_lanes": OUTCOME_LANES,
"blocked_actions": BLOCKED_ACTIONS,
"operator_interpretation": [
"Wazuh 已建置是偵測平台存在訊號,不代表 IwoooS 已驗收 110 / 188 入侵事件。",
"IwoooS 只能接收脫敏 refs、hash、ticket、artifact pointer 與 owner response不收 secret、private key、runner token 或 raw payload。",
"外部 Agent 對木馬、RCE、Zero-Trust、清除或封鎖的宣稱必須用 Wazuh event refs 與 host forensic refs 交叉驗證。",
"active response、host containment、firewall drop、restart、runner change、secret rotation 與 production write 仍全部需要獨立人工批准。",
],
}
def main() -> int:
parser = argparse.ArgumentParser(description="IwoooS Wazuh / 主機入侵 read-only 接入計畫產生器")
parser.add_argument("--root", default=".", help="repo root")
parser.add_argument("--output", help="寫出 JSON 報告")
parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用")
args = parser.parse_args()
root = Path(args.root).resolve()
report = build_report(root, args.generated_at)
payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)
if args.output:
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(payload + "\n", encoding="utf-8")
else:
print(payload)
summary = report["summary"]
print(
"WAZUH_IWOOOS_INTRUSION_READBACK_PLAN_OK "
f"candidates={summary['readback_candidate_count']} "
f"hosts={summary['affected_host_alias_count']} "
f"checks={summary['reviewer_check_count']} "
f"blocked={summary['blocked_action_count']} "
f"runtime_gate={summary['runtime_gate_count']}",
file=sys.stderr,
)
return 0
if __name__ == "__main__":
sys.exit(main())