Chapter 5. RBAC for Least Privileges
Where should “RBAC Least Privilege” live?
Keep RBAC as its own short chapter (Ops hardening) with hands-on Role/RoleBinding/SA steps. It’s a foundational Kubernetes control and applies whether or not Kyverno is present.
Reference Kyverno as an optional guardrail at the end of that chapter (e.g., “disallow wildcard RBAC”), since you already use Kyverno earlier. That keeps the learning path incremental:
- grant least privilege (RBAC files)
- verify with
kubectl auth can-i - optionally enforce via Kyverno policy
Minimal Makefile additions (fit your style)
These targets don’t change any existing flow; they’re opt-in helpers.
# ---- RBAC (namespace-scoped least privilege) ----
k8s-rbac:
@echo "== Apply app ServiceAccount + Role + RoleBinding =="
@kubectl apply -f $(K8S_DIR)/rbac/sa.yaml
@kubectl apply -f $(K8S_DIR)/rbac/role.yaml
@kubectl apply -f $(K8S_DIR)/rbac/binding.yaml
rbac-verify:
@echo "== RBAC can-i checks (evidence) =="
@mkdir -p artifacts
@{ \
date; \
echo "# RBAC verify (serviceaccount $(NAMESPACE):app-sa)"; \
kubectl -n $(NAMESPACE) auth can-i get pods --as=system:serviceaccount:$(NAMESPACE):app-sa; \
kubectl -n $(NAMESPACE) auth can-i list pods --as=system:serviceaccount:$(NAMESPACE):app-sa; \
kubectl -n $(NAMESPACE) auth can-i watch pods --as=system:serviceaccount:$(NAMESPACE):app-sa; \
kubectl -n $(NAMESPACE) auth can-i create pods --as=system:serviceaccount:$(NAMESPACE):app-sa; \
kubectl -n $(NAMESPACE) auth can-i get secrets --as=system:serviceaccount:$(NAMESPACE):app-sa; \
kubectl -n kube-system auth can-i list pods --as=system:serviceaccount:$(NAMESPACE):app-sa; \
} | tee artifacts/rbac-verify.txt
@echo "wrote artifacts/rbac-verify.txt"
RBAC files (namespace $(NAMESPACE))
infra/k8s/rbac/sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: ship
automountServiceAccountToken: true
infra/k8s/rbac/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-read
namespace: ship
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
infra/k8s/rbac/binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-read-binding
namespace: ship
subjects:
- kind: ServiceAccount
name: app-sa
namespace: ship
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: app-read
And add to your Deployment (already present file):
spec:
template:
spec:
serviceAccountName: app-sa
Optional: Kyverno guardrail for RBAC (put in your existing policies/)
Since you already install/apply Kyverno, add a simple policy to block wildcard RBAC. This belongs in the Kyverno section and you can link it from the RBAC chapter as “turn on this safety net”.
policies/disallow-rbac-wildcards.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-rbac-wildcards
spec:
validationFailureAction: enforce
background: true
rules:
- name: no-wildcard-role
match:
any:
- resources:
kinds: ["Role","ClusterRole"]
validate:
message: "Avoid wildcard RBAC; specify explicit verbs/resources."
pattern:
spec:
rules:
- verbs: "!*=*"
resources: "!*=*"
Wire it with your existing target (no new targets needed):
# just run:
make kyverno-apply