Skip to content

Security Model

KubeManta is secure by default: a vanilla install is read-only, with no cluster-admin access, no stored LLM keys, and no egress for cluster data.


Authentication

Layer Detail
Login Password authentication — passwords are bcrypt-hashed, never stored in plaintext. Sessions use signed, time-limited tokens (24-hour lifetime).
Reset kubectl exec -n kubemanta-system deploy/kubemanta-agent -- km-reset-pw
Lockout 6 failed attempts per IP triggers a 15-minute lockout.
Programmatic access Per-user API keys (X-API-Key header), created per account in Admin → Users. No shared secret exists. See Programmatic access.

RBAC — read-only by default

ServiceAccount Role When active
kubemanta kubemanta-reader (ClusterRole) Always — get/list/watch on pods, logs, services, configmaps, events, deployments, statefulsets, daemonsets, ingresses, jobs, cronjobs, nodes, namespaces
kubemanta + write verbs Only when rbac.allowWrites=true: adds pods create/delete, pods/exec, secrets create/update/patch, deployments patch/update, namespaces create
kubemanta-expert cluster-admin Only when expertMode.enabled=true. The token lives only in a separate, isolated pod — the main agent pod never mounts it.

Secrets read (get/list on secrets) is separately gated by rbac.readSecrets (default false). Enable only if Secret inspection via the AI agent or terminal is required.


Namespace blocklist

The following namespaces are always hidden from the UI and excluded from RBAC — they cannot be observed, diagnosed, or modified through KubeManta regardless of settings:

kube-system · kube-public · kube-node-lease · istio-system · paas-core · cert-manager · ingress-nginx · traefik · monitoring · logging · flux-system · argo · argocd · kubemanta-system

All namespaces with the prefixes gke-*, gmp-*, kube-* are also blocked by prefix match.


Programmatic access

API access and the MCP server authenticate with per-user API keys. There is no shared secret: the previous security.apiKey Helm value was removed because a single cluster-wide token could not be attributed to a person, could not be revoked without a redeploy, and bypassed the seat model.

Creating a key. An admin opens Admin → Users, expands a user's API keys, and creates one. The secret is displayed exactly once — only a SHA-256 hash is stored, so a lost key must be revoked and replaced rather than recovered.

Format is km_<prefix>_<secret>: a 160-bit CSPRNG secret, looked up by the indexed prefix and compared with a constant-time check.

A key always acts as its owner, right now. The owner's role is read from the users table on every request:

What you do to the user What happens to their existing keys
Demote admin → viewer Keys survive and immediately act as a viewer
Promote viewer → admin Keys immediately regain admin reach
Deactivate the user Every key returns 401
Revoke one key That key returns 401; the owner's other keys are unaffected

Viewers may hold keys — they are simply read-only.

Keys can never manage identity or billing. Even for an admin owner, a key is refused (403 api_key_forbidden) on /admin/users*, /admin/api-keys*, /me/api-keys*, /admin/sso*, /admin/sessions*, /admin/license*, and writes to /admin/seats. This is enforced in middleware, before any handler runs, so a stolen key cannot mint another key or escalate its own account.

Every API-key request is attributed in the audit log as <username>@<ip>, which is what makes credential sharing visible.


AI key handling

LLM API keys are passed per-request in the request body. They are:

  • Never written to disk or SQLite
  • Never logged
  • Never sent to KubeManta infrastructure

The PTY terminal is BYO-LLM: ANTHROPIC_API_KEY (and every other secret-bearing variable) is stripped from the terminal environment before a session spawns. Claude Code in the terminal authenticates per-user via its own claude OAuth login (persisted in ~/.claude on the PVC) or a key the operator exports themselves. A server-side Anthropic key (Helm agent.agentApiKey, stored in a Kubernetes Secret) is used only by the agent's autonomous alert investigation and scheduled reports — never exposed to terminal sessions.


Three-pod isolation

KubeManta splits privilege across three distinct security contexts so that no single compromised process holds everything:

Pod / context Holds Reaches
Agent The database, encryption key, session secrets, SSO config Read-only reader ServiceAccount (writes only with rbac.allowWrites)
Expert-exec pod The cluster-admin token (only when expertMode.enabled=true) Full cluster — but only via bearer-authenticated calls from the agent over a NetworkPolicy-restricted hop, using an ephemeral kubeconfig that is discarded after every operation
Terminal sidecar Only its own storage (Claude Code session + caches) No cluster-admin token, no database, no encryption key — runs as a distinct, lower-privilege user in its own process namespace

