Runtime Enforcement
Kernel-level runtime enforcement using Cilium Tetragon. Provides the third layer of Lattice's defense-in-depth model alongside L4 network policies and L7 authorization policies.
Defense in Depth
Lattice enforces security at three layers. Runtime enforcement adds kernel-level protection that cannot be bypassed from userspace, even if network and application controls are compromised.
Cilium NetworkPolicies control pod-to-pod connectivity at the network layer.
Istio AuthorizationPolicies enforce identity-based access at the application layer.
Tetragon TracingPolicies attach eBPF programs to LSM hooks, blocking dangerous operations at the kernel.
Policy Tiers
Installed at cluster bootstrap. Unconditionally blocks dangerous syscalls across all workload namespaces.
ptrace — anti-debugging / container escape init_module / finit_module — kernel modules mount / umount — filesystem manipulation unshare / setns — namespace escape /etc/shadow, /etc/passwd, /etc/sudoers Excluded namespaces: kube-system, cilium-system, istio-system, lattice-system, cert-manager.
Generated per-service by the compiler. Only binaries in the final allowlist may execute — everything else receives SIGKILL via the security_bprm_check LSM hook. The allowlist is the union of explicitly declared allowedBinaries and auto-detected entrypoints from container commands and exec probes.
If a container has no command and no allowedBinaries, the image entrypoint is unknown — the compiler grants an implicit wildcard and no binary policy is generated. Use allowedBinaries: ["*"] to explicitly disable binary restrictions.
Derived from your LatticeService security context. The compiler generates enforcement policies that match your declared security posture.
readOnlyRootFilesystem: true → Blocks open() with write flags on non-tmpfs runAsNonRoot: true → Blocks setuid / setgid syscalls capabilities.drop: [ALL] → Blocks capset entirely How It Works
Runtime enforcement policies are automatically generated during service compilation. The compiler reads your service's security context and generates the appropriate Tetragon TracingPolicies.
command and exec probe entrypoints. SIGKILL — the process is terminated immediately. allowedBinaries
The allowedBinaries field in your container's security context declares which binaries your service needs to execute. The compiler builds a final allowlist from the union of declared binaries and auto-detected entrypoints, then generates an allow-binaries TracingPolicy that kills any process not on the list.
| Value | command | Behavior |
|---|---|---|
allowedBinaries: ["/usr/bin/app", ...] | any | Only listed binaries plus auto-detected entrypoints may execute. Everything else is killed. |
| (not set) | declared | Policy generated. Only command[0] and exec probe entrypoints may execute. |
| (not set) | (not set) | Implicit wildcard. Image entrypoint is unknown — no binary policy generated. |
allowedBinaries: ["*"] | any | Explicit wildcard. No binary restrictions. The allow-binaries policy is not generated. |
Policies are pod-scoped. For multi-container pods, allowedBinaries lists and auto-detected entrypoints from all containers and sidecars are merged into one union allowlist. If any container uses the * wildcard or has an unknown entrypoint (no command and no allowedBinaries), binary restrictions are disabled for the entire pod.
Cedar Authorization
Every allowedBinary entry — whether explicit or implicit — requires authorization from Cedar policy before the service can compile. The compiler generates a SecurityOverrideRequest for each binary, and Cedar must return a permit decision. This is default-deny: without a matching Cedar policy, compilation fails.
| Scenario | Cedar Override Generated |
|---|---|
allowedBinaries: ["/usr/bin/app"] | allowedBinary:/usr/bin/app |
allowedBinaries: ["*"] | allowedBinary:* |
Implicit wildcard (no command, no allowedBinaries) | allowedBinary:* |
Both implicit and explicit wildcards require allowedBinary:* to be permitted by Cedar. If your Cedar policy only permits specific binary paths, a wildcard allowlist will be rejected. This ensures that platform teams retain control over which services can run arbitrary binaries, even when workload authors omit binary restrictions.
Generated Policies
For a service with a fully hardened security context, the compiler generates four TracingPolicyNamespaced resources:
| Policy | Condition | LSM Hook |
|---|---|---|
allow-binaries-{name} | Unless * wildcard or unknown entrypoint | security_bprm_check |
block-rootfs-write-{name} | readOnlyRootFilesystem: true | security_file_open |
block-setuid-{name} | runAsNonRoot: true | security_task_fix_setuid |
block-capset-{name} | capabilities is empty or restricted | security_capset |
LSM Hooks
All hooks use architecture-independent Linux Security Module (LSM) functions, not arch-specific syscall numbers.
LSM hooks used by Lattice runtime enforcement Hook Purpose Tier security_ptrace_access_check Block process debugging 1 security_kernel_module_request Block kernel module loading 1 security_sb_mount Block mount operations 1 security_sb_umount Block unmount operations 1 security_bprm_check Binary execution allowlist 1b security_file_open Block file access (rootfs writes) 1 + 2 security_task_fix_setuid Block UID/GID changes 2 security_capset Block capability changes 2
Examples
Fully hardened service with explicit binary allowlist:
api-server.yaml apiVersion: lattice.dev/v1alpha1
kind: LatticeService
metadata:
name: api-server
namespace: production
spec:
workload:
containers:
- name: app
image: api-server:latest
securityContext:
readOnlyRootFilesystem: true # → block-rootfs-write policy
runAsNonRoot: true # → block-setuid policy
capabilities:
drop: [ALL] # → block-capset policy
allowedBinaries: # → allow-binaries policy
- /usr/bin/api-server
- /usr/bin/curl
PHP app that shells out to ImageMagick and ffmpeg — declare the binaries you need, and exec probe entrypoints are auto-detected:
image-processor.yaml apiVersion: lattice.dev/v1alpha1
kind: LatticeService
metadata:
name: image-processor
namespace: media
spec:
workload:
containers:
- name: php
image: image-processor:latest
securityContext:
allowedBinaries:
- /usr/sbin/php-fpm
- /usr/bin/convert # ImageMagick
- /usr/bin/ffmpeg
- /usr/bin/curl # used by probe
livenessProbe:
exec:
command: ["/bin/sh", "-c", "curl -f localhost:9000/health"]
# /bin/sh auto-detected from probe command[0], curl explicitly allowed
Generated allow-binaries policy for the image-processor (uses NotEqual — anything not in the list is killed). Note /bin/sh auto-detected from the exec probe:
allow-binaries-image-processor.yaml apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: allow-binaries-image-processor
namespace: media
labels:
lattice.dev/managed-by: lattice
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: image-processor
kprobes:
- call: security_bprm_check
syscall: false
args:
- index: 0
type: file
selectors:
- matchArgs:
- index: 0
operator: NotEqual # SIGKILL anything NOT in this list
values:
- /usr/sbin/php-fpm
- /usr/bin/convert
- /usr/bin/ffmpeg
- /usr/bin/curl
- /bin/sh # auto-detected from probe
matchActions:
- action: Sigkill
CI runner that needs unrestricted binary execution:
build-runner.yaml apiVersion: lattice.dev/v1alpha1
kind: LatticeService
metadata:
name: build-runner
namespace: ci
spec:
workload:
containers:
- name: runner
image: ci-runner:latest
securityContext:
allowedBinaries: ["*"] # disable binary restrictions entirely
Cluster-wide baseline policy (installed automatically at bootstrap):
lattice-baseline-runtime.yaml apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: lattice-baseline-runtime
spec:
kprobes:
- call: security_ptrace_access_check
syscall: false
args:
- index: 1
type: int
selectors:
- matchNamespaces:
- namespace: Namespace
operator: NotIn
values:
- kube-system
- cilium-system
- istio-system
- lattice-system
- cert-manager
matchActions:
- action: Sigkill
Entrypoint Auto-detection
The compiler extracts command[0] from every container command and exec probe (liveness, readiness, startup) across all containers and sidecars. Only the binary path is taken — arguments are ignored. HTTP probes are not scanned since they use native network calls. The final allowlist is the union of all declared allowedBinaries and all auto-detected entrypoints.
Multi-Container Aggregation
Tetragon policies are pod-scoped, not container-scoped. For pods with multiple containers or sidecars, the compiler aggregates using the most permissive setting across all containers:
allowedBinaries and auto-detected entrypoints are merged into a single union allowlist. - If any container uses the
* wildcard, binary restrictions are disabled for the entire pod. - If any container has an unknown entrypoint (no
command and no allowedBinaries), the pod gets an implicit wildcard. - If any container sets
readOnlyRootFilesystem: false, the rootfs write policy is skipped for the entire pod.