Protection Score Reference¶
Phase L4 status — code merged, kernel-matrix validation pending
The BPF LSM enforcement layer (cloudtaser-ebpf#179 Phases L1-L4) has its code merged to main (PRs #183, #185, #190, #193, plus wrapper #123). It has not yet been validated on the kernel matrix because the kernel-test workflow is blocked on cloudtaser-pipeline#227. Until that resolves and a tagged release ships, the per-distro LSM-tier claims below are derived from kernel configuration (which distros build CONFIG_BPF_LSM=y with bpf in the boot LSM stack), not from observed ebpf_lsm_enforce.Active = true in production. The wrapper-side check is honestly gated on agentStatus.LSMCount > 0 so customers always see the actual posture, not the aspirational one.
The protection score is a numeric measure of which defense mechanisms are active for a cloudtaser-protected process. The maximum score is 125 points across 13 checks. This page documents every check, its point value, what it proves, and how to enable it.
The score reflects a tiered enforcement model:
- LSM tier (strongest) —
ebpf_lsm_enforce(15 pts). BPF LSM hooks provide synchronous in-kernel deny with noALLOW_ERROR_INJECTIONgate. Designed to work on every cloud-standard distro that shipsCONFIG_BPF_LSM=ywithbpfin the boot LSM stack: GKE COS, GKE Ubuntu, EKS Bottlerocket, EKS AL2023, AKS Azure Linux 3.0+, Talos. This is the headline design intent of the BPF LSM migration (cloudtaser-ebpf#179 Phases L1-L4); per-kernel runtime confirmation lands once kernel-test infra (#227) is unblocked. - Kprobe tier (fallback) —
ebpf_kprobes(10 pts). Synchronous deny viabpf_override_return(). Requires bothCONFIG_BPF_KPROBE_OVERRIDE=yand the target syscall in the upstreamALLOW_ERROR_INJECTIONallow-list. Stays attached as a structural fallback on hooks where LSM is also live; does the deny on hooks LSM cannot reach. - Detect-only tier (weakest) — tracepoint detection plus userspace reactive kill. Available even on kernels without LSM or kprobe override. The agent's
enforcement_modereportsdetect-onlyin this state.
The wrapper queries the agent's /status endpoint and awards the appropriate tier's points based on what is actually attached. Both LSM and kprobe checks can be active simultaneously when the per-hook decision matrix keeps both attached for the same vector — they protect different syscalls in that case, so the points are not double-counted (kprobe was demoted from 15 to 10 in Phase L4 specifically to reflect this).
Score Breakdown¶
| Check | Points | What It Proves | How to Enable |
|---|---|---|---|
memfd_secret |
15 | Secret pages physically removed from kernel direct map | Automatic (requires Linux 5.14+ kernel) |
mlock |
10 | Secret pages pinned in RAM, cannot be swapped to disk | Automatic |
core_dump_exclusion |
5 | Secret pages excluded from core dumps via MADV_DONTDUMP |
Automatic |
dumpable_disabled |
5 | Process marked non-dumpable via PR_SET_DUMPABLE(0); /proc access restricted, ptrace blocked |
Automatic |
token_protected |
10 | OpenBao authentication token stored in protected memory (not on disk or in env) | Automatic |
environ_scrubbed |
5 | Wrapper's /proc/self/environ is structurally clean -- secrets enter Go heap memory after the wrapper has already exec'd |
Automatic (structural property of fork+exec architecture) |
getenv_interposer |
10 | Optional LD_PRELOAD interposer blocks getenv() from returning secrets on the heap (glibc-only enhancement) |
Use a glibc-based container image (not Alpine/musl) |
ebpf_agent_connected |
10 | eBPF enforcement agent is active and monitoring this process | Add cloudtaser.io/ebpf: "true" annotation to the pod |
cpu_mitigations |
5 | Spectre/Meltdown hardware mitigations active | Automatic on modern CPUs |
ebpf_enforce_mode |
15 | eBPF agent running in blocking mode (not just monitoring) | Deploy eBPF daemonset with enforceMode: true |
ebpf_lsm_enforce |
15 | Synchronous in-kernel deny via BPF LSM hooks (strongest enforcement tier) | Kernel with CONFIG_BPF_LSM=y AND bpf in the boot LSM stack |
ebpf_kprobes |
10 | Synchronous syscall blocking via kprobe override (fallback tier behind LSM) | Ubuntu / AL2023 / AKS Azure Linux 3.0+ with CONFIG_BPF_KPROBE_OVERRIDE=y |
confidential_vm |
10 | Hardware memory encryption (AMD SEV-SNP, Intel TDX) active | GKE: --enable-confidential-nodes with N2D machine type |
Maximum score: 125 points
Phase L4 score rebalance (2026-05)
ebpf_lsm_enforce is new. ebpf_kprobes was demoted from 15 to 10 because BPF LSM now covers the same vectors with a stronger primitive. Net effect: max moved from 115 to 125. Existing alerting thresholds based on the old maximum are listed under Score Interpretation — review your CLOUDTASER_MIN_PROTECTION_SCORE value if you pinned it against the old 115 ceiling.
When both LSM and kprobe checks are active
The agent's per-hook decision matrix keeps both LSM and kprobe attached when both load. The LSM hook fires first synchronously; the kprobe stays as the structural fallback (covers the rare case of LSM detach via manual unpin or kernel module reload). Awarding both checks does NOT double-count the same vector — they protect different syscalls in steady state, plus kprobe's per-point worth was reduced specifically to reflect the LSM-as-primary semantic.
Checks Explained¶
memfd_secret (15 points)¶
The highest-value automatic check. When the kernel supports memfd_secret (Linux 5.14+), the wrapper stores secrets in memory pages that are physically removed from the kernel's direct map. This means even a root attacker with kernel module access cannot read the secret pages through the direct map -- they are simply not there.
Fails when: The kernel version is below 5.14, or CONFIG_SECRETMEM is not set.
mlock (10 points)¶
Secrets are pinned in physical RAM using mlock(). This prevents the kernel from swapping secret pages to disk, where they could be recovered from swap partitions or swap files.
Fails when: CAP_IPC_LOCK is not available and the RLIMIT_MEMLOCK limit is too low. Most Kubernetes runtimes grant sufficient limits by default.
core_dump_exclusion (5 points)¶
Secret pages are marked with MADV_DONTDUMP, which excludes them from core dump files. If the application crashes, the core dump will not contain secret values.
Fails when: The madvise syscall fails (extremely rare).
dumpable_disabled (5 points)¶
The wrapper calls prctl(PR_SET_DUMPABLE, 0) to mark itself as non-dumpable. This restricts /proc/pid/mem access and blocks ptrace attach from non-root processes. Combined with the eBPF agent, this closes the /proc attack surface.
Fails when: The prctl syscall fails (extremely rare).
token_protected (10 points)¶
OpenBao authentication token is stored in the same protected memory as the secrets themselves (memfd_secret when available, mlock-ed pages otherwise). The token never appears in environment variables, files, or unprotected memory.
Fails when: memfd_secret is not available and mlock also fails.
environ_scrubbed (5 points)¶
The wrapper's own /proc/self/environ is structurally clean of secret values. This is a property of the fork+exec architecture rather than a runtime mutation:
- The wrapper's container is started with a configuration-only env (
CLOUDTASER_*,VAULT_ADDR, etc.). No secret bytes are present in the kernel'senvp[]at exec time. - Linux freezes
/proc/PID/environas the byte rangemm->env_start..mm->env_endrecorded at the moment ofexecve(). The kernel does NOT update this byte range when a process subsequently callsos.Setenv/setenv/putenv-- those calls modify the C library's heap-allocatedenvironarray, not the kernel's snapshot. - Secrets are fetched from vault into Go heap memory (and into the protected
memfd_secretregion) only after the wrapper has exec'd. They never enter the wrapper's frozenenvp[]snapshot.
The check therefore confirms a structural invariant: an attacker reading /proc/<wrapper-PID>/environ finds only the configuration env that the operator webhook set. The child's /proc/<child-PID>/environ does contain the secrets (this is how the application reads them) and is protected from cross-process reads by the eBPF kprobe on openat("/proc/<pid>/environ") for all monitored PIDs in the protected pod cgroup.
Fails when: The wrapper detects secret keys in its own /proc/self/environ -- which would indicate a configuration bug (operator webhook setting secrets in the container spec) rather than a wrapper failure.
getenv_interposer (10 points)¶
An optional glibc-only enhancement. The wrapper injects an LD_PRELOAD library (libcloudtaser.so) that intercepts getenv() calls in the child process. When the application calls getenv("PGPASSWORD"), the interposer returns a pointer to the memfd_secret-backed memory instead of a heap-allocated copy. This prevents secrets from leaking onto the application's heap.
This is an enhancement on top of the default env-var delivery path, not a replacement. All binaries -- including those that do not activate the interposer -- receive secrets through the universal fork+exec execve() mechanism.
Fails when: The container uses musl libc (Alpine) or statically linked binaries, which do not support LD_PRELOAD. Switch to a Debian, Ubuntu, or glibc-based image to enable this optional check.
ebpf_agent_connected (10 points)¶
The eBPF enforcement agent running on the node has registered this process for protection. The agent monitors all relevant syscalls and will block or kill any process attempting to read the protected process's memory, environment, or file descriptors.
Fails when: The eBPF daemonset is not deployed, or the pod does not have the cloudtaser.io/ebpf: "true" annotation.
cpu_mitigations (5 points)¶
The kernel has Spectre and Meltdown mitigations active. These hardware mitigations prevent speculative execution side-channel attacks that could leak secret values across process boundaries.
Fails when: The kernel is booted with mitigations=off, or the CPU does not support the required microcode updates. Modern cloud provider kernels always have mitigations enabled.
ebpf_enforce_mode (15 points)¶
The eBPF agent is running in enforce (blocking) mode, not just monitoring mode. In enforce mode, the agent actively blocks syscalls that would leak secrets (returning EPERM or overriding the syscall to return an error). In monitoring mode, the agent only logs violations without blocking them.
Fails when: The eBPF daemonset is deployed with enforceMode: false (monitoring only).
ebpf_lsm_enforce (15 points)¶
Strongest enforcement tier. The eBPF agent has attached BPF LSM hooks to one or more security-critical kernel paths. LSM hooks (security/security.c) are the canonical Linux Security Module API: they fire inside the kernel before the protected operation runs, and the LSM program returns 0 (allow) or -EPERM (deny) to control whether the operation completes. Unlike kprobe override, LSM does NOT require the syscall function to appear in any allow-list — every kernel that builds with CONFIG_BPF_LSM=y and has bpf in its boot LSM stack accepts BPF LSM programs against any LSM hook the kernel exposes.
This closes the fundamental gap of the kprobe path: every cloud-standard distro that ships with a default kernel (GKE COS, GKE Ubuntu, EKS Bottlerocket, EKS AL2023, AKS Azure Linux 3.0+, Talos) supports BPF LSM. The previous "Use Ubuntu nodes for synchronous block" advice is no longer needed for the vectors LSM covers.
Fails when: The kernel was not built with CONFIG_BPF_LSM=y, OR the boot LSM stack does not include bpf (check /sys/kernel/security/lsm — the comma-separated list must contain bpf). On lsm= cmdline kernels you may need to append bpf to the active stack; on CONFIG_LSM=-built kernels the default already includes it on every distro listed above.
The agent's /status endpoint reports lsm_count (number of BPF LSM hooks successfully attached). The wrapper awards this check's points when lsm_count > 0. The agent also publishes cloudtaser_ebpf_lsm_loaded{program="..."} per-program metrics for granular alerting.
ebpf_kprobes (10 points)¶
Fallback enforcement tier behind LSM. The eBPF agent is using kprobe-based synchronous syscall blocking via bpf_override_return(). When CONFIG_BPF_KPROBE_OVERRIDE=y AND the target syscall function appears in the upstream kernel's ALLOW_ERROR_INJECTION allow-list, the agent can prevent a syscall from executing by overriding its return value before it completes.
In Phase L4 of the BPF LSM migration (cloudtaser-ebpf#179), the kprobe tier was demoted from 15 to 10 points because BPF LSM now covers the same primary vectors with a stronger primitive (no ALLOW_ERROR_INJECTION gate, more distros). Kprobes remain valuable for two reasons:
- Structural fallback on LSM-covered hooks — when both LSM and kprobe load for the same vector, the agent's per-hook decision matrix keeps both attached. The LSM hook fires first synchronously; the kprobe stays loaded so a later LSM detach (manual unpin, kernel module reload) still leaves a synchronous deny path.
- Hooks LSM cannot reach — a small number of vectors are kprobe-only because no equivalent LSM hook exists upstream. The kprobe path covers those independently.
Fails when: The kernel does not have CONFIG_BPF_KPROBE_OVERRIDE=y. This is the case on GKE COS, EKS Bottlerocket, AKS Azure Linux, and RHEL 8. On those distros you should rely on ebpf_lsm_enforce (15 pts) for synchronous deny — that's the design intent of the L1-L4 migration.
confidential_vm (10 points)¶
The node is running on Confidential Computing hardware (AMD SEV-SNP, Intel TDX, or ARM CCA). Hardware memory encryption ensures that the hypervisor and cloud provider cannot read VM memory, even with physical access to the host machine.
Fails when: The node is not a Confidential Computing instance. On GKE, enable with --enable-confidential-nodes and --machine-type n2d-standard-2.
Score Interpretation¶
| Score Range | Assessment | Recommendation |
|---|---|---|
| 115--125 | Full protection | All critical defenses active including the LSM tier. Suitable for regulated production workloads. |
| 90--114 | Strong protection | Most defenses active. One or two non-critical checks missing, or kprobe tier present without LSM (acceptable on Ubuntu nodes). Review which and evaluate risk. |
| 60--89 | Partial protection | Key mechanisms missing. Not recommended for regulated workloads without a documented risk acceptance. |
| Below 60 | Insufficient | Multiple critical mechanisms inactive. Investigate immediately. |
A score below 60 means secrets are vulnerable to a determined attacker
Without memfd_secret (15 points) and at least one of ebpf_lsm_enforce (15 points) or ebpf_kprobes (10 points) — paired with ebpf_enforce_mode (15 points) — a determined root attacker can read process memory through standard kernel interfaces. The LSM tier is preferred where available because it works on every cloud-standard distro; the kprobe tier is the Ubuntu-class fallback.
Achieving Maximum Score by Platform¶
The numbers below assume the wrapper-side checks (memfd_secret 15, mlock 10, core_dump_exclusion 5, dumpable_disabled 5, token_protected 10, environ_scrubbed 5, getenv_interposer 10, ebpf_agent_connected 10, cpu_mitigations 5, ebpf_enforce_mode 15) all pass — that's a 90-point baseline. The tier columns add the eBPF enforcement points (ebpf_lsm_enforce 15 and/or ebpf_kprobes 10) and confidential_vm 10.
| Platform | LSM tier (kernel-config supports[^lsm-validation]) | Kprobe tier | Confidential VM | Achievable | How to Get the Maximum |
|---|---|---|---|---|---|
| GKE Ubuntu + Confidential Nodes | yes (+15) | yes (+10) | yes (+10) | 125 | --image-type UBUNTU_CONTAINERD --enable-confidential-nodes --machine-type n2d-standard-2 |
| GKE Ubuntu (non-confidential) | yes (+15) | yes (+10) | no | 115 | Add --enable-confidential-nodes for full 125 |
| GKE COS + Confidential Nodes | yes (+15) | no | yes (+10) | 115 | Already at the LSM-tier ceiling without Ubuntu — kprobe override unsupported on COS |
| GKE COS (non-confidential) | yes (+15) | no | no | 105 | Add Confidential Nodes for +10 |
| EKS Amazon Linux 2023 | yes (+15) | yes (+10) | no | 115 | No AWS confidential VM equivalent yet |
| EKS Bottlerocket | yes (+15) | no | no | 105 | Pair with AL2023 nodes for double-coverage; Bottlerocket lacks kprobe override but has BPF LSM |
| AKS Ubuntu 22.04 | yes (+15) | yes (+10) | no | 115 | Use DCasv5/ECasv5 Confidential VMs for +10 |
| AKS Azure Linux 3.0+ | yes (+15) | yes (+10) | no | 115 | Same posture as Ubuntu — BPF_KPROBE_OVERRIDE supported |
| AKS Azure Linux <3.0 / RHEL 8 | yes (+15) | no | no | 105 | Move to a 5.7+ kernel for the kprobe tier |
| GKE Autopilot | varies | no | no | varies | Use GKE Standard for full eBPF privileges |
[^lsm-validation]: "Kernel-config supports" reflects what the distro builds, not what the cloudtaser LSM hooks have been observed to load on each kernel. Per-kernel runtime validation requires the kernel-test workflow (cloudtaser-pipeline#227) to be unblocked. Until then, treat the LSM-tier column as the design target. The wrapper-side score check is honestly gated on the agent's actual lsm_count > 0 reporting so customers always see observed reality, not the table's expectation.
LSM tier landed in 2026-05 (Phases L1-L4 of cloudtaser-ebpf#179)
Before the LSM migration, GKE COS / EKS Bottlerocket / AKS Azure Linux were stuck at the kprobe-less ceiling because their kernels do not enable CONFIG_BPF_KPROBE_OVERRIDE. Phases L1 (perf_event_open), L2 (ptrace, file_open, bpf_prog_load), and L3 (socket_sendmsg, kernel_module_load) ship BPF LSM hooks for those vectors. Phase L4 (this section) flips the wrapper's protection score to award them honestly. Result: COS / Bottlerocket / Talos move from a 75-point ceiling (pre-LSM) to a 105-point ceiling (post-LSM, non-confidential), or 115 with Confidential Nodes.
Checking the Score¶
Wrapper Logs¶
The wrapper logs the protection score at startup. Example output (synthetic, illustrating a hypothetical GKE Ubuntu Confidential node on kernel 6.8.0-1042-gke once the Phase-L4 eBPF agent has been validated end-to-end and tagged):
[cloudtaser-wrapper] Protection score: 125/125
[cloudtaser-wrapper] memfd_secret: OK (+15)
[cloudtaser-wrapper] mlock: OK (+10)
[cloudtaser-wrapper] core_dump_exclusion: OK (+5)
[cloudtaser-wrapper] dumpable_disabled: OK (+5)
[cloudtaser-wrapper] token_protected: OK (+10)
[cloudtaser-wrapper] environ_scrubbed: OK (+5)
[cloudtaser-wrapper] getenv_interposer: OK (+10)
[cloudtaser-wrapper] ebpf_agent_connected: OK (+10)
[cloudtaser-wrapper] cpu_mitigations: OK (+5)
[cloudtaser-wrapper] ebpf_enforce_mode: OK (+15)
[cloudtaser-wrapper] ebpf_lsm_enforce: OK (+15)
[cloudtaser-wrapper] ebpf_kprobes: OK (+10)
[cloudtaser-wrapper] confidential_vm: OK (+10)
On GKE COS / EKS Bottlerocket the same node would log ebpf_kprobes: MISSING (-10) because those kernels do not enable CONFIG_BPF_KPROBE_OVERRIDE, but ebpf_lsm_enforce still passes — that is the design intent of the LSM migration. The achievable ceiling on those distros is 115 (with Confidential Nodes) or 105 (without).
CLI Status Command¶
Kubernetes Events¶
The wrapper emits Kubernetes events on the pod for score changes:
# Synthetic example — illustrative shape, not captured from a live cluster
# (see Phase L4 status note at the top of this page).
Events:
Type Reason Message
---- ------ -------
Normal ProtectionActive Protection score: 125/125 (all defenses active)
Warning ProtectionDegraded Protection score: 105/125 (ebpf_kprobes, confidential_vm unavailable on COS)
Prometheus Metrics¶
# HELP cloudtaser_protection_score Current protection score
# TYPE cloudtaser_protection_score gauge
cloudtaser_protection_score{pod="my-app-7d8f9c6b4-xk2mn",namespace="production"} 125
# HELP cloudtaser_protection_max Maximum possible protection score
# TYPE cloudtaser_protection_max gauge
cloudtaser_protection_max{pod="my-app-7d8f9c6b4-xk2mn",namespace="production"} 125
Enforcing Minimum Scores¶
Use the CLOUDTASER_MIN_PROTECTION_SCORE environment variable to prevent the wrapper from starting if the score is too low:
| Environment | Recommended Minimum | Rationale |
|---|---|---|
| Production (regulated) | 115 | All software protections active including the LSM tier |
| Production (general) | 90 | Core protections with eBPF enforcement (LSM or kprobe) |
| Staging | 60 | Allow testing without full enforcement |
| Development | 0 | No enforcement |
Migrating from the pre-Phase-L4 thresholds
If you previously set CLOUDTASER_MIN_PROTECTION_SCORE=105 against the old max of 115, the equivalent under the new max of 125 is 115 (still requires every software protection plus an eBPF tier). The old =80 general-prod threshold maps to 90 for the equivalent posture. The numeric values changed because ebpf_lsm_enforce (15 pts) was added — the underlying meaning ("LSM or kprobe synchronous deny is on") is what stayed constant.
Related¶
- Security Model -- trust boundaries and threat model
- Memory Protection -- how memfd_secret and mlock work
- eBPF Enforcement -- how the eBPF agent blocks attack vectors
- GKE Deployment Guide -- achieving 125/125 on GKE
- Kubernetes Compatibility -- per-distribution feature support