Skip to content

Security Model — Trust Chain and Per-Pod Isolation

CloudTaser enforces secret isolation per pod, not per cluster. Compromise radius is bounded to one pod's declared paths even if a workload is attacker-controlled. This page explains the token cascade and the four per-pod isolation layers.


Token cascade — where secrets actually come from

When a protected pod uses cloudtaser.io/vault-auth-method: token (the flagship, beacon-relayed path):

┌──────────────────────────────────────────────────────────────────┐
│ Vault (EU-hosted, on-prem / self-hosted)                         │
│   Root policies, root token held in OpenBao                      │
└────────────┬─────────────────────────────────────────────────────┘
             │ mTLS through the beacon P2P relay
┌──────────────────────────────────────────────────────────────────┐
│ Operator's own token (cluster-wide)                              │
│   Policy: mint child tokens + read cluster-meta                  │
│   Obtained via /v1/bridge/init — delivered into operator         │
│   process memory, NEVER on disk / K8s Secret / etcd              │
│   TTL: hours, renewable                                          │
└────────────┬─────────────────────────────────────────────────────┘
             │ calls `auth/token/create` with pod-specific policy
┌──────────────────────────────────────────────────────────────────┐
│ Per-pod child token (one per protected pod)                      │
│   Policy: ONLY paths from this pod's                             │
│     cloudtaser.io/secret-paths annotation                        │
│   TTL: minutes (typical 5-15), not renewable beyond pod life     │
└────────────┬─────────────────────────────────────────────────────┘
             │ delivered to wrapper via HKDF-v2 encrypted envelope
┌──────────────────────────────────────────────────────────────────┐
│ Wrapper (inside THIS pod)                                        │
│   Uses child token to fetch only THIS pod's secrets              │
│   Delivers secret to app via memfd (kernel-only inheritance)     │
│   eBPF enforcement blocks /proc/environ, /proc/mem, ptrace       │
│   from the outside                                               │
└──────────────────────────────────────────────────────────────────┘

Per-pod isolation layers

Four layers fire independently at unseal time. None share state between pods:

Layer What's per-pod Consequence
ECDH keypair Wrapper generates a fresh X25519 keypair on startup (in memory, never persisted). Different shared secret per pod.
HKDF-derived AES key Derived from (wrapper pubkey) + (broker ephemeral pubkey). Ciphertext intercepted on the wire is useless for any other pod.
Child vault token Minted with pod-specific policy, short TTL. Compromised pod → only its declared paths leak, token auto-expires.
memfd + eBPF Anonymous memfd inherited across fork+exec; eBPF blocks external readers. Sibling pod / host process cannot read the environment variables or memory of this pod.

What each component sees — blast-radius table

Component compromise What the attacker gets
Protected pod Only that pod's declared secret-paths, scoped by child token. Token expires in minutes.
Operator Ability to mint new child tokens. Still has to mint + wait for unseal; every mint is auditable in vault. Does NOT get direct pod memory access.
Beacon relay Encrypted TCP frames. Cannot decrypt — does not hold HKDF keys. Acts as a dumb forwarder.
Bridge (on-prem) Proxy to vault. Holds operator's bridge cert (in memory only). Does NOT hold per-pod tokens.
Kubernetes API / etcd Nothing sensitive. CloudTaser stores zero secrets in K8s Secrets, Helm release state, or etcd.
Cloud provider (AWS/GCP/Azure) Encrypted traffic + container images. Cannot access vault tokens or decrypt per-pod traffic.

Two auth methods — when each applies

CloudTaser supports two authentication methods for delivering secrets. Both provide per-pod isolation, but the trust models differ.

vault-auth-method: token — flagship path (beacon-relayed)

Used for data sovereignty workloads. The wrapper never holds a long-lived credential. The operator mints a short-lived child token in vault and delivers it via the broker's HKDF-v2 encrypted envelope. Traffic between operator and vault flows through the P2P beacon relay and on-prem bridge — the cloud provider never sees plaintext.

Annotations required:

cloudtaser.io/inject: "true"
cloudtaser.io/vault-auth-method: "token"
cloudtaser.io/vault-address: "https://your-vault.example.eu:8200"
cloudtaser.io/secret-paths: "secret/data/prod/db-creds"
cloudtaser.io/env-map: "password=DB_PASSWORD,username=DB_USER"

vault-auth-method: kubernetes — direct fallback

Used where beacon-relayed operator is not deployed (for example, traditional single-region vault + vault-agent migrations). The wrapper authenticates directly to vault using its own ServiceAccount JWT, scoped by vault's Kubernetes-auth role bindings.

Isolation is still enforced at the vault-role level (each pod gets its own token with narrow policy), but the P2P beacon relay, HKDF-v2 envelope, and ECDH per-pod crypto are skipped. This path exists for migration compatibility — production deployments should prefer token mode.


Metric / signal What it proves
cloudtaser_broker_unseal_protocol_version_total{version="v2"} HKDF-v2 unseal envelope was used on this unseal. Non-zero = flagship path active.
cloudtaser_ebpf_enforcement_mode{mode=...} (roadmap) Which of LSM / kprobe-override / reactive-kill is active per node.

Further reading