Skip to main content
RunBook Academy

KubernetesXXI · SecretsSecrets

RBAC for Secrets — least-privilege access to credentials

Advanced⏱ ~17 minkubectlkubeadm

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

Not yet marked complete on this device.

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 view reads every Secret in the namespace.
  • A Pod running as the default ServiceAccount has only the default ClusterRole’s permissions (none for Secrets in most clusters).
  • A Pod running as a ServiceAccount bound to a restricted-view ClusterRole 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

  1. Q1. What is the recommended RBAC pattern for Secret access in production?

  2. Q2. Default view ClusterRole includes get secrets, which means developers with view access can read every Secret cluster-wide.

  3. 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.

  4. 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 view and create restricted-view without get secrets.
  • Use resourceNames to 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 secrets is 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.