RSS

Getting started with modern BPF probe in Falco

Discover the modern BPF world with Falco

The new BPF probe has landed among us 👽 and it brings to the table new shiny features. The BPF world grows continuously and every new kernel release introduces some unbelievable novelties!

To take advantage of these we have created a completely new architecture, new BPF programs and maps. The main goal is to improve performance, maintainability, and user experience, shipping a unique, powerful, self-contained Falco executable. (... Oops, I said too much but don't burn the steps, first things first.)

What's new 🗞️

We wouldn't be able to cover all the details here so we will just go through the most outstanding features. If you are interested in technical aspects you can always take a look at the proposal merged upstream some months ago with its corresponding discussion.

CO-RE paradigm

Portability is one of the biggest issues we have with the current BPF driver. Our infrastructure tries to compile a BPF probe for every supported kernel since version 4.14! As you can imagine, this is not a simple task, and it causes some pain to both, users and maintainers. For this reason, the Falco maintainers have been working hard to adopt the so-called CO-RE paradigm.

CO-RE stands for "Compile-once-run-everywhere", so as you may imagine, this paradigm allows compiling the BPF probe just once for all kernels!

You understood well: No more missing drivers, and no more painful local builds requiring the much-loved KERNEL HEADERS.

BPF Ring Buffer map

Today, whenever a BPF program needs to send data to userspace, it first copies it into a BPF map, and then it pushes its content to a shared buffer located between userspace and kernel: The so-called perf-buffer.

This solution works great, but it has two major shortcomings that prove to be inconvenient in practice: Inefficient APIs and extra data copying.

The ring buffer can be considered an evolution of the perf buffer. We still use a shared buffer but with some advantages.

First, the APIs to interact with are more efficient. Second, and more importantly, we now have the possibility of writing data directly into this shared buffer, without having to write it twice!

This is a game changer in scenarios where high throughput is required, like running Falco, since it could save many cycles during the collection phase.

BTF-enabled program

Kernel engineers recently introduced a sort of debugging information for BPF programs/maps. This is called BTF and stands for "BPF Type Format". This feature shook the foundations of the BPF world because it finally offers the possibility to write code without BPF helpers like the famous bpf_probe_read().

This will not only increase the readability of the BPF code but will also reduce the bytecode dimension, allowing the crafting of smaller and more efficient programs!

BPF global variables

The addition of global variables points in the same direction as BTF: Simplify the code experience, increase the readability and reduce the performance overhead due to BPF helpers.

While BTF allows to deference kernel pointer without the use of the bpf_probe_read() helper, global variables allow to access BPF maps without the use of helpers like bpf_map_lookup_elem().

You can find further technical information about these 2 new features in the probe proposal.

BPF skeleton

Do you remember when we talked about Falco being a self-contained binary?

Well the BPF skeleton concept allows us to achieve the dream: Ship Falco as a unique, self-contained executable!

Under the hood, the probe is compiled once into Falco when the package is built. Hence, when you deploy Falco on different machines it will automatically inject the code without any extra effort on your side. Hard to believe? Try it here.

Multi-arch support

The modern BPF probe also supports multiple architectures by design. The actual targets are x86_64, arm64, and s390x, but new ones can be added at any time.

If you have a project that needs BPF instrumentation for one of these architectures you could simply link the Falco libraries (libsinsp, libscap) to obtain a working solution out of the box. We would to thank Hendrik Brueckner for the huge help he gives in reviewing and implementing the multi-arch support!

At this moment, we ship Falco for x86_64 and arm64 architectures only, due to their popularity in the community.

Requirements ⛓️

To use the modern BPF probe, there are 2 main requirements:

  1. A kernel that implements all the aforementioned features. Linux kernel version 5.8 is the first kernel that supports all of them, and for this reason, we consider it the minimum required one.

    Nevertheless, these features could also be backported into older kernels, so it wouldn't be completely fair to define the 5.8 as the first supported version. This is just a strict assumption that we put in place since we still miss the logic to detect all the necessary features of your kernel.

    Until that day arrives, if your kernel is older than 5.8, you might find the following error when trying to start the modern probe:

    Error: Actual kernel version is: '5.4.0' while the minimum required is: '5.8.0'
    
  2. A kernel that exposes BTF types. This shouldn't be a big issue since we already require a kernel version newer than 5.8 and most recent Linux distributions come with kernel BTF capabilities.

    If you want to be sure you can easily check their presence by typing:

    ls /sys/kernel/btf/vmlinux
    

    If your kernel supports them you should see:

    /sys/kernel/btf/vmlinux
    

Now, if your machine satisfies these 2 requirements, you are ready to have fun with the modern probe! 🚀

Modern BPF in action 🏎️

Falco provides you with pre-release packages to try the modern BPF probe, but the full release will take place in Falco 0.34.

If you happen find some bugs or misbehaviors, please be kind with us and open an issue on Falco 🙏

Having said that, you have 3 main ways to try Falco with the modern probe.

1. Pre-built deb, rpm packages

You can find here the x86_64 packages and here the arm64 ones.

Taking as an example the deb package, you have simply to download it and type the following command:

sudo dpkg -i <your_deb_package.deb> 

Now there are 2 possibilities:

  1. If you have the dialog binary installed on your system you should see a dialog window like this (the dialog is a new feature that will be regularly shipped in Falco 0.34 for all the drivers!):

You have simply to choose the "Modern eBPF" option.

  1. If you don't have dialog installed, you must start the service manually:
sudo systemctl start falco-modern-bpf.service

Typing sudo systemctl status falco-modern-bpf.service should either way show you a similar output to:

Nov 15 14:50:46 ip-172-31-13-74 systemd[1]: Started Falco: Container Native Runtime Security with modern ebpf.
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Falco version: 0.32.1-291+5d1b0c5 (x86_64)
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Falco initialized with configuration file: /etc/falco/falco.yaml
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Loading rules from file /etc/falco/falco_rules.yaml
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Loading rules from file /etc/falco/falco_rules.local.yaml
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: The chosen syscall buffer dimension is: 8388608 bytes (8 MBs)
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Starting health webserver with threadiness 8, listening on port 8765
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Enabled event sources: syscall
Nov 15 14:50:46 ip-172-31-13-74 falco[1587330]: Opening capture with modern BPF probe

2. Tar archive

You can download here the x86_64 tar.gz and here the arm64 one.

Here the procedure is very simple, you can extract the contents of the archive and execute the Falco binary:

tar -xvf <targz_package.tar.gz>
cd <untar_folder>
sudo ./usr/bin/falco --modern-bpf -c ./etc/falco/falco.yaml -r ./etc/falco/falco_rules.yaml

Please note: The command line option required to run Falco with the modern BPF probe is --modern-bpf

3. Docker image

In an environment where you can start containers, you can simply use the docker images from DockerHub:

# x86_64
docker pull andreater/falco-modern-x86:latest
docker run --rm -i -t \
           --privileged \
           -v /var/run/docker.sock:/host/var/run/docker.sock \
           -v /proc:/host/proc:ro \
           andreater/falco-modern-x86:latest

# arm64
docker pull andreater/falco-modern-arm64:latest
docker run --rm -i -t \
           --privileged \
           -v /var/run/docker.sock:/host/var/run/docker.sock \
           -v /proc:/host/proc:ro \
           andreater/falco-modern-arm64:latest

Please note: The helm chart is not available yet since this is a pre-release but it will be shipped as expected with Falco 0.34

Current syscall support ⏲️

The modern BPF probe doesn't yet support all the syscalls supported by the current probe. It only supports the "simple consumer set", which means that we support all the syscalls necessary to run Falco without -A option, also known as the default Falco mode. At the moment, adding the -A flag wouldn't have any further effect.

Here you can find the complete list of syscalls currently supported by the modern probe.

The good news is that we are actively working to include all the syscalls supported by the current probe 💥

Next steps 🔮

  • Release Falco 0.34 with the modern probe as an experimental feature.
  • Reach the full support of syscall.
  • Introduce new points of instrumentation like LSM hooks to collect security events.

Falco and modern BPF at eBPF summit 🐝

If you want to know more about how the modern BPF probe works under the hood take a look at the eBPF summit presentation.