Operator Privilege Profile (least-privilege assessment)
Paths.
analysis-results/…andprogress-tracker/…in this skill are the default workspace layout. They resolve throughlocations.yamlin$TRAUST_CONFIG_HOME(docs/setup.md, Storage locations); substitute your configured roots.
Answers "does this operator run with the least privileges it requires?" in three fidelity tiers. The findings tree alone cannot answer this — it records privilege violations, not inventories; this skill builds the has vs. needs picture.
| Tier | Question | Source | Tool |
|---|---|---|---|
| 1 | What does the operator ask for? | manifests + CSV in the repo | build_priv_profile.py |
| 2 | What does it declare it needs? | +kubebuilder:rbac: markers vs shipped roles |
same (diff section) |
| 3 | What does it actually run with? | live cluster (pod openshift.io/scc, oc auth can-i --list) |
capture_runtime_privileges.py |
Tier 1+2 — static profile
python harnessing/3-audit/operator-priv-profile/scripts/build_priv_profile.py \
--repo-url <url> [--ref <ref>] --name <repo-name> \
--out-dir analysis-results/findings/<product>/<repo>/
Emits <repo>-priv-profile.{json,md} next to the repo's audit report:
- SCC requests — RBAC
useverbs onsecuritycontextconstraints(withresourceNames) plus any shipped SCC objects. No request recorded ⇒ OpenShift assignsrestricted-v2by default — say that, never imply "unknown". - securityContext — pod- and container-level for every workload (Deployment/StatefulSet/DaemonSet + CSV-embedded deployments), plus host{Network,PID,IPC} and hostPath volumes.
- Namespaces / install modes — Namespace objects, workload
namespaces, CSV
installModes, OperatorGroup targetNamespaces. - Complete RBAC enumeration — every Role/ClusterRole/CSV-permission rule with scope + source, and risk flags (wildcards, secrets access, RBAC write, escalate/bind/impersonate, pods/exec, nodes).
- Tier-2 diff — shipped rules with no
+kubebuilder:rbacmarker backing them (shipped_not_declared) are the least-privilege surplus candidates. Repos without markers get an honest "not derivable statically" note — do NOT infer need from silence.
Fleet rollup (after profiling a set):
python harnessing/3-audit/operator-priv-profile/scripts/build_priv_profile.py \
--rollup 'analysis-results/findings/*/*/*-priv-profile.json' \
--rollup-out progress-tracker/metrics/dashboards/operator-least-priv/
Emits four artifacts, one row per operator with one column per assessment
question (SCCs requested / securityContext posture / namespaces + install
modes / RBAC): priv-profile-rollup.md (paste-into-doc table),
priv-profile-rollup.json (full rows), priv-profile-rollup.csv
(spreadsheet export), and priv-profile-dashboard.html (KPI tiles + top-60
table). Exclude catalog aggregation trees (certified-operators,
community-operators, marketplace) from profiling — a catalog's thousands of
third-party bundles would read as one giant "operator" and distort the
ranking.
Tier 3 — runtime capture (gated; execute only when scheduled)
python harnessing/3-audit/operator-priv-profile/scripts/capture_runtime_privileges.py \
--namespace <ns> --name <repo-name> \
--out-dir analysis-results/findings/<product>/<repo>/ \
--i-am-authorized [--kubeconfig <path>]
Read-only (get/auth can-i only, audit trail embedded). Fail-closed:
refuses without --i-am-authorized, which asserts the cluster is
within the campaign's authorized scope (same discipline as
validate-findings). Captures per pod: the ACTUALLY-ASSIGNED SCC
annotation, container securityContexts as admitted, the namespace PSA
labels, and each ServiceAccount's effective permission list. The
deploy-operator / validate-operator-live lanes are the natural place to
run this right after install.
Interpretation rules
- Requested ≠ assigned. Tier 1 shows intent; only tier 3 shows the SCC actually applied. Keep the tiers labeled in every artifact.
- Surplus needs verification before filing.
shipped_not_declaredrules may be exercised by non-kubebuilder code paths (plain client-go, helpers); confirm with a call-site search before calling it excess. Confirmed surplus feeds/file-security-defector the audit report as an authorization finding. - Helm/Go-templated manifests are skipped by the parser — a profile from a chart-heavy repo is partial; say so rather than presenting it as complete.
- Profiles are inventory documents, not findings: place them beside the
audit reports in
analysis-results; only the rollup goes toprogress-tracker/metrics/dashboards/(rollups-only discipline — the convention is documented for every skill indocs/artifacts.md).
Integrations
/secure-code-auditruns the profiler as a conditional pre-scan on manifest-bearing repos (recordspriv-profileindeterministic_steps) — every audit/re-audit refreshes the fleet's profiles./secure-container-auditprofiles extracted operator bundle images (/manifeststree; tier-2 n/a — no Go source in images)./isolation-reviewconsumes existing profiles as privilege-dimension evidence (never re-derives RBAC by hand where a profile exists).
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
| Empty profile from an operator repo | manifests are helm-templated or generated at build time | note partial coverage; profile the bundle/CSV repo instead |
surplus = n/a fleet-wide |
non-kubebuilder codebase | tier 3 or manual call-site review is the needs-baseline |
| Runtime capture refuses to run | missing --i-am-authorized |
intentional — verify cluster authorization first |