Skip to content

Volume Shield Configuration Reference

All Volume Shield behavior is controlled by PVC annotations (per-volume) and Helm values (cluster-wide). Component version strings below refer to wrapper v0.2.168 / chart v1.0.264.

PVC annotations

Annotation Values Default Description
cloudtaser.io/encrypt-pvc "true" Enables Volume Shield for this PVC. Read at pod admission time.
cloudtaser.io/pvc-chunk-size-kb integer KiB 64 Encryption chunk size. Larger chunks reduce per-chunk overhead but increase the minimum read-modify-write granularity. Choose once, before first write.
cloudtaser.io/vs-io-mode cached | strict cached Per-PVC I/O mode, see below. Invalid values fail closed to cached.
cloudtaser.io/vs-freshness local | vault | secretstore inherit cluster default (vault, since the 2026-07-06 default flip — roadmap#197) Per-PVC trusted-freshness selection. vault is the default at both cluster and PVC level — see upgrade cohorts and the TOFU window for what that means for pre-existing volumes. local opts the volume OUT of rollback/replay detection — required for workloads that need atomic directory renames (ClickHouse part commits, Prometheus TSDB compaction, VictoriaMetrics) or hardlink-based snapshots (e.g. Cassandra nodetool snapshot); see opting out. Invalid values fail closed to vault (the most secure mode), never to the opt-out.
cloudtaser.io/pvc-transit-key key name Reserved for Vault Transit envelope encryption (not yet active).

I/O modes: cached vs strict

cached (default) strict
Kernel page cache Active (write-through) Bypassed (FOPEN_DIRECT_IO per file)
mmap(MAP_SHARED) Works Fails with ENODEV
Plaintext residency Decrypted pages may reside in the kernel page cache while the file is in use No plaintext page-cache residency beyond the active I/O
Suitable for SQLite/WAL, Postgres, Cassandra, LMDB — anything that mmaps or relies on page-cache performance Workloads that never mmap, under regulatory policy requiring zero page-cache plaintext

Do not set strict for mmap-dependent workloads

strict intentionally breaks mmap(MAP_SHARED). Databases that memory-map their files (most of them) will fail to start. The default is cached for this reason; measure before switching.

The annotation is per-PVC, so a pod can mix modes — e.g. strict for a compliance-scoped volume and cached for a WAL volume in the same pod.

Helm values

volumeShield:
  enabled: false            # master switch
  nonceBudget:
    mode: ""                # "" (process-local) | "vault" | "secretstore"
    leaseSize: 1048576      # GCM seal invocations reserved per lease
    leaseTimeout: "10s"
  freshness:
    mode: "vault"           # "vault" (default) | "local" (cluster-wide opt-out) | "secretstore"
    timeout: "10s"
  broker:
    mode: "fdpass"          # "fdpass" (default) | "serve" (legacy)
    daemonset:
      enabled: false        # per-node DaemonSet (recommended)
    image:
      repository: europe-west4-docker.pkg.dev/skipopsmain/cloudtaser/cloudtaser-wrapper
      tag: ""               # REQUIRED — wrapper release tag
    healthPort: 8081        # HTTPS kubelet probes
    vaultAddr: ""           # REQUIRED with daemonset.enabled=true
    vaultRole: "cloudtaser-vs-broker"
    vaultCACert: ""
    audienceScopedToken:
      enabled: false
      audience: "cloudtaser-vs-broker"
      expirationSeconds: 3600
  sidecar:
    image:
      repository: europe-west4-docker.pkg.dev/skipopsmain/cloudtaser/cloudtaser-wrapper
      tag: ""               # REQUIRED — wrapper release tag
    healthPort: 8082        # must differ from broker.healthPort

Nonce budget (volumeShield.nonceBudget)

AES-GCM with random nonces is bounded by NIST SP 800-38D to 2³² seal operations per key. Volume Shield always enforces this bound in-process and fails closed at exhaustion (warns at 2³¹).

  • mode: "" (default) — the counter is process-local. A remount resets it while the DEK is unchanged, so the cumulative cross-session count is not bounded.
  • mode: "vault" — the sidecar reserves high-water leases in OpenBao KV v2 (cloudtaser/data/volumeshield-state/<namespace>/<pvc-uid>). Remounts continue from the trusted high-water mark; crashes can waste leased invocations but never reuse them. A DEK change starts a fresh epoch.

leaseSize trades vault round-trips against waste on crash: each lease is one vault CAS write per leaseSize encrypted chunks.

No automatic DEK rotation yet

Trusted leasing accounts for nonce use; it does not rotate the DEK before exhaustion. Rotation currently requires re-encrypting the volume under a new DEK.

Trusted freshness (volumeShield.freshness)

Default flipped 2026-07-06 (roadmap#197)

vault mode is now the default, cluster-wide and per-PVC. Previously the default was disabled (chunk-level integrity only, no rollback/replay detection). The safe rollout path for that flip — volume-scoped baseline gating — is what the upgrade cohorts below describe.

  • mode: "vault" (default) — every decrypt verifies the exact ciphertext bytes against per-file digests in OpenBao (.../files/<sha256(relpath)>); every successful mutation commits a new digest; unlink/rename tombstone old paths. Missing state, tombstones, digest mismatch, DEK-epoch mismatch, and unauthenticated plaintext all fail closed — subject to the TOFU window described below for volumes upgrading into the default.
  • mode: "local" (cluster-wide opt-out; ""/unset also resolves to the vault default, not to local — the opt-out must be explicit) — chunk-level integrity only (CTVS v3 AAD). Rollback/replay of correctly-encrypted old bytes, and plaintext injection via CTVS header stripping, are not detected. See opting out below.

Upgrade cohorts and the TOFU window

The moment freshness.mode resolves to vault for a volume — by default, or by explicit configuration — that volume falls into exactly one of three cohorts:

Cohort Condition Behavior
Already tracked Volume already has freshness state in vault (was already running freshness.mode: vault) Strict immediately. Fail-closed on missing state, unchanged from before the default flip — zero behavior change for existing vault-mode users.
New Volume is empty at first mount Strict from birth. Every file gets trusted state the moment it's written; nothing is ever "old" without state.
Upgrade cohort Volume is populated but has no freshness state (e.g. running Volume Shield in local mode, or newly onboarded to the default) Trust-on-first-use (TOFU) window. Files bearing a valid CTVS header are baselined into vault on first verify, each baseline logged at WARN and counted by cloudtaser_vs_freshness_baselined_files_total{pvc_uid,namespace}. The window closes only when an operator runs the graduation migration.

TOFU window security meaning — precise boundary

During the TOFU window, and only for the upgrade cohort:

  • Not yet protected: replay of authentic, same-volume ciphertext at a path that has never been baselined — an attacker holding legitimately-encrypted bytes from elsewhere on the same volume can plant them at an unbaselined path and have them accepted as that path's first version.
  • Unchanged, still protected: plaintext or header-stripped injection stays blocked — the not-encrypted/bad-header check runs before any freshness state lookup, so it is never gated by baseline status. Tombstoned (deleted) paths stay tombstoned. Any path already baselined — including everything baselined earlier in the same window — gets full rollback/replay detection from that point forward.

This is strictly better than local mode (which detects none of this, indefinitely), and it is why the default-on rollout is TOFU-then-graduate rather than either an indefinite reduced-security default or a hard break on upgrade.

Run the migration entrypoint (see graduating from the TOFU window) to close the window on a volume: it walks every file, baselines anything not yet tracked, and flips the volume to strict once the walk completes with zero errors.

Opting out (local mode)

local exists for workloads whose I/O pattern is fundamentally incompatible with per-path freshness state — not as a general performance or convenience knob:

  • Directory-rename-as-commit workloads — ClickHouse part commits, Prometheus TSDB compaction, VictoriaMetrics — hit EXDEV on subtree renames under vault mode.
  • Hardlink-based snapshot workloads — e.g. Cassandra nodetool snapshot — hit EOPNOTSUPP on hard-link creation under vault mode.

See the troubleshooting symptom table for both. Set cloudtaser.io/vs-freshness: "local" on the PVC, or volumeShield.freshness.mode: "local" cluster-wide. Invalid annotation values fail closed to vault, never to the opt-out. Opting out does not exist on a spectrum — a local-mode volume has no rollback/replay or plaintext-injection detection, permanently, not a transitional gap like the TOFU window above.

Operational notes for vault mode:

  • Directory renames return EXDEV (moving a subtree would need a multi-file trusted-state transaction). EXDEV makes mv, Python shutil.move, Java NIO Files.move (without ATOMIC_MOVE), and Kafka's move-with-fallback degrade to copy+delete automatically, with every copied file tracked by freshness. Workloads that REQUIRE atomic directory renames or hard links should use the local opt-out instead (see above). File renames work natively.
  • Each verified read/committed write adds a vault round-trip — budget for the latency on hot paths, or scope volumes that don't need anti-rollback to the local opt-out.

Broker mode (volumeShield.broker.mode)

  • fdpass (default) — privilege-separated: broker mounts, unprivileged sidecar serves and holds the DEK. Use this.
  • serve — legacy: the broker both mounts and serves FUSE, holding DEKs in the per-node privileged process. Retained for backward compatibility only.

Environment variables (advanced)

These are set by the operator/chart; listed for debugging and non-Helm installs.

Variable Component Purpose
CLOUDTASER_VOLUME_SHIELD_ENABLED wrapper Master gate, set to true by webhook
CLOUDTASER_VOLUME_SHIELD_MOUNTS wrapper, sidecar JSON mount descriptor list (all encrypted PVCs of the pod, including per-mount io_mode)
CLOUDTASER_VS_BROKER_SOCK wrapper Broker Unix socket (hostPath)
CLOUDTASER_VS_BROKER_MODE wrapper fdpass when a sidecar is injected
CLOUDTASER_VS_SIDECAR_SOCK sidecar, broker fd-pass Unix socket
CLOUDTASER_VS_STRICT_MODE sidecar Process-wide strict-mode fallback; per-mount io_mode takes precedence
CLOUDTASER_VS_NONCE_BUDGET / CLOUDTASER_VS_NONCE_LEASE_SIZE / CLOUDTASER_VS_NONCE_LEASE_TIMEOUT sidecar Trusted nonce leasing
CLOUDTASER_VS_FRESHNESS / CLOUDTASER_VS_FRESHNESS_TIMEOUT sidecar Trusted freshness

Choosing a production posture

Posture Settings Protects against
Baseline volumeShield.enabled=true (freshness defaults to vault) CSP reading disks/snapshots/backups; in-flight tamper (swap/reorder/bitflip/truncate); rollback/replay and header-strip plaintext injection — immediately for new/already-tracked volumes, after the TOFU window + graduation migration for pre-existing populated volumes (see upgrade cohorts)
Recommended + nonceBudget.mode: vault + cross-remount nonce exhaustion accounting
Maximum + vs-io-mode: strict on compliance-scoped PVCs + plaintext page-cache residency (at the cost of mmap)