TECHNICAL REFERENCE SPEC

How cloudtaser works, layer by layer.

A reference for the four layers that form the cryptographic boundary - injection, encryption, enforcement, and connectivity - plus components, kernel requirements, supported platforms, and security guarantees.

The provider's trust boundary starts at entrypoint rewrite.

A mutating admission webhook intercepts pod creation, resolves the container's original entrypoint from the OCI registry, and rewrites command to launch through the cloudtaser wrapper. From that moment forward, the app's address space holds secrets that are unreachable by any process the provider controls.

How the webhook rewrites a pod

On CREATE or UPDATE of a pod carrying cloudtaser.io/inject: "true", the webhook queries the container registry (supporting private registries via imagePullSecrets) to resolve the image's original ENTRYPOINT and CMD. It injects a read-only init container that copies a single static wrapper binary into an emptyDir, then overwrites the main container's command to launch the wrapper with the original entrypoint as arguments.

The webhook verifies the image digest comes from a trusted registry before mutating. Injected containers automatically receive hardened pod security: seccomp: RuntimeDefault, readOnlyRootFilesystem: true, and explicit resource limits. No CRDs are required - the wrapper's configuration travels in annotations.

The wrapper as PID 1

At pod start, the wrapper runs as PID 1. It authenticates to the secret store (Kubernetes auth or token auth, mTLS-unsealed), fetches the secret paths listed in cloudtaser.io/secret-paths, and allocates each value into a memfd_secret(2) file descriptor. These file descriptors are special: their backing pages are physically removed from the kernel's direct memory map and cannot be re-mapped by any process including the kernel itself.

The wrapper then fork+execs the original application and stays alive as the parent - renewing vault tokens, renewing secret leases, re-fork+execing on rotation, and forwarding signals. Total footprint: one pure-Go binary, ~400 lines, zero native dependencies, no sidecar container.

Transparent delivery via execve() inheritance

The child inherits its environment - including secret values - through the standard Unix execve() mechanism. Applications call getenv("DB_PASSWORD") / os.Getenv / process.env and receive the secret value with zero source changes. This works for all binaries - including statically-linked Go (CGO_ENABLED=0), Rust x86_64-unknown-linux-musl, and hand-compiled -static C. No dynamic linker required. No SDK required. No Dockerfile edits.

The asterisk is sensitivity, not compatibility: once delivered, the secret is present in /proc/PID/environ of the child process. The wrapper's dumpable=0 raises the kernel-enforced procfs barrier; the eBPF layer detects cross-process reads of /proc/PID/environ and blocks them synchronously on kernels with CONFIG_BPF_KPROBE_OVERRIDE=y (e.g. Ubuntu 24.04). On kernels without that config (e.g. GKE COS), the eBPF layer detects and logs the attempt - detection is active everywhere; synchronous block is kernel-dependent. If your threat model rejects the env-var intermediate entirely, the optional cloudtaser Go SDK reads directly from the wrapper's memfd_secret FD - one import, one-line replacement for os.Getenv.

Memory protection beyond memfd

Three additional hardenings layer on top of memfd_secret(2):

  • mlock(2) - locks secret pages in RAM so they are never paged to swap. cloudtaser_REQUIRE_MEMFD_SECRET strict mode refuses to start on kernels without memfd or when mlock limits are insufficient - fail-fast rather than silent degradation.
  • PR_SET_DUMPABLE = 0 - excludes secret pages from core dumps so crash reports never contain credentials, even if the application ends up on a shared filesystem.
  • PID pre-registration - the wrapper registers child PIDs with the eBPF agent over a Unix domain socket before exec(), closing the zero-second gap between process start and enforcement activation.

Supported container images

Because the webhook resolves the original entrypoint from the OCI registry, any base image works: scratch, distroless, Alpine, Debian, Ubuntu, RHEL, Wolfi, custom. No Dockerfile edits, no Helm template changes, no sidecar config files to render. Three annotations on the deployment and a kubectl rollout restart.

Ciphertext leaves the pod. Plaintext never leaves memory.

The S3 proxy is a drop-in, S3-compatible reverse proxy that performs envelope encryption on every object before it reaches cloud storage. Point your existing SDK at the proxy, and the semantics of PutObject, GetObject, and multipart uploads are preserved - but the provider only ever receives AES-256-GCM ciphertext.

Envelope encryption per object

