<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tetragon Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/tetragon/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/tetragon/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Mon, 06 Jul 2026 03:36:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>Tetragon Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/tetragon/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>Detection Engineering with eBPF: Kernel-Level Visibility for Cloud Incidents</title>
		<link>https://linuxcent.com/detection-engineering-ebpf-cloud/</link>
					<comments>https://linuxcent.com/detection-engineering-ebpf-cloud/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Mon, 06 Jul 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Purple Team]]></category>
		<category><![CDATA[Detection Engineering]]></category>
		<category><![CDATA[eBPF]]></category>
		<category><![CDATA[Falco]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Tetragon]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1873</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 13</span> <span class="rt-label rt-postfix">minutes</span></span>eBPF gives detection engineers kernel-level visibility that no attacker can evade without kernel access. Build detection rules for the techniques your SIEM misses entirely.</p>
<p>The post <a href="https://linuxcent.com/detection-engineering-ebpf-cloud/">Detection Engineering with eBPF: Kernel-Level Visibility for Cloud Incidents</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 13</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><a href="/what-is-purple-team-security/">What is purple team security</a> → <a href="/owasp-top-10-cloud-infrastructure/">OWASP Top 10 mapped to cloud infrastructure</a> → <a href="/cloud-security-breaches-2020-2025/">Cloud security breaches 2020–2025</a> → <a href="/broken-access-control-aws/">Broken access control in AWS</a> → <a href="/mfa-fatigue-attack/">MFA fatigue attacks</a> → <a href="/cicd-secrets-exposure/">CI/CD secrets exposure</a> → <a href="/ssrf-cloud-metadata-attack/">SSRF to cloud metadata</a> → <a href="/kubernetes-container-escape/">Kubernetes container escape</a> → <a href="/supply-chain-attack-detection/">Supply chain attack detection</a> → <a href="/cloud-lateral-movement-iam-role-chaining/">Cloud lateral movement</a> → <strong>Detection Engineering with eBPF</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>Detection engineering with eBPF</strong> addresses OWASP A09 directly: most process-level attack techniques leave no trace in CloudTrail, VPC Flow Logs, or syslog — eBPF hooks in the kernel observe them before the attacker has any ability to suppress the record</li>
<li>CloudTrail is API-plane only; VPC Flow Logs are network-plane only with a 15-minute aggregation delay and no process context; syslog captures only what userspace processes voluntarily emit — all three miss the OS-level attack surface entirely</li>
<li>eBPF attaches to kernel syscall tracepoints and kprobes to capture <code class="" data-line="">connect()</code>, <code class="" data-line="">execve()</code>, <code class="" data-line="">mount()</code>, <code class="" data-line="">setuid()</code>, and <code class="" data-line="">open()</code> with full context: PID, process name, container cgroup, parent process, timestamp — in real time</li>
<li>Falco and Tetragon are the production-grade always-on options; bpftrace is the ad-hoc investigation tool — use each for what it is designed for</li>
<li>Tetragon&#8217;s <code class="" data-line="">TracingPolicy</code> can kill a process at the moment of the violating syscall, before the attack completes — this is enforcement, not just alerting</li>
<li>Every attack in EP07 through EP10 has a detectable kernel-level signal; this episode maps each one to a concrete eBPF detection rule</li>
</ul>
<hr />
<blockquote>
<p><strong>OWASP Mapping:</strong> A09 Security Logging and Monitoring Failures — the structural gap this series has referenced from EP04 onward: attacks that succeed not because defenses are absent, but because the telemetry layer cannot see the OS surface where the attacks execute.</p>
</blockquote>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">┌─────────────────────────────────────────────────────────────────────────┐
│                  DETECTION ENGINEERING WITH eBPF                        │
│                                                                         │
│   KERNEL SPACE                          USERSPACE                       │
│                                                                         │
│   syscall/kprobe hooks                                                  │
│   ┌──────────────────┐                                                  │
│   │ connect()        │──&#x25b6; ring buffer ──&#x25b6; Tetragon ──&#x25b6; Hubble/SIEM     │
│   │ execve()         │                                                  │
│   │ mount()          │──&#x25b6; ring buffer ──&#x25b6; Falco   ──&#x25b6; Slack/PagerDuty │
│   │ setuid()         │                                                  │
│   │ open()           │──&#x25b6; perf buffer ──&#x25b6; bpftrace ──&#x25b6; stdout/log     │
│   └──────────────────┘                                                  │
│          │                                                              │
│          │  Context captured at hook:                                   │
│          │  PID · comm · cgroup (container ID) · args · timestamp      │
│          │  parent PID · network namespace · mount namespace           │
│                                                                         │
│   ═══════════════════════════════════════════════════════════           │
│   WHAT OTHER TOOLS SEE                                                  │
│   CloudTrail:     API calls only — nothing below the AWS SDK            │
│   VPC Flow Logs:  src/dst IP+port only — 15-min delay, no PID          │
│   Syslog:         What the process chose to log — attacker controls it  │
│   eBPF:           Every syscall — attacker cannot suppress it          │
│                   without kernel access                                 │
└─────────────────────────────────────────────────────────────────────────┘
</code></pre>
<p><strong>Detection engineering with eBPF</strong> closes the observability gap that every previous episode in this series exploited. The SSRF in EP07 made an outbound connection to 169.254.169.254 — the EC2 metadata endpoint — from a web application process. VPC Flow Logs show that IP eventually. CloudTrail shows nothing. eBPF shows the <code class="" data-line="">connect()</code> syscall with the PID, the process name, the container cgroup ID, and the timestamp, in the sub-millisecond window it occurred.</p>
<hr />
<h2 id="the-problem-your-siem-has-a-15-minute-hole">The Problem: Your SIEM Has a 15-Minute Hole</h2>
<p>During a cloud incident response engagement, the question came up in the first hour: did this process make any outbound connections in the last 30 minutes?</p>
<p>Four telemetry sources, four answers:</p>
<p><strong>CloudTrail:</strong> Not applicable. CloudTrail records AWS API calls. A process inside an EC2 instance making a raw TCP connection to an external IP — or to the metadata endpoint — is OS-level activity. CloudTrail has no record of it.</p>
<p><strong>VPC Flow Logs:</strong> Maybe, eventually. Flow Logs aggregate at 1-minute or 10-minute intervals (configurable), then land in S3 or CloudWatch Logs with additional delay. In practice, you&#8217;re looking at 10–15 minutes before the data is queryable. The flow record contains source IP, destination IP, source port, destination port, protocol, bytes, packets — and nothing else. There is no PID. There is no process name. There is no indication of which container inside the EC2 instance made the connection. If ten pods are running on the same node, VPC Flow Logs tells you the node talked to an external IP. You don&#8217;t know which pod.</p>
<p><strong>Syslog:</strong> Nothing logged. The process — a compromised web application exploited via SSRF — didn&#8217;t log the connection. It wouldn&#8217;t. Application code doesn&#8217;t emit syslog entries for every outbound connection it makes. And an attacker controlling the process would not add logging.</p>
<p><strong>eBPF TC hook:</strong> Every TCP connection attempt, from the moment it entered the network stack, with PID, process name, container cgroup ID, destination IP, destination port, source IP, and timestamp — in real time, with zero delay.</p>
<p>That is the gap. Everything in EP04 through EP10 of this series lived in it.</p>
<p>The OWASP A09 framing is exactly right: these are not failures of detection rules, they are failures of the telemetry layer. You cannot write a SIEM rule for data that is never collected. eBPF collects the data that the other layers structurally cannot.</p>
<hr />
<h2 id="what-ebpf-detects-that-other-tools-miss">What eBPF Detects That Other Tools Miss</h2>
<table>
<thead>
<tr>
<th>Technique</th>
<th>CloudTrail</th>
<th>VPC Flow Logs</th>
<th>Syslog</th>
<th>eBPF</th>
</tr>
</thead>
<tbody>
<tr>
<td>Process spawn inside container</td>
<td>No</td>
<td>No</td>
<td>Maybe (if auditd configured)</td>
<td>Yes — execve(): PID, command, args, parent PID, container cgroup</td>
</tr>
<tr>
<td>Outbound TCP connection</td>
<td>No</td>
<td>IP+port, 15-min delay, no PID</td>
<td>No</td>
<td>connect(): IP+port+PID+comm+container, real-time</td>
</tr>
<tr>
<td>File write to /etc/passwd</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>openat()+write(): exact path, PID, comm, container</td>
</tr>
<tr>
<td>Privilege escalation (setuid/setgid)</td>
<td>No</td>
<td>No</td>
<td>Maybe (auditd)</td>
<td>Yes — setuid() syscall args: target UID, calling PID, comm</td>
</tr>
<tr>
<td>Container escape attempt via mount</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>mount(): args, mount namespace ID, calling PID — namespace mismatch detectable</td>
</tr>
<tr>
<td>SSRF to 169.254.169.254</td>
<td>No</td>
<td>IP only, 15-min delay</td>
<td>No</td>
<td>connect() from app process to metadata IP — PID, comm, container, real-time</td>
</tr>
<tr>
<td>Binary execution with unusual parent</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>execve(): full parent chain — detects shell spawned from web process</td>
</tr>
<tr>
<td>Kubernetes secret file read</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>openat() on /run/secrets/kubernetes.io/serviceaccount/token</td>
</tr>
<tr>
<td>STS credential fetch from Lambda</td>
<td>No</td>
<td>Endpoint IP only</td>
<td>No</td>
<td>connect() to sts.amazonaws.com from unexpected process</td>
</tr>
</tbody>
</table>
<p>The pattern across the table is consistent: CloudTrail covers the AWS control plane. VPC Flow Logs cover the network plane with delay and no process context. Syslog covers what processes choose to emit. eBPF covers the syscall surface — the layer where every one of these events must pass, regardless of what the attacker wants.</p>
<blockquote>
<p><strong>For operators not writing eBPF:</strong> This table tells you what your current SIEM can and cannot see. If your threat model includes container escapes, SSRF-to-metadata attacks, or post-compromise lateral movement through process execution, the detection signal for those techniques does not exist in your CloudTrail or your flow logs. It exists only at the kernel level.</p>
</blockquote>
<hr />
<h2 id="detection-rule-1-unexpected-outbound-from-an-application-container">Detection Rule 1: Unexpected Outbound from an Application Container</h2>
<p>The SSRF attack in EP07 — and the lateral movement in EP10 — both required an outbound TCP connection from a process that had no legitimate reason to make one. This is the detection.</p>
<h3 id="ad-hoc-investigation-with-bpftrace">Ad-hoc investigation with bpftrace</h3>
<p>When you&#8217;re on a node right now and need to know what&#8217;s connecting outbound:</p>
<pre><code class="" data-line=""># Shows PID, process name, and destination IP in real time
# Run on the node (requires root or CAP_BPF)
bpftrace -e &#039;
#include &lt;linux/socket.h&gt;
#include &lt;linux/in.h&gt;

