Wiki - https://fedoraproject.org/wiki/Changes/UnprivilegedEBPFRBACDaemon Discourse poll - https://discussion.fedoraproject.org/t/f46-change-proposal-unprivileged-ebpf-rbac-daemon-with-lsm-enforcement-and-rootless-access-system-wide/204731/2
This is a proposed Change for Fedora Linux. This document represents a proposed Change. As part of the Changes process, proposals are publicly announced in order to receive community feedback. This proposal will only be implemented if approved by the Fedora Engineering Steering Committee. == Summary == Ship <code>bpf-rbacd</code>, a system daemon that enables unprivileged, policy-controlled eBPF access in Fedora. It delegates BPF privileges to containers via BPF tokens with in-kernel LSM enforcement, and provides rootless BPF access to desktop users via proxy execution — both governed by a single YAML policy and gated by an <code>ebpf</code> system group. == Owner == * Name: [[Fedora eBPF SIG|https://fedoraproject.org/wiki/SIGs/eBPF]] * FAS group: [https://accounts.fedoraproject.org/group/ebpf-sig/ @ebpf-sig] * Email: [email protected] * Members: ** Daniel Mellado — [email protected] ** Toke Høiland-Jørgensen — [email protected] ** Viktor Malik — [email protected] == Detailed Description == === Background === eBPF is a powerful kernel technology for networking, observability, and security, but its use requires system-wide capabilities (<code>CAP_BPF</code> combined with <code>CAP_PERFMON</code>, <code>CAP_NET_ADMIN</code>, or <code>CAP_SYS_ADMIN</code>). This is an all-or-nothing proposition: applications need privileges close to root, which blocks adoption in containers and restricts desktop developers. The kernel's BPF token mechanism (Linux 6.9+) allows a privileged issuer to delegate a constrained set of BPF operations to unprivileged consumers inside user namespaces. However, tokens alone have two significant limitations: # '''No per-entity enforcement.''' Token constraints are set at the bpffs mount level and apply uniformly to all consumers. There is no mechanism to give different containers different allowed program types, or to revoke access dynamically. # '''No desktop path.''' BPF tokens are not supported in the initial user namespace (<code>BPF_TOKEN_CREATE</code> returns <code>EOPNOTSUPP</code>). Desktop developers running tracers or profilers must still use <code>sudo</code>. This change addresses both gaps through a single daemon that operates in two complementary modes simultaneously. === Dual-mode architecture === <code>bpf-rbacd</code> serves two distinct classes of eBPF consumers from a single process, using the same policy engine and the same YAML configuration: {| class="wikitable" ! Mode !! Target environment !! Mechanism !! LSM enforcement |- | '''Capability granting''' || Containers and services in user namespaces || BPF token delegation via bpffs mount + "nsenter dance" || Yes — three LSM hooks enforce per-namespace policy bitmaps in-kernel |- | '''Proxy execution''' || Desktop users and applications in the initial user namespace (<code>init_user_ns</code>) || Daemon executes <code>bpf()</code> on behalf of clients, passes FDs via <code>SCM_RIGHTS</code> over a Unix socket || N/A — policy enforced daemon-side before executing the syscall |} '''Why two modes?''' BPF tokens only work inside user namespaces — the kernel explicitly rejects <code>BPF_TOKEN_CREATE</code> in <code>init_user_ns</code> with <code>EOPNOTSUPP</code>. Containers naturally have user namespaces, so tokens are the right mechanism there. Desktop users do not, so they need a different path. The proxy mode fills that gap without requiring application changes: standard tools like bpftrace or bcc can be adapted to request FDs from the daemon instead of calling <code>bpf()</code> directly. Both modes share the same policy model (allow-list only, fails closed, system-policy intersection) and the same group-based role resolution (<code>ebpf</code>, <code>ebpf-net</code>, <code>ebpf-admin</code>). An administrator writes one policy file and both modes obey it. <pre> bpf-rbacd (privileged daemon) ┌──────────────────────────────┐ │ Policy Engine │ │ (YAML → role → bitmaps) │ └──────┬───────────┬────────────┘ │ │ ┌───────────▼──┐ ┌────▼──────────────┐ │ Capability │ │ Proxy execution │ │ granting │ │ (SCM_RIGHTS) │ │ mode │ │ mode │ └──────┬───────┘ └────┬───────────────┘ │ │ ┌───────────▼──┐ ┌────────▼────────────┐ │ Container / │ │ Desktop user │ │ service in │ │ (init_user_ns) │ │ user ns │ │ │ │ │ │ $ bpf-rbac create-map│ │ Uses BPF │ │ → daemon executes │ │ token from │ │ → FD passed back │ │ bpffs │ │ via SCM_RIGHTS │ │ ↓ │ └──────────────────────┘ │ LSM enforces │ │ per-ns policy│ └──────────────┘ </pre> === What this change delivers === ==== BPF token delegation and the ebpf system group ==== <code>bpf-rbacd</code> uses systemd-driven bpffs delegation to provide BPF tokens to containers and services in user namespaces: * A fixed <code>ebpf</code> system group gates who may consume the delegated bpffs/token path. The group is for OS-level discovery and packaging defaults; the kernel still enforces token constraints independently. * The daemon performs the '''"nsenter dance"''' — entering the target user namespace via <code>setns()</code> before mounting bpffs, so the <code>s_user_ns</code> is set correctly and <code>BPF_TOKEN_CREATE</code> works inside the namespace. * Integration with systemd <code>PrivateBPF=</code> (upstream [https://github.com/systemd/systemd/pull/36134 PR #36134]) enables automatic namespace delegation for systemd-managed services. * Libraries such as libbpf, bpftool, and bcc automatically discover and use delegated tokens without requiring <code>sudo</code>. ==== In-kernel eBPF LSM policy enforcement ==== The daemon loads eBPF LSM programs at startup that attach to the kernel's security hooks for BPF operations. Each hook looks up the calling process's user namespace inode ID in an eBPF hash map, retrieves the policy bitmaps for that namespace's role, and returns allow or deny. This gives administrators '''per-namespace, per-role granularity''' enforced in-kernel, on top of the bpffs mount-level token constraints. '''Core hooks''' (implemented in both the Aya and C codebases): {| class="wikitable" ! LSM Hook !! What it controls !! Policy dimension |- | <code>security_bpf</code> || Every <code>bpf()</code> syscall invocation || <code>allowed_cmds</code> bitmap (PROG_LOAD, MAP_CREATE, etc.) |- | <code>security_bpf_prog_load</code> || Program loading — inspects program type || <code>allowed_prog_types</code> bitmap (kprobe, xdp, etc.) |- | <code>security_bpf_map_create</code> || Map creation — inspects map type || <code>allowed_map_types</code> bitmap (hash, array, etc.) |} '''Extended hooks''' (implemented in the C codebase — [https://github.com/tohojo/bpf-rbac-lsm tohojo/bpf-rbac-lsm]): {| class="wikitable" ! LSM Hook !! What it controls !! Why it matters |- | <code>security_bpf_prog</code> || FD-level access to existing programs || Controls who can inspect or reuse loaded programs |- | <code>security_mmap</code> || Memory-mapping of BPF maps || Controls map data access mode (read/write/exec) |} '''Bytecode walking for helper/kfunc policy:''' The C implementation also uses open-coded iterators to walk BPF program bytecode at <code>security_bpf_prog_load</code> time, extracting the helper function IDs and kfunc BTF IDs that the program calls. This enables a future policy dimension: restricting ''which kernel functions'' a program may invoke, not just which program type it is. Events are reported to userspace via a BPF ring buffer for audit logging and policy decisions. '''How LSM and tokens complement each other:''' <pre> Token says "you may load LSM checks: "your namespace kprobe programs" role only permits tracepoint" ↓ → DENIED </pre> Even if a token is overly permissive (broad bpffs mount options), the per-namespace LSM policy restricts actual operations to the role's intersection with the system policy. '''eBPF program loading at daemon startup:''' The daemon loads and attaches the LSM programs before accepting any delegation requests. This requires <code>CAP_BPF</code> + <code>CAP_SYS_ADMIN</code> on the daemon itself (granted via the systemd unit's <code>AmbientCapabilities</code>). Once loaded, the LSM hooks are active system-wide; the eBPF policy map serves as the control plane between the daemon and the LSM. ==== Rootless BPF for desktop users (proxy mode) ==== For users in the initial user namespace (desktops, developer workstations), <code>bpf-rbacd</code> provides a proxy execution mode: # User runs <code>bpf-rbac <command></code> (e.g., <code>bpf-rbac create-map --type hash ...</code>) # The CLI connects to the daemon's Unix socket at <code>/run/bpf-rbac.sock</code> # The daemon reads <code>SO_PEERCRED</code> — kernel-verified, tamper-proof credentials # Group membership is resolved to a policy role # The daemon executes the allowed <code>bpf()</code> syscall with its own privileges # Resulting file descriptors are passed back via <code>SCM_RIGHTS</code> # Denied operations return actionable error messages === Policy model === Policies are defined in <code>/etc/bpf-rbac/policy.yaml</code> using an allow-list model that always fails closed: <pre> system_policy: # System-wide ceiling commands: [PROG_LOAD, MAP_CREATE, MAP_LOOKUP_ELEM, MAP_UPDATE_ELEM, LINK_CREATE, OBJ_PIN, OBJ_GET, BTF_LOAD] prog_types: kprobe: [load, attach, detach] tracepoint: [load, attach, detach] xdp: [load, attach, detach] map_types: hash: [create, read, write] array: [create, read, write] ringbuf: [create, read] roles: ebpf: # Tracing role groups: [ebpf] commands: [PROG_LOAD, MAP_CREATE, MAP_LOOKUP_ELEM, MAP_UPDATE_ELEM, LINK_CREATE, BTF_LOAD] prog_types: kprobe: [load, attach] tracepoint: [load, attach] map_types: hash: [create, read, write] array: [create, read, write] ringbuf: [create, read] ebpf-net: # Networking role groups: [ebpf-net] prog_types: xdp: [load, attach, detach] sched_cls: [load, attach, detach] map_types: hash: [create, read, write] devmap: [create, read, write] ebpf-admin: # Unrestricted groups: [ebpf-admin, wheel] commands: [any] prog_types: {any: [any]} map_types: {any: [any]} </pre> The effective policy for any role is the '''set intersection''' of the role policy and the system policy. A role can never exceed administrator-defined system limits. === Two LSM implementations and the Rust BPF toolchain gap === The eBPF LSM programs exist in two parallel implementations. This is not redundancy — it is a deliberate response to a Rust compiler limitation that currently prevents the Aya-based implementation from using advanced BPF features. ==== The Rust/DWARF/BTF problem ==== eBPF programs that call kernel functions (kfuncs) or reference kernel symbols (ksyms) require BTF (BPF Type Format) metadata in the compiled ELF. The toolchain flow is: <pre> Rust source → rustc (BPF target) → LLVM → ELF with DWARF → bpf-linker → ELF with BTF (including .ksyms DATASEC) → kernel verifier resolves kfuncs/ksyms at load time </pre> The problem: '''<code>rustc</code> does not emit DWARF debug info for <code>extern</code> declarations on BPF targets.''' When a Rust eBPF program declares: <pre> #[link_section = ".ksyms"] extern "C" { static LINUX_KERNEL_VERSION: u32; // ksym fn bpf_iter_num_new(it: *mut bpf_iter_num, start: i32, end: i32); // kfunc } </pre> …the compiler produces the LLVM global/function declaration but attaches no <code>DIGlobalVariable</code> or <code>DISubprogram</code> metadata. Without DWARF, <code>bpf-linker</code> cannot generate the <code>.ksyms</code> BTF DATASEC entry, and the kernel verifier cannot resolve these symbols at load time. The program fails to load. This blocks: * '''Open-coded iterators''' (<code>bpf_iter_num_new/next/destroy</code>) — needed for bytecode walking in the <code>security_bpf_prog_load</code> hook * '''kfunc calls''' (<code>bpf_task_acquire</code>, etc.) — needed for CO-RE task/namespace introspection * '''The userns ID resolver''' — <code>current->nsproxy->user_ns->ns.inum</code> access via BTF, currently stubbed to return 0 The three core bitmap-based hooks (<code>security_bpf</code>, <code>security_bpf_prog_load</code>, <code>security_bpf_map_create</code>) work today with Aya because they only use map lookups and context argument reads — no kfuncs or ksyms. But the extended hooks (bytecode walking, FD access control, mmap control) require kfuncs and are blocked. ==== The upstream Rust compiler fix ==== We are driving the fix in [https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390] ("Generate debug info for BPF extern declarations"). This patch: * Allows <code>#[link_section]</code> on <code>ForeignStatic</code> and <code>ForeignFn</code> (gated to BPF targets) * Adds an <code>LLVMRustDIBuilderCreateGlobalVariableExpression</code> wrapper exposing the <code>isDefinition</code> parameter (the LLVM-C API hard-codes it to <code>true</code>) * Emits <code>DIGlobalVariable</code> with <code>isDefinition: false</code> for extern statics * Emits <code>DISubprogram</code> without <code>SPFlagDefinition</code> for extern functions This is a reworked version of [https://github.com/rust-lang/rust/pull/152899 #152899], rebased on current <code>main</code> with review feedback addressed. It is currently in review with the Rust compiler team (assigned to @jdonszelmann, reviews from @bjorn3 and @tamird). The Aya maintainer (@vadorovsky) is also involved. Once this lands in nightly <code>rustc</code>, the full chain works: Rust eBPF → DWARF with extern metadata → <code>bpf-linker</code> → BTF with <code>.ksyms</code> → kernel loads kfuncs. This unblocks the complete Aya-based LSM. ==== The C implementation: tohojo/bpf-rbac-lsm ==== [https://github.com/tohojo/bpf-rbac-lsm tohojo/bpf-rbac-lsm] is an actively developed C-based LSM implementation (Toke Høiland-Jørgensen, latest commits Sep 2026) that sidesteps the Rust toolchain limitation entirely. It uses: * '''C for the eBPF programs''' — compiled with clang/llvm, full BTF/CO-RE support, no DWARF issues * '''Rust + libbpf-rs for userspace''' — loads the C eBPF programs via libbpf skeleton generation, processes ring buffer events, manages policies This implementation is more capable than the Aya version today: {| class="wikitable" ! Feature !! Aya (Rust eBPF) !! C eBPF + libbpf-rs |- | <code>security_bpf</code> (command filtering) || ✅ || ✅ |- | <code>security_bpf_prog_load</code> (type filtering) || ✅ || ✅ |- | <code>security_bpf_map_create</code> (type filtering) || ✅ || ✅ |- | <code>security_bpf_prog</code> (FD access) || ❌ || ✅ |- | <code>security_mmap</code> (map mmap control) || ❌ || ✅ |- | Userns ID resolution (CO-RE/BTF) || ❌ (stubbed) || ✅ |- | Bytecode walking (open-coded iterators) || ❌ (blocked on rustc) || ✅ |- | Helper/kfunc ID extraction || ❌ (blocked on rustc) || ✅ |- | Policy enforcement per namespace || ❌ (stub returns 0) || ✅ |- | Ring buffer event reporting || ❌ || ✅ |} ==== Integration strategy and the path to a pure Rust/Aya stack ==== The two implementations share the same userspace daemon (<code>bpf-rbacd</code>) and the same policy engine. They are designed to converge: '''Phase 1 — Fedora 46 delivery (C eBPF + Rust userspace):''' Use the C eBPF programs from <code>bpf-rbac-lsm</code> loaded via <code>libbpf-rs</code> in the <code>bpf-rbacd</code> Rust userspace daemon. This gives us the full feature set (all hooks, bytecode walking, namespace resolution, policy enforcement) without waiting for the Rust compiler fix. The daemon, policy engine, CLI, and all userspace logic remain in Rust; only the eBPF kernel programs are in C. This is a pragmatic split — the same pattern used by many production eBPF projects (e.g., Cilium, bpfman). '''Phase 2 — After <code>rustc</code> fix lands (Aya catches up):''' Once [https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390] is merged and reaches stable <code>rustc</code>, we migrate the eBPF programs to Rust/Aya. The groundwork is already done: * The Aya-based LSM in [https://github.com/danielmellado/bpf-rbacd danielmellado/bpf-rbacd] already compiles and produces correct ELF relocations for the three core hooks. * The [https://github.com/tohojo/bpf-rbacd/tree/load-lsm load-lsm branch] already has kfunc declarations and open-coded iterator code that compiles — it only needs the BTF DATASEC generation that the <code>rustc</code> fix enables. * Aya's kfunc relocation support ([https://github.com/aya-rs/aya/pull/1372 aya-rs/aya#1372]) is progressing upstream. The migration is a drop-in replacement: the userspace daemon, policy engine, and CLI are unchanged. Only the eBPF binary source changes from C to Rust. '''Phase 3 — Target state (pure Rust):''' A fully Rust stack — daemon, CLI, policy engine, ''and'' eBPF kernel programs — all built with <code>cargo</code>. This is the desired end state for <code>bpf-rbacd</code> because: * '''Single toolchain:''' One <code>cargo build</code> compiles everything. No separate clang/llvm dependency for the eBPF programs. * '''Shared types:''' The <code>bpf-rbacd-common</code> crate already defines <code>#[repr(C)]</code> policy types (<code>PolicyKey</code>, <code>PolicyValue</code>) that compile for both <code>no_std</code> (eBPF target) and <code>std</code> (userspace). With Aya, these types are shared at compile time with full type safety — no manual struct layout synchronization between C and Rust. * '''Memory safety:''' Rust's borrow checker and type system apply to the eBPF programs too. While eBPF's verifier provides runtime safety, compile-time guarantees from Rust catch bugs earlier. * '''Ecosystem alignment:''' Aya is the standard Rust eBPF framework. Contributing improvements back (kfunc support, LSM patterns) benefits the entire Rust eBPF community. The <code>rustc</code> fix is not just a <code>bpf-rbacd</code> concern — it unblocks kfunc/ksym usage for ''every'' Rust eBPF program. We are driving this upstream because it is the right thing for the ecosystem, and <code>bpf-rbacd</code> is a concrete, high-profile consumer that validates the fix. This phased approach means the Fedora 46 deliverable is '''not blocked by the Rust compiler fix''', while the Rust/Aya migration remains a first-class goal tracked by the eBPF SIG. === Kernel requirements === {| class="wikitable" ! Config !! Required for !! In Fedora? |- | <code>CONFIG_BPF_SYSCALL=y</code> || All BPF operations || Yes |- | <code>CONFIG_BPF_LSM=y</code> || LSM hook attachment || Yes |- | <code>bpf</code> in active LSM list || LSM program loading || Needs verification |- | <code>CONFIG_DEBUG_INFO_BTF=y</code> || CO-RE/BTF for portable eBPF || Yes |- | <code>CONFIG_BPF_TOKEN</code> || BPF token delegation || Yes |} '''Action item:''' Verify that <code>bpf</code> is in the kernel's compiled-in LSM list or <code>lsm=</code> boot parameter. If not, a kernel config change is needed to add <code>bpf</code> to the LSM order alongside <code>lockdown,yama,integrity,selinux</code>. == Feedback == The design of <code>bpf-rbacd</code> is being presented at [https://lpc.events/event/20/contributions/2441/ Linux Plumbers Conference 2026] (Oct 6, eBPF Track) to solicit community feedback. The talk covers the architecture, policy model, and LSM enforcement, with the goal of making the design useful to other distributions and operators. Community consultation with kernel, systemd, and security stakeholders is ongoing. == Benefit to Fedora == * '''Security posture:''' Moves eBPF access from all-or-nothing capabilities toward granular, role-based delegation. LSM enforcement adds a mandatory kernel-level policy layer that token delegation alone cannot provide. * '''Per-entity granularity:''' Different containers, services, or users get different eBPF permissions based on their role — not a single system-wide delegation. * '''Developer experience:''' Desktop developers and operators can run eBPF tools without <code>sudo</code>, with clear errors when an operation is outside their role's policy. * '''Unified policy:''' One YAML file governs both container (LSM + tokens) and desktop (proxy) access. Administrators learn one model. * '''Dynamic policy and revocation:''' Policies can be updated at runtime; removing a namespace's map entry causes the LSM to deny subsequent operations without killing processes. * '''Downstream alignment:''' Establishes the reference RBAC implementation for RHEL, OpenShift (user namespaces GA in 4.21+), and CoreOS. Kubernetes user namespaces are GA as of v1.36. * '''Rust BPF ecosystem:''' The upstream <code>rustc</code> fix ([https://github.com/rust-lang/rust/pull/161390 #161390]) benefits the entire Rust eBPF community. <code>bpf-rbacd</code> is designed to become a fully pure-Rust eBPF project (daemon + kernel programs via Aya) once the toolchain gap is closed, demonstrating that production eBPF LSM programs can be written entirely in Rust. * '''Community leadership:''' Fedora becomes the first distribution with a complete, integrated eBPF RBAC stack spanning containers and desktops. == Scope == * Proposal owners: ** Package <code>bpf-rbacd</code> (daemon), <code>bpf-rbac</code> (CLI client), and <code>bpf-rbacd-common</code> (shared types) as RPMs. Source is hosted on [https://forge.fedoraproject.org/ebpf/bpf-rbacd Fedora Forge]. ** Ship the eBPF LSM programs from [https://github.com/tohojo/bpf-rbac-lsm tohojo/bpf-rbac-lsm] (C eBPF + libbpf-rs for loading). Migrate to Rust/Aya once [https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390] lands. ** Deliver systemd unit (<code>bpf-rbacd.service</code>) with security hardening (<code>ProtectSystem=strict</code>, <code>ProtectHome=true</code>, capability bounding). ** Deliver default policy (<code>/etc/bpf-rbac/policy.yaml</code>) reviewed with the Security Team. ** Create <code>ebpf</code>, <code>ebpf-net</code>, and <code>ebpf-admin</code> system groups via <code>systemd-sysusers</code>. ** Implement systemd-driven bpffs delegation (mount units, <code>PrivateBPF=</code> coordination). ** Coordinate with kernel team to ensure <code>bpf</code> is in the active LSM list. ** Drive the Rust compiler fix ([https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390]) or use C fallback. ** Complete the CO-RE/BTF userns ID resolver in the eBPF LSM. ** Wire container/namespace discovery into the daemon (cgroup events or CRI hooks). ** Coordinate library/tool updates for automatic token usage (libbpf, bpftool, bcc). ** File Fedora package review, FESCo process, and tracker bugs. ** Write Fedora Quick Docs article on usage and security model. * Other developers: ** Kernel maintainers: Confirm <code>CONFIG_BPF_LSM=y</code> and <code>bpf</code> in LSM list; advise on LSM hook semantics and regression triage. ** systemd maintainers: Coordinate on <code>PrivateBPF=</code> integration so systemd-launched services automatically get delegated namespaces. ** Rust compiler team: Review and land [https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390]. ** Aya maintainers: kfunc relocation support ([https://github.com/aya-rs/aya/pull/1372 aya-rs/aya#1372]). ** Security Team: Review default policy, group membership model, and LSM enforcement guarantees. ** eBPF tooling maintainers (bcc, bpftool, libbpf consumers): Ensure tools work with proxy-mode FD passing and token-based flows. ** Container runtime maintainers (podman, CRI-O): Test user namespace + BPF token + LSM enforcement end-to-end. ** Edition WGs (Workstation, Server, CoreOS): Consider default group membership and documentation per edition. * Release engineering: No release engineering changes required. <code>bpf-rbacd</code> is a new opt-in package with standard RPM packaging — no mass rebuild, no installer image changes, and no compose modifications. * Policies and guidelines: The initial delivery uses C eBPF programs compiled with clang/llvm and loaded via libbpf-rs — this follows standard Fedora packaging (clang and libbpf are already packaged). The future Aya-based path targets <code>bpfel-unknown-none</code> and requires nightly Rust + <code>rust-src</code>, which may need a packaging exemption or pre-built binary vendoring when that migration occurs. Default policy requires Security Team review. * Trademark approval: N/A (not needed for this Change) * Alignment with the Fedora Strategy: Aligns with secure-by-default, developer-friendly platform, and upstream-first goals. The change drives fixes in <code>rustc</code>, Aya, and systemd rather than carrying downstream patches. == Upgrade/compatibility impact == This change introduces a new opt-in package. It does not modify existing system behavior. * '''Existing systems:''' No change unless <code>bpf-rbacd</code> is installed. Workflows that use root or explicit capabilities continue to work. * '''New path:''' Users or administrators who want unprivileged eBPF install <code>bpf-rbacd</code>, add users to the <code>ebpf</code>, <code>ebpf-net</code>, or <code>ebpf-admin</code> groups, and enable the service. * '''Kernel config:''' If <code>bpf</code> needs to be added to the LSM list, this could interact with other LSM consumers (notably SELinux). Must be coordinated with the kernel team. * '''ABI:''' No Fedora-specific ABI commitment beyond documented <code>bpf()</code> semantics and the policy YAML format. Tools that gain automatic token discovery may succeed in situations where they previously failed without sudo. == Early Testing (Optional) == Do you require 'QA Blueprint' support? N == How To Test == # '''Hardware/data:''' Standard x86_64 or aarch64 Fedora install. VM acceptable if BPF features are not stripped. # '''Prepare:''' Install <code>bpf-rbacd</code>, start the service, add test user to <code>ebpf</code> group. # '''Proxy mode (desktop):''' #: <pre> # As a user in the ebpf group: $ bpf-rbac status Connected to: /run/bpf-rbac.sock Your roles: ebpf Allowed operations: Maps: hash, array, percpu_hash, percpu_array, ringbuf Programs: kprobe, uprobe, tracepoint, perf_event # Create a map (should succeed) $ bpf-rbac create-map --type hash --name test \ --key-size 4 --value-size 8 --max-entries 64 # Attempt a denied operation (should fail with clear error) $ bpf-rbac create-map --type devmap --name test \ --key-size 4 --value-size 8 --max-entries 64 Error: map type 'devmap' is not allowed by your role 'ebpf' </pre> # '''LSM enforcement (requires root for setup):''' Launch a podman container in a user namespace (<code>podman run --userns=auto ...</code>). Verify that the daemon delegates bpffs and populates the policy map. Inside the container, attempt to load an allowed program type (should succeed) and a denied type (should fail with <code>EPERM</code>). # '''Non-member:''' As a user NOT in any ebpf group, verify that all operations are denied. # '''Unit tests (no root required):''' #: <pre> cargo test --lib # Policy parsing, bitmaps, intersection cargo test --test integration # Non-privileged integration tests </pre> # '''Expected results:''' Delegated operations succeed within the role's allowlist; out-of-scope operations fail with clear errors; no system instability or privilege escalation beyond the documented policy. == User Experience == * Most users see no change unless they use eBPF tooling. * Desktop developers who run tracers or networking experiments may stop needing <code>sudo</code> for supported scenarios, with clear errors when an operation is denied. * Container workloads are transparent — applications use standard <code>bpf()</code> with automatic token acquisition; the LSM silently enforces the namespace's policy. * Administrators get a single documented policy file (<code>/etc/bpf-rbac/policy.yaml</code>) and <code>journalctl -u bpf-rbacd</code> for audit logging. == Dependencies == * Linux kernel: 6.9+ with <code>CONFIG_BPF_TOKEN</code>, <code>CONFIG_BPF_LSM=y</code>, <code>bpf</code> in active LSM list, <code>CONFIG_DEBUG_INFO_BTF=y</code> * Rust: Stable 1.85+ (daemon and CLI) * [https://github.com/libbpf/libbpf libbpf] and [https://github.com/libbpf/libbpf-rs libbpf-rs]: For loading C-based eBPF LSM programs from the Rust daemon (initial delivery path) * clang/llvm: For compiling the C eBPF programs with full BTF/CO-RE support * [https://github.com/aya-rs/aya Aya]: eBPF library for Rust (long-term target once [https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390] lands; requires nightly Rust + <code>bpfel-unknown-none</code> target) * systemd: <code>PrivateBPF=</code> support ([https://github.com/systemd/systemd/pull/36134 PR #36134]) * Optional: podman with <code>--userns=auto</code>, CRI-O with user namespace support == Contingency Plan == * Contingency mechanism: ** The primary delivery path already uses the C eBPF programs (via libbpf-rs), so the Rust compiler fix ([https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390]) is not on the critical path for F46. ** If BPF LSM cannot be enabled in the Fedora kernel (e.g., SELinux interaction with <code>bpf</code> in the LSM list): disable LSM loading in <code>bpf-rbacd</code>; ship proxy mode only and rely on token-level constraints for container workloads. ** If proxy mode has unresolved security concerns: ship LSM/container path only; desktop users fall back to <code>sudo</code>/<code>CAP_BPF</code>. ** If neither component is ready: do not ship <code>bpf-rbacd</code>. eBPF access continues to work as it does today (privileged only). No regression. * Contingency deadline: Beta freeze * Blocks release? No. <code>bpf-rbacd</code> is an opt-in package. == Documentation == * Upstream code: [https://forge.fedoraproject.org/ebpf/bpf-rbacd Fedora Forge ebpf/bpf-rbacd] (primary), [https://github.com/danielmellado/bpf-rbacd GitHub mirror] * C reference implementation: [https://github.com/tohojo/bpf-rbac-lsm tohojo/bpf-rbac-lsm] * Rust compiler fix: [https://github.com/rust-lang/rust/pull/161390 rust-lang/rust#161390] * Kernel docs: [https://docs.kernel.org/bpf/bpf_token.html BPF Token documentation] * systemd: [https://github.com/systemd/systemd/pull/36134 PrivateBPF= PR] * LPC 2026 talk: [https://lpc.events/event/20/contributions/2441/ BPF-RBACd: Delegating BPF permissions with high granularity] * Fedora: Quick Docs article covering installation, group membership, policy customization, security model, and container vs. desktop usage paths. * eBPF SIG: [https://fedoraproject.org/wiki/SIGs/eBPF SIGs/eBPF], Matrix: [https://matrix.to/#/#ebpf:fedoraproject.org #ebpf:fedoraproject.org] == Release Notes == Fedora 46 introduces <code>bpf-rbacd</code>, a system daemon that enables unprivileged, policy-controlled eBPF access through two complementary modes: * '''Capability granting''' for containers and services in user namespaces — transparent eBPF access via BPF token delegation with mandatory in-kernel LSM enforcement (three hooks: <code>security_bpf</code>, <code>security_bpf_prog_load</code>, <code>security_bpf_map_create</code>). * '''Proxy execution''' for desktop users in the initial user namespace — rootless BPF access via the <code>bpf-rbac</code> CLI, with the daemon executing allowed operations on the user's behalf and passing file descriptors back via <code>SCM_RIGHTS</code>. Both modes are governed by a single YAML policy file (<code>/etc/bpf-rbac/policy.yaml</code>) and gated by role-based groups (<code>ebpf</code>, <code>ebpf-net</code>, <code>ebpf-admin</code>). Install with <code>sudo dnf install bpf-rbacd</code>. -- Aoife Moloney Fedora Operations Architect Fedora Project Matrix: @amoloney:fedora.im IRC: amoloney -- _______________________________________________ devel-announce mailing list -- [email protected] To unsubscribe send an email to [email protected] Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedoraproject.org/archives/list/[email protected] Do not reply to spam, report it: https://forge.fedoraproject.org/infra/tickets/issues/new -- _______________________________________________ devel mailing list -- [email protected] To unsubscribe send an email to [email protected] Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedoraproject.org/archives/list/[email protected] Do not reply to spam, report it: https://forge.fedoraproject.org/infra/tickets/issues/new