On every PutObject, the proxy generates a fresh Data Encryption Key (DEK) and encrypts the object with AES-256-GCM. The DEK itself is then wrapped through OpenBao's Transit secret engine using a transient key - the plaintext DEK exists only in the proxy's memory during the encryption operation and is zeroed on completion. The wrapped DEK is stored as object metadata alongside the ciphertext. On GetObject, the wrapped DEK is unwrapped, the object is decrypted, and the DEK is again zeroed.

Transient keys and zero persistence

Transient keys do not live in the OpenBao KV store - they are derived, used, and destroyed within a single unwrap operation. This means the plaintext DEK never persists to disk, swap, or any audit log. What the provider's storage endpoint sees is a stream of AES-256-GCM ciphertext bytes plus wrapped-DEK metadata that is meaningless without the OpenBao instance.

Multipart uploads and range reads

S3's multipart upload semantics require per-chunk encryption so that individual parts can be uploaded and retried independently. The proxy derives a per-part DEK from the object's root DEK via HKDF, so large objects never require a single in-memory buffer. Range reads are handled by decrypting only the blocks that cover the requested byte range, keeping memory usage bounded for seek-heavy access patterns.

Backend compatibility

Any S3-API backend works as a downstream: AWS S3, Google Cloud Storage via interop mode, MinIO, Wasabi, Backblaze B2, Cloudflare R2. The proxy speaks the v4 signature variant and supports presigned URLs (encryption is opaque to the signed URL itself - the proxy decrypts on the read side, not the signer's side).

Layered enforcement. 35+ vectors, continuously active.

Two layers compose. First the wrapper sets PR_SET_DUMPABLE=0 on every protected process - that one prctl flips /proc/$pid/mem to mode 0600 owner-only and routes every external ptrace attach through ptrace_may_access, which the kernel denies same-namespace. Second the eBPF agent runs as a least-privilege DaemonSet - not with privileged: true, but with explicit capabilities (CAP_BPF, CAP_PERFMON, CAP_SYS_ADMIN where strictly required). It attaches to more than twenty kernel hooks covering the residual CAP_SYS_PTRACE-elevated bypass paths and the broader memory-read syscall surface - logging exfiltration attempts in real time. Where CONFIG_BPF_KPROBE_OVERRIDE=y is enabled (the kernel-side prerequisite for kprobe override), the hooks return -EPERM synchronously; on kernels without that config (e.g. GKE COS default node image), detection is active but synchronous blocking is not. BPF-LSM active enforcement, which does not depend on kprobe override, is in beta and ships default-on in v0.6 for kernel 5.15+.

Blocked attack vectors

  • /proc path hardening - 12+ procfs paths monitored at open() via kprobe: mem, maps, smaps, environ, syscall, stack, pagemap, auxv, clear_refs, map_files, and more. Access attempts are logged and synchronously denied on kernels with CONFIG_BPF_KPROBE_OVERRIDE=y; detected and logged on all other kernels.
  • ptrace attach - ptrace() syscall denied for protected PIDs. Standard debugger attach path is closed.
  • perf_event_open - hardware watchpoints denied; the breakpoint-triggered read path is closed.
  • io_uring submission queues - ring setup and submit intercepted via tracepoints, preventing async I/O bypass of seccomp filters.
  • userfaultfd - userspace page-fault side-channel closed via syscall denial on protected PIDs.
  • Global kernel module and eBPF program loading - init_module and bpf(BPF_PROG_LOAD) are watched system-wide, not just per-process. Loading a rogue LSM or kmod to read memory fires an alert regardless of which process does it.
  • Spectre/Meltdown mitigation verification - agent reads /sys/devices/system/cpu/vulnerabilities/* at startup and feeds the result into a per-node protection score. Nodes with disabled mitigations are flagged.

Where the attack surface ends

After these vectors, the only remaining path to reading a protected secret is hypervisor-level physical memory access. In a typical cloud deployment the provider controls the hypervisor and can, with non-trivial effort, read guest physical memory. This is the surface cloudtaser cannot close alone. Closing it requires the CPU itself to enforce memory isolation from the hypervisor - AMD SEV-SNP, Intel TDX, or ARM CCA - which AWS, GCP, and Azure are rolling out as "confidential computing" node types. cloudtaser runs unchanged on confidential compute and closes the remaining gap.

Observability

The agent exposes 7 Prometheus metrics at /metrics:

  • cloudtaser_protected_processes_total - gauge
  • cloudtaser_enforcement_denials_total{vector=...} - counter, per-vector
  • cloudtaser_proc_access_blocked_total{path=...} - counter
  • cloudtaser_privilege_escalation_attempts_total - counter
  • cloudtaser_protection_score - gauge, per-node
  • cloudtaser_memfd_pages_locked_bytes - gauge
  • cloudtaser_cpu_mitigations_enabled{name=...} - gauge

Zero-gap registration

The wrapper registers its child PID with the agent over a Unix domain socket before the exec() that launches the application. The agent's eBPF fork tracker then follows any descendants automatically. This closes the millisecond-level window between process start and policy activation that would otherwise exist - at no point is a protected process unobserved.

No VPN. No public endpoint. Outbound 443 only.

The beacon relay lets your cluster and your EU/UK secret store find each other without punching inbound holes in either firewall. Both sides connect outbound to the beacon on TCP 443; peers discover each other via a SHA-256 info hash derived from shared configuration; the connection is mTLS end-to-end so the beacon itself only sees encrypted bytes.

How peer discovery works

At install, both the cluster operator and the secret-store bridge derive an info hash from SHA-256(shared-secret || context). Both sides publish their availability to the beacon under that hash. The beacon matches them and opens a duplex relay. Because the info hash is a cryptographic derivative, the beacon cannot enumerate what hashes exist for what clusters - it only knows the pairs currently connected to it.

mTLS end-to-end, not beacon-terminated

The beacon does not hold any TLS keys. The cluster and the secret store perform a mutual TLS handshake directly with each other through the relay - the beacon forwards the encrypted bytes without decrypting them. This means the beacon operator cannot read vault tokens, secret values, or any other content even if compromised. The beacon is customer-deployed infrastructure you run yourself; cloudtaser does not operate any beacon in a production data path. A public demo relay at beacon.cloudtaser.io exists for the live demo at cloudtaser.io/demo-lab and pilot scenarios only.

Bridge cert storage — current state and roadmap

Bridge certs are generated at install time and held in operator process memory — never written to etcd or Helm release state. On managed Kubernetes, the cloud provider's host-level role can read operator process memory via /proc/<pid>/mem because the operator does not yet call prctl(PR_SET_DUMPABLE, 0) for its own process. The fix — dumpable=0 for the operator plus memfd_secret-backed bridge-token storage — is tracked in cloudtaser-operator#306 and ships in the next operator release. Until then, enable kernel.yama.ptrace_scope=2 and the recommended AppArmor profile on operator nodes to restrict process-memory access at the host level.

High availability

Configure multiple beacon addresses in the Helm chart (--set operator.broker.beacon.endpoints=[...]). The operator connects to the first available and falls back automatically. There is no single point of failure.

Legacy direct-vault mode

If you cannot deploy a beacon relay (e.g., you already have private network connectivity and want to skip the hop), the Helm chart supports direct-vault mode with operator.secretBackend=vault and operator.broker.beacon.enabled=false. The cluster connects directly to the vault address.

05 · Components

The stack, componentized.

Every component is independently versioned and published to both Google Artifact Registry (chart default) and GitHub Container Registry.

ComponentRoleImageStatus
OperatorMutating webhook, entrypoint resolution, sealed-mode token brokering.Injectioncloudtaser-operatorBeta
WrapperPID 1 process wrapper, memfd delivery, fork+exec env-var handoff.Injectioncloudtaser-wrapperBeta
S3 ProxyEnvelope encryption for S3, GCS interop, MinIO. Transient DEKs.Encryptioncloudtaser-s3-proxyBeta
DB ProxyTransparent credential injection for PostgreSQL, MySQL connections.Encryptioncloudtaser-db-proxyBeta
eBPF Agent35+ detection vectors, Prometheus metrics, UDS IPC to wrapper. Synchronous block on kernels with CONFIG_BPF_KPROBE_OVERRIDE=y; BPF-LSM enforcement in v0.6.Enforcementcloudtaser-ebpfBeta
CLIInstall, discover, migrate, audit, debug - single static binary.OperationscloudtaserBeta
PlatformSaaS control plane for policy, audit, and multi-cluster observability.Management-Planned

Helm chart: charts.cloudtaser.io · images signed with Cosign · SBOM published · Trivy scanned.

06 · Requirements & platforms

Kernel 5.14+. Kubernetes 1.28+.

KERNEL

Linux 5.14 or newer

memfd_secret(2) was introduced in Linux 5.14. Ubuntu 22.04, Debian 12, RHEL 9, Amazon Linux 2023, and Bottlerocket all meet this. Earlier kernels are not supported - strict mode refuses to start.

KUBERNETES

Kubernetes 1.28+

cloudtaser uses v1 admission webhooks and Pod Security Admission. GKE, EKS, and AKS all support 1.28+ on current release tracks. Self-hosted and CNCF-conformant distributions work identically.

ARCH

amd64 and arm64

All components ship multi-arch images. Graviton (c7g, r7g), Axion, and Ampere Altra nodes are supported. The eBPF programs are CO-RE - one build runs on any kernel version that meets the floor.

CONFIDENTIAL

SEV-SNP · TDX · CCA

For hypervisor-level isolation, cloudtaser runs unchanged on AWS Nitro Enclaves, GCP Confidential VMs (SEV-SNP / TDX), and Azure Confidential Containers. Confidential compute closes the last remaining attack vector.

SECRET STORE

OpenBao or HashiCorp Vault

OpenBao 2.0+ (the open-source fork that preserves Transit) or HashiCorp Vault 1.13+. Self-host in any EU/UK data centre - Frankfurt, Amsterdam, Dublin, London - on VMs, bare metal, or a dedicated K8s cluster.

NETWORK

Outbound TCP 443

Beacon mode requires only outbound 443 from the worker nodes to your own beacon relay (deploy from the published Helm chart; ~2 MB static Go binary). No inbound rules, no VPN, no public vault endpoint, no static IPs, no third party in the data path.

07 · Security guarantees

Honest boundaries. No magic.

Every security claim cloudtaser makes is backed by a specific kernel-level or cryptographic mechanism. Anything outside those mechanisms is explicitly out of scope.

✓ ENFORCED

The provider cannot read secrets from process memory

Backed by memfd_secret(2) page unmapping + mlock(2) + 35+ eBPF enforcement vectors. Root on the node, including the provider's host agents, cannot access secret pages.

✓ ENFORCED

The provider cannot decrypt object storage

Client-side AES-256-GCM with unique DEKs, wrapped through OpenBao Transit. The storage backend holds ciphertext and wrapped DEKs that are meaningless without your OpenBao instance.

✓ ENFORCED

Secret rotation is automatic

Lease renewal and re-authentication handled by the wrapper. Rotated values are written to the same memfd_secret FDs; the application sees fresh credentials on the next getenv() call.

✓ ENFORCED

Audit trail covers every secret access

OpenBao's audit log records every unseal, fetch, and rotation. eBPF metrics record every enforcement denial. Prometheus scrape + your SIEM of choice.

- SCOPE

What memfd_secret protects vs the working heap

memfd_secret(2) covers your secret pages - API keys, DB credentials, tokens - by removing them from the kernel's direct map. Your app's working heap - DB rows you've fetched, decrypted bytes you're computing on - lives in ordinary guest RAM. On commodity compute the hypervisor can read that; on SEV-SNP, TDX, Nitro, or CCA it cannot. Sovereign deployment guide →

- OUT OF SCOPE

Hypervisor-level memory reads

A compromised or legally-compelled hypervisor can read guest physical memory. cloudtaser does not close this on commodity compute. Run on SEV-SNP, TDX, or CCA confidential compute to close it.

- OUT OF SCOPE

Side-channel attacks on CPU microarchitecture

Spectre, Meltdown, Downfall, and similar transient-execution attacks. cloudtaser verifies mitigations are enabled but cannot invent new ones. Mitigated kernels + confidential compute are required for high-threat scenarios.

- OUT OF SCOPE

Application-level vulnerabilities

If your application logs secrets to stdout, echoes them in error messages, or sends them over unencrypted HTTP to a third party, cloudtaser cannot help. Protection ends at the application's address space boundary.

- OUT OF SCOPE

Compromised OpenBao operators

The trust root is your OpenBao. An attacker with root on the OpenBao VM can read everything. Standard OpenBao hardening (HSM-backed storage, auto-unseal, audit logging, operator access controls) applies.

08 · See it in action

A secret crossing the boundary.

Step 6 of the live demo: kubectl rollout restart fires, the cloudtaser-init container injects the wrapper, and the secret travels from the EU OpenBao through the beacon relay into a memfd_secret page — never touching etcd or disk via the wrapper's secret store. The wrapped application's runtime (JVM heap dumps, kernel coredumps) is governed by host policy — see the per-runtime hardening matrix. Three panes, one moment.

TARGET CLUSTER US STEP 6
$ kubectl rollout restart deployment/payments-api -n demo-app deployment.apps/payments-api restarted $ kubectl logs -n demo-app -l app=payments-api \ -c cloudtaser-init --follow cloudtaser-init time=2026-04-25T14:32:01Z level=info msg="wrapper starting" pid=1 version=v0.2.1 cloudtaser-init time=2026-04-25T14:32:01Z level=info msg="authenticating" method=kubernetes-auth role=payments-api vault=demo-secret-store.cloudtaser.io cloudtaser-init time=2026-04-25T14:32:02Z level=info msg="fetching secret" path=secret/data/demo-app/db cloudtaser-init time=2026-04-25T14:32:02Z level=info msg="memfd_secret allocated" fd=5 pages=1 keys="DB_PASSWORD" mlock=true dumpable=false cloudtaser-init time=2026-04-25T14:32:02Z level=info msg="secret delivered" latency_ms=312 registered_with_ebpf=true child_pid=23 $ kubectl exec -n demo-app deploy/payments-api -- \ sh -c "echo \$DB_PASSWORD | wc -c" 33
BEACON RELAY EU LIVE
beacon 2026-04-25T14:32:00Z listening addr=:443 tls=mutual peers=2 beacon 2026-04-25T14:32:01Z pair_open hash=a3f9c1 bridge=eu-west4 broker=us-west1 latency_ms=18 beacon 2026-04-25T14:32:01Z tls_relay direction=bridge→broker bytes=0 msg="TLS handshake forwarded (opaque)" beacon 2026-04-25T14:32:01Z tls_relay direction=broker→bridge bytes=0 msg="TLS handshake complete (opaque)" beacon 2026-04-25T14:32:02Z relay bytes_fwd=4096 bytes_rev=1024 plaintext=none keys=none msg="encrypted bytes only; beacon sees nothing" beacon 2026-04-25T14:32:02Z pair_close hash=a3f9c1 duration_ms=1021 bytes_total=5120 reason=eof beacon 2026-04-25T14:32:02Z idle active_pairs=0 total_served=14
SECRET STORE EU LIVE
# OpenBao audit log — europe-west4 # tail -f /var/log/openbao/audit.log {"time":"2026-04-25T14:32:01Z", "type":"request","auth":{"display_name": "kubernetes-payments-api","policies": ["cloudtaser-demo-app"]}, "request":{"operation":"read", "path":"secret/data/demo-app/db"}} {"time":"2026-04-25T14:32:01Z", "type":"response","auth":{"display_name": "kubernetes-payments-api"}, "response":{"data":{"metadata":{ "version":3}}}, "error":""} {"time":"2026-04-25T14:32:02Z", "type":"request","auth":{"display_name": "kubernetes-payments-api"}, "request":{"operation":"renew", "path":"auth/token/renew-self"}} → secret delivered to process memory → wrapper store: never written to etcd or disk → memfd_secret pages: kernel-direct-map removed → eBPF detection: active on PID 23
How cloudtaser threads a secret end-to-end. Cluster (US) → beacon relay (EU Frankfurt) → OpenBao (EU Netherlands) → memfd_secret page in process memory. The cloud provider sees ciphertext at every hop. Live demo at /demo-lab →
09 · How the demo lab works

Real cluster. Real protocol.

The /demo-lab page runs the actual beacon relay and secret delivery flow on a real managed GKE cluster in the US plus two EU-hosted VMs — not a simulation. Here is what runs, where, and what it costs.

§ Page details

/demo-lab is publicly accessible. It is linked from the navigation, included in sitemap.xml, and listed in llms.txt. Direct-URL sharing and search discovery both work.

§ Demo infrastructure
US
demo-cluster.cloudtaser.io
GKE zonal · n2d-highcpu-2 · AMD SEV · us-west1-a
Operator + wrapper + eBPF agent + demo-app pod
~$30–46/mo
EU
demo-beacon.cloudtaser.io
e2-micro · Frankfurt (europe-west3)
cloudtaser-beacon relay · orchestrator · two ttyds
~$9/mo
EU
demo-secret-store.cloudtaser.io
e2-micro · Netherlands (europe-west4)
OpenBao · cloudtaser-cli · EU key custody
~$9/mo

Total: ~$48–64/month for the full topology (GKE cluster + 2 EU VMs). Public DNS via Cloudflare Tunnels; no direct internet exposure to any node or VM.

Honest disclosure: the GKE cluster and both EU VMs are on GCP, a US-parented provider. Per our own sovereign-deployment guide, GCP EU regions do not establish sovereignty under CLOUD Act analysis. For production sovereignty the secret store would move to Hetzner, OVH, Scaleway, IONOS, or Exoscale. Migration for the demo is planned.

Ready to install?

The live demo at cloudtaser.io/demo-lab streams three real systems - a managed GKE cluster in the US, a beacon relay VM in Frankfurt, and an OpenBao secret store VM in the Netherlands - end to end. No signup, no install. Watch when someone else is driving, or take the wheel yourself when it is idle.