BPF Token
Version 6.9
The BPF token is a mechanism that allows privileged processes (such as container runtimes, located in the init namespace) to delegate certain functionalities of the BPF subsystem to unprivileged processes (such as processes within containers, located in the user namespace).
eBPF and Linux Capabilities
When eBPF was first introduced in the Linux kernel, operations such as loading programs and creating maps required <span class="language-plaintext">CAP_SYS_ADMIN</span>. However, the permissions granted by <span class="language-plaintext">CAP_SYS_ADMIN</span> far exceed what eBPF itself requires, posing a risk of excessive permissions.
Therefore, starting from version 5.8, <span class="language-plaintext">CAP_BPF</span> was introduced. It allows for more granular control over the eBPF functionalities that a process can use. For example, to load network-related eBPF programs (such as TC or XDP), one needs <span class="language-plaintext">CAP_BPF</span> + <span class="language-plaintext">CAP_NET_ADMIN</span>; to load tracing-related eBPF programs (such as kprobe or raw_tracepoint), one needs <span class="language-plaintext">CAP_BPF</span> + <span class="language-plaintext">CAP_PERFMON</span>, and so on.
eBPF and User Namespaces
User namespaces are a type of namespace used to isolate user IDs (UIDs), group IDs (GIDs), keys, and capabilities. Within a user namespace, a process can behave as if it has root privileges, but this only applies to resources managed by the user namespace.
For example, a process within a user namespace can even have <span class="language-plaintext">CAP_NET_ADMIN</span> and create new network namespaces and network devices. However, it cannot connect virtual Ethernet (veth) devices to the initial namespace (init namespace), because creating devices in the initial namespace requires the <span class="language-plaintext">CAP_NET_ADMIN</span> capability from the initial namespace.
So what about eBPF? Can a process with <span class="language-plaintext">CAP_BPF</span> + <span class="language-plaintext">CAP_PERFMON</span> in a user namespace load tracing eBPF programs? The answer is no. Essentially, eBPF can observe many sensitive behaviors within the kernel. Therefore, allowing unprivileged users to load eBPF programs is unsafe.
Permission Delegation via BPF Tokens
The kernel does not allow unprivileged users to load eBPF programs, but it is acceptable if certain permissions are delegated by a privileged process. This is where BPF tokens come into play.
Here is a rough outline of the process using BPF tokens:
- An unprivileged process creates and enters a user namespace.
- The unprivileged process creates a mount namespace and uses the
<span class="language-plaintext">fsopen(2)</span>function to obtain a BPFFS file descriptor (FD). - The unprivileged process sends the BPFFS file descriptor to the privileged process via a Unix domain socket.
- The privileged process uses the
<span class="language-plaintext">fsconfig(2)</span>function to configure the delegated permissions for the BPFFS file descriptor and instantiates it using the<span class="language-plaintext">FSCONFIG_CMD_CREATE</span>command. - The privileged process creates a mount file descriptor using the
<span class="language-plaintext">fsmount(2)</span>function and sends it back to the unprivileged process. - The unprivileged process uses the BPF token file descriptor to call the
<span class="language-plaintext">bpf(2)</span>function with the<span class="language-plaintext">BPF_TOKEN_CREATE</span>command. - The unprivileged process uses the BPF token file descriptor to invoke commands like
<span class="language-plaintext">BPF_PROG_LOAD</span>on the<span class="language-plaintext">bpf(2)</span>function.
This is a very complex process, but eBPF applications typically do not need to perform this process themselves. Steps 1-5 are usually executed by a privileged process (such as a container runtime), while eBPF loader libraries like libbpf handle step 6 and transparently pass it to the <span class="language-plaintext">bpf(2)</span> function. It is important to note that the BPF token file descriptor is bound to the user namespace that created it and cannot be used outside that namespace.
Delegation Options
Currently, the <span class="language-plaintext">fsconfig(2)</span> function provides the following delegation options:
<span class="language-plaintext">delegate_cmds</span>: Allows execution of specific BPF system call commands.<span><span class="language-plaintext">delegate_maps</span></span>: Allows creation of specific types of maps.<span><span class="language-plaintext">delegate_progs</span></span>: Allows loading of specific types of programs.<span><span class="language-plaintext">delegate_attachs</span></span>: Allows use of specific mount types.
References
- Kernel Support Patch Set
https://patchwork.kernel.org/project/linux-fsdevel/cover/[email protected]/
- Test Code
https://github.com/torvalds/linux/blob/master/tools/testing/selftests/bpf/prog_tests/token.c
Source
https://docs.ebpf.io/linux/concepts/token/
Last updated: September 23, 2024