Skip to content

Memory Protection

Authorised application boundary

Authorised application code is trusted. An authorised app can send out a secret it has copied or encoded. Current content checks do not provide a general guarantee against this disclosure or a malicious authorised app. Memory-read tests and a Ready agent do not prove copied/encoded-secret exfiltration prevention. Enforced NetworkPolicy can restrict destinations, but allowed traffic remains a channel. See Security Model.

cloudtaser delivers fetched secrets through process memory rather than Kubernetes Secrets or disk files. Inherited environment delivery places values in the child's /proc/<pid>/environ; active eBPF enforcement restricts external reads. This page documents each memory protection mechanism, how it works at the kernel level, and what it defends against.

Compliance Summary

cloudtaser combines memory isolation, read-path enforcement and tamper signals. Protection provides tamper signals within the deployed enforcement boundary, not absolute against node root or arbitrary kernel code. These controls provide evidence for a deployment-specific compliance assessment; they do not establish compliance or eliminate provider risk by themselves.


Overview

The wrapper fetches secrets after startup, prepares protected mappings and selects a delivery mode before launching the application:

  • Eligible glibc-dynamic programs use the LD_PRELOAD interposer by default. Mapped secret keys are removed from the exec environment and served through supported getenv() calls.
  • Static binaries, musl environments and explicit inherited-environment mode receive actual child environment entries. Those entries and application-created copies require active read-path enforcement.
  • The wrapper's own exec environment contains configuration, not fetched secrets. Memory allocation, locking and dump restrictions are checked separately from delivery compatibility.

See Wrapper runtime compatibility. These layers depend on kernel integrity; they do not make a compromised kernel trustworthy.


memfd_secret(2)

Kernel requirement: Linux 5.14+

memfd_secret creates an anonymous file descriptor whose mapped pages have no kernel direct-map alias. It strengthens isolation of that mapping; it does not protect every copy of a secret.

How It Works

int fd = memfd_secret(0);
ftruncate(fd, secret_size);
void *addr = mmap(NULL, secret_size, PROT_READ | PROT_WRITE,
                   MAP_SHARED, fd, 0);
// Write secrets to addr
// The mapped pages have no kernel direct-map alias

When memfd_secret is called, the kernel:

  1. Allocates pages from the buddy allocator
  2. Removes those pages from the kernel's direct map (the linear mapping of all physical memory)
  3. Maps the pages into processes holding the descriptor

Kernel integrity remains part of the boundary

Removing the direct-map alias restricts ordinary read paths. It does not make the pages unreachable to arbitrary kernel code, and it does not protect ordinary heap or environment copies. Unrestricted node root can load modules/BPF where permitted, use kexec on non-lockdown images, inspect copies through /proc/kcore, or remove bpffs pins after killing the agent. CloudTaser provides tamper signals within its enforcement boundary, not an absolute root or kernel barrier. See the security model.

The Linux memfd_secret manual describes the direct-map and cross-process restrictions without guaranteeing protection against every kernel attack.

What It Defends Against

Attack Vector Scope
Ordinary cross-process memory reads Secret mappings have additional kernel restrictions; ordinary application copies need active eBPF protection
Arbitrary kernel module or privileged kernel-code execution Outside the isolation guarantee
Ordinary heap/environment copies Outside the secret mapping; protection depends on delivery mode and active read-path enforcement
Hypervisor memory inspection Requires a confidential-compute substrate; memfd_secret alone does not address it

Configuration

env:
  - name: CLOUDTASER_REQUIRE_MEMFD_SECRET
    value: "true"   # Fail startup if memfd_secret unavailable

When CLOUDTASER_REQUIRE_MEMFD_SECRET=true, the wrapper refuses startup if the required secret mapping is unavailable. Kernel version alone does not establish availability; configuration and architecture matter. This is a software isolation requirement, not hardware memory encryption.


mlock(2)

Kernel requirement: Any Linux

mlock pins memory pages in physical RAM, preventing the kernel from swapping them to disk.

How It Works

mlock(secret_addr, secret_size);

The kernel marks the pages as non-evictable. They will remain in RAM until explicitly unlocked or the process exits.

What It Defends Against

