DEV Community

Running Tetragon in AWS CodeBuild to Block npm postinstall Network Access

In July 2026, I attended Liz Rice's session, "Detecting Compromised CI with eBPF and Cilium Tetragon," at KubeCon + CloudNativeCon Japan 2026.

The session demonstrated how Tetragon can be used to detect and prevent suspicious behavior inside CI environments. I wanted to try this myself, so I ran a GitHub Actions job on AWS CodeBuild and started Tetragon inside the CodeBuild environment.

For the experiment, I created a dependency with a postinstall script that initiates a network connection and checked whether Tetragon could stop it.

What is Tetragon?

Tetragon is a tool for monitoring processes running on Linux, including their network connections, file access, and other activities.

It uses eBPF to observe events at the kernel level and can record operations that match defined policies or terminate processes when necessary.

Rather than checking whether a package contains a known vulnerability, Tetragon observes what a process actually does when it runs. What should be blocked is defined explicitly in policies.

Calling curl from postinstall

For this experiment, I created two components:

  • demo/victim, which acts as the application
  • demo/compromised-dependency, a custom library installed by the application

The custom library contains a postinstall script that invokes /usr/bin/curl.

I then added this custom library to the application's package.json under dependencies.

When npm ci runs in the application directory, the library is installed and its postinstall script launches curl.

This setup reproduces a situation where installing a dependency causes network communication during the installation process.

Both projects are stored in the same repository and are not published to npm.

To check whether the request was successfully sent, I start a simple Node.js HTTP server inside the CI job.

The server listens on port 18080. When it receives a fixed dummy value from curl, it writes a record of the request to a file.

Both the sending curl process and the receiving Node.js process run inside the same CodeBuild runner.

The Node.js server acts as a substitute for an external destination, so the dummy value is never sent over the Internet. No real malware or credentials are used in this experiment.

Tetragon starts during the CodeBuild PRE_BUILD phase, after which the GitHub Actions job is executed.

CodeBuild configuration notes

The build image is aws/codebuild/amazonlinux-x86_64-standard:5.0, the host kernel is LINUX_KERNEL_6, and privileged mode is enabled.

Tetragon uses BTF type information to load eBPF programs that match the kernel of the execution environment.

For this experiment, I selected a Linux 6 environment where BTF is available.

Privileged mode is enabled in this configuration so that Tetragon can load and attach eBPF programs to the kernel.

image.png

The host kernel is configured separately from the build image, as described in the AWS documentation.

With CDK, I configured it as follows:

const cfnProject = this.runnerProject.node.defaultChild as codebuild.CfnProject;
cfnProject.addPropertyOverride('Environment.HostKernel', 'LINUX_KERNEL_6');
Enter fullscreen mode Exit fullscreen mode

I also added buildspec-override:true to the workflow so that Tetragon can be started during PRE_BUILD.

See the AWS documentation for configuring CodeBuild-hosted GitHub Actions runners.

runs-on:
  - codebuild-tetragon-codebuild-guard-${{ github.run_id }}-${{ github.run_attempt }}
  - buildspec-override:true
  - tetragon-${{ matrix.mode }}
Enter fullscreen mode Exit fullscreen mode

image.png

Adding a policy

The policy condition is simple:

If /usr/bin/curl attempts to connect to an address outside 127.0.0.0/8, send it SIGKILL.

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-curl-egress
spec:
  kprobes:
    - call: tcp_connect
      syscall: false
      args:
        - index: 0
          type: sock
      selectors:
        - matchArgs:
            - index: 0
              operator: NotDAddr
              values:
                - 127.0.0.0/8
          matchBinaries:
            - operator: In
              values:
                - /usr/bin/curl
          matchActions:
            - action: Sigkill
Enter fullscreen mode Exit fullscreen mode

I ran the experiment in three modes:

  • baseline: no policy
  • observe: record matching events only
  • enforce: actually block matching activity

Tetragon itself runs in all three cases.

For observe and enforce, I use the same YAML policy and only change the policy mode when loading it.

After copying the policy file into the Tetragon container, I use the following commands.

Each mode runs on a separate runner.

# observe
tetra tracingpolicy add --mode monitor /tmp/block-curl-egress.yaml

# enforce
tetra tracingpolicy add --mode enforce /tmp/block-curl-egress.yaml
Enter fullscreen mode Exit fullscreen mode

Results

Mode Policy connection events curl result Dummy value received
baseline 0 exit 0 Yes
observe 1 exit 0 Yes
enforce 1 SIGKILL No

Only in enforce mode was curl terminated with SIGKILL, and no receive record was created.

In both baseline and observe modes, the request completed successfully and npm ci also finished normally.

image.png

The enforce job is also shown as green, because the test considers successfully blocking the request to be a successful result.

The simulated attack step uses continue-on-error, and a later step evaluates the result.

The error shown near the bottom of the enforce job is the record of the simulated attack being terminated.

The postinstall log also shows why curl exited:

The simulated request did not complete: SIGKILL
Enter fullscreen mode Exit fullscreen mode

image.png

As part of the verification, I confirmed all of the following:

  • The destination address in the policy event matched the test server
  • The curl process exited with the SIGKILL signal
  • No receive record was created

Conclusion

In this experiment, Tetragon did not determine that "this dependency is malicious."

It simply blocked the process based on the configured conditions: the curl binary and its destination address.

With this policy as-is, legitimate downloads matching the same conditions would also be blocked.

If I were to use this approach in a real CI environment, I would first run Tetragon in observe mode to understand what kinds of network communication normally occur, organize what should be allowed, and only then experiment with enforce mode.

Next, I would like to check whether I can trace process parent-child relationships inside CodeBuild and narrow the policy down to curl processes launched specifically from npm.

Top comments (0)

The discussion has been locked. New comments can't be added.