Skip to main content

kuberc Is On by Default: Your kubectl Habits Finally Move Out of the Kubeconfig

8 min readDora NodaDora Noda
Share
On this page

Every team running its own Kubernetes fleet has one machine with a peculiar smell: the shared ops box. Five operators SSH into the same bastion host, source the same dotfiles, and type the same muscle-memory aliases against whichever cluster KUBECONFIG happens to point at today. Last month kgp meant kubectl get pods against staging. This month a teammate repointed the shared kubeconfig's current context at production, the alias didn't change, and the "harmless read" listed the wrong fleet — on a worse day, with a write alias, it would have been worse than a listing. Meanwhile, sitting in that same home directory, there is a file that can execute an arbitrary local binary every time kubectl runs — and until recently, your preferences and that loaded weapon lived in the same YAML document.

That file is the kubeconfig. The fix is kuberc: a separate, credential-free preferences file at ~/.kube/kuberc that holds your aliases, your default flags, and — since Kubernetes 1.35 — the policy deciding which credential-helper binaries kubectl is allowed to execute at all. Kubernetes 1.36, released in April 2026, keeps kuberc in beta and on by default, gives it a real management CLI, and tightens the one security-sensitive corner of its schema with a hard error. If you operate a shared bastion for a Cluster-API fleet, this is the release where kuberc stops being trivia and starts being hygiene.

The split: what lives where now

For a decade, kubectl had exactly one client-side config file, and it mixed two things that never belonged together: what you can authenticate to (clusters, users, certificates, tokens) and how you like to work (aliases, default output flags). The old kubeconfig even had a preferences stanza for the second half — documented for years as effectively unused — so everyone stuffed their habits into shell aliases instead, and shell aliases live outside version control, outside kubectl, and outside any notion of which cluster they were written for.

kuberc (KEP-3104, alpha in 1.33, beta since 1.34, enabled by default from 1.34 onward) ends that. Concretely:

Stays in kubeconfigMoves to ~/.kube/kuberc
Cluster endpoints and CA dataCommand aliases (kgpget pods)
Users, client certs, tokensDefault flags per command (get-o wide)
exec credential-plugin definitions (which binary produces a token)Credential-plugin execution policy (which binaries may run)
Contexts binding the three togetherNothing secret, ever — safe to dotfiles-repo

Getting started is three commands:

bash
kubectl kuberc set --section aliases --name kgp --command "get pods"
kubectl kuberc set --section defaults --command get --option output=wide
kubectl kuberc view

Notes for the shared box: --kuberc /path/to/file overrides the location per invocation, so a team can ship a read-only team-defaults file alongside per-operator overrides. Aliases cannot override built-in commands, and they resolve between builtins and plugins (builtins first, then aliases, then plugins). And when something behaves strangely, KUBERC=off disables the whole mechanism for one session, while KUBECTL_KUBERC=false turns the feature off entirely — the escape hatches you want before you page anyone.

