
security
What a kubeconfig actually grants, and how to scope one down
A kubeconfig grants nothing by itself. It carries an identity, and the cluster decides what that identity can do. How to find out, and how to hand out less.
A kubeconfig grants nothing. It is a file containing an address, a way to trust the server, and a credential that proves who you are. Everything you are allowed to do is decided by the cluster, from RBAC bound to that identity — which is why “can I give someone my kubeconfig” is the wrong question, and “what is this identity bound to” is the right one.
The practical consequence is that scoping down a kubeconfig is never done by editing the kubeconfig. It is done by issuing a different identity.
The fastest check
Who does this file claim to be:
kubectl auth whoami
ATTRIBUTE VALUE
Username kubernetes-admin
Groups [kubeadm:cluster-admins system:authenticated]
Extra: authentication.kubernetes.io/credential-id [X509SHA256=7e012c904b0423960669c1ce8897aeddbcc3f8c86f8ed2aa353df253c7044bea]
And what that identity may do:
kubectl auth can-i --list
Resources Non-Resource URLs Resource Names Verbs
*.* [] [] [*]
[*] [] [*]
*.* with verb * is cluster-admin. If that is what your everyday kubeconfig
prints, every tool you point at it — every dashboard, every CI job, every
half-finished script — is running as cluster-admin, whatever it claims about being
read-only.
Note the two lines. The second, with an empty resource and [*] under non-resource
URLs, is authority over non-resource paths like /healthz and /metrics. RBAC
treats those separately from resources, and a role that grants everything on
resources still grants nothing on those paths unless it says so.
The three parts, and which one carries the power
kubectl config view --minify --flatten
--minify reduces the output to the current context only, and --flatten inlines
file references so you see what would actually be sent. On a certificate-based
config the user block looks like this, with the data elided:
users:
- name: kind-glance-blog
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
That is the whole grant: a client certificate whose CN becomes the username and
whose O values become groups. Three properties follow, and all three are the
reason certificate-based kubeconfigs are a poor thing to hand out:
- It cannot be scoped. The identity is fixed at issue time and RBAC is bound to it cluster-side. Nothing in the file limits it.
- It cannot be revoked. Kubernetes has no certificate revocation path. Until it expires it is valid, and the only real remedy is rotating the cluster CA.
- It usually does not expire soon. Installer-issued admin certificates are commonly valid for a year.
A bearer token in the same position is revocable by deleting the object behind it,
and an exec credential plugin holds no secret at all — it shells out to your
cloud CLI or OIDC helper on each call. Prefer either to a certificate for anything
you did not issue for yourself.
Handing out less: a scoped identity in four commands
Do not copy your kubeconfig and delete things from it. Create an identity that never had the permissions in the first place.
kubectl -n prod create serviceaccount viewer
kubectl -n prod create rolebinding viewer-can-view \
--clusterrole=view --serviceaccount=prod:viewer
view is a built-in ClusterRole; binding it with a RoleBinding rather than a
ClusterRoleBinding confines it to that one namespace, which is the part people get
wrong. Verify rather than assume — impersonation lets you ask the cluster directly
without ever holding the credential:
kubectl auth can-i --list --as=system:serviceaccount:prod:viewer -n prod
Resources Verbs
configmaps [get list watch]
endpoints [get list watch]
events [get list watch]
...
and the negative cases, which are the ones worth checking:
kubectl auth can-i delete pods --as=system:serviceaccount:prod:viewer -n prod # no
kubectl auth can-i get secrets --as=system:serviceaccount:prod:viewer -n prod # no
kubectl auth can-i list pods --as=system:serviceaccount:prod:viewer -n default # no
Those three no answers are the specification. view deliberately excludes
Secrets, and the RoleBinding stops it at the namespace edge.
Then mint a token with a lifetime, rather than a permanent one:
kubectl -n prod create token viewer --duration=1h
This is the modern replacement for the legacy Secret-backed service account token.
It is signed, time-bound, and it expires without anybody having to remember to
clean it up. A token minted with --duration is the correct thing to paste into a
tool you are trying out; a long-lived admin certificate is not.
Assembled into a kubeconfig, the user block becomes one line, and the file is now worth exactly one hour of namespace-scoped reads.
Credentials expire, and the errors do not say so
The failure mode worth planning for is that an expired credential rarely produces a message about expiry. What you get is a 401 rendered by whatever tool you were using, and tools in this category are notoriously bad at it — one popular terminal client reports a missing command rather than a lapsed token. When something that worked yesterday stops working today and the error is strange, check the credential before you check anything else:
kubectl auth whoami
An expired credential fails this immediately and unambiguously, which makes it a better first diagnostic than any request against a real resource. The related transport and trust errors — the ones that look similar and are not this — are worked through in kubectl connection and TLS errors.
Rules that hold up
- Never hand out the installer’s admin kubeconfig. It is unscopable, unrevocable and long-lived. Issue a service account token instead, every time.
- Bind with a RoleBinding unless you mean the whole cluster. A ClusterRoleBinding
to
viewgrants read of every namespace including whatever gets created next year. - Remember
viewexcludes Secrets andeditdoes not.editcan read every Secret in its scope, which for most people is the moment “it is only edit” stops being reassuring. - Check what a tool asks for before pointing it at a cluster. Anything that needs write access to do a read is asking for the wrong thing.
- Keep separate contexts for separate authority. A context whose credential is read-only cannot be turned destructive by a mistyped command, which is a stronger guarantee than any confirmation prompt. Which file wins when you have several is covered in kubeconfig merge rules.
For the copy-pasteable Role that a read-only dashboard actually needs — narrower
than view, and with the reasoning for each verb — see
a read-only RBAC role for a Kubernetes dashboard.
The Kubernetes dashboard that fits in your pocket
KubeGlance is a native Kubernetes client for iPhone and iPad — the real dashboard, not a companion — with a full Mac app on the same core. Pods, workloads, logs and events, straight from your kubeconfig.
Download KubeGlance

