Skip to main content

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.

v1 scope
  • 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​

Permissionk8s group impersonatedWhat it allows
kubernetes:readproxima:kube-readonly (+ system:authenticated)get/list/describe/logs across resources — excludes secrets
kubernetes:executeadditionally proxima:kube-execkubectl 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:

ToolAnswersAgent
k8s_live_workloadReplica and pod state now, and when each condition last changed — what separates a failure a rollout caused from one that began after it0.7.7
k8s_live_network_policiesWhether 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 line0.7.9
k8s_live_pod_logsA container's own output — the only tool that can see it. The host log pipeline ships journald and file logs, never container stdout0.7.12
k8s_live_endpointsWhether anything is listening: ready and not-ready endpoints per Service, and the pod behind each address0.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-investigator for unattended triage.
  • Scoped. The backend only reaches clusters in the caller's hosts:read scope, 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.

One cluster, one client

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