feat: adopt security zones and explicit workload refs
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0291a-1e87-7151-9934-fcbfe3f65eb1
This commit is contained in:
tegwick 2026-08-22 15:36:37 +02:00
parent 12c637cbf2
commit 7ce58ae638
52 changed files with 1547 additions and 658 deletions

View file

@ -55,12 +55,79 @@ def _caring_descriptor(actor_type: str, resource_id: str) -> dict[str, Any]:
}
def build_registry(inventory: dict[str, Any]) -> dict[str, Any]:
def _resolved_by_workload(zone_resolutions: dict[str, Any] | None) -> dict[str, Any]:
records = (zone_resolutions or {}).get("records") or []
resolved: dict[str, Any] = {}
for record in records:
workload_id = str(record.get("workload_id") or "")
if not workload_id:
continue
if workload_id in resolved:
raise ValueError(f"duplicate security-zone resolution for {workload_id!r}")
resolved[workload_id] = record
return resolved
def _zone_attributes(
actor: str,
entry: dict[str, Any],
resolutions: dict[str, Any],
) -> dict[str, Any]:
subject = entry.get("zone_subject")
if not isinstance(subject, dict):
return {
"security_zone": "unknown",
"security_zone_admission": "unknown",
"security_zone_reason": "catalog_applicability_absent",
}
applicability = subject.get("applicability")
if applicability == "not-applicable":
reason = str(subject.get("reason") or "").strip()
if not reason:
raise ValueError(f"{actor}.zone_subject.reason is required")
return {
"security_zone": "unknown",
"security_zone_admission": "not-applicable",
"security_zone_reason": reason,
}
if applicability != "applicable":
raise ValueError(
f"{actor}.zone_subject.applicability must be applicable or not-applicable"
)
workload_id = str(subject.get("workload_id") or "").strip()
if not workload_id:
return {
"security_zone": "unknown",
"security_zone_admission": "unknown",
"security_zone_reason": "workload_reference_absent",
}
record = resolutions.get(workload_id)
if record is None:
return {
"workload_id": workload_id,
"security_zone": "unknown",
"security_zone_admission": "unknown",
"security_zone_reason": "workload_resolution_absent",
}
return {
"workload_id": workload_id,
"security_zone": str(record.get("effective_zone") or "unknown"),
"security_zone_declared": record.get("declared_zone"),
"security_zone_admission": str(record.get("admission") or "unknown"),
"security_zone_reason": str(record.get("admission_reason") or "unknown"),
"security_zone_revision": record.get("membership_revision"),
}
def build_registry(
inventory: dict[str, Any], zone_resolutions: dict[str, Any] | None = None
) -> dict[str, Any]:
actors: dict[str, Any] = inventory.get("actors") or {}
resources: list[dict[str, Any]] = []
subjects: list[dict[str, Any]] = []
groups: dict[str, list[str]] = {gid: [] for gid in GROUP_BY_TYPE.values()}
relationships: list[dict[str, Any]] = []
resolutions = _resolved_by_workload(zone_resolutions)
for name, entry in sorted(actors.items()):
actor_type = str(entry["type"])
@ -74,7 +141,6 @@ def build_registry(inventory: dict[str, Any]) -> dict[str, Any]:
"id": resource_id,
"type": "ssh-certificate",
"labels": ["ssh-signing", actor_type],
"trust_zone": "platform",
"owner": "team:platform-security",
"attributes": {
"actor_id": name,
@ -82,6 +148,7 @@ def build_registry(inventory: dict[str, Any]) -> dict[str, Any]:
"allowed_subjects": [name, f"iam:{name}"],
"allowed_principals": principals,
"max_ttl_hours": ttl_hours,
**_zone_attributes(name, entry, resolutions),
},
}
)
@ -156,8 +223,8 @@ def build_registry(inventory: dict[str, Any]) -> dict[str, Any]:
"caring_profiles": ["caring-0.4.0-rc2"],
"metadata": {
"flex_auth_contract": "protected-system-v0",
"ops_warden_policy_gate": "v2",
"policy_enabled_config": "policy.enabled",
"ops_warden_policy_gate": "security-zones-v0.1",
"security_zone_standard": "security-zones_v0.1",
"tenant": "tenant:platform",
},
}
@ -186,14 +253,24 @@ def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("inventory", type=Path, help="ops-warden inventory.yaml")
parser.add_argument("-o", "--output", type=Path, required=True)
parser.add_argument(
"--zone-resolutions",
type=Path,
help="zone-engine resolved-view JSON; absent references remain unknown",
)
args = parser.parse_args()
inventory = yaml.safe_load(args.inventory.read_text()) or {}
registry = build_registry(inventory)
zone_resolutions = (
json.loads(args.zone_resolutions.read_text())
if args.zone_resolutions is not None
else None
)
registry = build_registry(inventory, zone_resolutions)
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(registry, indent=2) + "\n")
print(f"Wrote {args.output} ({len(registry['subjects'])} actors)")
if __name__ == "__main__":
main()
main()

