Skip to content

Operator Installation

The cloudtaser operator is the core component that enables secret injection into Kubernetes workloads. It runs as a Deployment with a mutating admission webhook that intercepts pod creation, injects the wrapper binary via an init container, and rewrites container entrypoints so secrets are fetched from an EU-hosted OpenBao directly into process memory.

Prerequisites

Before installing the operator, ensure the following requirements are met.

Required

All three prerequisites are mandatory. The operator will not function without a compatible Kubernetes cluster, Helm, and a reachable OpenBao instance.

Requirement Minimum Recommended
Kubernetes 1.28+ 1.30+
Helm 3.x Latest 3.x
Secret store OpenBao 2.x (also compatible with HashiCorp Vault 1.15+) OpenBao (no license concerns)

Kubernetes Cluster

The operator is tested on GKE Standard, EKS (managed node groups), and AKS (regular node pools). Serverless environments such as GKE Autopilot and AWS Fargate are not supported because the eBPF agent requires host-level access.

EU-Hosted OpenBao

cloudtaser requires an OpenBao instance hosted in the EU (also compatible with HashiCorp Vault). This is the foundation of the data sovereignty guarantee -- secrets transit only between the EU OpenBao instance and process memory on the Kubernetes node. They never pass through etcd, Kubernetes Secrets, or any US-controlled storage layer.

Recommended EU regions

  • GCP: europe-west1 (Belgium), europe-west3 (Frankfurt), europe-west4 (Netherlands)
  • AWS: eu-central-1 (Frankfurt), eu-west-1 (Ireland), eu-west-3 (Paris)
  • Azure: westeurope (Netherlands), germanywestcentral (Frankfurt)
  • Self-hosted: Any EU data center

The OpenBao instance must have:

  • KV v2 secrets engine enabled
  • Kubernetes auth backend enabled
  • TLS configured (the wrapper validates the server certificate)
  • Network reachability from the Kubernetes cluster

Installation

Install the operator and all cloudtaser components using the unified Helm chart.

Beacon relay mode requires only outbound TCP 443. No OpenBao address is needed at install time -- the operator connects through the beacon relay.

helm repo add cloudtaser https://charts.cloudtaser.io
helm repo update

helm install cloudtaser cloudtaser/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.broker.beacon.enabled=true \
  --set operator.broker.beacon.address=beacon.example.com:443 \
  --set operator.broker.beacon.infoHash=<generated-hash>

The info hash is generated by cloudtaser-cli source register. See Beacon Relay for details.

If your OpenBao instance is directly reachable from the cluster (same VPC, VPN, or peered network):

helm repo add cloudtaser https://charts.cloudtaser.io
helm repo update

helm install cloudtaser cloudtaser/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.secretstore.address=https://vault.eu.example.com

Registry authentication

If your cluster requires authentication to pull cloudtaser container images from ghcr.io, create an image pull secret and reference it in your values:

kubectl create secret docker-registry ghcr-credentials \
  --docker-server=ghcr.io \
  --docker-username=<username> \
  --docker-password=<token> \
  --namespace cloudtaser-system
helm install cloudtaser cloudtaser/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.secretstore.address=https://vault.eu.example.com \
  --set imagePullSecrets[0].name=ghcr-credentials

Key Helm Values

The following values control operator behavior. For the full values reference, see Helm Values.

values.yaml
operator:
  # Vault endpoint (required)
  vaultAddress: "https://vault.eu.example.com"

  # Container image
  image:
    repository: ghcr.io/cloudtaser/cloudtaser-operator
    tag: "v0.6.9"
    pullPolicy: IfNotPresent

  # Replicas (use 3+ for production HA)
  replicaCount: 1
  ha: false
  leaderElect: false

  # Resource requests and limits
  resources:
    requests:
      cpu: 50m
      memory: 64Mi
    limits:
      cpu: 200m
      memory: 128Mi

  # Webhook configuration
  webhook:
    port: 9443
    failurePolicy: Fail      # (1)!
    timeoutSeconds: 10
    # Provide your own TLS cert (optional)
    # certSecret: my-webhook-certs

