Every operating system leaves room for improvement. But modifying any OS can create security risks and performance penalties, as well as create dependencies that are almost impossible to accommodate in OS version and module updates.
The goal of OS programmability is deceptively simple: allow additional code to run without any change to the OS kernel's source code or create unwanted dependencies in new modules.
OS modification is nothing new. Early designs of virtualization relied on OS modifications to support forms of paravirtualization. But whenever you customize software, you may create dependencies that make it difficult -- even impossible -- to update the underlying software. When a new version emerges, IT must rework and retest those existing customizations.
It's a developer's worst nightmare.
What is eBPF?
The release of Linux kernel 4 offered a new approach to customization called extended Berkeley Packet Filter (eBPF) technology. The kernel incorporates a sandbox environment that lets BPF bytecode run, which can affect the kernel and use kernel resources -- but doesn't actually change the kernel itself.
Early eBPF tools focused on behaviors such as monitoring and security when filtering network packets, but its use has expanded through tools that IT admins can program directly or acquire from third-party sources.
The eBPF programs are loaded into the Linux environment and use specific trigger events, called hooks. Hooks include network event instances, kernel trace points and kernel functions. When a hook -- also called a trigger -- is encountered, the corresponding eBPF code is compiled, verified and executed.
The verification process ensures the eBPF program is safe. This process checks for three things:
- The process loading the eBPF program has the privileges to do so.
- The eBPF program can run to completion and does not loop.
- The eBPF program does not have an adverse effect on the kernel or the system.
Verification prevents accidental -- or malicious -- eBPF code problems.
Usually an eBPF program alone is not enough to modify the kernel. The eBPF program relies on additional kernel functions, called helper calls, that grant the eBPF program access to kernel functions, such as memory access. Effectively, helper calls form an API that allows eBPF programs to access the kernel.
Helper calls include:
- random number generator
- network packet manipulation
- access to date and time
- access to Linux process and cgroup resources
Additionally, eBPF programs collect and share information stored in a wide range of data structures, dubbed eBPF maps. The eBPF programs access both the maps and user applications. This enables eBPF programs to provide data to other applications in the environment.
- hash tables
- ring buffers
- stack traces
What is eBPF used for?
An eBPF program completes relatively simple and straightforward tasks that demand minimal overhead or impact on the Linux environment. The two most common uses are networking and monitoring, though security is growing as well.
As with any OS, Linux provides a default networking path to direct packets to their destinations. Every packet must traverse this path.
When an eBPF program augments networking, packets intended for specific destinations can bypass the default network path and move directly to an intended destination. This improves networking performance and is often used in complex containerized environments to boost traffic behavior in microservices or other cases where multiple container stacks operate. Similarly, eBPF can apply network policy to packets, which helps route packets effectively and apply policies to traffic.
As another example, eBPF can manage load balancing at the source of the connection, rather than within a complex container system. In the latter scenario, IT pros must perform network address translation (NAT) to translate virtual IP addresses into physical IP addresses. Using eBPF programs to avoid NAT can reduce -- or even eliminate -- the overhead of such address translation and improve network performance.
Most often, eBPF is associated with a type of monitoring called observability: IT organizations must collect statistics about key functions in the Linux kernel, such as the number of calls and the process that made the calls.
In addition, the eBPF program can alter the data that a function is processing, which allows data to be easily preprocessed, normalized or otherwise manipulated to grant administrators a better understanding of the Linux environment.
The eBPF programs are helpful for time-sensitive, highly distributed, container-based environments where traditional security monitoring tactics don't quite work. In environments such as Kubernetes, eBPF offers granular visibility into HTTP traffic, which traditional security monitoring might miss. Also, eBPF can also be used for security in firewalls (replacing traditional IP tables), device drivers and network activity monitoring.
How is eBPF added?
Linux 4.4 and onward support eBPF; although, kernel version 4.9 and onward offer more maturity. For example, Red Hat Enterprise Linux 7.6 uses an earlier kernel and introduces eBPF as a technology preview. While eBPF support is already available, the actual process to create eBPF programs is more complicated. Coding eBPF resembles assembly code programming and there are limits to its capabilities -- such as no looping, which is detected and prohibited by eBPF validation. However, various tools help developers create eBPF programs, such as:
- BPF Compiler Collection supports eBPF creation in Python and Lua environments.
- eBPF Go Library offers tools that load, compile and debug eBPF programs.
- libbpf contains an eBPF loader to process Low-Level Virtual Machine-generated (LLVM) eBPF files for loading into the kernel.
- LLVM creates eBPF programs in C.
There are also several projects that involve or offer eBPF programs and projects:
- bpftrace provides a high-level tracing language.
- Cilium provides varied eBPF tools for networking, monitoring and security.
- Falco is a Linux container security tool based on eBPF.
- Katran provides a Layer 4 load balancer based on eBPF.
- Pixie provides monitoring tools for Kubernetes clusters based on eBPF.
Challenges of eBPF
Given the risks inherent in OS modifications, eBPF creators went to great lengths to secure the platform via privileges, verification and hardening. For example, only processes with root privileges can load eBPF programs -- unless you enable unprivileged eBPF -- which prevents untrusted applications from loading eBPF programs.
Similarly, the verification process ensures all eBPF programs are safe and follow eBPF rules, such as no loops, size or complexity limits, as well as variable restrictions. Finally, the eBPF program is hardened to protect the memory holding the eBPF program, as well as to bind constants and prevent CPU branch prediction, all of which can present possible attack vectors.
Although eBPF is a powerful and efficient means to adapt a Linux kernel, the eBPF platform can pose risks and frustrations for developers. And eBPF is not suited to every project or situation. For example, eBPF only works with recent Linux kernels, so the code is not easily portable to other OSes or projects.
In addition, the security features that protect eBPF program execution also limit eBPF functionality, and such limits must be tracked throughout eBPF program development and deployment.