Deterministic, kernel-level runtime security policies for Cilium Tetragon designed to block stealthy post-exploitation implants (such as GSocket), unauthorized reverse shells, and interactive container escapes (docker exec ... sh).
Recent threat intelligence (such as Microsoft's analysis of threat actors weaponizing GSocket) highlights how attackers maintain persistent C2 access on Linux container hosts even after initial vulnerabilities are patched:
- No fixed C2 IP/domain: Traffic is relayed over public shared infrastructure via outbound TLS connections, bypassing inbound firewall rules.
- Process masquerading: Binaries are staged in memory-backed directories (
/dev/shm) and executed withexec -ato mimic kernel threads (e.g.,[kaluad]). - Short-lived payloads: User-space polling scripts inspecting
psorsssuffer from race conditions against fastcurl | shdrop-and-execute chains.
Instead of polling in user space or relying on heuristic/AI anomaly detection, this repository provides declarative eBPF TracingPolicy manifests that enforce two deterministic rules synchronously inside the Linux kernel:
- Container Executable Allowlisting (
security_bprm_check): Every binary executed inside a non-host PID namespace must match an explicit allowlist of ground-truth binary paths. Anything else—including/bin/sh,/bin/bash,curl, or binaries staged in/dev/shm—receives an immediate, synchronous kernelSIGKILLbefore execution completes. - Host Outbound TCP Zero-Trust (
tcp_connect): On dedicated container hosts (Docker, Swarm, or Kubernetes nodes) where all application workloads run inside containers, any process in the roothost_nsinitiating a TCP connection to a public (non-RFC1918) IP address is killed immediately.
tetragon-outgoing-guard/
├── README.md
├── LICENSE
├── policies/
│ ├── standalone-docker.yaml # TracingPolicy for Docker / Swarm / standalone Linux hosts
│ └── kubernetes-workload.yaml # TracingPolicyNamespaced with podSelector for K8s clusters
├── scripts/
│ └── discover-binaries.sh # Helper to extract ground-truth binary paths from running containers
└── examples/
└── docker-compose.tetragon.yml # Deploy Tetragon via Docker Compose or Docker Swarm (global mode)
Never enable action: Sigkill before mapping all legitimate binaries used by your containers (including tini, entrypoint wrappers, and application runtimes).
Run the discovery helper on your host:
chmod +x scripts/discover-binaries.sh
sudo ./scripts/discover-binaries.sh
Copy the output paths into policies/standalone-docker.yaml under matchBinaries.
Before enforcing Sigkill, comment out the matchActions: - action: Sigkill blocks in policies/standalone-docker.yaml and start Tetragon using the provided Compose file:
docker compose -f examples/docker-compose.tetragon.yml up -d
Watch live kernel events to verify no legitimate workload triggers the policy:
docker exec -it tetragon tetra getevents -o compact
Uncomment matchActions: - action: Sigkill in policies/standalone-docker.yaml and restart Tetragon:
docker compose -f examples/docker-compose.tetragon.yml restart tetragon
Verify enforcement by attempting to spawn a shell inside any running container:
docker exec -it <your-container> sh
Tetragon runs alongside any CNI (including Calico, Flannel, or Cilium) without conflicts, as it hooks into kernel LSM/kprobe points rather than CNI packet routing.
- Install Tetragon via Helm:
helm repo add cilium https://cilium.io
helm repo update
helm install tetragon cilium/tetragon -n kube-system
- Edit
policies/kubernetes-workload.yamlto match your Pod labels and allowed binaries, then apply:
kubectl apply -f policies/kubernetes-workload.yaml
Because standalone-docker.yaml blocks outbound public TCP connections from host_ns, package managers on the host ( apt, dnf, yum) will be terminated while the policy is active.
Use a shell alias to temporarily unload the policy during host updates:
alias sysupgrade='docker compose -f /path/to/examples/docker-compose.tetragon.yml stop tetragon && sudo apt update && sudo apt upgrade -y; docker compose -f /path/to/examples/docker-compose.tetragon.yml start tetragon'
(Note: When the Tetragon user-space daemon stops, the kernel automatically closes its BPF file descriptors and unloads the hooks until the container starts again.)
- Interpreted languages (Python, Node.js, PHP):
security_bprm_checkevaluates the binary executable passed toexecve(e.g.,/usr/local/bin/python3.12), not the.pyscript argument. Allowlisting the interpreter allows any script executed by that interpreter, while still blocking sub-shells (os.system(),subprocesscalling/bin/sh). - Healthchecks: If your
Dockerfileordocker-compose.ymldefines aHEALTHCHECKusingcurlorwget, those binaries will be killed unless added to the allowlist. Prefer native TCP/HTTP probes or distroless/Go healthcheck binaries.
Created by Daniele Cruciani (@danielecr).
Licensed under the Apache License 2.0.