View file

@ -24,7 +24,6 @@ from __future__ import annotations
import argparse
import json
import os
import re
import subprocess
import sys

View file

@ -1,13 +1,13 @@
#!/usr/bin/env python3
"""Readiness gate for the flex-auth ``policy.enabled`` flip (WARDEN-WP-0031).
"""Readiness gate for the zone-aware flex-auth caller identity.
flex-auth deployed ``flex-auth-ops-warden`` (FLEX-WP-0016) in ``callerAuth.mode:
warn``: it authenticates the caller with a Kubernetes TokenReview and binds
``resource.system: ops-warden`` to ``system:serviceaccount:ops-warden:ops-warden``,
but a caller that sends no ``Authorization`` header only produces a
``caller authentication warning`` and is still served. That pin cannot move to
``enforce`` and therefore ``policy.enabled: true`` cannot be set until
ops-warden's calling side actually presents a token.
``enforce`` until ops-warden's calling side actually presents a token. The
former repo-wide ``policy.enabled`` switch is retired by WARDEN-WP-0032.
This script asserts the calling side *without* flipping anything:
@ -57,9 +57,7 @@ def run_checks(config_path: Optional[Path], url: Optional[str]) -> List[Check]:
return [("fail", "warden.yaml", str(e))]
policy = cfg.policy
checks.append(
("ok", "warden.yaml", f"loaded; policy.enabled={str(policy.enabled).lower()}")
)
checks.append(("ok", "warden.yaml", "loaded; security-zones_v0.1 profile"))
mode = policy.caller_auth.mode
if mode == "none":
@ -84,12 +82,12 @@ def run_checks(config_path: Optional[Path], url: Optional[str]) -> List[Check]:
)
target = url or policy.flex_auth_url
if url is None and not policy.enabled:
if target is None:
checks.append(
(
"skip",
"live /v1/check",
f"policy.enabled=false; pass --url to smoke {target} anyway",
"policy.flex_auth_url is absent; pass --url to run the live smoke",
)
)
return checks
@ -188,13 +186,13 @@ def main() -> int:
if failed:
print(
f"\nNOT READY — {len(failed)} check(s) failed. "
"Do not ask flex-auth to enforce, and do not set policy.enabled: true."
"Do not ask flex-auth to enforce caller authentication."
)
return 1
print(
"\nREADY — the calling side presents an identity. Next: tell flex-auth to set "
"callerAuth.mode: enforce on flex-auth-ops-warden, re-run this check, then set "
"policy.enabled: true with fail_closed: true."
"callerAuth.mode: enforce on flex-auth-ops-warden and re-run this check. "
"Zone-specific PEP failure modes already replace the retired global switches."
)
return 0

View file

@ -136,8 +136,13 @@ def main() -> int:
if args.check:
current = args.out.read_text() if args.out.exists() else ""
# generated_at always differs; compare everything else.
strip = lambda t: "\n".join(l for l in t.splitlines() if not l.startswith("generated_at:"))
if strip(current) != strip(content):
def strip_generated_at(text: str) -> str:
return "\n".join(
line for line in text.splitlines()
if not line.startswith("generated_at:")
)
if strip_generated_at(current) != strip_generated_at(content):
print(f"STALE: {args.out} does not match the catalog. Re-run without --check.")
return 1
print(f"fresh: {args.out} matches the catalog ({count} concrete paths)")