So the cluster-admin token is never in the agent pod, the terminal process cannot reach the agent's secrets or database, and privileged operations are delegated over an authenticated, network-policy-restricted hop. The runtime Expert-Mode toggle is enforced on both the privileged HTTP/Helm endpoints and the AI agent write tools.

The terminal WebSocket is authenticated before the connection is accepted — an unauthenticated upgrade is refused, no session spawned — and is subject to the same IP access control as the rest of the product.


Autonomous agent write safety

When the AI agent is asked to perform a write operation (delete pod, restart deployment, scale):

  • Normal mode (default): the agent pauses and waits for explicit human approval in the UI before executing
  • Auto mode: executes writes without approval — requires explicit user opt-in; never the default
  • Blast radius limit: hard cap of 5 write operations per session (agent.blastRadiusLimit)
  • Circuit breaker: trips after 3 consecutive write failures (agent.circuitBreakerThreshold); blocks all writes until auto-reset (agent.circuitBreakerResetSecs = 300s)
  • Max remediation cycles: 2 autonomous cycles per app per hour (anti-flap throttle)

Data storage

Data Where stored Notes
Workload data, logs, events Never stored — fetched live from the K8s API
Audit log, alert history, settings Embedded database on the PVC In-cluster only
Terminal home (Claude Code session, kubeconfig, history) PVC Persistent across pod restarts
Cloud credentials (AWS/GCP/Azure) Embedded database, AES-256-GCM encrypted Masked •••• in all API responses
Helm config/data/cache PVC
LLM API keys Never persisted Per-request only
Admin password bcrypt hash, in-cluster only

Encryption at rest

Cloud credentials, Helm registry passwords, and integration secrets are encrypted with AES-256-GCM before being written to the embedded database. A random 256-bit encryption key is generated on first install, stored in a Kubernetes Secret, and preserved across upgrades and uninstalls. You can supply your own key (bring-your-own-key) via security.encryptionKey.

To disable (plaintext storage): --set security.encryptionAtRest=false (not recommended).


Pod hardening

Control Detail
seccompProfile RuntimeDefault on all pods
PodSecurity baseline enforce + restricted warn on kubemanta-system (applied manually via kubectl label namespace)
NetworkPolicy Blocks all egress to 169.254.169.254 (cloud Instance Metadata Service) from all pods in kubemanta-system. Disable with security.blockImds=false only if your workloads need IMDS for Workload Identity bootstrap.
readOnlyRootFilesystem true on the UI container; /tmp overlay via emptyDir
Non-root Every container runs as a non-root user; the terminal sidecar runs as a distinct, lower-privilege user in its own process namespace

Supply-chain integrity

All published images (kubemanta-agent, kubemanta-ui) are keyless-signed with cosign by digest and ship an SPDX SBOM attestation, both bound to the GitHub Actions OIDC identity. You can cosign verify any image before deploying it. See Supply Chain.


Tamper-evident audit

Every audited action is recorded in an append-only audit log that is hash-chained — each entry cryptographically covers the one before it, so any edit or interior deletion is detectable on demand, with no configuration. Actor attribution is derived from the real client IP (security.trustedProxyCidrs governs when X-Forwarded-For is trusted) and is email-aware for SSO sessions. An optional off-box WORM export (audit.worm) writes each entry to your own Object-Lock S3 bucket to close the trimming-the-newest-rows gap. See Activity & Audit.


Access & exposure

An in-product IP access layer (allowlist / denylist / durable auto-ban), a sliding session idle-timeout, and a service-only exposure mode let you front KubeManta with your own edge/WAF. All Free. See Access Control.


Enterprise SSO

Authenticate operators through your own identity provider — SAML 2.0, OIDC, or LDAP — with JIT provisioning and group→role mapping; the local admin password is always retained as break-glass. See Enterprise SSO.


AI Guardrails

Org-wide AI policy (master on/off switch, data residency, cost caps, write safety, namespace scope, egress allowlist, prompt audit, redaction) is configurable in Admin → AI Guardrails. See AI Guardrails.