tracepoint:syscalls:sys_enter_connect {
  $sa = (struct sockaddr_in *)args-&gt;uservaddr;
  if ($sa-&gt;sin_family == AF_INET) {
    printf(&quot;connect: pid=%-6d comm=%-20s dst=%s:%d\n&quot;,
           pid,
           comm,
           ntop($sa-&gt;sin_addr.s_addr),
           (uint16)bswap($sa-&gt;sin_port));
  }
}
&#039;
</code></pre>
<p>Sample output — what you&#8217;d see during an SSRF exploit targeting the EC2 metadata service:</p>
<pre><code class="" data-line="">connect: pid=18422  comm=python3              dst=169.254.169.254:80
connect: pid=18422  comm=python3              dst=169.254.169.254:80
connect: pid=18432  comm=curl                 dst=169.254.169.254:80
</code></pre>
<p>The <code class="" data-line="">python3</code> process — your web application — connecting to <code class="" data-line="">169.254.169.254</code> is the metadata endpoint. That&#8217;s not a legitimate application dependency. That&#8217;s the SSRF signal.</p>
<blockquote>
<p><strong><a href="/bpftrace-kernel-observability/">bpftrace — kernel answers in one line</a></strong> goes deep on the tracepoint/kprobe model and how to filter by cgroup for container-specific traces. The one-liners above are the starting point; that post covers building targeted investigation scripts.</p>
</blockquote>
<h3 id="production-grade-enforcement-with-tetragon">Production-grade enforcement with Tetragon</h3>
<p>bpftrace is for investigation. Tetragon is for always-on detection — and optionally, prevention.</p>
<pre><code class="" data-line=""># TracingPolicy: alert on outbound connections from non-host network namespaces
# (any container making outbound TCP connections)
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: &quot;detect-outbound-connections&quot;
spec:
  kprobes:
  - call: &quot;tcp_connect&quot;
    syscall: false
    args:
    - index: 0
      type: &quot;sock&quot;
    selectors:
    - matchNamespaces:
      - namespace: Net
        operator: NotIn
        values:
        - &quot;host&quot;
      matchActions:
      - action: Post   # Generate an alert event; change to Sigkill to prevent
</code></pre>
<p>To detect specifically the SSRF-to-metadata pattern — connections to <code class="" data-line="">169.254.169.254</code>:</p>
<pre><code class="" data-line="">apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: &quot;detect-imds-access&quot;
spec:
  kprobes:
  - call: &quot;tcp_connect&quot;
    syscall: false
    args:
    - index: 0
      type: &quot;sock&quot;
    selectors:
    - matchArgs:
      - index: 0
        operator: &quot;Equal&quot;
        values:
        - &quot;169.254.169.254/32&quot;
      matchActions:
      - action: Post
        rateLimit: &quot;1/minute&quot;
</code></pre>
<p>Tetragon events include <code class="" data-line="">process_kprobe</code> JSON with the pod name, namespace, container ID, binary path, parent binary, and all arguments. This feeds directly into your SIEM or to Hubble&#8217;s flow log.</p>
<hr />
<h2 id="detection-rule-2-process-execution-inside-a-container">Detection Rule 2: Process Execution Inside a Container</h2>
<p>A shell spawning inside a container that has no business running a shell is a post-compromise indicator. It covers the container escape setup from EP08, the supply chain implant from EP09, and any hands-on-keyboard phase after initial access.</p>
<h3 id="falco-rule-shell-spawned-from-application-container">Falco rule: shell spawned from application container</h3>
<pre><code class="" data-line=""># Falco rule: detect any shell spawned in a container
# Add to /etc/falco/rules.d/purple-team.yaml
- list: shell_binaries
  items: [bash, sh, zsh, ksh, fish, tcsh, csh, dash]

- list: allowed_shell_images
  items: [
    &quot;debug-tools&quot;,     # Your approved debug container image names
    &quot;toolbox&quot;
  ]

- rule: Shell Spawned in Container
  desc: &gt;
    A shell was spawned inside a container. In application containers (web servers,
    APIs, data processors) this is almost always a post-compromise indicator.
  condition: &gt;
    evt.type = execve and
    evt.dir = &lt; and
    container and
    container.image.repository != &quot;&quot; and
    proc.name in (shell_binaries) and
    not proc.pname in (shell_binaries) and
    not container.image.repository in (allowed_shell_images) and
    not k8s.ns.name in (kube-system, kube-public)
  output: &gt;
    Shell spawned in container
    (user=%user.name
     container=%container.name
     image=%container.image.repository
     cmd=%proc.cmdline
     parent=%proc.pname
     pod=%k8s.pod.name
     ns=%k8s.ns.name)
  priority: WARNING
  tags: [purple-team, post-compromise, container]
