Skip to content

Build1 publisher3 min readPublished

A kprobe on tcp_connect killed an npm postinstall's curl inside AWS CodeBuild

Tetragon ran inside a CodeBuild GitHub Actions runner and killed /usr/bin/curl when a dependency's postinstall script tried to reach a non-loopback address. The control needs a pinned host kernel and privileged mode.

The Engineer · Build desk

Illustration accompanying A kprobe on tcp_connect killed an npm postinstall's curl inside AWS CodeBuild

What happened

  • A dev.to write-up added a custom library whose postinstall script invokes /usr/bin/curl to an application's package.json, so running npm ci launched curl during dependency installation.
  • The author ran the job three times, with no policy, with the policy recording matches only, and with the policy enforcing, keeping Tetragon running in all three.
  • Only the enforce run terminated curl with SIGKILL, and only that run left no record at the local Node.js server standing in for the external destination.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost Buying this control means enabling privileged mode on the build project and pinning the host kernel, so the process policing the build holds more kernel access than the build itself.
  • constraint The selector names a single executable path, so protection grows one binary at a time and a postinstall that opens its own socket from node sits outside the rule entirely.
  • decision Because monitor and enforce load the same YAML with one flag changed, the choice to arm SIGKILL can be deferred until a team has counted matches on its own pipelines.
  • exposure Existing buildspecs are the first casualties of the armed policy: a release step pulling an artifact with curl trips the same condition as the malicious postinstall.

Tetragon's policy here is a single kprobe on `tcp_connect`, declared with `syscall: false`, reading argument index 0 as a `sock` [14]. Two conditions have to hold together before anything dies: the destination address is not inside 127.0.0.0/8, and the calling binary is /usr/bin/curl [14]. The matched action is `Sigkill` [14].

Coverage stops at that second condition. Because the policy identifies the offender by executable path, the same postinstall opening a socket from node, or from a copy of curl written elsewhere on disk, never matches the selector [19]. Tetragon's premise is that you describe behaviour instead of enumerating known vulnerabilities [3], and the behaviour described here is one binary.

A Node.js process listening on port 18080 in the same CodeBuild runner stands in for the external destination, and it writes a record to a file when curl's dummy value arrives [6][7]. The kill rule fires only on destinations outside 127.0.0.0/8 [14]. So for enforce mode to kill anything, curl has to target a non-loopback address that still reaches that local listener [20]. The post does not state which address it requests [21].

There are three settings to change. BTF type information is what lets Tetragon load eBPF programs matched to the running kernel, so the run pins a Linux 6 host kernel where BTF is available [10]. That kernel is configured separately from the build image, through a CDK escape hatch onto the `CfnProject`: `addPropertyOverride('Environment.HostKernel', 'LINUX_KERNEL_6')` [12]. A `buildspec-override:true` label goes on `runs-on` so Tetragon can be started during PRE_BUILD [13], and privileged mode is enabled so the eBPF programs can be loaded and attached to the kernel [11].

Observe and enforce load the identical YAML and differ only in the flag passed to `tetra tracingpolicy add`, monitor against enforce, with each mode on its own runner [16]. Rollout is the cheap part. A team can therefore run monitor across real pipelines and count matches before arming the kill. Any /usr/bin/curl call to a non-loopback address matches this rule, a release step fetching an artifact included [22].

The write-up follows Liz Rice's session "Detecting Compromised CI with eBPF and Cilium Tetragon" at KubeCon + CloudNativeCon Japan 2026 in July 2026 [1].

For the enforce result to transfer, four things have to be true in your pipeline: curl sits at /usr/bin/curl, the host kernel exposes BTF, your account permits privileged CodeBuild projects, and no legitimate build step needs curl off loopback. In the published run, enforce terminated curl with SIGKILL and produced no receive record [17], while baseline and observe let the request reach the file [18]. The compromised dependency was one the author wrote for the test and never published to npm [4][5].

What to watch

  • Whether the same policy stops a postinstall that opens the connection from node's own HTTP client instead of calling curl.
  • Whether CodeBuild host kernels other than LINUX_KERNEL_6 expose the BTF information Tetragon needs to load its programs.
  • What the monitor-mode match count looks like on real pipelines that already fetch artifacts with curl.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories