feat(iwooos): 強化主機服務事故回補 gate
This commit is contained in:
@@ -156,8 +156,8 @@ CONTROL_STATUS_BY_CATEGORY = {
|
||||
"next_owner_action": "補 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse / Telegram owner、live drift evidence、reload window、receiver owner、route smoke plan、noise budget、rollback owner 與 no-secret-value evidence。",
|
||||
},
|
||||
"docker_compose_systemd_host_config": {
|
||||
"coverage_status": "owner_response_acceptance_ledger_ready_needs_live_owner_evidence",
|
||||
"coverage_percent": 54,
|
||||
"coverage_status": "incident_recovery_backfill_ready_needs_live_owner_evidence",
|
||||
"coverage_percent": 58,
|
||||
"evidence_refs": [
|
||||
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
|
||||
"docs/security/HOST-SERVICE-CONFIG-INVENTORY.md",
|
||||
@@ -168,8 +168,8 @@ CONTROL_STATUS_BY_CATEGORY = {
|
||||
"docs/security/host-service-owner-response-acceptance.snapshot.json",
|
||||
"docs/security/DEV-HOSTS-112-111-168-OBSERVE-ONLY-MAPPING.md",
|
||||
],
|
||||
"current_gap": "已固定 9 份 Docker / systemd / host service owner response acceptance candidate;仍缺 owner response、110 / 188 live hash、maintenance / restart window、rollback owner、post-check plan、disable switch 與 no-secret-value evidence。",
|
||||
"next_owner_action": "補 owner-provided live hash / disposition、compose / systemd owner、maintenance / restart window、rollback owner、post-check plan 與 disable switch。",
|
||||
"current_gap": "已固定 9 份 Docker / systemd / host service owner response acceptance candidate,並加入事故恢復、依賴圖、port binding、cold-start sequence、source-of-truth 與 daemon / runner 競爭回補欄位;仍缺 owner response、110 / 188 live hash、maintenance / restart window、rollback owner、post-check plan、disable switch 與 no-secret-value evidence。",
|
||||
"next_owner_action": "補 owner-provided live hash / disposition、compose / systemd owner、maintenance / restart window、rollback owner、post-check plan、disable switch、source-of-truth、服務依賴圖、port binding、cold-start sequence、incident recovery evidence 與 daemon / runner contention review。",
|
||||
},
|
||||
"ssh_firewall_network_access": {
|
||||
"coverage_status": "incident_change_evidence_acceptance_ready_needs_network_owner_evidence",
|
||||
@@ -384,6 +384,7 @@ def build_report(root: Path, generated_at: str | None) -> dict[str, Any]:
|
||||
"policy_ready_needs_drift_evidence",
|
||||
"inventory_needed",
|
||||
"repo_only_inventory_ready_needs_live_owner_evidence",
|
||||
"incident_recovery_backfill_ready_needs_live_owner_evidence",
|
||||
"policy_ready_needs_network_matrix",
|
||||
"policy_ready_needs_dry_run_pack",
|
||||
}
|
||||
|
||||
@@ -48,11 +48,26 @@ ACCEPTANCE_FIELDS = [
|
||||
"rollback_owner",
|
||||
"post_check_plan",
|
||||
"disable_switch",
|
||||
"config_source_of_truth_ref",
|
||||
"service_dependency_map_ref",
|
||||
"port_binding_inventory_ref",
|
||||
"cold_start_sequence_ref",
|
||||
"incident_recovery_evidence_ref",
|
||||
"daemon_runner_contention_ref",
|
||||
"reviewer_outcome",
|
||||
"followup_owner",
|
||||
"not_approval",
|
||||
]
|
||||
|
||||
INCIDENT_OWNER_RESPONSE_FIELDS = [
|
||||
"config_source_of_truth_ref",
|
||||
"service_dependency_map_ref",
|
||||
"port_binding_inventory_ref",
|
||||
"cold_start_sequence_ref",
|
||||
"incident_recovery_evidence_ref",
|
||||
"daemon_runner_contention_ref",
|
||||
]
|
||||
|
||||
REVIEWER_CHECKS = [
|
||||
{"check_id": "owner_identity_present", "instruction": "owner role / team 必須可追溯。"},
|
||||
{"check_id": "decision_reason_present", "instruction": "decision 與 decision reason 必須同時存在。"},
|
||||
@@ -65,6 +80,13 @@ REVIEWER_CHECKS = [
|
||||
{"check_id": "rollback_owner_present", "instruction": "rollback owner、rollback ref 或 disable path 必須存在。"},
|
||||
{"check_id": "post_check_plan_present", "instruction": "post-check 必須列服務健康、route、queue、log 與 rollback 停止條件。"},
|
||||
{"check_id": "disable_switch_present", "instruction": "repair-bot、Ansible role 或 service config 需有 disable switch 或 freeze rule。"},
|
||||
{"check_id": "config_source_of_truth_present", "instruction": "必須提供 repo source、live source、runner source 與 backup source 的真相來源 ref;不得只用口頭描述。"},
|
||||
{"check_id": "service_dependency_map_present", "instruction": "必須提供服務依賴 ref,涵蓋上游、下游、資料庫、queue、registry、AI provider 與 public route 影響。"},
|
||||
{"check_id": "port_binding_inventory_present", "instruction": "必須提供 port binding / exposure inventory ref,避免 host port、container port、proxy 與防火牆狀態彼此漂移。"},
|
||||
{"check_id": "cold_start_sequence_present", "instruction": "必須提供 cold-start / recovery sequence ref,明確列出 Docker daemon、compose stack、systemd unit、runner 與 post-check 順序。"},
|
||||
{"check_id": "incident_recovery_evidence_present", "instruction": "若回覆涉及服務異常、重啟或端口事故,必須提供恢復時間、服務健康、route health 與 operator notice ref。"},
|
||||
{"check_id": "daemon_runner_contention_reviewed", "instruction": "必須說明 Docker daemon、iptables / xtables、runner、repair-bot、backup job 或 compose action 是否可能互相競爭。"},
|
||||
{"check_id": "silent_restart_not_accepted", "instruction": "不得接受沒有 actor、原因、依賴圖、port inventory、回滾與 post-check 的靜默 restart / reload。"},
|
||||
{"check_id": "write_capable_requires_extra_review", "instruction": "write-capable surface 必須進額外 reviewer review,不得直接 accepted。"},
|
||||
{"check_id": "no_runtime_request", "instruction": "夾帶 SSH、Docker、systemctl、repair-bot、Ansible、sudo 或 host write 要求時拒收。"},
|
||||
{"check_id": "counts_transition_safe", "instruction": "只有 reviewer record 可更新 received / accepted / rejected;不得同時開 runtime gate。"},
|
||||
@@ -75,6 +97,7 @@ OUTCOME_LANES = [
|
||||
{"lane_id": "quarantine_secret_or_raw_payload", "meaning": "收到 secret、env dump、raw compose、raw systemd unit 或未脫敏 host config 時隔離。"},
|
||||
{"lane_id": "reject_execution_request", "meaning": "夾帶 SSH、docker compose、systemctl、repair-bot、Ansible、sudo 或 host write 要求時拒收。"},
|
||||
{"lane_id": "request_supplement", "meaning": "欄位不足、scope 不清、live hash ref / rollback / post-check 缺失時要求補件。"},
|
||||
{"lane_id": "incident_recovery_backfill_required", "meaning": "涉及服務異常、靜默重啟、端口事故或 cold-start recovery 時,必須進事故回補,不得直接 accepted。"},
|
||||
{"lane_id": "ready_for_host_service_review", "meaning": "metadata 合格後,只能進 host service reviewer review。"},
|
||||
{"lane_id": "owner_review_only_update", "meaning": "只允許更新只讀 owner review ledger,不得改 compose、systemd、repair-bot 或 Ansible。"},
|
||||
{"lane_id": "waiting_runtime_gate", "meaning": "即使 owner response accepted,runtime gate 仍等待獨立人工批准。"},
|
||||
@@ -99,6 +122,13 @@ BLOCKED_ACTIONS = [
|
||||
"raw_live_config_storage",
|
||||
"restart_without_window",
|
||||
"rollback_without_owner",
|
||||
"accept_silent_restart",
|
||||
"treat_service_healthy_as_config_accepted",
|
||||
"skip_config_source_of_truth_review",
|
||||
"skip_service_dependency_map",
|
||||
"skip_port_binding_review",
|
||||
"skip_cold_start_sequence",
|
||||
"hide_daemon_runner_contention",
|
||||
"runtime_gate_open",
|
||||
"add_action_button",
|
||||
]
|
||||
@@ -151,10 +181,16 @@ def acceptance_candidate(request: dict[str, Any]) -> dict[str, Any]:
|
||||
"rollback_owner": "pending_owner_response",
|
||||
"post_check_plan": "pending_owner_response",
|
||||
"disable_switch": "pending_owner_response",
|
||||
"config_source_of_truth_ref": None,
|
||||
"service_dependency_map_ref": None,
|
||||
"port_binding_inventory_ref": None,
|
||||
"cold_start_sequence_ref": None,
|
||||
"incident_recovery_evidence_ref": None,
|
||||
"daemon_runner_contention_ref": None,
|
||||
"reviewer_outcome": "waiting_owner_response",
|
||||
"followup_owner": "pending_owner_response",
|
||||
"acceptance_fields": ACCEPTANCE_FIELDS,
|
||||
"required_owner_fields": request["required_owner_fields"],
|
||||
"required_owner_fields": [*request["required_owner_fields"], *INCIDENT_OWNER_RESPONSE_FIELDS],
|
||||
"reviewer_checks": [item["check_id"] for item in REVIEWER_CHECKS],
|
||||
"outcome_lanes": [item["lane_id"] for item in OUTCOME_LANES],
|
||||
"blocked_actions": BLOCKED_ACTIONS,
|
||||
@@ -173,6 +209,12 @@ def acceptance_candidate(request: dict[str, Any]) -> dict[str, Any]:
|
||||
"rollback_owner_accepted": False,
|
||||
"post_check_plan_accepted": False,
|
||||
"disable_switch_accepted": False,
|
||||
"config_source_of_truth_accepted": False,
|
||||
"service_dependency_map_accepted": False,
|
||||
"port_binding_inventory_accepted": False,
|
||||
"cold_start_sequence_accepted": False,
|
||||
"incident_recovery_evidence_accepted": False,
|
||||
"daemon_runner_contention_accepted": False,
|
||||
"host_write_authorized": False,
|
||||
"ssh_read_authorized": False,
|
||||
"ssh_write_authorized": False,
|
||||
@@ -216,7 +258,7 @@ def build_report(
|
||||
"write_capable_acceptance_candidate_count": len(write_capable),
|
||||
"live_evidence_required_candidate_count": len(live_evidence),
|
||||
"acceptance_field_count": len(ACCEPTANCE_FIELDS),
|
||||
"required_owner_field_count": len(requests[0]["required_owner_fields"]) if requests else 0,
|
||||
"required_owner_field_count": len(acceptance_candidates[0]["required_owner_fields"]) if acceptance_candidates else 0,
|
||||
"reviewer_check_count": len(REVIEWER_CHECKS),
|
||||
"outcome_lane_count": len(OUTCOME_LANES),
|
||||
"blocked_action_count": len(BLOCKED_ACTIONS),
|
||||
@@ -234,6 +276,12 @@ def build_report(
|
||||
"rollback_owner_accepted_count": 0,
|
||||
"post_check_plan_accepted_count": 0,
|
||||
"disable_switch_accepted_count": 0,
|
||||
"config_source_of_truth_accepted_count": 0,
|
||||
"service_dependency_map_accepted_count": 0,
|
||||
"port_binding_inventory_accepted_count": 0,
|
||||
"cold_start_sequence_accepted_count": 0,
|
||||
"incident_recovery_evidence_accepted_count": 0,
|
||||
"daemon_runner_contention_accepted_count": 0,
|
||||
"host_write_authorized_count": 0,
|
||||
"ssh_read_authorized_count": 0,
|
||||
"ssh_write_authorized_count": 0,
|
||||
@@ -272,9 +320,9 @@ def build_report(
|
||||
"blocked_actions": BLOCKED_ACTIONS,
|
||||
"acceptance_candidates": acceptance_candidates,
|
||||
"next_steps": [
|
||||
"等待 owner 以脫敏 metadata ref 回覆 live config hash、maintenance / restart window、rollback owner、post-check plan 與 disable switch。",
|
||||
"等待 owner 以脫敏 metadata ref 回覆 live config hash、maintenance / restart window、rollback owner、post-check plan、disable switch、source of truth、依賴圖、port binding、cold-start sequence、incident recovery evidence 與 daemon / runner contention review。",
|
||||
"收到回覆後先做欄位完整性、敏感 payload 隔離與 execution request 拒收,不得直接 host read、restart、repair-bot 或 Ansible apply。",
|
||||
"write-capable surface 必須額外 reviewer review,且 runtime gate 需獨立人工批准、rollback 與 post-check 成立。",
|
||||
"write-capable 或事故回補 surface 必須額外 reviewer review,且 runtime gate 需獨立人工批准、rollback、dependency / port / cold-start 與 post-check 成立。",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ EXPECTED_CATEGORIES = {
|
||||
"backup_restore_credential": 62,
|
||||
"agent_bounty_protocol_runtime": 68,
|
||||
"monitoring_alerting_observability": 66,
|
||||
"docker_compose_systemd_host_config": 54,
|
||||
"docker_compose_systemd_host_config": 58,
|
||||
"ssh_firewall_network_access": 62,
|
||||
"ai_provider_model_routing": 60,
|
||||
"product_surface_runtime_routes": 72,
|
||||
@@ -171,13 +171,17 @@ ARTIFACT_SPECS = [
|
||||
"status": "owner_response_acceptance_ledger_ready_no_runtime_action",
|
||||
"list_counts": {
|
||||
"acceptance_candidates": 9,
|
||||
"blocked_actions": 20,
|
||||
"reviewer_checks": 14,
|
||||
"outcome_lanes": 7,
|
||||
"blocked_actions": 27,
|
||||
"reviewer_checks": 21,
|
||||
"outcome_lanes": 8,
|
||||
},
|
||||
"summary_counts": {
|
||||
"acceptance_candidate_count": 9,
|
||||
"write_capable_acceptance_candidate_count": 3,
|
||||
"required_owner_field_count": 18,
|
||||
"reviewer_check_count": 21,
|
||||
"outcome_lane_count": 8,
|
||||
"blocked_action_count": 27,
|
||||
"owner_response_received_count": 0,
|
||||
"owner_response_accepted_count": 0,
|
||||
"runtime_gate_count": 0,
|
||||
|
||||
@@ -2719,7 +2719,7 @@ def validate(root: Path) -> None:
|
||||
assert_equal(
|
||||
"high_value_config_coverage.summary.needs_live_evidence_count",
|
||||
high_value_config_coverage["summary"]["needs_live_evidence_count"],
|
||||
9,
|
||||
10,
|
||||
)
|
||||
for key in [
|
||||
"owner_response_received_count",
|
||||
@@ -3135,12 +3135,12 @@ def validate(root: Path) -> None:
|
||||
assert_equal(
|
||||
"high_value_config_coverage.coverage_categories.docker.coverage_percent",
|
||||
docker_systemd_category["coverage_percent"],
|
||||
54,
|
||||
58,
|
||||
)
|
||||
assert_equal(
|
||||
"high_value_config_coverage.coverage_categories.docker.coverage_status",
|
||||
docker_systemd_category["coverage_status"],
|
||||
"owner_response_acceptance_ledger_ready_needs_live_owner_evidence",
|
||||
"incident_recovery_backfill_ready_needs_live_owner_evidence",
|
||||
)
|
||||
for evidence_ref in [
|
||||
"docs/security/HOST-SERVICE-CONFIG-INVENTORY.md",
|
||||
@@ -3474,11 +3474,11 @@ def validate(root: Path) -> None:
|
||||
"acceptance_candidate_count": 9,
|
||||
"write_capable_acceptance_candidate_count": 3,
|
||||
"live_evidence_required_candidate_count": 8,
|
||||
"acceptance_field_count": 28,
|
||||
"required_owner_field_count": 12,
|
||||
"reviewer_check_count": 14,
|
||||
"outcome_lane_count": 7,
|
||||
"blocked_action_count": 20,
|
||||
"acceptance_field_count": 34,
|
||||
"required_owner_field_count": 18,
|
||||
"reviewer_check_count": 21,
|
||||
"outcome_lane_count": 8,
|
||||
"blocked_action_count": 27,
|
||||
"request_sent_count": 0,
|
||||
"recipient_confirmed_count": 0,
|
||||
"owner_response_received_count": 0,
|
||||
@@ -3493,6 +3493,12 @@ def validate(root: Path) -> None:
|
||||
"rollback_owner_accepted_count": 0,
|
||||
"post_check_plan_accepted_count": 0,
|
||||
"disable_switch_accepted_count": 0,
|
||||
"config_source_of_truth_accepted_count": 0,
|
||||
"service_dependency_map_accepted_count": 0,
|
||||
"port_binding_inventory_accepted_count": 0,
|
||||
"cold_start_sequence_accepted_count": 0,
|
||||
"incident_recovery_evidence_accepted_count": 0,
|
||||
"daemon_runner_contention_accepted_count": 0,
|
||||
"host_write_authorized_count": 0,
|
||||
"ssh_read_authorized_count": 0,
|
||||
"ssh_write_authorized_count": 0,
|
||||
@@ -3551,6 +3557,13 @@ def validate(root: Path) -> None:
|
||||
"rollback_owner_present",
|
||||
"post_check_plan_present",
|
||||
"disable_switch_present",
|
||||
"config_source_of_truth_present",
|
||||
"service_dependency_map_present",
|
||||
"port_binding_inventory_present",
|
||||
"cold_start_sequence_present",
|
||||
"incident_recovery_evidence_present",
|
||||
"daemon_runner_contention_reviewed",
|
||||
"silent_restart_not_accepted",
|
||||
"write_capable_requires_extra_review",
|
||||
"no_runtime_request",
|
||||
"counts_transition_safe",
|
||||
@@ -3565,6 +3578,7 @@ def validate(root: Path) -> None:
|
||||
"quarantine_secret_or_raw_payload",
|
||||
"reject_execution_request",
|
||||
"request_supplement",
|
||||
"incident_recovery_backfill_required",
|
||||
"ready_for_host_service_review",
|
||||
"owner_review_only_update",
|
||||
"waiting_runtime_gate",
|
||||
@@ -3593,6 +3607,13 @@ def validate(root: Path) -> None:
|
||||
"raw_live_config_storage",
|
||||
"restart_without_window",
|
||||
"rollback_without_owner",
|
||||
"accept_silent_restart",
|
||||
"treat_service_healthy_as_config_accepted",
|
||||
"skip_config_source_of_truth_review",
|
||||
"skip_service_dependency_map",
|
||||
"skip_port_binding_review",
|
||||
"skip_cold_start_sequence",
|
||||
"hide_daemon_runner_contention",
|
||||
"runtime_gate_open",
|
||||
"add_action_button",
|
||||
]
|
||||
@@ -3605,27 +3626,27 @@ def validate(root: Path) -> None:
|
||||
assert_equal(
|
||||
f"host_service_owner_response_acceptance.{item['acceptance_candidate_id']}.acceptance_fields",
|
||||
len(item["acceptance_fields"]),
|
||||
28,
|
||||
34,
|
||||
)
|
||||
assert_equal(
|
||||
f"host_service_owner_response_acceptance.{item['acceptance_candidate_id']}.required_owner_fields",
|
||||
len(item["required_owner_fields"]),
|
||||
12,
|
||||
18,
|
||||
)
|
||||
assert_equal(
|
||||
f"host_service_owner_response_acceptance.{item['acceptance_candidate_id']}.reviewer_checks",
|
||||
len(item["reviewer_checks"]),
|
||||
14,
|
||||
21,
|
||||
)
|
||||
assert_equal(
|
||||
f"host_service_owner_response_acceptance.{item['acceptance_candidate_id']}.outcome_lanes",
|
||||
len(item["outcome_lanes"]),
|
||||
7,
|
||||
8,
|
||||
)
|
||||
assert_equal(
|
||||
f"host_service_owner_response_acceptance.{item['acceptance_candidate_id']}.blocked_actions",
|
||||
len(item["blocked_actions"]),
|
||||
20,
|
||||
27,
|
||||
)
|
||||
assert_true(
|
||||
f"host_service_owner_response_acceptance.{item['acceptance_candidate_id']}.not_approval",
|
||||
@@ -3646,6 +3667,12 @@ def validate(root: Path) -> None:
|
||||
"rollback_owner_accepted",
|
||||
"post_check_plan_accepted",
|
||||
"disable_switch_accepted",
|
||||
"config_source_of_truth_accepted",
|
||||
"service_dependency_map_accepted",
|
||||
"port_binding_inventory_accepted",
|
||||
"cold_start_sequence_accepted",
|
||||
"incident_recovery_evidence_accepted",
|
||||
"daemon_runner_contention_accepted",
|
||||
"host_write_authorized",
|
||||
"ssh_read_authorized",
|
||||
"ssh_write_authorized",
|
||||
@@ -6325,8 +6352,8 @@ def validate(root: Path) -> None:
|
||||
"high_value_config_control_coverage_category_count": 14,
|
||||
"high_value_config_control_coverage_c0_category_count": 8,
|
||||
"high_value_config_control_coverage_c1_category_count": 4,
|
||||
"high_value_config_control_coverage_average_percent": 68,
|
||||
"high_value_config_control_coverage_needs_live_evidence_count": 9,
|
||||
"high_value_config_control_coverage_average_percent": 69,
|
||||
"high_value_config_control_coverage_needs_live_evidence_count": 10,
|
||||
"high_value_config_control_coverage_owner_response_required_count": 14,
|
||||
"high_value_config_control_coverage_owner_response_received_count": 0,
|
||||
"high_value_config_control_coverage_owner_response_accepted_count": 0,
|
||||
@@ -16212,7 +16239,7 @@ def validate(root: Path) -> None:
|
||||
assert_text_contains(
|
||||
"iwooos_page.high_value_config_control_coverage_docker_systemd_percent",
|
||||
iwooos_projection_page,
|
||||
"{ key: 'dockerSystemd', rank: 'P1-1', value: '54%'",
|
||||
"{ key: 'dockerSystemd', rank: 'P1-1', value: '58%'",
|
||||
)
|
||||
assert_text_contains(
|
||||
"iwooos_page.high_value_config_control_coverage_ssh_network_percent",
|
||||
@@ -16242,7 +16269,7 @@ def validate(root: Path) -> None:
|
||||
"high_value_config_control_coverage_c0_category_count=8",
|
||||
"high_value_config_control_coverage_c1_category_count=4",
|
||||
"high_value_config_control_coverage_average_percent=69",
|
||||
"high_value_config_control_coverage_needs_live_evidence_count=9",
|
||||
"high_value_config_control_coverage_needs_live_evidence_count=10",
|
||||
"high_value_config_control_coverage_owner_response_required_count=14",
|
||||
"high_value_config_control_coverage_owner_response_received_count=0",
|
||||
"high_value_config_control_coverage_owner_response_accepted_count=0",
|
||||
@@ -16326,7 +16353,10 @@ def validate(root: Path) -> None:
|
||||
"host_service_config_inventory_runtime_gate_count=0",
|
||||
"host_service_owner_response_acceptance_candidate_count=9",
|
||||
"host_service_owner_response_acceptance_write_capable_candidate_count=3",
|
||||
"host_service_owner_response_acceptance_reviewer_check_count=14",
|
||||
"host_service_owner_response_acceptance_required_owner_field_count=18",
|
||||
"host_service_owner_response_acceptance_reviewer_check_count=21",
|
||||
"host_service_owner_response_acceptance_outcome_lane_count=8",
|
||||
"host_service_owner_response_acceptance_blocked_action_count=27",
|
||||
"host_service_owner_response_acceptance_runtime_gate_count=0",
|
||||
"ssh_network_access_inventory_surface_count=16",
|
||||
"ssh_network_access_inventory_write_capable_surface_count=6",
|
||||
|
||||
Reference in New Issue
Block a user