Attack Vector Blocked? Why
Swap file forensics Yes The memfd_secret region is not swappable; ordinary application heap/environment copies can be swapped
Disk imaging after shutdown Yes (for swap) No swap footprint to recover
OOM killer eviction to swap Yes Pages are pinned regardless of memory pressure

mlock does not hide memory from root

A root attacker can still read mlocked pages via /proc/PID/mem or ptrace. mlock only prevents disk exposure. It is a complement to memfd_secret, not a substitute.

Configuration

env:
  - name: CLOUDTASER_REQUIRE_MLOCK
    value: "true"   # Fail startup if mlock fails

CAP_IPC_LOCK

The wrapper container requires CAP_IPC_LOCK to call mlock. cloudtaser's Helm chart sets this capability by default. Without it, mlock will fail and the wrapper will fall back to unprotected memory (unless CLOUDTASER_REQUIRE_MLOCK=true).


MADV_DONTDUMP

Kernel requirement: Linux 3.4+

MADV_DONTDUMP tells the kernel to exclude specific memory regions from core dumps.

How It Works

madvise(secret_addr, secret_size, MADV_DONTDUMP);

When the process crashes and the kernel generates a core dump, pages marked with MADV_DONTDUMP are omitted. The core file contains a hole where the secret region was.

What It Defends Against

Attack Vector Blocked? Why
Core dump analysis Yes Secret pages excluded from dump
Crash dump collection services Yes Secrets not in the dump file
Cloud provider crash analysis pipelines Yes Marked mappings are omitted; ordinary application copies need separate protection

Defense in depth with PR_SET_DUMPABLE

MADV_DONTDUMP protects the specific memory region. PR_SET_DUMPABLE(0) prevents core dumps entirely. cloudtaser applies both.


PR_SET_DUMPABLE(0)

Kernel requirement: Any Linux

PR_SET_DUMPABLE(0) sets the process as non-dumpable, which has several security effects beyond just preventing core dumps.

How It Works

prctl(PR_SET_DUMPABLE, 0);

Effects

Effect Description
No core dumps Kernel will not generate core dump files for this process
Restricted /proc access /proc/PID/{mem,maps,environ,syscall,stack} become unreadable by non-root
ptrace restriction Prevents ptrace ATTACH from non-parent processes (defense in depth with eBPF)
No SIGABRT dumps Abort signals do not produce dump files

Root can override PR_SET_DUMPABLE

PR_SET_DUMPABLE(0) is not a boundary against arbitrary kernel code or all privileged access. coredump_filter controls dump contents; it does not change the dumpable attribute. See PR_SET_DUMPABLE.


Wrapper environ is structurally clean (environ_scrubbed)

Kernel requirement: Any Linux

The wrapper's own /proc/self/environ does not contain secret values. This is a structural property of the fork+exec architecture, not a runtime mutation.

How It Works

/proc/PID/environ on Linux is a frozen snapshot of the bytes between mm->env_start and mm->env_end set at execve() time. The kernel does not update this region when a process subsequently calls os.Setenv / setenv / putenv — those calls modify the C library's environ array in heap memory, not the kernel's record of the original envp[].

The wrapper exploits this:

  1. The wrapper's container is started by the operator webhook with a configuration-only env (CLOUDTASER_*, VAULT_ADDR, etc.). No secrets in envp[] at exec time.
  2. After the wrapper starts, it fetches secrets from vault into Go heap memory, the protected memfd_secret region, or both. This Go-heap data does not flow back into the kernel's frozen /proc/self/environ.
  3. Strict interposer delivery removes mapped secret keys from the child's exec environment. In inherited-environment mode, BuildChildEnv supplies actual values to the child, so its initial /proc/PID/environ contains them. Neither mode adds fetched secrets to the wrapper's original exec environment.

The environ_scrubbed protection score check therefore confirms a structural property: at no point does the wrapper's own /proc/self/environ byte-range contain secret keys.

Child environment and application copies

Inherited-environment mode leaves secret values in the child's initial environment. The wrapper's clean environment does not remove them. Active target-anchored BPF LSM enforcement protects external procfs reads; it does not erase values or prevent the authorized application from copying or transmitting them. Strict interposer delivery avoids the initial environment entries, but applications may still make ordinary copies.

What It Defends Against

