KubernetesXXI · SecretsSecrets
RBAC for Secrets — least-privilege access to credentials
What you'll learn
- Apply RBAC to limit who can read Secrets
- Distinguish "read all Secrets in a namespace" from "read specific Secret"
- Audit Secret access in production
- Use ServiceAccount tokens with limited scope
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
Kubernetes’ default RBAC grants get secrets to anyone with
the view role. This is too permissive for production: any
developer with cluster access can read every credential.
This lesson covers the RBAC patterns that restrict Secret
access to specific identities and the audit discipline that
catches over-permission.
The default and the gap
The default ClusterRole view:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: view
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
A RoleBinding of view to a user or ServiceAccount
grants the ability to read every Secret in the namespace.
In a cluster with default RBAC and many developers, this is
a credential leak waiting to happen.
flowchart LR
A["Developer with<br/>view role"] -->|kubectl get secrets| B[API server]
B -->|RBAC allows| C[Plaintext values]
C --> D[Credential leak]
The principle of least privilege
A Secret should be readable by:
- The Pods that consume it (via the kubelet, which has the cluster-admin ServiceAccount).
- Operators that need to debug (specific identities, not the entire team).
- Automation that rotates the credential (a specific
ServiceAccount with
update).
It should not be readable by:
- Every developer in the namespace.
- Every ServiceAccount in the namespace.
- CI systems that only need a specific Secret.
Tightening Secret access
Step 1: remove get secrets from view
# This requires modifying the ClusterRole
kubectl edit clusterrole view
# remove the rules for secrets
Or create a custom ClusterRole that excludes Secrets:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: restricted-view
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", ...]
verbs: ["get", "list", "watch"]
# NO secrets rule
Bind users to restricted-view instead of view.
Step 2: create a Secret-specific Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: prod
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials", "reg-credentials"]
verbs: ["get"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secret-reader-binding
namespace: prod
subjects:
- kind: ServiceAccount
name: web
namespace: prod
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
The ServiceAccount web can read only db-credentials and
reg-credentials. Other Secrets are invisible.
flowchart TB
A[ServiceAccount web] -->|kubectl get secret| B[API server]
B --> C{Resource in<br/>allowlist?}
C -->|yes| D[Return value]
C -->|no| E[403 Forbidden]
Step 3: scoped ClusterRole for cross-namespace reads
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader-prod
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"]
verbs: ["get"]
Cluster-wide binding; only the specific Secret is readable from any namespace. Useful for a centralised secret-rotation service.
Auditing Secret access
Who can read a Secret?
kubectl auth can-i get secret/db-credentials -n prod \
--as=system:serviceaccount:prod:web
# yes
kubectl auth can-i get secret/db-credentials -n prod \
--as=system:serviceaccount:prod:debug
# no
# List every identity that can read
kubectl get rolebindings,clusterrolebindings -A -o json | \
jq -r '.items[] | select(.roleRef.name | test("view|admin|edit")) | "\(.metadata.namespace // "cluster") \(.subjects[]?.kind)/\(.subjects[]?.name) can read secrets"'
flowchart LR
A["Audit: who can<br/>get secret X?"] --> B["All RoleBindings<br/>+ ClusterRoleBindings"]
B --> C["Filter by secret<br/>resource"]
C --> D["List of identities<br/>with access"]
Audit logging
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
The audit log records every Secret access: who, when, from which IP. A regular audit identifies anomalies (an identity that has never read a Secret before reading it now).
Event correlation
# Get recent Secret access events
kubectl get events -A --field-selector reason=SecretReadError
Failed reads (a Pod trying to read a non-existent Secret) are recorded as events. A spike in failed reads indicates misconfigured RBAC or a misconfigured Pod.
ServiceAccount tokens
ServiceAccounts authenticate Pods to the API server. Tokens are mounted automatically as files; the Pod uses them to authenticate to the API server.
flowchart LR
A[Pod ServiceAccount token] -->|Bearer auth| B[API server]
B --> C{RBAC for token}
C -->|allowed| D[API response]
C -->|denied| E[403]
For Secrets:
- A ServiceAccount’s RBAC applies. A ServiceAccount bound
to
viewreads every Secret in the namespace. - A Pod running as the
defaultServiceAccount has only the default ClusterRole’s permissions (none for Secrets in most clusters). - A Pod running as a ServiceAccount bound to a
restricted-viewClusterRole cannot read Secrets.
The principle: every ServiceAccount has the least privilege needed for its workload. A web ServiceAccount needs to mount Secrets; it does not need to read them via the API server.
Production patterns
Pattern 1: workload-specific ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: web
namespace: prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: web-secret-reader
namespace: prod
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials", "reg-credentials"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: web-secret-reader
namespace: prod
subjects:
- kind: ServiceAccount
name: web
namespace: prod
roleRef:
kind: Role
name: web-secret-reader
apiGroup: rbac.authorization.k8s.io
The web ServiceAccount can read only the two Secrets it
needs. Other Secrets are invisible.
Pattern 2: namespace isolation
A namespace for sensitive workloads (production credentials) has tighter RBAC than a namespace for non-sensitive workloads (development).
flowchart TB
subgraph "Namespace: prod"
A1["ServiceAccount: web<br/>can read db-credentials"]
A2["ServiceAccount: backup<br/>can read all Secrets"]
end
subgraph "Namespace: dev"
B1["ServiceAccount: dev<br/>cannot read Secrets"]
end
A1 --> S1["Secret: db-credentials"]
A2 --> S1
A2 --> S2["Secret: tls"]
B1 -.->|403| S1
Pattern 3: secret rotation ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: secret-rotator
namespace: prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-rotator
namespace: prod
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"]
verbs: ["get", "update", "patch"]
The secret-rotator ServiceAccount can read and update
db-credentials. It cannot read other Secrets.
Quiz
Knowledge check · 4 questions
Q1. What is the recommended RBAC pattern for Secret access in production?
Q2. Default view ClusterRole includes get secrets, which means developers with view access can read every Secret cluster-wide.
Q3. Your team has a developer with view ClusterRole in the prod namespace. The developer can read every Secret in prod. Diagnose and remediate.
Developer alice has RoleBinding to view in prod. view includes get secrets. Alice reads db-credentials via kubectl get secret db-credentials -n prod -o yaml.
Q4. Explain the principle of least privilege for Secret RBAC, and how to apply it.
Passing score: 75%. Answers are checked in this browser.
Production discipline
- Default RBAC is too permissive. Tighten
viewand createrestricted-viewwithoutget secrets. - Use
resourceNamesto scope Secret access. A Role that lists specific Secret names is more restrictive than one that allows all Secrets. - Audit Secret RBAC regularly. Every RoleBinding that
grants
get secretsis a potential leak. - Audit Secret reads in the API server log. A regular audit identifies unusual access patterns.
- Use scoped ServiceAccount tokens. A workload that needs one Secret should have a ServiceAccount bound to that Secret’s Role, not the default ServiceAccount.
RBAC is the API-server-side protection for Secrets. The
discipline is in the granularity: every identity with get secrets is a potential leak. Operators who restrict
deliberately have Secrets that are not casually readable.