</code></pre>
<p>The <code class="" data-line="">proc.pname</code> condition is the key signal: a shell spawned by a web server process (<code class="" data-line="">nginx</code>, <code class="" data-line="">node</code>, <code class="" data-line="">gunicorn</code>, <code class="" data-line="">java</code>) is a different threat than a shell spawned by another shell in a debug context. The rule above passes the second case through the <code class="" data-line="">allowed_shell_images</code> exclusion; it flags the first.</p>
<h3 id="detecting-the-supply-chain-implant-pattern">Detecting the supply chain implant pattern</h3>
<p>EP09 covered supply chain attacks where a build artifact executes unexpected binaries at runtime. The bpftrace version for ad-hoc investigation of what a specific container is executing:</p>
<pre><code class="" data-line=""># bpftrace: trace all execve() calls from processes inside a specific container
# First, find the container&#039;s cgroup ID:
# systemd-cgls | grep &lt;pod-name&gt;
# Or: cat /sys/fs/cgroup/unified/&lt;cgroup-path&gt;/cgroup.procs

bpftrace -e &#039;
tracepoint:syscalls:sys_enter_execve {
  printf(&quot;execve: pid=%-6d ppid=%-6d comm=%-20s file=%s\n&quot;,
         pid,
         curtask-&gt;real_parent-&gt;tgid,
         comm,
         str(args-&gt;filename));
}
&#039; 2&gt;/dev/null | grep -v &quot;^\[&quot; | head -50
</code></pre>
<p>Sample output during a supply chain compromise scenario — unexpected binary execution from a package manager implant:</p>
<pre><code class="" data-line="">execve: pid=31204  ppid=31190  comm=node                 file=/bin/sh
execve: pid=31205  ppid=31204  comm=sh                   file=/tmp/.x/beacon
execve: pid=31206  ppid=31205  comm=beacon               file=/usr/bin/curl
</code></pre>
<p>The chain <code class="" data-line="">node → sh → /tmp/.x/beacon → curl</code> — application process spawning a shell, which executes an unknown binary from <code class="" data-line="">/tmp</code>, which runs <code class="" data-line="">curl</code> — is the supply chain implant execution pattern. None of this appears in CloudTrail.</p>
<hr />
<h2 id="detection-rule-3-privilege-escalation-setuid0-and-capability-abuse">Detection Rule 3: Privilege Escalation — setuid(0) and Capability Abuse</h2>
<p>A process calling <code class="" data-line="">setuid(0)</code> to elevate to root, or <code class="" data-line="">setcap</code> to acquire new capabilities, is a privilege escalation indicator. The EP08 container escape path used a setuid binary to gain root inside the container as the first step toward escaping the namespace.</p>
<h3 id="bpftrace-catch-setuid0-calls-in-real-time">bpftrace: catch setuid(0) calls in real time</h3>
<pre><code class="" data-line=""># bpftrace: alert on any process calling setuid(0)
# Any process attempting to switch to UID 0
bpftrace -e &#039;
tracepoint:syscalls:sys_enter_setuid {
  if (args-&gt;uid == 0) {
    printf(&quot;ALERT setuid(0): pid=%-6d comm=%-20s ppid=%d pcomm=%s\n&quot;,
           pid,
           comm,
           curtask-&gt;real_parent-&gt;tgid,
           str(curtask-&gt;real_parent-&gt;comm));
  }
}
tracepoint:syscalls:sys_enter_setresuid {
  if (args-&gt;ruid == 0 || args-&gt;euid == 0) {
    printf(&quot;ALERT setresuid(root): pid=%-6d comm=%-20s\n&quot;, pid, comm);
  }
}
&#039;
</code></pre>
<h3 id="falco-rule-setuid-binary-execution-inside-container">Falco rule: setuid binary execution inside container</h3>
<pre><code class="" data-line="">- rule: Setuid Binary Executed in Container
  desc: &gt;
    A setuid binary was executed inside a container. Setuid binaries inside
    containers are a privilege escalation path — they run as root regardless
    of the container&#039;s user setting.
  condition: &gt;
    evt.type = execve and
    evt.dir = &lt; and
    container and
    proc.is_suid_exe = true
  output: &gt;
    Setuid binary executed in container
    (binary=%proc.exepath
     user=%user.name
     container=%container.name
     pod=%k8s.pod.name
     cmd=%proc.cmdline)
  priority: ERROR
  tags: [purple-team, privilege-escalation, container]
</code></pre>
<hr />
<h2 id="detection-rule-4-container-escape-attempt-via-namespace-crossing-mount">Detection Rule 4: Container Escape Attempt via Namespace-Crossing Mount</h2>
<p>The privileged container escape path from EP08 requires calling <code class="" data-line="">mount()</code> from a container namespace to access the host filesystem. The kernel records the mount namespace of the calling process — an eBPF kprobe on <code class="" data-line="">mount()</code> can detect when the caller&#8217;s mount namespace differs from the host namespace.</p>
<h3 id="tetragon-policy-kill-any-mount-from-a-non-host-namespace">Tetragon policy: kill any mount from a non-host namespace</h3>
<pre><code class="" data-line=""># This covers the --privileged container escape path documented in EP08
# The mount() call that crosses from container namespace to host filesystem
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: &quot;detect-container-mount-escape&quot;
spec:
  kprobes:
  - call: &quot;security_sb_mount&quot;
    syscall: false
    args:
    - index: 0
      type: &quot;string&quot;     # dev_name
    - index: 3
      type: &quot;string&quot;     # mount flags
    selectors:
    - matchNamespaces:
      - namespace: Mnt
        operator: NotIn
        values:
        - &quot;host&quot;
      matchArgs:
      - index: 0
        operator: &quot;NotEqual&quot;
        values:
        - &quot;proc&quot;
        - &quot;sysfs&quot;
        - &quot;tmpfs&quot;        # Common legitimate mounts in containers
      matchActions:
      - action: Sigkill
        rateLimit: &quot;10/minute&quot;
