fix(security): route Wazuh to canonical executor
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m32s
CD Pipeline / build-and-deploy (push) Failing after 15m21s
CD Pipeline / post-deploy-checks (push) Has been skipped
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m32s
CD Pipeline / build-and-deploy (push) Failing after 15m21s
CD Pipeline / post-deploy-checks (push) Has been skipped
This commit is contained in:
@@ -389,7 +389,12 @@ def resolve_typed_alert_target(
|
|||||||
_text_token(target_resource)
|
_text_token(target_resource)
|
||||||
== "sentry-self-hosted-snuba-profiling-functions-consumer-1"
|
== "sentry-self-hosted-snuba-profiling-functions-consumer-1"
|
||||||
)
|
)
|
||||||
if not exact_sentry_consumer:
|
exact_registry_catalog_target = bool(
|
||||||
|
domain == "host_systemd"
|
||||||
|
and _text_token(target_resource)
|
||||||
|
== _text_token(str(identity.get("service_name") or ""))
|
||||||
|
)
|
||||||
|
if not (exact_sentry_consumer or exact_registry_catalog_target):
|
||||||
catalog_ids = []
|
catalog_ids = []
|
||||||
stateful_level = str(identity.get("stateful_level") or "")
|
stateful_level = str(identity.get("stateful_level") or "")
|
||||||
risk = (
|
risk = (
|
||||||
|
|||||||
@@ -181,3 +181,35 @@ def test_typed_target_preview_is_no_write_and_fail_closed() -> None:
|
|||||||
assert agent99_payload["route"]["agent99_bridge"][
|
assert agent99_payload["route"]["agent99_bridge"][
|
||||||
"dispatch_allowed"
|
"dispatch_allowed"
|
||||||
] is True
|
] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_wazuh_typed_target_preview_is_bounded_and_public_safe() -> None:
|
||||||
|
app = FastAPI()
|
||||||
|
app.include_router(router, prefix="/api/v1")
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
"/api/v1/agents/controlled-alert-target-preview",
|
||||||
|
json={
|
||||||
|
"alertname": "WazuhSecurityEvent",
|
||||||
|
"target_resource": "wazuh-manager",
|
||||||
|
"namespace": "awoooi",
|
||||||
|
"labels": {"service": "wazuh-manager", "tool": "wazuh"},
|
||||||
|
"alert_category": "security",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
payload = response.json()
|
||||||
|
route = payload["route"]
|
||||||
|
assert payload["preview_only"] is True
|
||||||
|
assert payload["dispatch_performed"] is False
|
||||||
|
assert payload["runtime_closure_verified"] is False
|
||||||
|
assert route["resolution_status"] == "resolved"
|
||||||
|
assert route["canonical_asset_id"] == "service:wazuh-manager"
|
||||||
|
assert route["host"] == "host:kali-readonly"
|
||||||
|
assert route["allowed_catalog_ids"] == [
|
||||||
|
"ansible:wazuh-manager-posture-readback"
|
||||||
|
]
|
||||||
|
assert route["allowed_inventory_hosts"] == ["host_112"]
|
||||||
|
assert route["cross_domain_fallback_allowed"] is False
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ PLAYBOOK = (
|
|||||||
)
|
)
|
||||||
SENTRY_CONTAINER = "sentry-self-hosted-snuba-profiling-functions-consumer-1"
|
SENTRY_CONTAINER = "sentry-self-hosted-snuba-profiling-functions-consumer-1"
|
||||||
SENTRY_CATALOG = "ansible:110-sentry-profiling-consumer-recovery"
|
SENTRY_CATALOG = "ansible:110-sentry-profiling-consumer-recovery"
|
||||||
|
WAZUH_CATALOG = "ansible:wazuh-manager-posture-readback"
|
||||||
|
|
||||||
|
|
||||||
def _sentry_incident() -> dict:
|
def _sentry_incident() -> dict:
|
||||||
@@ -110,6 +111,49 @@ def test_sentry_container_resolves_to_exact_host110_domain_route() -> None:
|
|||||||
assert route["reasoning_contract"]["paid_call_allowed"] is False
|
assert route["reasoning_contract"]["paid_call_allowed"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_wazuh_manager_exact_service_resolves_to_host112_posture_lane() -> None:
|
||||||
|
registry = ServiceRegistryClient(REGISTRY)
|
||||||
|
route = resolve_typed_alert_target(
|
||||||
|
alertname="WazuhSecurityEvent",
|
||||||
|
target_resource="wazuh-manager",
|
||||||
|
namespace="awoooi",
|
||||||
|
labels={"service": "wazuh-manager", "tool": "wazuh"},
|
||||||
|
alert_category="security",
|
||||||
|
registry=registry,
|
||||||
|
)
|
||||||
|
|
||||||
|
identity = registry.resolve_identity("wazuh-manager")
|
||||||
|
assert identity["resolution_status"] == "resolved"
|
||||||
|
assert identity["canonical_id"] == "service:wazuh-manager"
|
||||||
|
assert identity["allowed_catalog_ids"] == [WAZUH_CATALOG]
|
||||||
|
assert route["resolution_status"] == "resolved"
|
||||||
|
assert route["target_kind"] == "host_systemd"
|
||||||
|
assert route["canonical_asset_id"] == "service:wazuh-manager"
|
||||||
|
assert route["executor"] == "host_ansible_executor"
|
||||||
|
assert route["verifier"] == "iwooos_wazuh_controlled_executor_runtime"
|
||||||
|
assert route["allowed_catalog_ids"] == [WAZUH_CATALOG]
|
||||||
|
assert route["allowed_inventory_hosts"] == ["host_112"]
|
||||||
|
assert route["controlled_apply_allowed"] is True
|
||||||
|
assert route["critical_break_glass_required"] is False
|
||||||
|
assert route["cross_domain_fallback_allowed"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_wazuh_systemd_alias_cannot_inherit_execution_catalog() -> None:
|
||||||
|
route = resolve_typed_alert_target(
|
||||||
|
alertname="SystemdUnitUnavailable",
|
||||||
|
target_resource="wazuh-manager.service",
|
||||||
|
namespace="awoooi",
|
||||||
|
labels={},
|
||||||
|
alert_category="security",
|
||||||
|
registry=ServiceRegistryClient(REGISTRY),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert route["resolution_status"] == "resolved"
|
||||||
|
assert route["canonical_asset_id"] == "service:wazuh-manager"
|
||||||
|
assert route["allowed_catalog_ids"] == []
|
||||||
|
assert route["cross_domain_fallback_allowed"] is False
|
||||||
|
|
||||||
|
|
||||||
def test_sentry_incident_cannot_fall_to_host188_or_full_convergence() -> None:
|
def test_sentry_incident_cannot_fall_to_host188_or_full_convergence() -> None:
|
||||||
hints = _catalog_hints(_sentry_incident(), None)
|
hints = _catalog_hints(_sentry_incident(), None)
|
||||||
|
|
||||||
@@ -363,6 +407,48 @@ def test_sentry_candidate_reaches_bounded_check_mode_with_exact_scope() -> None:
|
|||||||
assert claim["controlled_apply_allowed"] is True
|
assert claim["controlled_apply_allowed"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_wazuh_incident_reaches_bounded_host112_check_mode() -> None:
|
||||||
|
class Incident:
|
||||||
|
incident_id = "INC-WAZUH-TYPED-ROUTE"
|
||||||
|
project_id = "awoooi"
|
||||||
|
alertname = "WazuhSecurityEvent"
|
||||||
|
alert_category = "security"
|
||||||
|
notification_type = "TYPE-2"
|
||||||
|
severity = None
|
||||||
|
affected_services = ["wazuh-manager"]
|
||||||
|
signals = [
|
||||||
|
{
|
||||||
|
"alert_name": "WazuhSecurityEvent",
|
||||||
|
"labels": {
|
||||||
|
"service": "wazuh-manager",
|
||||||
|
"tool": "wazuh",
|
||||||
|
"namespace": "awoooi",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
payload = build_ansible_decision_audit_payload(
|
||||||
|
incident=Incident(),
|
||||||
|
proposal_data={"source": "test", "risk_level": "medium"},
|
||||||
|
decision_path="repair_candidate_controlled_queue",
|
||||||
|
not_used_reason="test",
|
||||||
|
)
|
||||||
|
assert payload is not None
|
||||||
|
|
||||||
|
claim = build_ansible_check_mode_claim_input(
|
||||||
|
source_candidate_op_id="00000000-0000-0000-0000-000000112112",
|
||||||
|
candidate_input=payload["input"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert claim["catalog_id"] == WAZUH_CATALOG
|
||||||
|
assert claim["inventory_hosts"] == ["host_112"]
|
||||||
|
assert claim["typed_domain"] == "host_systemd"
|
||||||
|
assert claim["canonical_asset_id"] == "service:wazuh-manager"
|
||||||
|
assert claim["target_scope_validated"] is True
|
||||||
|
assert claim["cross_domain_fallback_allowed"] is False
|
||||||
|
assert claim["controlled_apply_allowed"] is True
|
||||||
|
|
||||||
|
|
||||||
def test_sentry_playbook_and_post_verifier_are_exact_container_bounded() -> None:
|
def test_sentry_playbook_and_post_verifier_are_exact_container_bounded() -> None:
|
||||||
payload = yaml.safe_load(PLAYBOOK.read_text(encoding="utf-8"))
|
payload = yaml.safe_load(PLAYBOOK.read_text(encoding="utf-8"))
|
||||||
source = PLAYBOOK.read_text(encoding="utf-8")
|
source = PLAYBOOK.read_text(encoding="utf-8")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ metadata:
|
|||||||
app: awoooi
|
app: awoooi
|
||||||
component: service-registry
|
component: service-registry
|
||||||
annotations:
|
annotations:
|
||||||
awoooi.wooo.work/source-sha256: "1662532ccf9c574e9853291a6f2881377c1a5b2519c4123ae05caf45be904121"
|
awoooi.wooo.work/source-sha256: "5548d19a6060900d51810cb3b1c3e1dc5f2fbb2b7c77df20227499cdb68599fc"
|
||||||
data:
|
data:
|
||||||
service-registry.yaml: |
|
service-registry.yaml: |
|
||||||
# ops/config/service-registry.yaml
|
# ops/config/service-registry.yaml
|
||||||
@@ -165,6 +165,20 @@ data:
|
|||||||
containers: ["alertmanager"]
|
containers: ["alertmanager"]
|
||||||
|
|
||||||
# ─── AUTO:無狀態,允許自動修復 ──────────────────────────────────────
|
# ─── AUTO:無狀態,允許自動修復 ──────────────────────────────────────
|
||||||
|
- name: wazuh-manager
|
||||||
|
canonical_id: "service:wazuh-manager"
|
||||||
|
display_name: "Wazuh Manager (host_112)"
|
||||||
|
host: "192.168.0.112"
|
||||||
|
stateful_level: AUTO
|
||||||
|
asset_domain: host_systemd
|
||||||
|
reason: "Only the bounded no-write posture catalog is enabled; active response and arbitrary service mutation remain unavailable"
|
||||||
|
executor: host_ansible_executor
|
||||||
|
verifier: iwooos_wazuh_controlled_executor_runtime
|
||||||
|
aliases: ["wazuh-manager.service"]
|
||||||
|
containers: []
|
||||||
|
allowed_catalog_ids:
|
||||||
|
- ansible:wazuh-manager-posture-readback
|
||||||
|
|
||||||
- name: nginx
|
- name: nginx
|
||||||
display_name: "Nginx (反向代理)"
|
display_name: "Nginx (反向代理)"
|
||||||
host: "192.168.0.110"
|
host: "192.168.0.110"
|
||||||
|
|||||||
@@ -151,6 +151,20 @@ services:
|
|||||||
containers: ["alertmanager"]
|
containers: ["alertmanager"]
|
||||||
|
|
||||||
# ─── AUTO:無狀態,允許自動修復 ──────────────────────────────────────
|
# ─── AUTO:無狀態,允許自動修復 ──────────────────────────────────────
|
||||||
|
- name: wazuh-manager
|
||||||
|
canonical_id: "service:wazuh-manager"
|
||||||
|
display_name: "Wazuh Manager (host_112)"
|
||||||
|
host: "192.168.0.112"
|
||||||
|
stateful_level: AUTO
|
||||||
|
asset_domain: host_systemd
|
||||||
|
reason: "Only the bounded no-write posture catalog is enabled; active response and arbitrary service mutation remain unavailable"
|
||||||
|
executor: host_ansible_executor
|
||||||
|
verifier: iwooos_wazuh_controlled_executor_runtime
|
||||||
|
aliases: ["wazuh-manager.service"]
|
||||||
|
containers: []
|
||||||
|
allowed_catalog_ids:
|
||||||
|
- ansible:wazuh-manager-posture-readback
|
||||||
|
|
||||||
- name: nginx
|
- name: nginx
|
||||||
display_name: "Nginx (反向代理)"
|
display_name: "Nginx (反向代理)"
|
||||||
host: "192.168.0.110"
|
host: "192.168.0.110"
|
||||||
|
|||||||
Reference in New Issue
Block a user