Pods get their secrets from Conjur so none holds a static credential. Then a second check hunts the RBAC permissions that make perfect secrets management pointless. Both halves, because doing one without the other is theatre.
| Domains | CyberArk/Idira, Linux, Kubernetes |
| Built on | cyberark/conjur + helm chart, cyberark/KubiScan |
| Cost | $0 (local kind/minikube). Runtime ~4 hours |
| Status | Run against a real kind cluster. The linter silently missed every planted role on live kubectl output; fixed, 15 findings now (output in findings/). KubiScan cross-check still pending |
You can do secrets perfectly, with Conjur brokering every credential and nothing static in a pod, and still be wide open. A service account that can escalate, bind, or create pods can mint itself a token that reads everything. Secrets injection and RBAC are two halves of one problem.
Do both halves: the clean secrets path, and the audit that checks whether the cluster's permissions quietly undo it.
KubiScan (CyberArk's tool) runs against the live cluster and finds risky subjects, roles, and pods from the real permissions. I also wrote an offline pre check (scripts/rbac_lint.py) that reads the RBAC YAML and flags the same escalation tricks before anything is applied, so they are caught at PR time.
To have something to catch, k8s/risky-rbac.yaml plants six escalation tricks, each of which beats good secrets hygiene:
| Risk | Why it beats secrets injection |
|---|---|
| escalate | grant yourself any permission |
| bind | bind full admin to yourself |
| secrets read | just read all the secrets directly |
| pods create | schedule a pod wearing a privileged token |
| wildcard everything | full admin by another name |
| SA to wildcard binding | makes the wildcard live |
Run against a real kind cluster, the linter catches every planted role — after a run that first proved why it's needed. Pointed at live kubectl -o json output, the first version reported No risky RBAC found on a cluster holding four cluster-admin-equivalent roles: kubectl wraps everything in a single kind: List document the linter did not descend into. Every unit test had passed the whole time, because every test fed it a hand-written manifest — the one shape it never meets in production. Fixed, and it now surfaces 15 findings, separating the planted roles from Kubernetes' own built-ins by label rather than by name.
15 tests plus a CI canary that runs the linter against this repo's own dangerous manifest in both input shapes and fails if either comes back clean — so a linter that silently stops detecting can never ship green again. kubeconform schema-checks every manifest alongside it. Full output in findings/live-cluster-run.txt.
Conjur and KubiScan are CyberArk's. The workload, the planted RBAC fixture, the offline linter, and the tests are mine.
make cluster
make conjur
make deploy
make apply-rbac
make kubiscan
python scripts/rbac_lint.py k8s/*.yaml # offline cross check
make destroyNeeds kind or minikube, kubectl, Helm, Python 3.
findings/ holds the live-cluster run: the linter silently missing every planted role on kubectl output, then catching all 15 after the fix. LAB-NOTES.md is the log.
Lab code: MIT (LICENSE). Conjur and KubiScan keep their licenses, credited above.