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.
Related observability¶
| 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¶
- Security Model (threat model and defense layers)
- Memory Protection
- Beacon Relay Architecture
- Zero Kubernetes Secrets
- RFC 5869 — HKDF specification
- X25519 / ECDH for ephemeral key exchange
- The
cloudtaser-operatorbroker handshake (internal/broker/broker.go) cloudtaser-wrapperunseal receiver (internal/health/health.go)- Beacon relay design (repo:
cloudtaser-beacon)