Attack Vector Blocked? Why
Reading wrapper's /proc/1/environ for secrets Yes Wrapper exec'd without secrets in envp[]; secrets enter only after exec, in Go heap
Reading child's /proc/<child>/environ Delegated to eBPF Active target-anchored BPF LSM policy restricts external procfs reads
Container runtime env inspection (docker inspect) Yes Container spec contains only CLOUDTASER_* configuration, not secrets
Kubernetes API env inspection N/A Secrets never set via K8s env vars

Why this matters

Many secret injection tools set secrets as environment variables in the container spec (the env: field in the pod YAML, or envFrom against a Kubernetes Secret). Those values land in the kernel's envp[] at exec time and are then readable in /proc/PID/environ for the entire lifetime of the process — by anything with ptrace_may_access-equivalent permissions on the namespace. cloudtaser fetches secrets from EU vault directly into wrapper memory after the wrapper has already exec'd, so the wrapper's frozen envp[] snapshot never contained them. The child's snapshot contains them in inherited-environment mode; strict interposer mode avoids those initial entries. Active eBPF enforcement remains required in both modes.


LD_PRELOAD Interposer

Compatibility: Eligible glibc-dynamic programs calling libc getenv() / secure_getenv().

The wrapper enables the interposer by default for eligible dynamic programs. It removes mapped secret keys from the exec environment and supplies supported reads from the shared secret mapping. Static and musl programs use inherited environment delivery instead.

Copies and runtime APIs

Returning a pointer into the mapping avoids an initial environment entry for that read. The application can still copy the value to ordinary memory, log it, or pass it to a child. The interposer does not guarantee that only one copy exists.

Languages that snapshot environ do not necessarily call libc getenv() for later reads. In particular, enabling CGO does not make Go's os.Getenv use the interposer. Snapshot mode and inherited environment mode have different exposure and rotation properties; consult the runtime compatibility table before choosing one.


Fallback Behavior on Older Kernels

When memfd_secret is unavailable and fallback is permitted, the wrapper uses an anonymous mapping with the available locking and dump restrictions. This loses the secret mapping's direct-map isolation. Availability depends on the running kernel and configuration, not just its version.

Root and kernel limitations apply on every kernel

A newer kernel with memfd_secret does not make arbitrary kernel execution safe. Keep node privileges restricted and verify the active enforcement profile. Tier 2 retains primary BPF LSM read-path protection but has detect-only secondary vectors; see platform compatibility.

Strict Mode

Set CLOUDTASER_REQUIRE_MEMFD_SECRET=true to require successful secret-memory allocation:

env:
  - name: CLOUDTASER_REQUIRE_MEMFD_SECRET
    value: "true"

This requires the mapping; it does not establish a data-sovereignty guarantee or replace a deployment-specific threat assessment.


Summary Table

Mechanism Defends Against Kernel Can Root Bypass? eBPF Backup?
memfd_secret Ordinary access to the secret mapping 5.14+ with support enabled Arbitrary kernel code is outside the guarantee Ordinary copies still need enforcement
mlock Swap to disk Any No (but irrelevant -- root reads RAM directly) N/A
MADV_DONTDUMP Dumping the marked region 3.4+ Arbitrary kernel code is outside the guarantee Does not cover ordinary copies
PR_SET_DUMPABLE(0) ptrace, /proc access, core dumps Any Yes (kernel module) With active BPF LSM: blocks covered ptrace access and detects module-loading attempts
Wrapper environ clean (environ_scrubbed) Structural read of wrapper's own /proc/self/environ Any N/A -- secrets never enter wrapper envp[] at exec time Active target-anchored BPF LSM policy restricts external reads
LD_PRELOAD Initial secret environment entries in strict mode Eligible glibc-dynamic programs Application-created copies remain possible Active enforcement remains required

eBPF Enforcement | Protection Score

The default CLOUDTASER_REQUIRE_MEMFD_SECRET=true refuses fallback. Standalone code with that requirement disabled can use memfd_create, then an immediately unlinked file in /dev/shm or the system temporary directory. The operator locks the requirement on injected workloads. This exception does not qualify a production path. Memory-backed volumes hold support files; they do not extend the protected-region guarantee to runtime heap or environment copies.

Cgroup anchor registration is inferred from startup wiring, not a measured refusal guarantee: the wrapper can log and continue without the anchor when cgroup v2 or ID resolution is unavailable. Verify the runtime state on the selected node.