Skip to content

feat: Add in-cluster service account support (#768) - #4112

Open
pre wants to merge 1 commit into
derailed:masterfrom
pre:feat/in-cluster-config
Open

feat: Add in-cluster service account support (#768)#4112
pre wants to merge 1 commit into
derailed:masterfrom
pre:feat/in-cluster-config

Conversation

@pre

@pre pre commented Jul 10, 2026

Copy link
Copy Markdown

Motivation

Running k9s inside a cluster currently requires generating a kubeconfig from the mounted service account before starting k9s (wrapper scripts around kubectl config set-credentials etc.). kubectl and client-go fall back to in-cluster credentials automatically; k9s should too.

Addresses #768 (closed as stale — please consider reopening; I'm the original reporter).

Approach

When no kubeconfig and no explicit --server/--token are provided and the service account mount (/var/run/secrets/kubernetes.io/serviceaccount) is present, client.Config synthesizes a single-context kubeconfig named in-cluster:

  • server from KUBERNETES_SERVICE_HOST/KUBERNETES_SERVICE_PORT
  • CA and token as file references, so token rotation keeps working via BearerTokenFile
  • the service account namespace as the default namespace

All downstream context machinery (context activation, per-context config, skins, context view) works unchanged off that synthesized config. Detection mirrors clientcmd's inClusterClientConfig.Possible(), so REST credentials stay consistent with client-go's own in-cluster fallback. An existing kubeconfig always takes precedence, matching kubectl. No new flags.

Testing

  • Unit tests: in-cluster detection, kubeconfig precedence, and unchanged no-config behavior (internal/client/config_test.go).
  • e2e in kind: pod with a dedicated ServiceAccount and no kubeconfig, k9s started with just the SA mount:
 Context: in-cluster [RW]                          <0> all       <a>       Attach       <ctrl-k>  Kill           … ____  __ ________
 Cluster: in-cluster                               <1> default   <ctrl-d>  Delete       <l>       Logs            |    |/  /   __   \______
 User:    in-cluster                                             <d>       Describe     <p>       Logs Previous   |       /\____    /  ___/
 K9s Rev: v0.0.0                                                 <e>       Edit         <shift-f> Port-Forward    |    \   \  /    /\___  \
 K8s Rev: v1.36.1                                                <?>       Help         <z>       Sanitize        |____|\__ \/____//____  /
 CPU:     n/a                                                    <shift-j> Jump Owner   <s>       Shell                    \/           \/
 MEM:     n/a
┌──────────────────────────────────────────────── pods(default)[1] ────────────────────────────────────────────────┐
│ NAME↑         PF        READY        STATUS          RESTARTS IP                  NODE                     AGE   │
│ k9s           ●         1/1          Running                0 10.244.0.5          k9s-test-control-plane   22h   │
│                                                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  <pod>

Startup log:

DBG Using active context context=in-cluster
INF ✅ Kubernetes connectivity OK

(CPU/MEM: n/a is only the missing metrics-server in kind; K9s Rev: v0.0.0 is a dev build without version ldflags — neither is related to this change.)

Allow running k9s inside a pod without a kubeconfig by using the
mounted service account credentials, as kubectl and client-go do.

When no kubeconfig (or explicit --server/--token) is provided and the
pod service account mount is present, client.Config synthesizes a
single-context kubeconfig named "in-cluster" from the mount: server
from KUBERNETES_SERVICE_HOST/PORT, CA and rotating token as file refs,
and the service account namespace as the default namespace. All
downstream context machinery (context activation, per-context config,
skins, views) works unchanged off that synthesized config, and REST
credentials stay consistent with clientcmd's own in-cluster fallback.

An existing kubeconfig always takes precedence, matching kubectl.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant