Kubernetes Access (pc kube)
Proxima Console gives operators audited, short-lived, identity-aware kubectl
access to monitored Kubernetes clusters — the Teleport tsh kube experience,
built on the same rails as Terminal Access. The real
apiserver is never exposed: the in-cluster Proxima agent dials out, and every
request is authorized per-user and surfaced in the cluster's own audit log.
- Two capability tiers: read (
kubernetes:read) and exec (kubernetes:execute) — see the read/execute model. - A cluster is reachable only when it runs the Proxima cluster-agent agent with kube access enabled.
- v1 assumes one cluster = one Proxima client (see Security → tenancy).
How it works
The kubeconfig points at the Proxima backend, never at the apiserver. The backend is a per-request L7 proxy; the agent is the only component that talks to the apiserver, authenticating with its own ServiceAccount and impersonating your identity. Because k8s RBAC evaluates the impersonated groups, the apiserver — not Proxima — is the final authority on what you can do, and the cluster's audit log shows your email, not the agent.
This reuses the existing platform primitives: the SSH-relay byte transport
(kube.in/out NATS subjects), the SSH-CA short-lived-credential pattern (the
kube token), and the terminal RBAC/tenant-scoping model.
Using it
1. List reachable clusters
pc kube ls
Lists every cluster in your permitted scope — it is a plain paginated
GET /api/v1/clusters gated on assets:read, printed unfiltered. It does not
filter to clusters with kube access enabled; a cluster without it appears here and
then fails at pc kube login.
2. Log in
pc kube login <cluster>
Like tsh kube login, this merges a context into your standard kubeconfig
($KUBECONFIG, or ~/.kube/config) and switches to it — so kubectl works
immediately with no export KUBECONFIG. It prints:
Logged into Kubernetes cluster "<cluster>". Try 'kubectl version' to test the connection.
Each login adds another context (existing contexts are preserved); switch between
clusters with kubectl config use-context or another pc kube login. More:
pc kube login --all # add every cluster you can access
pc kube login prod --kubeconfig ./kc # update a specific file
pc kube login prod --print # print to stdout, touch no file
pc kube logout prod # remove a context (--all removes every Proxima context)
The merged context points at the backend, with an exec-credential plugin that mints/refreshes a short-lived token automatically:
clusters:
- name: proxima-<cluster>
cluster:
server: https://api-console.prxm.uz/api/v1/kube/<cluster-id> # the proxy
users:
- name: proxima-<cluster>
user:
exec:
apiVersion: client.authentication.k8s.io/v1
command: pc
args: ["kube", "credentials", "--cluster", "<cluster-id>"]
contexts:
- {name: proxima-<cluster>, context: {cluster: proxima-<cluster>, user: proxima-<cluster>}}
current-context: proxima-<cluster>
3. Use native tooling
kubectl get pods -A
kubectl logs -f deploy/api # long-lived streams work (no 30s cut-off)
kubectl exec -it pod/web -- sh # requires kubernetes:execute; recorded
kubectl, k9s, helm, lens, etc. all work unchanged — they only ever talk
to Proxima, and pc refreshes the credential on demand. There is no cluster
CA to distribute (kubectl validates the backend's public TLS cert).
Read/execute model
| Permission | k8s group impersonated | What it allows |
|---|---|---|
kubernetes:read | proxima:kube-readonly (+ system:authenticated) | get/list/describe/logs across resources — excludes secrets |
kubernetes:execute | additionally proxima:kube-exec | kubectl exec/attach into pods |
The two tiers are separate permissions backed by separate k8s groups, so the
read/exec split is enforced by real k8s RBAC: the backend only asserts the
proxima:kube-exec group for a caller who actually holds kubernetes:execute,
and the read-only group's ClusterRole cannot exec. A read-only user physically
cannot kubectl exec — the apiserver returns 403.
Both permissions are granted by default to the same roles that hold
terminal:write (Super Admin, Admin, Engineer, Team Lead). Exec is the
higher-privilege grant — see Security → exec is not read-only.
L1 live reads
The L1 incident agent uses the same read tier to look at the cluster live during an
investigation, instead of relying only on the inventory snapshot the cluster-agent syncs every
few minutes. The backend sends a kube_query request to the cluster's agent over NATS; the
agent reads and replies within seconds.
There are four live reads, each answering something a snapshot structurally cannot:
| Tool | Answers | Agent |
|---|---|---|
k8s_live_workload | Replica and pod state now, and when each condition last changed — what separates a failure a rollout caused from one that began after it | 0.7.7 |
k8s_live_network_policies | Whether a policy exists right now. A policy applied minutes before the alert is exactly the one not collected yet, and a policy drops traffic with no event, restart or log line | 0.7.9 |
k8s_live_pod_logs | A container's own output — the only tool that can see it. The host log pipeline ships journald and file logs, never container stdout | 0.7.12 |
k8s_live_endpoints | Whether anything is listening: ready and not-ready endpoints per Service, and the pod behind each address | 0.7.12 |
The last two exist because every other Kubernetes tool reports the shape of a failure and
stops where the cause begins. A crashlooping pod yields CrashLoopBackOff, a restart count and
an exit code — the stack trace is on the container's stdout. A Service whose selector matches no
ready pod black-holes every connection while the Service object looks perfectly healthy, and
Kubernetes emits nothing at all for it.
k8s_live_pod_logs takes previous=true to read the instance that already exited, which for a
crashlooping container is the one holding the explanation; the running instance is usually
seconds old and silent. k8s_live_endpoints pairs with the policy read: one says whether traffic
is filtered, the other whether anything is listening, and from the caller's side both are the
same timeout.
- Read-only by construction. The agent always impersonates
proxima:kube-readonly(+system:authenticated). A request names who is reading, never what they may read, so it cannot widen access. Secrets are excluded by the tier. - Attributed. The cluster's k8s audit log records the read under the calling user's
email in chat, or
proxima-l1-investigatorfor unattended triage. - Scoped. The backend only reaches clusters in the caller's
hosts:readscope, and refuses a namespace that exists in several of them rather than guessing a cluster. - Bounded. At most 8 live reads per investigation shared across all four tools — the budget bounds load on the apiserver, not any one tool, so a second live tool spends the same allowance rather than doubling the real ceiling. The agent answers at most 4 at a time and gives each one 10 seconds. Log reads are additionally capped at 300 lines and 256 KB.
Live reads need kube access enabled on the cluster (below) and a cluster-agent new enough for
that read — the versions in the table above. The gate is per read, not one minimum for the
whole surface: an agent that can serve a workload read keeps serving it and reports honestly on
the reads it cannot. Without either, the tool says so and the investigation falls back to the
snapshot tools — except for k8s_live_pod_logs, which has no snapshot equivalent, so its
fallback says the container's own output could not be read rather than offering search_logs,
whose silence about a container is not evidence. The one cluster, one client rule applies here
too: the read tier is cluster-wide.
Enabling a cluster
Kube access is off by default. Enable it on the agent Helm release for a cluster (see Deployment → Helm chart for the full values):
kubeAccess:
enabled: true
apiServerURL: "https://kubernetes.default.svc"
verifyTLS: true # verify the apiserver cert against the in-cluster CA
Enabling it installs three RBAC objects (the two tier ClusterRoles and a
resourceNames-restricted impersonate ClusterRole bound to the collector
ServiceAccount) and turns on the collector's kube reverse-proxy. With it off,
none of those objects render and the collector behaves exactly as before.
Only enable kube access on a cluster dedicated to a single Proxima client. The read-only ClusterRole is cluster-wide, so on a cluster shared by multiple clients a user of one client could read another's namespaces. See Security → tenancy.
See Security model for the full threat model and the hardening guarantees (impersonation, identity isolation, revocation, audit).