# Wrapper image injected into workload pods
wrapper:
  image:
    repository: ghcr.io/cloudtaser/cloudtaser-wrapper
    tag: "v0.1.6"
  1. Fail means pods will not be created if the webhook is unavailable. Use Ignore only in non-production environments where you prefer availability over enforcement.

Verify Installation

After the Helm install completes, verify the operator is running:

Check Pods

kubectl get pods -n cloudtaser-system

Expected output:

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

Check Webhook Configuration

kubectl get mutatingwebhookconfigurations | grep cloudtaser

Expected output:

cloudtaser-operator-webhook   1          45s

Verify the webhook is configured with the correct CA bundle:

kubectl get mutatingwebhookconfiguration cloudtaser-operator-webhook -o yaml

The caBundle field should be populated and the rules section should target pod creation.

Check Operator Logs

kubectl logs -n cloudtaser-system deployment/cloudtaser-operator --tail=20

Look for a log line confirming the webhook server has started and OpenBao connectivity is established.

Run Validation (Optional)

If you have the cloudtaser CLI installed, run a full validation:

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

Create a CloudTaserConfig CR

Instead of specifying OpenBao configuration on every workload via annotations, you can define a shared CloudTaserConfig custom resource. Workloads reference the config by name, reducing annotation duplication and centralizing management.

cloudtaserconfig.yaml
apiVersion: api.cloudtaser.io/v1alpha1
kind: CloudTaserConfig
metadata:
  name: default
  namespace: cloudtaser-system
spec:
  vaultAddress: "https://vault.eu.example.com"
  vaultRole: "cloudtaser"
  secretPaths:
    - "secret/data/shared/config"
  envMap:
    db_password: PGPASSWORD
    api_key: API_KEY
  rotation:
    strategy: restart     # restart | sighup | none
    interval: 1h

Apply the config:

kubectl apply -f cloudtaserconfig.yaml

Workloads can reference this config with a single annotation instead of specifying all OpenBao parameters individually:

annotations:
  cloudtaser.io/inject: "true"
  cloudtaser.io/config: "default"

Annotate Your First Workload

Add cloudtaser annotations to a Deployment to enable secret injection:

myapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
      annotations:
        cloudtaser.io/inject: "true"
        cloudtaser.io/config: "default"
    spec:
      containers:
        - name: myapp
          image: myapp:latest
myapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
      annotations:
        cloudtaser.io/inject: "true"
        cloudtaser.io/secretstore-address: "https://vault.eu.example.com"
        cloudtaser.io/secretstore-role: "cloudtaser"
        cloudtaser.io/secret-paths: "secret/data/myapp/config"
        cloudtaser.io/env-map: "db_password=PGPASSWORD,api_key=API_KEY"
    spec:
      containers:
        - name: myapp
          image: myapp:latest

Apply and verify:

kubectl apply -f myapp-deployment.yaml
kubectl get pods -l app=myapp

When the pod starts, the operator:

  1. Injects an init container that copies the wrapper binary to a memory-backed emptyDir (/cloudtaser/)
  2. Rewrites the container entrypoint to /cloudtaser/wrapper
  3. The wrapper authenticates to OpenBao using Kubernetes auth, fetches secrets, and fork+execs the original application with secrets as environment variables

Secrets in memory only

Secrets exist only in process memory. They never touch etcd, Kubernetes Secrets, or disk. The eBPF agent blocks attempts to read /proc/pid/environ, /proc/pid/mem, and ptrace calls on protected processes.

Compliance note

The operator's webhook uses failurePolicy: Fail by default, ensuring no pod starts without cloudtaser protection when configured. This guarantees that workloads cannot accidentally bypass secret injection and run with unprotected credentials.

Troubleshooting

Symptom Cause Fix
Pod stuck in Init Wrapper init container cannot pull image Check imagePullSecrets and registry access
Pod CrashLoopBackOff Wrapper cannot reach OpenBao Verify OpenBao endpoint is reachable from the cluster
Webhook not firing MutatingWebhookConfiguration missing or misconfigured Check kubectl get mutatingwebhookconfigurations
403 permission denied from OpenBao Kubernetes auth role misconfigured Run cloudtaser-cli target connect or verify the OpenBao role manually

For more details, see Troubleshooting.

Next Steps