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.

Check what is supported today

The measured customer path uses Linux host Port, a customer relay, a US cluster and an EU store. Kubernetes Port is not yet supported on this release: source install port (default type Kubernetes) and target install --install-port have no run here. Direct mode has separate limits. See Supported Paths.

Confidential VM recommended for full threat model

CloudTaser's full hypervisor-level protection requires AMD SEV-SNP or Intel TDX confidential compute nodes. On standard VMs, CloudTaser protects against all software-level access vectors but cannot prevent hypervisor memory inspection. See Confidential Computing Prerequisites.

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.30+ (stable admission matchConditions) Qualify the exact node image and agent release
Helm 3.x Latest 3.x
Secret store OpenBao 2.x (also compatible with HashiCorp Vault 1.15+) OpenBao (no license concerns)

Kubernetes Cluster

Platform support depends on the qualified release, node image and CNI.

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.

EU Linux host Port + customer relay

Follow the customer procedure from fresh source bootstrap through private introduction. It selects chart 1.0.359 with operator v0.13.209, wrapper/init v0.2.207 and Port v0.4.133, installed in cloudtaser-system. Use released CLI v0.19.176. Delivery passed with explicit app resources and with no resources block; the procedure states the unsupported features and token-lifetime limit. The direct profile below has a separate reachability and qualification contract.

Direct Helm installation

Install the published unified chart directly and select the direct secret-store profile explicitly. The chart default is the Port and beacon-relay profile (operator.secretBackend=inmemory, reverse-connect on, beacon dialer on). Setting only operator.secretstore.address leaves all three of those defaults in place and renders a Port configuration that needs a beacon address and auth key. Direct mode needs these overrides:

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

helm install cloudtaser cloudtaser/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.secretBackend=vault \
  --set operator.enableReverseConnect=false \
  --set operator.broker.beacon.enabled=false \
  --set operator.secretstore.address=https://vault.eu.example.com \
  --set networkPolicy.vaultCIDR=<secret-store CIDR>
Override Purpose
operator.secretBackend=vault The operator and wrapper talk to the secret store directly. The chart also grants the operator service account system:auth-delegator, which the secret store's Kubernetes auth mount needs for TokenReview.
operator.enableReverseConnect=false Turns off the reverse-connect broker that fronts the beacon relay.
operator.broker.beacon.enabled=false Turns off the beacon dialer. The chart refuses a render that disables reverse-connect while the beacon is still enabled.
operator.secretstore.address The secret-store URL, reachable from the pod network over TLS.
networkPolicy.vaultCIDR Required in direct mode while the default network policy is on: the CIDR the egress rule to the secret store allows.

Before installing, prepare the secret-store side. Enable and configure a Kubernetes auth mount (operator.vault.authPath defaults to auth/kubernetes; set it if your mount lives elsewhere), create a role for your workload service accounts, and make sure the wrapper can verify the secret store's TLS certificate. The In-Cluster Vault guide walks through the auth, role and policy setup. Do not disable eBPF or the fail-closed health gate to make an example work.

For a private CA, configure operator trust with --set-file operator.secretstore.caCert=/path/to/secret-store-ca.pem. Older injectors do not inherit this trust and the store URL into workloads: use the explicit workload address and CA ConfigMap for those releases. The ConfigMap contains public certificates only.

Direct mode has open issues on k3s

Direct mode is not a fully working path today. On k3s, wrapper startup under strict enforcement, the default direct authentication, and the cluster identity that cloudtaser-cli target connect --mode direct registers all have known failures, and cloudtaser-cli target install --mode direct does not yet select the direct profile above. Do not weaken eBPF enforcement to work around them.

This path uses direct secret-store connectivity. It does not deploy the Port or beacon-relay topology.

Use the EU OpenBao + Port + beacon procedure. It deploys the production/platform/site profiles and the external-Port runtime receiver, then uses the surviving private activation command. Hosted enrollment is not currently available; configure your own relay tenant. Do not use a single-tenant ctb1 token, persist the introduction in values, or add a second target Port.

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

Use the direct profile from the direct Helm installation above. Setting only operator.secretstore.address does not select direct mode.

Registry authentication

CloudTaser images are hosted on Google Artifact Registry (europe-west4-docker.pkg.dev/skipopsmain/cloudtaser/). If your cluster requires explicit authentication to pull from GAR, create an image pull secret and reference it in your values:

kubectl create secret docker-registry gar-credentials \
  --docker-server=europe-west4-docker.pkg.dev \
  --docker-username=_json_key \
  --docker-password="$(cat /path/to/sa-key.json)" \
  --namespace cloudtaser-system
