eBPF programs can dynamically enforce network and resource access policies on a per-process or per-container basis by hooking into the kernel’s control group (cgroup) mechanisms.

Let’s see this in action. Imagine you have a web server running in a container, and you want to ensure it only listens on specific ports and doesn’t exceed a certain CPU limit.

# First, ensure your kernel supports eBPF and cgroup v2
# You might need to enable specific kernel configs like CONFIG_BPF_SYSCALL, CONFIG_CGROUP_BPF

# Example eBPF program (written in C, compiled to BPF bytecode)
# This program checks if a process is trying to bind to a forbidden port.

#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/ptrace.h>
#include <linux/sched.h>

SEC("cgroup/connect")
int bpf_cgroup_connect(struct bpf_sock_addr *ctx) {
    // For simplicity, we'll just check if the destination port is 22 (SSH)
    // In a real scenario, you'd check against a map of allowed/denied ports.
    if (ctx->user_port == 22) {
        return BPF_DROP; // Deny the connection
    }
    return BPF_OK; // Allow the connection
}

SEC("cgroup/bind")
int bpf_cgroup_bind(struct bpf_sock_addr *ctx) {
    // For simplicity, we'll check if the bind port is above 1024
    // In a real scenario, you'd check against a map of allowed/denied ports.
    if (ctx->user_port > 1024) {
        return BPF_DROP; // Deny binding to privileged ports
    }
    return BPF_OK; // Allow binding
}

// This is a simplified example; actual programs would involve maps for configuration
// and more complex logic.

To attach this eBPF program to a cgroup, you’d typically use tools like bpftool or libraries like libbpf.

First, let’s set up a cgroup for our container. We’ll use cgroup v2, which is the modern standard.

# Create a new cgroup directory
sudo mkdir /sys/fs/cgroup/my-web-app.slice

# Attach the eBPF program to the cgroup.
# The exact command depends on your eBPF loader and kernel version.
# This is a conceptual example using bpftool.
# You'd first load the compiled eBPF object file.
# sudo bpftool prog load my_cgroup_prog.o /sys/fs/bpf/my_cgroup_prog

# Then attach it to the cgroup.
# sudo bpftool cgroup attach /sys/fs/cgroup/my-web-app.slice <prog_fd> cgroup_connect
# sudo bpftool cgroup attach /sys/fs/cgroup/my-web-app.slice <prog_fd> cgroup_bind

Now, any process or container running within /sys/fs/cgroup/my-web-app.slice will have its connect() and bind() syscalls intercepted by our eBPF programs. If a process tries to establish a network connection to port 22 or bind to a port above 1024, the eBPF program will return BPF_DROP, preventing the operation.

The real power comes from combining eBPF with cgroup resource controllers. You can limit CPU, memory, and I/O for processes within a cgroup.

# Limit CPU to 50% for processes in this cgroup
echo 50000 | sudo tee /sys/fs/cgroup/my-web-app.slice/cpu.max

# Limit memory to 1GB
echo 1G | sudo tee /sys/fs/cgroup/my-web-app.slice/memory.max

The eBPF programs, in this context, act as fine-grained policy enforcers that can react to cgroup events or preempt actions before they even hit the cgroup’s resource limits, offering a much more dynamic and granular control plane. For instance, an eBPF program could monitor network traffic patterns and dynamically adjust cgroup resource limits or even terminate misbehaving processes.

The mental model here is that cgroups provide the hierarchical boundaries and resource allocation mechanisms, while eBPF programs, attached to cgroup hooks, provide the dynamic, programmable logic that governs what actions are allowed within those boundaries. You’re not just setting static limits; you’re embedding intelligent agents directly into the kernel’s execution path for specific cgroup contexts.

A crucial, often overlooked aspect is the security context of the eBPF program itself. When an eBPF program is loaded, the kernel’s verifier performs a rigorous static analysis to ensure the program cannot crash the kernel, access arbitrary memory, or perform other unsafe operations. This verification process is fundamental to the security and stability guarantees of eBPF.

The next step is to explore how eBPF can be used for more sophisticated network policy enforcement, such as implementing custom firewall rules or rate limiting based on traffic characteristics.

Want structured learning?

Take the full Ebpf course →