Volume Shield Installation¶
This guide enables transparent PVC encryption for annotated workloads. Read the architecture overview first if you are new to Volume Shield.
Prerequisites¶
| Requirement | Notes |
|---|---|
| CloudTaser operator installed | See Operator Installation — Volume Shield is injected by the same webhook |
| EU-hosted OpenBao reachable | Same instance used for secret injection |
| Nodes allow a privileged DaemonSet | The vs-broker owns /dev/fuse and mount(2); workload pods stay unprivileged |
Wrapper image v0.2.168 or newer |
Broker and sidecar binaries ship in the wrapper image |
Serverless node pools are not supported
Like the eBPF agent, the vs-broker DaemonSet requires host-level access. GKE Autopilot and AWS Fargate cannot run Volume Shield.
Step 1 — Vault policy and role¶
The sidecar (and broker, for lifecycle state) authenticates via the Kubernetes auth backend using the cloudtaser-vs-broker role. Grant read access to the per-volume DEK path:
Also grant read + check-and-set write on the Volume Shield state path — this is needed by default now, since trusted freshness (vault mode) ships as the default (see the threat model); skip only if every PVC and the cluster-wide default are explicitly set to freshness.mode: local and you never enable trusted nonce budgeting:
path "cloudtaser/data/volumeshield-state/*" {
capabilities = ["create", "read", "update"]
}
path "cloudtaser/metadata/volumeshield-state/*" {
capabilities = ["read", "list"]
}
Missing state-path access fails closed: mounts with nonceBudget.mode or freshness.mode set to vault (the default) will refuse to serve.
Step 2 — Enable in Helm values¶
volumeShield:
enabled: true
broker:
mode: "fdpass" # default — privilege-separated architecture
daemonset:
enabled: true # per-node broker DaemonSet
image:
tag: "v0.2.168"
vaultAddr: "https://your-openbao.example.eu:8200"
vaultRole: "cloudtaser-vs-broker"
sidecar:
image:
tag: "v0.2.168"
# freshness.mode defaults to "vault" (trusted anti-rollback) — shown explicitly for clarity.
# nonceBudget.mode: "vault" is an additional recommended production setting (not default):
nonceBudget:
mode: "vault"
freshness:
mode: "vault"
Then upgrade the release:
helm repo update
helm upgrade cloudtaser cloudtaser/cloudtaser \
--namespace cloudtaser-system \
--version v1.0.264 \
-f values.yaml
Verify the broker is running on every node:
Step 3 — Annotate PVCs¶
Volume Shield is opt-in per PVC. Annotate the PVCs you want encrypted before the consuming pod starts:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
annotations:
cloudtaser.io/encrypt-pvc: "true"
# optional:
# cloudtaser.io/pvc-chunk-size-kb: "64"
# cloudtaser.io/vs-io-mode: "cached" # or "strict"
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
Any pod that mounts this PVC (and is in a namespace the webhook watches) is mutated at admission: the sidecar is injected, the PVC is shadow-remounted to a ciphertext path, and the application sees a transparent plaintext view at its original mount path. Pods with multiple annotated PVCs get one FUSE mount per PVC, each with its own DEK and its own per-mount I/O mode.
Filesystem volume mode only
Volume Shield encrypts volumeMode: Filesystem PVCs. Block-mode PVCs are ignored.
Step 4 — Verify¶
Start a workload and confirm:
# sidecar injected and ready (readiness = ALL mounts serving)
kubectl get pod <pod> -o jsonpath='{.spec.containers[*].name}'
# ... should include cloudtaser-vs-sidecar
# the application path shows plaintext
kubectl exec <pod> -c <app> -- head -c 100 /data/somefile
# the backing store holds ciphertext (CTVS magic)
NODE=$(kubectl get pod <pod> -o jsonpath='{.spec.nodeName}')
BROKER=$(kubectl get pod -n cloudtaser-system -l app.kubernetes.io/component=vs-broker \
--field-selector spec.nodeName=$NODE -o name | head -1)
kubectl exec -n cloudtaser-system $BROKER -- \
head -c 4 /var/lib/cloudtaser/raw/<pvc-name>/somefile # prints: CTVS
Encrypting pre-existing data¶
PVCs with existing plaintext data adopt encryption on write: reads pass through until a file is first written via the mount, at which point it is encrypted. To bulk-encrypt existing data instead, run the migration mode of the sidecar binary as a Kubernetes Job against the raw path — see bulk migration and TOFU-window graduation.
Trusted freshness no longer waits on this step
Before the 2026-07-06 default flip (roadmap#197), bootstrapping via this migration was a prerequisite to trusted freshness meaning anything on a populated volume. Now that vault mode is the default, a populated volume with no prior freshness state gets protection immediately via a trust-on-first-use (TOFU) window — see upgrade cohorts. Running this migration is how you graduate out of that window (bulk-encrypt remaining plaintext and flip the volume to full strict enforcement), not how you turn trusted freshness on in the first place.
Next steps¶
- Configuration reference — I/O modes, nonce budget, freshness, chunk sizing
- Threat model — what Volume Shield does and does not protect against