helm install cloudtaser cloudtaser/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.secretBackend=vault \
  --set operator.enableReverseConnect=false \
  --set operator.broker.beacon.enabled=false \
  --set operator.secretstore.address=https://vault.eu.example.com \
  --set networkPolicy.vaultCIDR=<secret-store CIDR> \
  --set imagePullSecrets[0].name=gar-credentials

Enroll protected workload namespaces

Before creating an annotated workload, label its dedicated namespace:

kubectl create namespace protected-workloads
kubectl label namespace protected-workloads cloudtaser.io/inject=true --overwrite

This namespace label opts in to workload mutation and validation, including exec/attach and scheduling admission. Each protected pod still needs the secretstore.cloudtaser.io/inject: "true" annotation on its pod template. Without the namespace label, these workload webhooks are not called and a pod annotation alone does not provide injection or protection. Keep namespace-label changes under administrator control and verify injection before sending real secrets. The operator namespace, kube-system and gke-* namespaces are excluded; do not use them for protected applications. The reserved Volume Shield broker has a separate admission rule. See Webhook Hardening.

Key Helm Values

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

Chart-bundled operator version

The current cloudtaser chart (1.0.359) bundles operator v0.13.209. The latest standalone operator release is v0.13.209; it is a separate release track and is not the default installed by this chart. The example below shows the chart-bundled version.

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

  # Container image
  image:
    repository: europe-west4-docker.pkg.dev/skipopsmain/cloudtaser/cloudtaser-operator
    tag: "v0.13.209"
    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
    timeoutSeconds: 10

# Wrapper image injected into workload pods
wrapper:
  image:
    repository: europe-west4-docker.pkg.dev/skipopsmain/cloudtaser/cloudtaser-wrapper
    tag: "v0.2.207"

The webhook template hardcodes Fail. Failure policy is not an effective configurable setting. The schema still accepts compatibility inputs for failure policy and its upgrade guard; schema acceptance does not change the rendered policy. While the operator runs, admission tamper reconciliation restores the expected policy after a live edit. Restore webhook availability instead of relying on a fail-open patch.

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: protected-workloads
spec:
  vaultAddress: "https://vault.eu.example.com"
  vaultRole: "cloudtaser"
  secretPaths:
    - "secret/data/shared/config"
  envMap: "db_password=PGPASSWORD,api_key=API_KEY"

A CloudTaserConfig applies only to pods in its own namespace. The cloudtaser.io/config annotation names one explicitly. Without the annotation, the operator uses the namespace's only CloudTaserConfig, or the one named default when there are several. Rotation is not a CloudTaserConfig field: set the cloudtaser.io/rotation pod annotation (default restart, see Rotation).

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:
  secretstore.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: protected-workloads
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
      annotations:
        secretstore.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: protected-workloads
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
      annotations:
        secretstore.cloudtaser.io/inject: "true"
        cloudtaser.io/secretstore-address: "https://vault.eu.example.com"
        cloudtaser.io/secretstore-role: "cloudtaser"
        secretstore.cloudtaser.io/secret-paths: "secret/data/myapp/config"
        secretstore.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

Inferred from the chart selector: wrapper injection and its webhook outage refusal apply only to namespaces labelled cloudtaser.io/inject=true, subject to the chart's namespace exclusions. Within that scope, an unreachable injector rejects Pod CREATE/UPDATE requests whether annotated or not. Outside it, an injection annotation alone does not invoke this webhook or provide wrapper protection. Other admission policies have their own scope; this is not a promise that every out-of-scope request is admitted.

Verify the namespace label and admitted Pod wrapper before relying on protection. See operational readiness.

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

Platform qualification

Platform Qualification Evidence limits
GKE Standard COS/Calico Qualification pending Historical delivery was recorded on 31 October 2026; a matched release qualification record is still required.
GKE Standard Ubuntu Qualification pending Node image, active BPF LSM and required deny paths must be verified.
EKS node groups Qualification pending No matched release deployment qualification is recorded here.
AKS Qualification pending Assessment is not a live run; verify the actual node and CNI.
k3s and other distributions Qualification pending Guest kernel tests alone do not qualify a chart deployment.

For the Linux-host customer path, use the host-Port installation procedure. The published CLI's source install port defaults to a Kubernetes route; select --type=linux explicitly when generating the host installer. A Kubernetes Port installation is unqualified and is not a substitute for that procedure.

cloudtaser-cli source install port --type=linux --dry-run > install-port.sh

Review the generated signed-binary installer and complete the documented host launch, relay trust and introduction steps. Rendering an installer does not establish end-to-end qualification or authorize changing enforcement settings.