fix(security): mount canonical registry in worker
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m31s
CD Pipeline / build-and-deploy (push) Successful in 16m46s
CD Pipeline / post-deploy-checks (push) Successful in 2m10s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m31s
CD Pipeline / build-and-deploy (push) Successful in 16m46s
CD Pipeline / post-deploy-checks (push) Successful in 2m10s
This commit is contained in:
@@ -842,14 +842,34 @@ async def enqueue_iwooos_wazuh_manager_posture_candidate_once(
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proposal_data = {
|
||||||
|
"source": _scheduler_source,
|
||||||
|
"risk_level": _risk_level,
|
||||||
|
"execution_priority": 0,
|
||||||
|
"action": _proposal_action,
|
||||||
|
}
|
||||||
|
if build_ansible_decision_audit_payload(
|
||||||
|
incident=incident,
|
||||||
|
proposal_data=proposal_data,
|
||||||
|
decision_path=_BACKFILL_DECISION_PATH,
|
||||||
|
not_used_reason=_reason,
|
||||||
|
) is None:
|
||||||
|
return {
|
||||||
|
"status": "blocked_canonical_asset_or_ansible_catalog_unresolved",
|
||||||
|
"queued": 0,
|
||||||
|
"existing": 1 if existing_row else 0,
|
||||||
|
"evidence_ready": 1,
|
||||||
|
"automation_run_id": automation_run_id,
|
||||||
|
"work_item_id": _work_item_id,
|
||||||
|
"retry_reason": retry_reason,
|
||||||
|
"active_blockers": [
|
||||||
|
"canonical_asset_or_ansible_catalog_unresolved"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
queued = await recorder(
|
queued = await recorder(
|
||||||
incident=incident,
|
incident=incident,
|
||||||
proposal_data={
|
proposal_data=proposal_data,
|
||||||
"source": _scheduler_source,
|
|
||||||
"risk_level": _risk_level,
|
|
||||||
"execution_priority": 0,
|
|
||||||
"action": _proposal_action,
|
|
||||||
},
|
|
||||||
decision_path=_BACKFILL_DECISION_PATH,
|
decision_path=_BACKFILL_DECISION_PATH,
|
||||||
not_used_reason=_reason,
|
not_used_reason=_reason,
|
||||||
automation_run_id=automation_run_id,
|
automation_run_id=automation_run_id,
|
||||||
@@ -863,9 +883,11 @@ async def enqueue_iwooos_wazuh_manager_posture_candidate_once(
|
|||||||
else "queued_for_controlled_executor"
|
else "queued_for_controlled_executor"
|
||||||
if queued
|
if queued
|
||||||
else "candidate_race_deduplicated"
|
else "candidate_race_deduplicated"
|
||||||
|
if existing_row
|
||||||
|
else "candidate_write_not_acknowledged"
|
||||||
),
|
),
|
||||||
"queued": 1 if queued else 0,
|
"queued": 1 if queued else 0,
|
||||||
"existing": 0 if queued else 1,
|
"existing": 1 if existing_row else 0,
|
||||||
"evidence_ready": 1 if evidence_ready else 0,
|
"evidence_ready": 1 if evidence_ready else 0,
|
||||||
"automation_run_id": automation_run_id,
|
"automation_run_id": automation_run_id,
|
||||||
"work_item_id": _work_item_id,
|
"work_item_id": _work_item_id,
|
||||||
@@ -878,7 +900,11 @@ async def enqueue_iwooos_wazuh_manager_posture_candidate_once(
|
|||||||
"controlled_apply_failed",
|
"controlled_apply_failed",
|
||||||
"post_apply_learning_incomplete",
|
"post_apply_learning_incomplete",
|
||||||
},
|
},
|
||||||
"active_blockers": [] if queued else ["candidate_write_not_acknowledged"],
|
"active_blockers": (
|
||||||
|
[]
|
||||||
|
if queued or existing_row
|
||||||
|
else ["candidate_write_not_acknowledged"]
|
||||||
|
),
|
||||||
}
|
}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ PLAYBOOK = (
|
|||||||
BROKER_DEPLOYMENT = (
|
BROKER_DEPLOYMENT = (
|
||||||
ROOT / "k8s" / "awoooi-prod" / "08-deployment-ansible-executor-broker.yaml"
|
ROOT / "k8s" / "awoooi-prod" / "08-deployment-ansible-executor-broker.yaml"
|
||||||
)
|
)
|
||||||
|
WORKER_DEPLOYMENT = ROOT / "k8s" / "awoooi-prod" / "08-deployment-worker.yaml"
|
||||||
CATALOG_ID = "ansible:wazuh-alertmanager-integration"
|
CATALOG_ID = "ansible:wazuh-alertmanager-integration"
|
||||||
|
|
||||||
|
|
||||||
@@ -325,6 +326,89 @@ async def test_wazuh_ingress_scheduler_records_one_high_risk_controlled_candidat
|
|||||||
"scheduled-wazuh-alert-ingress:2026071500"
|
"scheduled-wazuh-alert-ingress:2026071500"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
write_not_acknowledged = (
|
||||||
|
await job.enqueue_iwooos_wazuh_alertmanager_integration_candidate_once(
|
||||||
|
recorder=AsyncMock(return_value=False),
|
||||||
|
evidence_collector=AsyncMock(
|
||||||
|
return_value=MagicMock(snapshot_id="wazuh-ingress")
|
||||||
|
),
|
||||||
|
evidence_verifier=AsyncMock(return_value=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert write_not_acknowledged["status"] == "candidate_write_not_acknowledged"
|
||||||
|
assert write_not_acknowledged["existing"] == 0
|
||||||
|
assert write_not_acknowledged["active_blockers"] == [
|
||||||
|
"candidate_write_not_acknowledged"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_wazuh_ingress_scheduler_reports_unresolved_payload_before_recording(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
from src.jobs import awooop_ansible_candidate_backfill_job as job
|
||||||
|
|
||||||
|
class _Mappings:
|
||||||
|
def first(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
class _Result:
|
||||||
|
def mappings(self):
|
||||||
|
return _Mappings()
|
||||||
|
|
||||||
|
class _Db:
|
||||||
|
async def execute(self, _statement, _params):
|
||||||
|
return _Result()
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def fake_db_context(_project_id: str):
|
||||||
|
yield _Db()
|
||||||
|
|
||||||
|
recorder = AsyncMock(return_value=False)
|
||||||
|
monkeypatch.setattr(job, "get_db_context", fake_db_context)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
job.settings,
|
||||||
|
"ENABLE_IWOOOS_WAZUH_MANAGER_POSTURE_EXECUTOR",
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
job,
|
||||||
|
"build_ansible_decision_audit_payload",
|
||||||
|
MagicMock(return_value=None),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await job.enqueue_iwooos_wazuh_alertmanager_integration_candidate_once(
|
||||||
|
recorder=recorder,
|
||||||
|
evidence_collector=AsyncMock(return_value=MagicMock(snapshot_id="wazuh-ingress")),
|
||||||
|
evidence_verifier=AsyncMock(return_value=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["status"] == "blocked_canonical_asset_or_ansible_catalog_unresolved"
|
||||||
|
assert result["existing"] == 0
|
||||||
|
assert result["active_blockers"] == [
|
||||||
|
"canonical_asset_or_ansible_catalog_unresolved"
|
||||||
|
]
|
||||||
|
recorder.assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
|
def test_wazuh_ingress_scheduler_worker_mounts_canonical_service_registry() -> None:
|
||||||
|
deployment = yaml.safe_load(WORKER_DEPLOYMENT.read_text(encoding="utf-8"))
|
||||||
|
pod_spec = deployment["spec"]["template"]["spec"]
|
||||||
|
container = pod_spec["containers"][0]
|
||||||
|
|
||||||
|
mount = next(
|
||||||
|
item for item in container["volumeMounts"] if item["name"] == "service-registry"
|
||||||
|
)
|
||||||
|
volume = next(item for item in pod_spec["volumes"] if item["name"] == "service-registry")
|
||||||
|
|
||||||
|
assert mount == {
|
||||||
|
"name": "service-registry",
|
||||||
|
"mountPath": "/app/ops/config/service-registry.yaml",
|
||||||
|
"subPath": "service-registry.yaml",
|
||||||
|
"readOnly": True,
|
||||||
|
}
|
||||||
|
assert volume["configMap"]["name"] == "service-registry"
|
||||||
|
|
||||||
|
|
||||||
def test_wazuh_security_event_uses_secops_lifecycle() -> None:
|
def test_wazuh_security_event_uses_secops_lifecycle() -> None:
|
||||||
assert classify_alert_early(
|
assert classify_alert_early(
|
||||||
|
|||||||
@@ -220,6 +220,11 @@ spec:
|
|||||||
value: "180"
|
value: "180"
|
||||||
- name: AWOOOP_ANSIBLE_CHECK_MODE_STARTUP_SLEEP_SECONDS
|
- name: AWOOOP_ANSIBLE_CHECK_MODE_STARTUP_SLEEP_SECONDS
|
||||||
value: "30"
|
value: "30"
|
||||||
|
volumeMounts:
|
||||||
|
- name: service-registry
|
||||||
|
mountPath: /app/ops/config/service-registry.yaml
|
||||||
|
subPath: service-registry.yaml
|
||||||
|
readOnly: true
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: "100m"
|
cpu: "100m"
|
||||||
@@ -272,3 +277,7 @@ spec:
|
|||||||
matchLabels:
|
matchLabels:
|
||||||
app: awoooi-worker
|
app: awoooi-worker
|
||||||
topologyKey: kubernetes.io/hostname
|
topologyKey: kubernetes.io/hostname
|
||||||
|
volumes:
|
||||||
|
- name: service-registry
|
||||||
|
configMap:
|
||||||
|
name: service-registry
|
||||||
|
|||||||
Reference in New Issue
Block a user