101 lines
4.2 KiB
Python
101 lines
4.2 KiB
Python
"""Offline guard for the ops-mason builder identity (MASON-IN-0006 choice (b)).
|
|
|
|
Parses the GitOps manifests and asserts the founder's 2026-10-22 boundary:
|
|
the five granted rules only, no forbidden resources, no escalate/bind/impersonate,
|
|
no wildcards, a User subject with no ServiceAccount, and a manual, pinned Application.
|
|
"""
|
|
from pathlib import Path
|
|
import re
|
|
|
|
import yaml
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
ADDON = ROOT / "argocd/platform-addons/ops-mason-builder"
|
|
APP = ROOT / "argocd/railiance01/applications/ops-mason-builder.application.yaml"
|
|
|
|
WRITE_VERBS = ["create", "get", "list", "watch", "update", "patch", "delete"]
|
|
READ_VERBS = ["get", "list", "watch"]
|
|
FORBIDDEN_RESOURCES = {
|
|
"secrets", "pods", "serviceaccounts/token", "configmaps", "roles",
|
|
"rolebindings", "clusterroles", "clusterrolebindings", "nodes",
|
|
"nodes/proxy", "pods/exec", "pods/log",
|
|
}
|
|
FORBIDDEN_VERBS = {"escalate", "bind", "impersonate"}
|
|
EXPECTED_RULES = [
|
|
{"apiGroups": [""], "resources": ["namespaces"], "verbs": WRITE_VERBS},
|
|
{"apiGroups": [""], "resources": ["serviceaccounts"], "verbs": WRITE_VERBS},
|
|
{"apiGroups": ["networking.k8s.io"], "resources": ["networkpolicies"],
|
|
"verbs": WRITE_VERBS},
|
|
{"apiGroups": [""], "resources": ["services"], "verbs": READ_VERBS},
|
|
{"apiGroups": ["authorization.k8s.io"],
|
|
"resources": ["selfsubjectaccessreviews", "selfsubjectrulesreviews"],
|
|
"verbs": ["create"]},
|
|
]
|
|
|
|
|
|
def _docs():
|
|
kust = yaml.safe_load((ADDON / "kustomization.yaml").read_text())
|
|
docs = []
|
|
for res in kust["resources"]:
|
|
docs.extend(d for d in yaml.safe_load_all((ADDON / res).read_text()) if d)
|
|
return docs
|
|
|
|
|
|
def _one(kind):
|
|
found = [d for d in _docs() if d["kind"] == kind]
|
|
assert len(found) == 1, kind
|
|
return found[0]
|
|
|
|
|
|
def test_only_expected_kinds_no_secret():
|
|
kinds = sorted(d["kind"] for d in _docs())
|
|
assert kinds == ["ClusterRole", "ClusterRoleBinding"]
|
|
for path in ADDON.iterdir():
|
|
if path.is_file():
|
|
assert "kind: Secret" not in path.read_text(), path
|
|
|
|
|
|
def test_cluster_role_exact_rules_no_wildcards():
|
|
role = _one("ClusterRole")
|
|
assert role["metadata"]["name"] == "ops-mason-builder"
|
|
assert "aggregationRule" not in role
|
|
assert role["rules"] == EXPECTED_RULES
|
|
granted = set()
|
|
for rule in role["rules"]:
|
|
values = [*rule["verbs"], *rule.get("apiGroups", []), *rule.get("resources", [])]
|
|
assert not any("*" in v for v in values), rule
|
|
assert FORBIDDEN_VERBS.isdisjoint(rule["verbs"]), rule
|
|
for resource in rule["resources"]:
|
|
assert resource not in FORBIDDEN_RESOURCES, resource
|
|
granted.add(resource)
|
|
assert granted == {
|
|
"namespaces", "serviceaccounts", "networkpolicies", "services",
|
|
"selfsubjectaccessreviews", "selfsubjectrulesreviews",
|
|
}
|
|
by_resource = {r["resources"][0]: r["verbs"] for r in role["rules"] if len(r["resources"]) == 1}
|
|
assert by_resource["namespaces"] == WRITE_VERBS
|
|
assert by_resource["serviceaccounts"] == WRITE_VERBS
|
|
assert by_resource["networkpolicies"] == WRITE_VERBS
|
|
assert by_resource["services"] == READ_VERBS
|
|
review = next(r for r in role["rules"] if "selfsubjectaccessreviews" in r["resources"])
|
|
assert review["verbs"] == ["create"]
|
|
assert review["resources"] == ["selfsubjectaccessreviews", "selfsubjectrulesreviews"]
|
|
|
|
|
|
def test_binding_targets_only_the_user():
|
|
binding = _one("ClusterRoleBinding")
|
|
assert binding["roleRef"]["name"] == "ops-mason-builder"
|
|
assert binding["roleRef"]["kind"] == "ClusterRole"
|
|
assert binding["subjects"] == [{"kind": "User", "name": "ops-mason-builder",
|
|
"apiGroup": "rbac.authorization.k8s.io"}]
|
|
assert all(s["kind"] != "ServiceAccount" for s in binding["subjects"])
|
|
|
|
|
|
def test_application_manual_and_pinned():
|
|
app = yaml.safe_load(APP.read_text())
|
|
spec = app["spec"]
|
|
assert spec["project"] == "railiance-platform-addons"
|
|
assert spec["source"]["path"] == "argocd/platform-addons/ops-mason-builder"
|
|
assert re.fullmatch(r"[0-9a-f]{40}", spec["source"]["targetRevision"])
|
|
assert "automated" not in spec.get("syncPolicy", {})
|
|
assert "finalizers" not in app["metadata"]
|