View file

@ -66,9 +66,8 @@ ca_key: $SMOKE_DIR/ca_key
state_dir: $SMOKE_DIR/state
inventory_path: $INVENTORY
policy:
enabled: true
flex_auth_url: http://$ADDR
fail_closed: true
zone_registry_path: $REGISTRY
tenant: tenant:platform
system: ops-warden
EOF
@ -106,9 +105,8 @@ vault:
inventory_path: $INVENTORY
state_dir: $SMOKE_DIR/state-vault
policy:
enabled: true
flex_auth_url: http://$ADDR
fail_closed: true
zone_registry_path: $REGISTRY
tenant: tenant:platform
system: ops-warden
EOF
@ -118,4 +116,4 @@ EOF
python3 -c "import json,sys; e=json.loads(sys.argv[1]); assert e.get('backend')=='vault' and e.get('policy_decision_id'); print('vault policy_decision_id:', e['policy_decision_id'])" "$VAULT_LINE"
fi
echo "OK — production registry policy gate smoke passed"
echo "OK — production registry policy gate smoke passed"

234
scripts/report_workload_join.py Executable file → Normal file
View file

@ -1,128 +1,156 @@
#!/usr/bin/env python3
"""Report the lane -> workload join, for the security-zone model (ZONE-WP-0001-T03).
"""Report explicit lane -> workload resolution for security-zones_v0.1.
A security zone's subject is the **workload**, not the lane — policy is about the
running thing and whoever answers for it. ops-warden's catalog is a credential
surface, so on its own it cannot name that subject. The other side of the join
lives in `rapp-*/declarations/rapp.yaml`, which declares `workload_identity`
along with `data_classification` and `criticality`; ops-warden's
`registry/policy/security-posture.yaml` then maps classification to a minimum
maturity via `dataclass_floor`.
The catalog owner declares whether each lane is workload-applicable. Managed
deployables use the exact Repo Manager v1 ``(rapp_id, name, deployable?)``
reference. Independently governed operational workloads use ``name`` plus an
owner declaration reference. Unknown and not-applicable are explicit results.
This script computes the join and reports its coverage. It asserts nothing it
cannot derive: a lane whose workload cannot be established is reported as
unmatched rather than guessed, because the unmatched set is the informative
output those are lanes existing for something that is not a declared workload.
Read-only. Touches no secret value and no live system.
Usage:
python scripts/report_workload_join.py [--rapp-root ~] [--json]
This script never parses a credential path, consults ``owner_repo`` as an
identity hint, or substitutes a repository name. It reads declarations only.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Any
_SRC = Path(__file__).resolve().parent.parent / "src"
if _SRC.is_dir() and str(_SRC) not in sys.path:
sys.path.insert(0, str(_SRC))
import yaml # noqa: E402
import yaml
def load_rapp_workloads(root: Path) -> Dict[str, Dict[str, Any]]:
"""workload name -> declaration, from every rapp-*/declarations/rapp.yaml."""
out: Dict[str, Dict[str, Any]] = {}
def load_rapp_workloads(root: Path) -> dict[tuple[str, str], dict[str, Any]]:
"""Exact Repo Manager v1 key -> authoritative declaration projection."""
out: dict[tuple[str, str], dict[str, Any]] = {}
for decl in sorted(root.glob("rapp-*/declarations/rapp.yaml")):
try:
data = yaml.safe_load(decl.read_text()) or {}
except yaml.YAMLError:
continue
identity = data.get("workload_identity") or {}
rapp_id = data.get("rapp_id")
name = identity.get("name")
if not name:
if not rapp_id or not name:
continue
out[str(name)] = {
"rapp_id": data.get("rapp_id"),
deployables = []
for member in (data.get("composition") or {}).get("member_repos") or []:
deployables.extend(str(value) for value in member.get("deployables") or [])
out[(str(rapp_id), str(name))] = {
"source": str(decl),
"deployables": sorted(set(deployables)),
"data_classification": data.get("data_classification"),
"criticality": data.get("criticality"),
"readiness_state": data.get("readiness_state"),
"bound_reefs": data.get("bound_reefs"),
}
return out
def candidate_workloads(path_template: Optional[str]) -> List[str]:
"""Workload names a path could plausibly name — never a single guess.
The convention is `platform/workloads/<domain>/<workload>/<bundle>`, but it
is not applied consistently: `platform/workloads/rapp-qonto/keycape-client`
has one level fewer, so position alone cannot say which segment is the
workload. Both are offered and matched against declarations.
"""
if not path_template or "<" in path_template:
return []
parts = [p for p in path_template.split("/") if p]
if len(parts) >= 4 and parts[1] == "workloads":
return [parts[3], parts[2]]
if parts and parts[0] == "tenants" and len(parts) >= 3:
return [parts[2], parts[1]]
return []
def _direct_declaration_path(
declaration_ref: str, *, catalog_path: Path, estate_root: Path
) -> Path:
ref = Path(declaration_ref)
if ref.is_absolute():
return ref
local = catalog_path.resolve().parents[2] / ref
return local if local.exists() else estate_root / ref
def build(catalog_path: Path, rapp_root: Path) -> Dict[str, Any]:
entries = (yaml.safe_load(catalog_path.read_text()) or {}).get("entries", [])
declared = load_rapp_workloads(rapp_root)
floor = (
yaml.safe_load(
(catalog_path.parent.parent / "policy" / "security-posture.yaml").read_text()
def _resolve_direct(
ref: dict[str, Any], *, catalog_path: Path, estate_root: Path
) -> tuple[dict[str, Any] | None, str | None]:
source = _direct_declaration_path(
str(ref["declaration_ref"]), catalog_path=catalog_path, estate_root=estate_root
)
if not source.exists():
return None, f"declaration not found: {source}"
try:
declaration = yaml.safe_load(source.read_text()) or {}
except yaml.YAMLError as exc:
return None, f"invalid declaration YAML: {exc}"
identity = declaration.get("workload_identity") or {}
if identity.get("name") != ref.get("name"):
return None, (
f"declared workload_identity.name={identity.get('name')!r}, "
f"expected {ref.get('name')!r}"
)
or {}
).get("dataclass_floor", {})
context = (declaration.get("zones") or {}).get("context", {})
return {
"source": str(source),
"data_classification": context.get("data_classification"),
"criticality": context.get("criticality"),
"maturity": context.get("maturity"),
"declared_zone": (declaration.get("zones") or {}).get("membership"),
}, None
matched, unmatched, no_path = [], [], []
def build(catalog_path: Path, estate_root: Path) -> dict[str, Any]:
entries = (yaml.safe_load(catalog_path.read_text()) or {}).get("entries", [])
managed = load_rapp_workloads(estate_root)
posture_path = catalog_path.parent.parent / "policy" / "security-posture.yaml"
floor = (yaml.safe_load(posture_path.read_text()) or {}).get("dataclass_floor", {})
resolved: list[dict[str, Any]] = []
unknown: list[dict[str, Any]] = []
not_applicable: list[dict[str, Any]] = []
for entry in entries:
lane = entry.get("id")
cands = candidate_workloads(entry.get("path_template"))
if not cands:
no_path.append({"lane": lane, "risk": entry.get("risk")})
lane = str(entry.get("id"))
ref = entry.get("workload_ref") or {}
applicability = ref.get("applicability")
if applicability == "not-applicable":
not_applicable.append({"lane": lane, "reason": ref.get("reason")})
continue
hit = next((c for c in cands if c in declared), None)
if hit is None:
unmatched.append(
{"lane": lane, "risk": entry.get("risk"), "candidates": cands}
if applicability != "applicable":
unknown.append({"lane": lane, "reason": "applicability missing or invalid"})
continue
if ref.get("unknown_reason"):
unknown.append({"lane": lane, "reason": ref["unknown_reason"]})
continue
projection: dict[str, Any] | None
error: str | None = None
if ref.get("rapp_id"):
key = (str(ref.get("rapp_id")), str(ref.get("name")))
projection = managed.get(key)
if projection is None:
error = f"Repo Manager reference does not resolve: {key[0]}/{key[1]}"
elif ref.get("deployable") and ref["deployable"] not in projection["deployables"]:
error = f"deployable {ref['deployable']!r} is not declared by {key[0]}/{key[1]}"
else:
projection, error = _resolve_direct(
ref, catalog_path=catalog_path, estate_root=estate_root
)
if error or projection is None:
unknown.append({"lane": lane, "reason": error or "reference unresolved"})
continue
decl = declared[hit]
cls = decl.get("data_classification")
matched.append(
classification = projection.get("data_classification")
resolved.append(
{
"lane": lane,
"workload": hit,
"risk": entry.get("risk"),
"data_classification": cls,
"criticality": decl.get("criticality"),
"min_maturity": floor.get(cls),
"unmapped_classification": bool(cls) and cls not in floor,
"workload_ref": ref,
"source": projection.get("source"),
"data_classification": classification,
"criticality": projection.get("criticality"),
"maturity": projection.get("maturity") or floor.get(classification),
"declared_zone": projection.get("declared_zone"),
"unmapped_classification": bool(classification) and classification not in floor,
}
)
return {
"declared_workloads": declared,
"matched": matched,
"unmatched": unmatched,
"no_path": no_path,
"dataclass_floor": floor,
"contract": "helixforge.workload-reference/v1",
"resolved": resolved,
"unknown": unknown,
"not_applicable": not_applicable,
"ok": len(resolved) + len(unknown) + len(not_applicable) == len(entries),
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--rapp-root", type=Path, default=Path.home())
parser.add_argument(
"--estate-root", "--rapp-root", dest="estate_root", type=Path, default=Path.home()
)
parser.add_argument("--json", action="store_true")
parser.add_argument(
"--catalog",
@ -133,40 +161,24 @@ def main() -> int:
/ "catalog.yaml",
)
args = parser.parse_args()
report = build(args.catalog, Path(os.path.expanduser(str(args.rapp_root))))
report = build(args.catalog, args.estate_root)
if args.json:
print(json.dumps(report, indent=2, sort_keys=True))
return 0
return 0 if report["ok"] else 1
print("lane -> workload join\n")
print(f" declared workloads (rapp-*): {len(report['declared_workloads'])}")
print(f" lanes matched to a workload: {len(report['matched'])}")
print(f" lanes unmatched: {len(report['unmatched'])}")
print(f" lanes with no usable path: {len(report['no_path'])}\n")
if report["matched"]:
print("MATCHED")
for m in report["matched"]:
flag = " <-- classification unmapped by dataclass_floor" if m[
"unmapped_classification"
] else ""
print(
f" {m['lane']:34} -> {str(m['workload']):16} "
f"{str(m['data_classification']):13} crit={str(m['criticality']):9} "
f"min={str(m['min_maturity']):4} risk={m['risk']}{flag}"
)
if report["unmatched"]:
print("\nUNMATCHED — a lane exists for something no rapp declares")
for u in report["unmatched"]:
print(
f" {u['lane']:34} risk={str(u['risk']):9} "
f"candidates={', '.join(u['candidates'])}"
)
if report["no_path"]:
print("\nNO USABLE PATH — not a KV lane, or a pattern rather than an address")
print(" " + ", ".join(n["lane"] for n in report["no_path"]))
return 0
print("explicit lane -> workload resolution\n")
print(f" resolved: {len(report['resolved'])}")
print(f" unknown: {len(report['unknown'])}")
print(f" not-applicable: {len(report['not_applicable'])}\n")
for row in report["resolved"]:
ref = row["workload_ref"]
prefix = f"{ref.get('rapp_id')}/" if ref.get("rapp_id") else ""
print(f"RESOLVED {row['lane']:34} -> {prefix}{ref.get('name')}")
for row in report["unknown"]:
print(f"UNKNOWN {row['lane']:34} {row['reason']}")
for row in report["not_applicable"]:
print(f"NOT-APPLICABLE {row['lane']:34} {row['reason']}")
return 0 if report["ok"] else 1
if __name__ == "__main__":