What 1.36 actually changes (and what it doesn't)

A correction worth making, because several release guides blur it: 1.36 did not introduce kuberc beta, and it did not newly flip it on. The honest timeline:

Versionkuberc status
1.33Alpha; opt in with KUBECTL_KUBERC=true
1.34Beta; enabled by default
1.35Beta; adds credentialPluginPolicy / credentialPluginAllowlist (new kubectl.config.k8s.io/v1beta1 API)
1.36Beta, still on by default; adds kubectl kuberc set management for the credential-plugin policy and deprecates name in allowlist entries

So 1.36 is the release where kuberc becomes operationally unavoidable rather than newly existent: the day your fleet's kubectl passes 1.36, every operator on the bastion has a live preferences file whether they created one or not, and the security policy section graduates from hand-edited YAML to a real subcommand:

bash
kubectl kuberc set --section credentialplugin --policy Allowlist \
  --allowlist-entry command=/usr/local/bin/team-credential-helper

If your runbook pins kubectl versions — and on a shared ops box it should — the 1.36 line in the version table is the one that converts kuberc from "nice for power users" into "audit every home directory on the box."

The security half: a kubeconfig can run binaries, and now you can say no

Here is the part that justifies the urgency. A kubeconfig's exec stanza names a local command that kubectl runs to fetch credentials. That is by design — cloud providers use it for login helpers — but it means a kubeconfig you did not write can execute an arbitrary binary on the operator's machine simply by being used. Public proof-of-concept repos demonstrate exactly this: trick someone into export KUBECONFIG=./shiny-new-cluster.yaml, and the "cluster config" is really a code-execution primitive. On a shared bastion where kubeconfigs get passed around in chat threads and pasted from wikis, that is not a theoretical shape. It is the daily workflow.

The credential-plugin policy in kuberc is the counterweight, and it lives in the right file on purpose: the policy belongs to the operator, not to the cluster definition that might be attacking them. Three values, all worth knowing exactly:

  • AllowAll — no restrictions. Identical to every Kubernetes before 1.35. This is also what you get when the field is unset, so a kuberc that only defines aliases is still running wide open.
  • DenyAll — no exec plugin runs, period. The correct default for a bastion that authenticates with client certificates or static tokens and has no legitimate login helper.
  • Allowlist — only the listed commands run, matched by basename or full path. Entries are OR'd: a plugin matching any entry is permitted.

Two fail-closed details show the schema was designed by people who have been bitten before. Setting an allowlist while the policy is anything other than Allowlist is a configuration error, not a silent no-op. And an empty or all-blank entry is rejected outright — the same fail-closed logic covers a misspelled or missing credentialPluginAllowlist key under an Allowlist policy: that is a configuration error and the plugin is blocked, never silently permitted. For a shared box, the copy-paste starting point is:

yaml
apiVersion: kubectl.config.k8s.io/v1beta1
kind: Preference
credentialPluginPolicy: DenyAll

Upgrade to a tight Allowlist the day a legitimate helper appears, and treat every new entry the way you'd treat a sudoers change — because that is what it is.

The 1.36 micro-breaking change: name is dead, command wins, both is an error

Buried in the 1.36 notes is a rename with teeth. Allowlist entries used to identify the permitted binary with a name field. From 1.36 on, the field is command (kubernetes/kubernetes#137272), name survives only as a deprecated beta alias, supplying both in one entry is a hard error, and name disappears entirely at GA. The official docs are blunt about why both-at-once is unforgivable: these are security-sensitive settings, and an entry that says two things about which binary may run is an entry nobody should trust.

The migration is a two-line diff per entry:

yaml
# before (still accepted in 1.36, deprecated)
- name: team-credential-helper
# after (the only form that survives to GA)
- command: team-credential-helper

And the audit is one command run in every home directory on every bastion you own:

bash
grep -rn "name:" ~/.kube/kuberc /home/*/.kube/kuberc /root/.kube/kuberc 2>/dev/null

Do it now rather than at GA. Deprecated-today-removed-tomorrow fields have a habit of becoming outage-today when the GA release lands mid-incident and someone's allowlist silently stops parsing.

What kuberc doesn't fix (say it plainly)

Three limits, so nobody over-claims this. First, kuberc is a per-user client file with no cluster-side enforcement — it protects the operator who configures it and nobody else; you cannot push a DenyAll policy onto a fleet from the API server. Second, the secure posture is opt-in: unset policy means AllowAll, so every operator who never touches the feature keeps the pre-1.35 behavior forever. Third, aliases still can't override builtins and still know nothing about context — kgp runs against whatever cluster is current, which means the original "muscle memory hits the wrong cluster" failure is reduced (flags travel with the user, not the file) but not eliminated. Pair kuberc with a prompt that shows the current context, not with confidence.

The shared-box rollout

For a Cluster-API fleet's bastion hosts, the work is small and concrete:

  1. Pin kubectl at 1.36 or newer on the box so every operator gets identical behavior.
  2. Ship a team-default kuberc with DenyAll (or a minimal Allowlist) via your existing machine configuration.
  3. Grep every existing ~/.kube/kuberc for the deprecated name key before GA removes it.
  4. Document KUBERC=off in the runbook next to the other "is it the tooling or is it the cluster" bisect steps.

Fifteen minutes per box buys you the separation this platform has needed since shell aliases: credentials describe what the box may touch, preferences describe how its operators like to work, and never again does accepting a kubeconfig from a teammate mean accepting their choice of binaries to execute.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex