Skip to content

Quickstart: Protect a Workload in 5 Minutes

This guide takes you from zero to a running CloudTaser-protected workload. You will install the operator, store a secret in OpenBao, annotate a deployment, and verify that the secret exists only in process memory.

What you will have at the end: a pod where PGPASSWORD is injected directly from an EU-hosted vault into process memory. It never touches etcd, Kubernetes Secrets, or disk.


Prerequisites

Tool Purpose
kubectl Kubernetes cluster access (1.28+)
helm v3 Operator deployment
A running OpenBao or Vault instance Secret storage (must be reachable from the cluster)
bao or vault CLI Creating test secrets

You also need the CloudTaser CLI installed.

brew install cloudtaser/tap/cloudtaser
# macOS (Apple Silicon)
curl -sL https://releases.cloudtaser.io/cli/v0.4.4/cloudtaser-cli-darwin-arm64 \
  -o /usr/local/bin/cloudtaser && chmod +x /usr/local/bin/cloudtaser

# Linux (x86_64)
curl -sL https://releases.cloudtaser.io/cli/v0.4.4/cloudtaser-cli-linux-amd64 \
  -o /usr/local/bin/cloudtaser && chmod +x /usr/local/bin/cloudtaser

Available platforms: darwin-amd64, darwin-arm64, linux-amd64, linux-arm64


Step 1: Install CloudTaser

Install the operator, mutating webhook, and eBPF agent from the OCI Helm chart:

helm install cloudtaser oci://ghcr.io/cloudtaser/cloudtaser-helm/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.vaultAddress=https://vault.eu.example.com

Replace https://vault.eu.example.com with your actual vault address.

Verify the pods are running:

kubectl get pods -n cloudtaser-system

Expected output:

NAME                                    READY   STATUS    RESTARTS   AGE
cloudtaser-operator-6f8b9c7d4-x2k9m    1/1     Running   0          30s
cloudtaser-ebpf-xxxxx                   1/1     Running   0          30s

Step 2: Connect the Cluster to Vault

Use the CLI to configure Kubernetes auth in your vault instance. This creates a ServiceAccount for vault token review, enables the Kubernetes auth backend, and creates a cloudtaser role:

cloudtaser connect \
  --vault-address https://vault.eu.example.com \
  --vault-token hvs.YOUR_ADMIN_TOKEN

Preview first

Add --dry-run to see what will be created without making changes.


Step 3: Create a Test Secret

Store a secret in your vault instance using the bao or vault CLI:

export BAO_ADDR=https://vault.eu.example.com
export BAO_TOKEN=hvs.YOUR_ADMIN_TOKEN

bao kv put secret/quickstart/db \
  password="s3cret-from-eu-vault" \
  username="app_user"

Step 4: Deploy a Protected Workload

Create a deployment with CloudTaser annotations that tell the operator where to find the secret and how to map it to environment variables:

quickstart-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: quickstart-app
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: quickstart-app
  template:
    metadata:
      labels:
        app: quickstart-app
      annotations:
        cloudtaser.io/inject: "true"
        cloudtaser.io/vault-address: "https://vault.eu.example.com"
        cloudtaser.io/vault-role: "cloudtaser"
        cloudtaser.io/secret-paths: "secret/data/quickstart/db"
        cloudtaser.io/env-map: "password=PGPASSWORD,username=PGUSER"
    spec:
      containers:
        - name: app
          image: busybox:latest
          command: ["sh", "-c", "echo 'PGPASSWORD is set (value hidden)' && sleep 3600"]

Apply it:

kubectl apply -f quickstart-app.yaml

Step 5: Verify the Secret is in Memory Only

Wait for the pod to be running:

kubectl get pods -l app=quickstart-app -w

Once READY 1/1, confirm the wrapper injected the secret:

# Check wrapper logs -- should show "secrets loaded, starting child process"
kubectl logs -l app=quickstart-app -c app | head -20

Verify the secret is NOT in the pod's environment (the wrapper scrubs it from /proc/1/environ):

kubectl exec deploy/quickstart-app -- cat /proc/1/environ | tr '\0' '\n' | grep PGPASSWORD

This should return nothing -- the secret is only in the child process's memory, not in the wrapper's /proc/1/environ.

Verify the init container and volume injection happened:

kubectl get pod -l app=quickstart-app -o jsonpath='{.items[0].spec.initContainers[*].name}'

Expected: cloudtaser-init


Step 6: Check Protection Score

Use the CLI to see the protection status of your cluster:

cloudtaser status

For a full validation:

cloudtaser validate \
  --vault-address https://vault.eu.example.com

For a compliance audit report:

cloudtaser audit \
  --vault-address https://vault.eu.example.com

What Just Happened

When the pod was created, the CloudTaser operator:

  1. Intercepted the pod creation via the mutating admission webhook
  2. Injected an init container (cloudtaser-init) that copied the wrapper binary to a memory-backed emptyDir at /cloudtaser/
  3. Rewrote the container entrypoint to /cloudtaser/wrapper
  4. Set environment variables (VAULT_ADDR, CLOUDTASER_SECRET_PATHS, CLOUDTASER_ENV_MAP, etc.) so the wrapper knows what to fetch
  5. Added CAP_IPC_LOCK so secrets can be locked in memory (no swap)

At runtime, the wrapper:

  1. Authenticated to vault using the pod's Kubernetes service account
  2. Fetched password and username from secret/data/quickstart/db
  3. Mapped them to PGPASSWORD and PGUSER
  4. Stored them in protected memory (memfd_secret + mlock)
  5. Fork+exec'd the original command (sh -c ...) with secrets in the child's environment
  6. Scrubbed secrets from its own /proc/1/environ
  7. Continues running as PID 1 for lease renewal and signal forwarding

Result: secrets travel directly from the EU vault to process memory. They never pass through etcd, Kubernetes Secrets, or any US-controlled storage layer.


Next Steps