</code></pre>
<p>Start with <code class="" data-line="">action: Post</code> and tune the exclusions for your environment before switching to <code class="" data-line="">Sigkill</code>. See the production gotchas below.</p>
<h3 id="bpftrace-ad-hoc-namespace-crossing-investigation">bpftrace: ad-hoc namespace crossing investigation</h3>
<pre><code class="" data-line=""># bpftrace: trace mount() calls and show the mount namespace of the caller
# Mount namespace ID of the host: read from /proc/1/ns/mnt
HOST_MNT_NS=$(readlink /proc/1/ns/mnt | grep -oP &#039;\d+&#039;)

bpftrace -e &#039;
#include &lt;linux/nsproxy.h&gt;
#include &lt;linux/mount.h&gt;

kprobe:__x64_sys_mount {
  $nsproxy = (struct nsproxy *)curtask-&gt;nsproxy;
  $mnt_ns_id = $nsproxy-&gt;mnt_ns-&gt;ns.inum;
  printf(&quot;mount: pid=%-6d comm=%-20s mnt_ns=%u\n&quot;,
         pid, comm, $mnt_ns_id);
}
&#039; 2&gt;/dev/null
</code></pre>
<p>Compare the <code class="" data-line="">mnt_ns</code> value in output against <code class="" data-line="">$HOST_MNT_NS</code>. Any mount call with a <code class="" data-line="">mnt_ns</code> value other than the host&#8217;s is from inside a container. A privileged container attempting host filesystem access shows a container namespace ID.</p>
<hr />
<h2 id="building-a-detection-pipeline">Building a Detection Pipeline</h2>
<p>Ad-hoc bpftrace commands answer questions during an incident. Always-on detection requires a pipeline that runs continuously, routes alerts to a durable destination, and survives pod restarts. The two production-grade options in this stack:</p>
<pre><code class="" data-line="">eBPF hooks
    │
    ├── Tetragon (always-on, Kubernetes-native)
    │       └── TracingPolicy CRDs
    │               └── JSON events → Hubble → Grafana
    │                               → SIEM (Splunk/Elastic)
    │                               → PagerDuty
    │
    └── Falco (rule-based, declarative)
            └── /etc/falco/rules.d/*.yaml
                    └── falcosidekick
                            ├── Slack
                            ├── PagerDuty
                            ├── Elasticsearch
                            └── AWS Lambda (custom response)
</code></pre>
<p>The <a href="/ebpf-tc-pod-policy/">TC eBPF pod-level network policy</a> post covers how Cilium and Tetragon share the same underlying kernel attachment points — understanding TC hooks helps explain why Tetragon&#8217;s network-level policies fire at the same layer as Cilium&#8217;s NetworkPolicy enforcement.</p>
<h3 id="falco-with-falcosidekick-complete-local-testing-setup">Falco with falcosidekick: complete local testing setup</h3>
<p>Use this to validate your Falco rules before deploying to a cluster. It routes Falco alerts to Slack in real time.</p>
<pre><code class="" data-line=""># docker-compose.yml — local Falco + falcosidekick testing
# Requires: Docker with kernel headers or eBPF driver support
version: &quot;3.8&quot;

services:
  falco:
    image: falcosecurity/falco-no-driver:latest
    privileged: true
    volumes:
      - /var/run/docker.sock:/host/var/run/docker.sock
      - /dev:/host/dev
      - /proc:/host/proc:ro
      - /boot:/host/boot:ro
      - /lib/modules:/host/lib/modules:ro
      - /usr:/host/usr:ro
      - /etc/falco:/etc/falco
      - ./rules:/etc/falco/rules.d:ro
    environment:
      FALCO_GRPC_ENABLED: &quot;true&quot;
      FALCO_GRPC_BIND_ADDRESS: &quot;0.0.0.0:5060&quot;
    ports:
      - &quot;5060:5060&quot;
    command: &gt;
      /usr/bin/falco
        --modern-bpf
        -o &quot;json_output=true&quot;
        -o &quot;grpc.enabled=true&quot;
        -o &quot;grpc_output.enabled=true&quot;

  falcosidekick:
    image: falcosecurity/falcosidekick:latest
    depends_on:
      - falco
    environment:
      FALCO_GRPC_CONN: &quot;falco:5060&quot;
      FALCO_GRPC_TLS: &quot;false&quot;
      SLACK_WEBHOOKURL: &quot;${SLACK_WEBHOOK}&quot;
      SLACK_MINIMUMPRIORITY: &quot;warning&quot;
      SLACK_MESSAGEFORMAT: &gt;
        &quot;[{{.Priority}}] {{.Rule}}
        | pod={{.OutputFields.k8s_pod_name}}
        | ns={{.OutputFields.k8s_ns_name}}
        | cmd={{.OutputFields.proc_cmdline}}&quot;
    ports:
      - &quot;2801:2801&quot;
</code></pre>
<pre><code class="" data-line=""># Start the stack (set SLACK_WEBHOOK first)
export SLACK_WEBHOOK=&quot;https://hooks.slack.com/services/YOUR/WEBHOOK/URL&quot;
docker compose up -d

# Trigger a test alert: exec into any running container
docker exec -it &lt;any-container&gt; /bin/sh

# Check falcosidekick received it
curl -s http://localhost:2801/metrics | grep falcosidekick_inputs_total
</code></pre>
<h3 id="deploying-falco-to-kubernetes-with-helm">Deploying Falco to Kubernetes with Helm</h3>
<pre><code class="" data-line=""># Add Falco Helm repo
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

# Install Falco with eBPF driver (not kernel module — required in Kubernetes)
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set driver.kind=modern_ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl=&quot;${SLACK_WEBHOOK}&quot; \
  --set falcosidekick.config.slack.minimumpriority=warning \
  --set customRules.&quot;purple-team\.yaml&quot;=&quot;$(cat ./rules/purple-team.yaml)&quot;

# Verify Falco pods are running on all nodes
kubectl get pods -n falco -o wide

# Tail Falco logs for a specific node&#039;s pod
kubectl logs -n falco -l app.kubernetes.io/name=falco -f
</code></pre>
<pre><code class="" data-line=""># Validate a specific rule is loaded
kubectl exec -n falco &lt;falco-pod&gt; -- falco --list-rules 2&gt;/dev/null | grep &quot;Shell Spawned&quot;
</code></pre>
<hr />
<h2 id="what-this-means-for-each-prior-attack">What This Means for Each Prior Attack</h2>
<p>Every attack in EP07 through EP10 had a detectable kernel-level signal that the standard telemetry stack missed. Here&#8217;s the detection mapping:</p>
<table>
<thead>
<tr>
<th>Episode</th>
<th>Attack</th>
<th>What Standard Telemetry Missed</th>
<th>eBPF Detection Signal</th>
</tr>
</thead>
<tbody>
<tr>
<td>EP07</td>
<td>SSRF to EC2 IMDS</td>
<td>CloudTrail: nothing. VPC Flow Logs: 169.254.169.254 destination, 15-min delay, no PID</td>
<td>TC kprobe: <code class="" data-line="">connect()</code> to <code class="" data-line="">169.254.169.254</code> from app process — PID, comm, container, real-time</td>
</tr>
<tr>
<td>EP08</td>
<td>Container escape via privileged mount</td>
<td>CloudTrail: nothing. Syslog: nothing</td>
<td>kprobe: <code class="" data-line="">security_sb_mount()</code> from non-host mount namespace — namespace ID mismatch fires alert</td>
</tr>
<tr>
<td>EP09</td>
<td>Supply chain implant execution</td>
<td>CloudTrail: nothing (OS-level). GuardDuty: maybe if beacon calls AWS APIs</td>
<td>kprobe: <code class="" data-line="">execve()</code> with anomalous parent chain — web process → shell → unknown binary from <code class="" data-line="">/tmp</code></td>
</tr>
<tr>
<td>EP10</td>
<td>Lateral movement via cross-account role chaining</td>
<td>CloudTrail: AssumeRole events present but no process context</td>
<td>TC hook: <code class="" data-line="">connect()</code> to <code class="" data-line="">sts.amazonaws.com</code> from Lambda handler process — unexpected process identity</td>
</tr>
</tbody>
</table>
<p>The table is not theoretical. It reflects what you would actually observe running these detection rules against the attack simulations in those episodes.</p>
<p>For the SSRF case (EP07): the connection to <code class="" data-line="">169.254.169.254</code> from the web application process would fire within milliseconds of the exploit. VPC Flow Logs would record the same IP 10–15 minutes later, with no information about which process made it. By the time the flow log is queryable, the attacker has the IAM credentials and may have made subsequent API calls in a different region.</p>
<p>For the container escape (EP08): the <code class="" data-line="">mount()</code> from a non-host mount namespace is the earliest detectable signal of the escape attempt. It fires before the attacker has host filesystem access. With <code class="" data-line="">action: Sigkill</code> in the Tetragon policy, the process is terminated at this syscall — the escape does not complete.</p>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>Use the eBPF driver for Falco in Kubernetes, not the kernel module.</strong> The kernel module requires installing a kernel module on every node, which creates a dependency on kernel headers being present and compatible. The <code class="" data-line="">modern_ebpf</code> driver (Falco 0.35+) uses BTF and CO-RE — it works on kernels 5.8+ without kernel module installation and survives kernel upgrades. In managed Kubernetes (EKS, GKE, AKS), the kernel module path often doesn&#8217;t work at all due to the OS image restrictions.</p>
<p><strong>Test Tetragon&#8217;s <code class="" data-line="">Sigkill</code> action exhaustively before enabling it in production.</strong> The <code class="" data-line="">Sigkill</code> action terminates the process at the moment of the violating syscall — before it completes. This is powerful for prevention but catastrophic if your exclusions are wrong. Common false positive sources: debug containers (<code class="" data-line="">kubectl debug</code>), init containers that perform legitimate mounts, Kubernetes admission webhooks calling shell scripts. Always deploy with <code class="" data-line="">action: Post</code> first, tune for two weeks of normal traffic, then switch to <code class="" data-line="">Sigkill</code> only on rules with zero false positives in your environment.</p>
<p><strong>bpftrace is an investigation tool, not a production detector.</strong> bpftrace compiles and loads an eBPF program per invocation — it has no persistence, no alerting, and no output routing to your SIEM. It is for the incident response scenario described in the opening: &#8220;did this process make outbound connections in the last 30 minutes?&#8221; (answered: it&#8217;s what&#8217;s happening right now). For always-on detection, use Tetragon or Falco. Running bpftrace as a daemon substitute introduces overhead without the management plane that production tools provide.</p>
<p><strong>The shell-in-container rule will fire on <code class="" data-line="">kubectl exec</code> sessions.</strong> Any time an operator runs <code class="" data-line="">kubectl exec -it &lt;pod&gt; -- /bin/bash</code>, the Falco rule above triggers. This is working as intended — <code class="" data-line="">kubectl exec</code> is a post-compromise technique as well as an operational tool. Handle this with an exclusion on the user identity or namespace:</p>
<pre><code class="" data-line=""># Add to the rule condition to exclude operator kubectl exec sessions
# Map your cluster admin users or service account here
and not user.name in (cluster-admin-users)
and not k8s.ns.name in (ops-tooling, debug-ns)
</code></pre>
<p><strong>High-frequency kprobes on hot paths add measurable overhead.</strong> Attaching to <code class="" data-line="">tcp_connect</code> fires on every outbound connection from every process on the node. On a node handling hundreds of microservices with high connection rates (service mesh with short-lived connections), this adds CPU overhead. Profile before deploying. Tetragon&#8217;s namespace-scoped selectors (<code class="" data-line="">matchNamespaces: NotHost</code>) help by skipping host-namespace processes. Filter as narrowly as your threat model allows.</p>
<p><strong>Ring buffer overflow silently drops events on high-throughput nodes.</strong> Both Falco and bpftrace use kernel ring buffers to pass events to userspace. If the userspace consumer (the Falco daemon, the bpftrace process) cannot keep up with the event rate, the kernel drops events silently. Falco exposes a <code class="" data-line="">falco_events_dropped_total</code> metric — monitor it. Tune <code class="" data-line="">ring_buffer_size</code> in the Falco configuration if drops occur on high-throughput nodes.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<table>
<thead>
<tr>
<th>Use Case</th>
<th>Tool</th>
<th>Hook Type</th>
<th>Detection Latency</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ad-hoc outbound connection investigation</td>
<td>bpftrace</td>
<td>tracepoint:syscalls:sys_enter_connect</td>
<td>Real-time</td>
</tr>
<tr>
<td>Always-on container shell detection</td>
<td>Falco</td>
<td>eBPF modern driver / syscall</td>
<td>&lt; 100ms</td>
</tr>
<tr>
<td>Container escape prevention</td>
<td>Tetragon + Sigkill</td>
<td>kprobe: security_sb_mount</td>
<td>Blocking (pre-completion)</td>
</tr>
<tr>
<td>Privilege escalation detection</td>
<td>Falco / bpftrace</td>
<td>tracepoint:syscalls:sys_enter_setuid</td>
<td>Real-time</td>
</tr>
<tr>
<td>Supply chain implant execution</td>
<td>Falco execve rule</td>
<td>eBPF modern driver</td>
<td>&lt; 100ms</td>
</tr>
<tr>
<td>SSRF-to-metadata detection</td>
<td>Tetragon kprobe</td>
<td>kprobe: tcp_connect</td>
<td>Real-time</td>
</tr>
<tr>
<td>Lateral movement via unexpected STS call</td>
<td>Tetragon kprobe</td>
<td>kprobe: tcp_connect + process filter</td>
<td>Real-time</td>
</tr>
<tr>
<td>Audit trail for incident response</td>
<td>Tetragon JSON events</td>
<td>kprobe / tracepoint</td>
<td>Persistent, SIEM-routable</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Best For</th>
<th>Not For</th>
</tr>
</thead>
<tbody>
<tr>
<td>bpftrace</td>
<td>Ad-hoc node investigation during IR</td>
<td>Always-on production detection</td>
</tr>
<tr>
<td>Falco</td>
<td>Rule-based behavioral detection</td>
<td>Network-layer enforcement</td>
</tr>
<tr>
<td>Tetragon</td>
<td>Always-on detection + optional enforcement</td>
<td>Ad-hoc one-liner investigation</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li><strong>Detection engineering with eBPF</strong> closes the telemetry gap that CloudTrail, VPC Flow Logs, and syslog cannot close: OS-level process activity is only visible at the kernel syscall layer, and eBPF is the only production-grade mechanism that reads it without kernel module risk</li>
<li>Every attack in EP07 through EP10 has a real-time kernel-level signal — SSRF connections, container mount calls, unexpected execve chains, privilege escalation attempts — none of which appear in your current SIEM unless you&#8217;ve built this layer</li>
<li>Falco provides declarative, rule-based behavioral detection; Tetragon provides syscall-level enforcement that can terminate an attack before it completes — use both with complementary scopes</li>
<li>bpftrace is the incident response tool for asking the kernel a direct question right now; it is not a monitoring agent and should not be treated as one</li>
<li>The false positive problem is real and must be addressed before enabling enforcement: <code class="" data-line="">kubectl exec</code>, debug containers, init containers with legitimate mounts — exclusions must be tuned per environment before moving from <code class="" data-line="">action: Post</code> to <code class="" data-line="">action: Sigkill</code></li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP11 closed the detection gap. You&#8217;ve instrumented the kernel, you&#8217;re receiving Falco alerts, Tetragon is firing on namespace-crossing mount attempts. Then the alert fires at 2:47 AM on a Sunday — not a test, not a false positive. Something got in.</p>
<p>EP12 is the playbook for the first 24 hours after a confirmed cloud breach: what to isolate and how without destroying forensic evidence, what to preserve before it rotates out of CloudTrail&#8217;s 90-day window, what eBPF data to capture while the node is still live, who to call and in what order, and how to avoid the common mistakes that turn a containable incident into a regulatory event. The response phase — where everything you built in EP04 through EP11 either pays off or reveals what you missed.</p>
<p>Get EP12 in your inbox when it publishes → <a href="#subscribe">subscribe at linuxcent.com</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&amp;linkname=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fdetection-engineering-ebpf-cloud%2F&#038;title=Detection%20Engineering%20with%20eBPF%3A%20Kernel-Level%20Visibility%20for%20Cloud%20Incidents" data-a2a-url="https://linuxcent.com/detection-engineering-ebpf-cloud/" data-a2a-title="Detection Engineering with eBPF: Kernel-Level Visibility for Cloud Incidents"></a></p><p>The post <a href="https://linuxcent.com/detection-engineering-ebpf-cloud/">Detection Engineering with eBPF: Kernel-Level Visibility for Cloud Incidents</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/detection-engineering-ebpf-cloud/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1873</post-id>	</item>
		<item>
		<title>LSM and Tetragon — When the Kernel Says No</title>
		<link>https://linuxcent.com/ebpf-lsm-tetragon-runtime-security/</link>
					<comments>https://linuxcent.com/ebpf-lsm-tetragon-runtime-security/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Fri, 12 Jun 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[eBPF]]></category>
		<category><![CDATA[Cilium]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[linux-security]]></category>
		<category><![CDATA[LSM]]></category>
		<category><![CDATA[Runtime Security]]></category>
		<category><![CDATA[SRE]]></category>
		<category><![CDATA[Tetragon]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1841</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutes</span></span>LSM eBPF Tetragon hooks enforce policy at the syscall boundary before it completes — how Tetragon kills processes from kernel space, and why that matters.</p>
<p>The post <a href="https://linuxcent.com/ebpf-lsm-tetragon-runtime-security/">LSM and Tetragon — When the Kernel Says No</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>eBPF: From Kernel to Cloud, Episode 12</em><br />
<a href="/what-is-ebpf-linux-kubernetes/">What Is eBPF?</a> · <a href="/bpf-verifier-kubernetes-safety/">The BPF Verifier</a> · <a href="/ebpf-vs-kernel-modules-kubernetes/">eBPF vs Kernel Modules</a> · <a href="/ebpf-program-types-kubernetes/">eBPF Program Types</a> · <a href="/ebpf-maps-explained/">eBPF Maps</a> · <a href="/ebpf-co-re-libbpf-portable-programs/">CO-RE and libbpf</a> · <a href="/ebpf-xdp-kubernetes-networking/">XDP</a> · <a href="/tc-ebpf-kubernetes-network-policy/">TC eBPF</a> · <a href="/bpftrace-kernel-observability/">bpftrace</a> · <a href="/ebpf-network-flow-observability/">Network Flow Observability</a> · <a href="/ebpf-dns-observability-kubernetes/">DNS Observability</a> · <strong>LSM and Tetragon</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>LSM eBPF Tetragon integrates Linux Security Module hooks with eBPF programs — enforcement happens at the syscall boundary, before the operation completes, with no detect-and-respond window<br />
  <em>(LSM hook = Linux Security Module hook: a callback point built into the kernel that fires before a security-relevant operation completes, allowing the security module to approve or reject it)</em></li>
<li>Falco and similar sidecar-based tools detect after the fact — the syscall returns, the file is written, the connection is established, the alert fires; with LSM, the syscall never returns success</li>
<li><code class="" data-line="">BPF_PROG_TYPE_LSM</code> is the eBPF program type that attaches to LSM hooks — introduced in kernel 5.7, stable in 5.10+; available on all current Ubuntu LTS, Fedora, and EKS/GKE nodes</li>
<li>Tetragon attaches eBPF programs to LSM hooks and kprobes simultaneously — observing and enforcing from the same kernel attachment point</li>
<li>Tetragon&#8217;s enforcement sends <code class="" data-line="">SIGKILL</code> from within the kernel context — not from a userspace agent reading an audit log and then killing the process</li>
<li>Production caution: LSM enforce mode without thorough policy testing in audit mode first will kill legitimate workloads; always audit before enforce</li>
</ul>
<hr />
<p>EP11 showed how to observe DNS queries at the kernel level — seeing what a workload resolves before it establishes a connection. But observation is passive. It tells you what happened. LSM eBPF Tetragon changes the question entirely: instead of watching the workload, the kernel refuses the operation. This episode covers how that enforcement layer works and why the difference between &#8220;detect&#8221; and &#8220;prevent&#8221; matters in runtime security.</p>
<h2 id="quick-check-is-your-cluster-running-lsm-based-enforcement">Quick Check: Is Your Cluster Running LSM-Based Enforcement?</h2>
<pre><code class="" data-line=""># On any cluster node — what security modules are active?
cat /sys/kernel/security/lsm

# Expected output on a modern kernel:
# lockdown,capability,landlock,yama,apparmor,bpf
#                                              ^^^
#                            &quot;bpf&quot; here means BPF LSM is enabled
</code></pre>
<pre><code class="" data-line=""># Is Tetragon running on this cluster?
kubectl get pods -n kube-system -l app.kubernetes.io/name=tetragon

# If Tetragon is present, check what TracingPolicies are enforcing:
kubectl get tracingpolicies -A

# Sample output:
# NAMESPACE    NAME                      AGE
# kube-system  block-privileged-exec     3d
# kube-system  restrict-sensitive-paths  3d
</code></pre>
<pre><code class="" data-line=""># See what eBPF programs Tetragon has loaded
bpftool prog list | grep -i tetragon

# Output sample:
# 89: lsm  name tetragon_lsm_bprm  tag 8f2a1c3e4d5b7a9f  gpl
#     loaded_at 2026-04-22T09:13:45+0530  uid 0
#     xlated 3312B  jited 2184B  memlock 8192B
# 91: kprobe  name tetragon_kp_exec tag 3c1d8e2f7a4b5c9d  gpl
</code></pre>
<p><code class="" data-line="">lsm</code> program type confirms LSM hook attachment. If you see <code class="" data-line="">tetragon_lsm_*</code> entries, Tetragon is enforcing at the kernel level on this node.</p>
<blockquote>
<p><strong>Not running Tetragon?</strong> Check if your cluster uses AppArmor or seccomp profiles instead — <code class="" data-line="">kubectl get pod &lt;name&gt; -o jsonpath=&#039;{.metadata.annotations}&#039;</code> and look for <code class="" data-line="">seccomp.security.alpha.kubernetes.io</code> or <code class="" data-line="">container.apparmor.security.beta.kubernetes.io</code> annotations. These are userspace-applied profiles that the kernel enforces. Tetragon is additive — it can run alongside AppArmor/seccomp and provides per-process, dynamic policy that static profiles cannot.</p>
</blockquote>
<hr />
<p>Falco fired at 03:14 AM. The alert: a process inside a production container had opened <code class="" data-line="">/etc/passwd</code> for writing. By the time I was on the call, the container had been restarted by a health check failure — the compromised process had already exited. The file had already been modified. Falco had detected the open, emitted the alert, and by the time any automated response could have acted, the syscall had returned, the write had completed, and the file was changed.</p>
<p>Falco did exactly what it&#8217;s designed to do: observe and alert. The gap isn&#8217;t in Falco — it&#8217;s in the architecture. When a tool detects from userspace by reading kernel audit events, there is always a window between the operation completing and the alert firing. For a fast exploit, that window is the entire attack.</p>
<p>I added a Tetragon TracingPolicy the following week:</p>
<pre><code class="" data-line="">spec:
  kprobes:
    - call: &quot;security_inode_permission&quot;
      syscall: false
      return: false
      args:
        - index: 0
          type: &quot;inode&quot;
      selectors:
        - matchArgs:
            - index: 0
              operator: &quot;Prefix&quot;
              values: [&quot;/etc/passwd&quot;, &quot;/etc/shadow&quot;]
          matchActions:
            - action: Sigkill
</code></pre>
<p>Next time a process tries to open <code class="" data-line="">/etc/passwd</code> for writing in a container covered by that policy, the kernel sends <code class="" data-line="">SIGKILL</code> from within the LSM hook. The open never completes. There is no window.</p>
<hr />
<h2 id="how-lsm-hooks-are-placed-in-the-kernel">How LSM Hooks Are Placed in the Kernel</h2>
<p>Linux Security Modules (LSM) is a framework built into the Linux kernel that inserts hook points before security-sensitive operations. The hook fires before the operation is allowed to complete — the LSM module can return an error code that causes the kernel to reject the operation and return <code class="" data-line="">-EPERM</code> to the calling process.</p>
<pre><code class="" data-line="">Process calls open(&quot;/etc/passwd&quot;, O_WRONLY)
      ↓
VFS (Virtual Filesystem) layer receives the request
      ↓
VFS calls security_inode_permission()   ← LSM hook fires here
      ↓
LSM module checks policy
      ↓
      ├── ALLOW → open() proceeds, file descriptor returned
      └── DENY  → open() returns -EPERM, process gets &quot;Permission denied&quot;
                  File is never touched
</code></pre>
<blockquote>
<p><strong><code class="" data-line="">LSM hook</code></strong> — a callback point embedded in Linux kernel source at every security-sensitive operation: file open, execute, socket connect, capability check, mount, ptrace, and more. The kernel calls registered LSM modules at each hook. Before BPF LSM (kernel 5.7), only statically compiled security modules (SELinux, AppArmor, BPF LSM itself) could register at these hooks.</p>
<p><strong><code class="" data-line="">BPF_PROG_TYPE_LSM</code></strong> — the eBPF program type that attaches to LSM hooks. Introduced in kernel 5.7. Requires BPF LSM to be enabled in the kernel (<code class="" data-line="">lsm=bpf</code> in kernel command line, or present alongside other LSMs). When this program type is loaded and attached to an LSM hook, the eBPF program runs at the hook point and returns 0 (allow) or a negative error code (deny).</p>
</blockquote>
<p>The full list of LSM hooks:</p>
<pre><code class="" data-line=""># All LSM hook points available for eBPF attachment
bpftool feature list | grep lsm_hook | head -20

# Or browse the kernel source list:
# include/linux/security.h — every security_*() function is an LSM hook point
</code></pre>
<p>There are 200+ LSM hook points. The most operationally relevant for container security:</p>
<table>
<thead>
<tr>
<th>LSM Hook</th>
<th>What it guards</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="" data-line="">security_bprm_check</code></td>
<td>Process execution (execve)</td>
</tr>
<tr>
<td><code class="" data-line="">security_inode_permission</code></td>
<td>File read/write/execute</td>
</tr>
<tr>
<td><code class="" data-line="">security_inode_create</code></td>
<td>File creation</td>
</tr>
<tr>
<td><code class="" data-line="">security_socket_connect</code></td>
<td>Outbound TCP/UDP connect</td>
</tr>
<tr>
<td><code class="" data-line="">security_socket_bind</code></td>
<td>Port binding</td>
</tr>
<tr>
<td><code class="" data-line="">security_ptrace_access_check</code></td>
<td>ptrace (debugger attach)</td>
</tr>
<tr>
<td><code class="" data-line="">security_capable</code></td>
<td>Capability checks (CAP_SYS_ADMIN etc.)</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="how-tetragon-combines-lsm-and-kprobe">How Tetragon Combines LSM and kprobe</h2>
<p>Tetragon attaches two types of programs simultaneously for comprehensive runtime security:</p>
<pre><code class="" data-line="">kprobe programs          LSM programs
(observation layer)      (enforcement layer)
       │                        │
       ↓                        ↓
Process executes              Kernel LSM hook fires
kernel function               BEFORE operation completes
       │                        │
       ↓                        ↓
Tetragon reads context:       Tetragon checks TracingPolicy:
  - process name                - selectors match?
  - PID, UID                    - action = Sigkill?
  - namespace, pod name         │
  - parent process              ↓
  - capabilities                SIGKILL sent from kernel context
       │                        Process terminated
       ↓                        Operation never completes
Tetragon exports event
  to userspace observer
</code></pre>
<p>The kprobe side provides the rich context (pod name, namespace, process tree) because it has access to Kubernetes metadata that Tetragon&#8217;s userspace component has pre-populated into maps. The LSM side provides the enforcement capability. Together, they give you context-aware kernel enforcement.</p>
<blockquote>
<p><strong><code class="" data-line="">SIGKILL</code> from kernel vs userspace kill</strong> — When a userspace process runs <code class="" data-line="">kill -9 &lt;pid&gt;</code>, it issues a kill syscall, the kernel schedules the signal delivery, and the target process dies on its next scheduler timeslice. There is a measurable delay — and more importantly, the target process may run for several more instructions before the signal is delivered. When a BPF LSM program returns a non-zero error code or calls <code class="" data-line="">bpf_send_signal(SIGKILL)</code> from within the hook, the signal is delivered synchronously within the kernel&#8217;s execution context. The process does not execute another instruction in the problematic syscall. This is not a speed difference — it is a structural difference in when the enforcement happens relative to the operation.</p>
</blockquote>
<hr />
<h2 id="writing-a-tetragon-tracingpolicy-for-enforcement">Writing a Tetragon TracingPolicy for Enforcement</h2>
<p>Tetragon policies are Kubernetes custom resources. Here&#8217;s a policy that prevents any container from executing shells:</p>
<pre><code class="" data-line="">apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-shell-exec
spec:
  kprobes:
    - call: &quot;security_bprm_check&quot;
      syscall: false
      args:
        - index: 0
          type: &quot;linux_binprm&quot;
      selectors:
        - matchBinaries:
            - operator: &quot;In&quot;
              values:
                - &quot;/bin/sh&quot;
                - &quot;/bin/bash&quot;
                - &quot;/bin/dash&quot;
                - &quot;/usr/bin/sh&quot;
                - &quot;/usr/bin/bash&quot;
          matchNamespaces:
            - namespace: Pid
              operator: &quot;NotIn&quot;
              values: [&quot;1&quot;]      # exclude host namespace (PID 1 = init)
          matchActions:
            - action: Sigkill
              argError: -1       # EPERM returned to the caller
</code></pre>
<p>Apply and verify:</p>
<pre><code class="" data-line="">kubectl apply -f block-shell-exec.yaml

# Confirm it&#039;s active
kubectl get tracingpolicies
# NAME               ENABLED   REASON   AGE
# block-shell-exec   true               5s

# Verify Tetragon loaded the eBPF program for this policy
bpftool prog list | grep bprm
# 94: lsm  name tetragon_lsm_bprm  tag 8f2a1c3e4d5b7a9f  gpl
#     loaded_at 2026-04-22T14:22:13+0530  uid 0
</code></pre>
<p>Test it (in a non-production namespace):</p>
<pre><code class="" data-line="">kubectl exec -it test-pod -- /bin/sh

# Expected output:
# OCI runtime exec failed: exec failed: unable to start container process:
# error during container init: error starting executable [&quot;/bin/sh&quot;]:
# container_linux.go: ... starting container process caused: process_linux.go:
# ... SIGKILL
</code></pre>
<p>The shell never started. The <code class="" data-line="">security_bprm_check</code> LSM hook fired, the Tetragon eBPF program evaluated the policy, returned <code class="" data-line="">SIGKILL</code> from kernel space. The exec system call returned <code class="" data-line="">-EPERM</code> to the container runtime. No shell process was created.</p>
<hr />
<h2 id="audit-mode-before-enforce-mode">Audit Mode Before Enforce Mode</h2>
<p>Running a new LSM policy in enforce mode without prior testing will kill legitimate workloads. Tetragon supports audit mode for every policy:</p>
<pre><code class="" data-line="">          matchActions:
            - action: Post     # audit mode: log event, do NOT kill
</code></pre>
<p><code class="" data-line="">Post</code> emits a Tetragon event that you can observe:</p>
<pre><code class="" data-line=""># Watch audit events for the policy (before switching to Sigkill)
kubectl exec -n kube-system -it \
  $(kubectl get pod -n kube-system -l app.kubernetes.io/name=tetragon -o name | head -1) \
  -- tetra getevents --event-types PROCESS_KPROBE | grep bprm
</code></pre>
<p>Sample audit event:</p>
<pre><code class="" data-line="">{
  &quot;process_kprobe&quot;: {
    &quot;process&quot;: {
      &quot;pod&quot;: {&quot;name&quot;: &quot;my-app-6d4f9-xk2p1&quot;, &quot;namespace&quot;: &quot;production&quot;},
      &quot;binary&quot;: &quot;/bin/sh&quot;,
      &quot;pid&quot;: 18293
    },
    &quot;function_name&quot;: &quot;security_bprm_check&quot;,
    &quot;action&quot;: &quot;KPROBE_ACTION_POST&quot;
  }
}
</code></pre>
<p>If <code class="" data-line="">my-app</code> legitimately needs <code class="" data-line="">/bin/sh</code> for its health check script, you&#8217;ll see it here before you kill it. Refine the selector (add <code class="" data-line="">matchLabels</code> to exclude that specific deployment, or add the binary to an allowlist) and then switch to <code class="" data-line="">Sigkill</code>.</p>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>Enforce mode kills anything the selector matches — including health checks and init containers.</strong> Most production containers have some shell usage: liveness probes that run <code class="" data-line="">sh -c</code>, init containers that <code class="" data-line="">chmod</code> files, entrypoint wrappers. Run in <code class="" data-line="">Post</code> (audit) mode for at least 48 hours across a representative workload set before switching to <code class="" data-line="">Sigkill</code>. Track all matched events and understand every process in the trace before enforcing.</p>
<p><strong>LSM hooks fire in kernel context — eBPF program complexity is limited.</strong> The verifier enforces strict limits on LSM programs because they run synchronously in the kernel&#8217;s hot path. Policies with many conditions or complex map lookups may be rejected by the verifier. Tetragon&#8217;s policy engine compiles your TracingPolicy into eBPF that stays within verifier limits, but very complex <code class="" data-line="">matchArgs</code> chains with many values can hit limits. Test with <code class="" data-line="">kubectl apply</code> and check Tetragon pod logs for verifier rejection messages.</p>
<p><strong><code class="" data-line="">BPF_PROG_TYPE_LSM</code> requires kernel 5.7+ and BPF LSM enabled.</strong> Check <code class="" data-line="">/sys/kernel/security/lsm</code> for <code class="" data-line="">bpf</code> in the list. EKS nodes running Amazon Linux 2 with kernel 5.10+ have BPF LSM available. GKE nodes with kernel 5.10+ on Container-Optimized OS have it enabled. Ubuntu 22.04 (kernel 5.15) has it enabled by default. Ubuntu 20.04 kernels before 5.7 do not — check your actual kernel version.</p>
<p><strong>Policy scope: Tetragon TracingPolicies are cluster-wide by default.</strong> A policy without a <code class="" data-line="">matchNamespaces</code> or <code class="" data-line="">matchLabels</code> selector applies to every pod on every node. Start with namespace-scoped policies during testing. Use <code class="" data-line="">namespaced</code> TracingPolicy resources (Tetragon 0.10+) to limit scope to a specific namespace.</p>
<p><strong><code class="" data-line="">bpf_send_signal(SIGKILL)</code> vs returning an error code.</strong> Tetragon&#8217;s <code class="" data-line="">Sigkill</code> action uses <code class="" data-line="">bpf_send_signal()</code> rather than returning a negative error from the LSM hook. This means the syscall may return before the signal is delivered — there can be a single instruction window. For critical enforcement paths, combining LSM deny (return <code class="" data-line="">-EPERM</code>) with <code class="" data-line="">bpf_send_signal(SIGKILL)</code> is the belt-and-suspenders approach; Tetragon&#8217;s maintainers have documented which actions use which mechanism.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<table>
<thead>
<tr>
<th>What you want</th>
<th>Command</th>
</tr>
</thead>
<tbody>
<tr>
<td>Is BPF LSM enabled?</td>
<td><code class="" data-line="">cat /sys/kernel/security/lsm</code> (look for <code class="" data-line="">bpf</code>)</td>
</tr>
<tr>
<td>What LSM programs are loaded?</td>
<td><code class="" data-line="">bpftool prog list | grep lsm</code></td>
</tr>
<tr>
<td>What Tetragon policies exist?</td>
<td><code class="" data-line="">kubectl get tracingpolicies -A</code></td>
</tr>
<tr>
<td>Audit events (before enforce)</td>
<td><code class="" data-line="">tetra getevents --event-types PROCESS_KPROBE</code></td>
</tr>
<tr>
<td>Watch Tetragon enforcement</td>
<td><code class="" data-line="">kubectl logs -n kube-system -l app.kubernetes.io/name=tetragon -f</code></td>
</tr>
<tr>
<td>Test a policy safely</td>
<td>Set <code class="" data-line="">action: Post</code> before <code class="" data-line="">action: Sigkill</code></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Tetragon action</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="" data-line="">Post</code></td>
<td>Log event only — audit mode</td>
</tr>
<tr>
<td><code class="" data-line="">Sigkill</code></td>
<td>Send SIGKILL from kernel context</td>
</tr>
<tr>
<td><code class="" data-line="">Override</code></td>
<td>Return custom error code to syscall caller</td>
</tr>
<tr>
<td><code class="" data-line="">FollowFD</code></td>
<td>Track file descriptor for future hook correlation</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>LSM hook</th>
<th>Protects</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="" data-line="">security_bprm_check</code></td>
<td>exec (block shell spawning)</td>
</tr>
<tr>
<td><code class="" data-line="">security_inode_permission</code></td>
<td>file access (block reads/writes to sensitive paths)</td>
</tr>
<tr>
<td><code class="" data-line="">security_socket_connect</code></td>
<td>outbound connections (block C2 connections)</td>
</tr>
<tr>
<td><code class="" data-line="">security_capable</code></td>
<td>capability escalation (block CAP_SYS_ADMIN attempts)</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>LSM eBPF Tetragon enforces at the syscall boundary — the operation either never completes or returns an error before the kernel performs the action, with no detect-and-respond window</li>
<li>Falco, Datadog, and sidecar-based tools detect events after the syscall returns; this is architectural, not a product limitation — they operate at a layer where the operation has already occurred</li>
<li><code class="" data-line="">BPF_PROG_TYPE_LSM</code> attaches eBPF programs directly to Linux Security Module hooks; available on kernel 5.7+, enabled on all current EKS/GKE LTS node images</li>
<li>Tetragon sends <code class="" data-line="">SIGKILL</code> from kernel context using <code class="" data-line="">bpf_send_signal()</code> — not from a userspace agent polling an audit log</li>
<li>Always run Tetragon policies in <code class="" data-line="">Post</code> (audit) mode for 48+ hours before switching to <code class="" data-line="">Sigkill</code> — legitimate workloads trigger many of the same LSM hooks that attacks use</li>
<li>The combination of kprobe (rich context: pod name, namespace, process tree) and LSM (enforcement) gives Tetragon context-aware kernel enforcement that static profiles (AppArmor, seccomp) cannot provide dynamically</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>LSM hooks prevent operations in the moment. But after an incident — when enforcement failed, or when you&#8217;re doing post-hoc forensics — the question changes: what did this process spawn, what files did it touch, what connections did it make, and in what order? Answering that from logs alone is guesswork. Answering it from kernel-level process lineage is reconstruction.</p>
<p>EP13 covers how eBPF kprobe hooks on <code class="" data-line="">fork</code> and <code class="" data-line="">exec</code> build a complete, tamper-resistant process tree. Even after the attacker&#8217;s process has exited, the record remains — in kernel maps, exported to a persistent store, tied to the pod that ran it.</p>
<p><em>Next: <a href="/ebpf-process-lineage-incident-response/">process lineage with eBPF — reconstructing what happened after the fact</a></em></p>
<p>Get EP13 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&amp;linkname=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Febpf-lsm-tetragon-runtime-security%2F&#038;title=LSM%20and%20Tetragon%20%E2%80%94%20When%20the%20Kernel%20Says%20No" data-a2a-url="https://linuxcent.com/ebpf-lsm-tetragon-runtime-security/" data-a2a-title="LSM and Tetragon — When the Kernel Says No"></a></p><p>The post <a href="https://linuxcent.com/ebpf-lsm-tetragon-runtime-security/">LSM and Tetragon — When the Kernel Says No</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/ebpf-lsm-tetragon-runtime-security/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1841</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-08-30 22:30:48 by W3 Total Cache
-->