Upgrade to Pro — share decks privately, control downloads, hide ads and more …

eBPF Signature with Hornet

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

eBPF Signature with Hornet

Enhancing eBPF program security via cryptographic signing integrated with the Hornet Linux Security Module (LSM). This approach verifies eBPF code integrity before kernel loading, reducing vulnerabilities in observability, networking, and security tools while ensuring safe deployment in production environments.

Avatar for Julian Zhuang

Julian Zhuang

March 09, 2026

More Decks by Julian Zhuang

Other Decks in Technology

Transcript

  1. 06.03.2026 eBPF Vienna 2 Julian Zhuang DevOps Engineer @ WhizUs

    GmbH 🚀 Cloud Native & Kubernetes 🔧 CI/CD & GitOps practioner 🔒 Supply Chain Engineer & Security enthusiast
  2. 06.03.2026 eBPF Vienna 4 Software Supply Chain Attacks SolarWinds (2020):

    Malicious code in signed updates XZ Utils (2024): Backdoor in compression library Hackerbot-claw (2025): "autonomous security agent" performing supply chain exploits → Attackers target trusted software delivery What about code running inside the kernel?
  3. 06.03.2026 eBPF Vienna 5 Supply Chain Signing: The Container World

    Sigstore / Cosign - Signing for container images: Fulcio: Ephemeral certificates (no key management!) Rekor: Immutable transparency log Container images are static blobs after build eBPF programs are modified during loading! # Sign an image (uses your GitHub/Google identity!) cosign sign ghcr.io/myorg/myimage:v1.0 # Verify cosign verify ghcr.io/myorg/myimage:v1.0
  4. 06.03.2026 eBPF Vienna 6 eBPF: Power = Risk eBPF is

    powerful: Runs in kernel space Access to all syscalls, network, storage Used for observability, networking, security eBPF is a target: Kernel-level execution privileges Can bypass userspace security tools Growing adoption = growing attack surface How do we ensure only trusted eBPF programs run?
  5. 06.03.2026 eBPF Vienna 7 Agenda 1. PKCS#7 - Digital signature

    format 2. LSM - Kernel security hooks 3. Hornet - eBPF signature verification 4. Demo - Three test cases
  6. 06.03.2026 eBPF Vienna 9 What is PKCS#7? Standard for signing

    and encrypting data (RFC 5652) Used by: S/MIME, code signing, kernel modules Signature contains: Hash of the data Encrypted hash (using private key) Signer’s certificate chain Verification: Decrypt hash with public key Compare against computed hash Validate certificate chain
  7. 06.03.2026 eBPF Vienna 10 Signature Flow — Signing (Developer Side)

    eBPF Program Private Key Developer eBPF Program Private Key Developer Write program Sign with private key PKCS Developer signs the eBPF bytecode + maps → PKCS#7 signature embedded in skeleton
  8. 06.03.2026 eBPF Vienna 11 Signature Flow — Verification (Kernel Side)

    Keyring Kernel eBPF Program Keyring Kernel eBPF Program Load via bpf() Lookup trusted keys Public key Verify signature Allow / Reject Kernel verifies PKCS#7 signature against trusted keyring at load time
  9. 06.03.2026 eBPF Vienna 12 Linux Kernel Keyring Trusted key storage

    in the kernel Almost immutable - mutations allowed if keys are added with a trusted key
  10. 06.03.2026 eBPF Vienna 14 What is LSM? Framework for pluggable

    security modules: SELinux - NSA’s Mandatory Access Control AppArmor - Path-based access control Landlock - User-defined sandboxing Hooks at security-sensitive kernel operations Modules can allow or deny actions Runs after DAC (traditional UNIX permissions)
  11. 06.03.2026 eBPF Vienna 15 LSM Hooks allowed allowed denied denied

    User Application System Call DAC Check LSM Hooks Kernel Operation EPERM Hooks exist for:
  12. 06.03.2026 eBPF Vienna 16 BPF-LSM: Dynamic Security Traditional LSM: Static

    policies, compiled into kernel BPF-LSM (Linux 5.7+): Write policies as eBPF programs! Load/unload policies at runtime Fine-grained context (PID, cgroup, user) Stack with SELinux/AppArmor SEC("lsm/file_open") int BPF_PROG(deny_tmp_exec, struct file *file) { // Block execution from /tmp if (is_tmp_path(file)) return -EPERM; return 0; }
  13. 06.03.2026 eBPF Vienna 18 The Catch-22 of eBPF Signing Traditional

    signing (kernel modules, containers): eBPF programs are modified during loading: Map FD fixups: placeholders → runtime file descriptors CO-RE relocations: field offsets adjusted for kernel version Function calls: BPF-to-BPF addresses resolved Sign before libbpf? → Signature invalid after modifications Sign after libbpf? → Can’t verify original source Sign → Ship → Verify → Load (unchanged)
  14. 06.03.2026 eBPF Vienna 19 Two Approaches Hornet: Light Skeletons Uses

    bpftool gen skeleton -L Produces relocation-free bytecode Signature stays valid Limitations: No runtime CO-RE (resolved at build time) No extern variables Binary tied to build kernel Alternative: 2-Phase Signing Sign original ELF (Phase 1) Sign modified + Phase1 sig (Phase 2) Chain of trust maintained
  15. 06.03.2026 eBPF Vienna 20 What is Hornet? Microsoft’s LSM for

    eBPF program signing: Verifies PKCS#7 signatures on eBPF programs Uses kernel keyring for trusted keys RFC posted Dec 2025 (11 patches, active review) NACKed by BPF maintainer (Alexei Starovoitov) Signature embedded in skeleton: Generated by bpftool gen skeleton -S -L + gen_sig PKCS#7 signature covers instructions AND maps Verified against kernel keyring at load time
  16. 06.03.2026 eBPF Vienna 21 How Hornet Verifies Has sig? No

    sig Valid Invalid User loads eBPF bpf syscall Hornet LSM Verify PKCS#7 UNSIGNED OK verdict BADSIG Load EPERM Verdicts:
  17. 06.03.2026 eBPF Vienna 22 Trust Model Trusted Key ✓ Untrusted

    Key ✗ Unsigned ⚠️ Program signed with key in kernel keyring → ALLOWED Program signed with key NOT in kernel keyring → REJECTED No signature present → ALLOWED (default) → BLOCKED (with policy)
  18. 06.03.2026 eBPF Vienna 23 The Signing Artifacts: insn + map

    What gets signed: Artifact Content Why? insn.bin Raw eBPF bytecode The executable instructions map.bin Frozen map state Prevent TOCTOU attacks A TOCTOU attack exploits the gap between checking something and using it. Between the moment you verify that something is safe and the moment you actually use it, an attacker swaps it out. The check passes, but you end up using something different.
  19. 06.03.2026 eBPF Vienna 24 Signing Workflow Build Sign .bpf.c clang

    .bpf.o bpftool loader.h extract gen_sig sig.bin signed.h key See: tools/testing/selftests/hornet/Makefile
  20. 06.03.2026 eBPF Vienna 25 Signing: Key Commands 1. Generate light

    skeleton with signing prep: 2. Generate PKCS#7 signature: 3. Embed signature into skeleton: bpftool gen skeleton -S -k signing_key.pem \ -i signing_key.x509 -L trivial.bpf.o > loader.h ./gen_sig -key signing_key.pem -cert signing_key.x509 \ -data insn.bin --add-hash map.bin -out sig.bin scripts/hornet/write-sig.sh loader.h sig.bin > signed_loader.h
  21. 06.03.2026 eBPF Vienna 26 The LSM Hook 10 // Enforce

    based on Hornet's verdict 11 if (verdict == LSM_INT_VERDICT_UNSIGNED || 12 verdict == LSM_INT_VERDICT_BADSIG || 13 verdict == LSM_INT_VERDICT_PARTIALSIG) 1 SEC("lsm/bpf_prog_load_post_integrity") 2 int BPF_PROG(block_unsigned, struct bpf_prog *prog, 3 union bpf_attr *attr, struct bpf_token *token, 4 bool kernel, const struct lsm_id *lsmid, int verdict) { 5 6 // Skip kernel-loaded programs (BPF_PRELOAD) 7 if (kernel) 8 return 0; 9 14 return -EPERM; // Block! 15 16 return 0; 17 }
  22. 06.03.2026 eBPF Vienna 27 Why Not Just SELinux/AppArmor? Feature SELinux/AppArmor

    Hornet Policy type Allowlist rules Cryptographic signatures What it checks "Can this user load eBPF?" "Is this eBPF program trusted?" Granularity Per-user/process Per-program Deployment Policy author required Sign-and-ship Hornet answers: "WHO wrote this code?" Not just "who is running it?"
  23. 06.03.2026 eBPF Vienna 28 Building a Hornet Kernel 1. Clone

    + apply RFC patches: 2. Enable Hornet + build: 3. Enable BPF-LSM (for policy): git clone --depth=1 https://git.kernel.org/.../linux.git b4 am [email protected] scripts/config --enable CONFIG_SECURITY_HORNET scripts/config --set-str CONFIG_LSM "landlock,lockdown,yama,integrity,apparmor,hornet" make -j$(nproc) bindeb-pkg # /etc/default/grub.d/99-bpf-lsm.cfg GRUB_CMDLINE_LINUX="lsm=landlock,lockdown,yama,integrity,apparmor,hornet,bpf"
  24. 06.03.2026 eBPF Vienna 30 Demo Environment Setup: Lima VM with

    custom kernel (6.19-rc1) Hornet LSM enabled BPF-LSM for policy enforcement Tools: bpftool with signing support Kernel signing key in keyring Policy loader (BPF-LSM program)
  25. 06.03.2026 eBPF Vienna 31 Test Cases Test Expected Result Signed

    with trusted key Loaded Signed with untrusted key Rejected Unsigned (no policy) Loaded Unsigned (with policy) Blocked Demo scripts: scripts/05-demo.sh , 06-policy-demo.sh , 07-bpftrace-demo.sh
  26. 06.03.2026 eBPF Vienna 32 Test eBPF Program (Simple) 1 //

    file: linux/tools/testing/selftests/hornet/trivial.bpf.c 2 ... 3 4 SEC("tracepoint/syscalls/sys_enter_unlinkat") 5 int handle_enter_unlink(struct trace_event_raw_sys_enter *ctx) 6 { 7 char filename[128] = { 0 }; 8 struct task_struct *task; 9 unsigned long start_time = 0; 10 int pid = bpf_get_current_pid_tgid() >> 32; 11 char *pathname_ptr = (char *) BPF_CORE_READ(ctx, args[1]); 12 13 bpf_probe_read_str(filename, sizeof(filename), pathname_ptr); 14 task = (struct task_struct *)bpf_get_current_task(); 15 start_time = BPF_CORE_READ(task, start_time); 16 17 bpf_printk("BPF triggered unlinkat by PID: %d, start_time %ld. pathname = %s", 18 pid, start_time, filename); 19 20 if (monitored_pid == pid) 21 bpf_printk("target pid found"); 22 23 return 0; 24 }
  27. 06.03.2026 eBPF Vienna 33 Why Does BPF_CORE_READ Work With Signing?

    Regular ELF — CO-RE relocations pending in .BTF.ext : Light skeleton ( bpftool gen skeleton -L ) — CO-RE resolved at build time: 20: b7 01 00 00 18 00 00 00 r1 = 0x18 ← placeholder offset for args[1] 21: 0f 17 00 00 00 00 00 00 r7 += r1 ← libbpf patches 0x18 to real offset at load! ... 34: b7 01 00 00 e8 07 00 00 r1 = 0x7e8 ← placeholder offset for start_time 35: 0f 10 00 00 00 00 00 00 r0 += r1 ← libbpf patches 0x7e8 to real offset at load! 58: 61 60 08 00 00 00 00 00 r0 = *(u32 *)(r6 + 0x8) ← 0x8 = final offset, baked in! 59: 18 61 00 00 00 00 00 00 r1 = map_val(0) ll ← map references pre-resolved ... (no relocation records — bytecode is final and signable) $ readelf -S trivial.bpf.o | grep BTF # Where are the CO-RE relocs? [20] .BTF.ext PROGBITS # ← CO-RE relocations live here [21] .rel.BTF.ext REL # ← libbpf resolves these at load $ wc -l trivial_unsigned.skel.h # regular skeleton — HAS relocs 31115 # embeds full ELF incl. .BTF.ext $ wc -l loader.h # light skeleton — NO relocs 1547 # all CO-RE resolved at build time
  28. 06.03.2026 eBPF Vienna 34 Policy 1 SEC("lsm/bpf_prog_load_post_integrity") 2 int BPF_PROG(block_unsigned,

    struct bpf_prog *prog, union bpf_attr *attr, 3 struct bpf_token *token, bool kernel, const struct lsm_id *lsmid, 4 int verdict) { 5 struct policy_event *e; 6 int should_block = 0; 7 8 // Skip kernel-loaded programs 9 if (kernel) 10 return 0; 11 12 // Check verdict 13 if (verdict == LSM_INT_VERDICT_UNSIGNED || 14 verdict == LSM_INT_VERDICT_BADSIG || 15 verdict == LSM_INT_VERDICT_PARTIALSIG) { 16 should_block = enforce_policy; 17 } 18 19 // Send event to userspace 20 e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); 21 if (e) {
  29. 06.03.2026 eBPF Vienna 36 Summary The Challenge: eBPF programs are

    modified during loading Hornet’s Solution: Light skeletons → relocation-free bytecode PKCS#7 signatures on instructions + maps Verification via kernel keyring Key points: 1. Unsigned programs allowed by default 2. BPF-LSM policy needed for enforcement 3. RFC stage - seeking community feedback
  30. 06.03.2026 eBPF Vienna 37 Open Questions Upstream status: RFC, active

    discussion on bpf@vger Performance: Signature verification overhead? Container integration: How to sign in CI/CD pipelines? Key management: HSMs, rotation, revocation at scale? Light skeleton limits: Path to CO-RE support?
  31. 06.03.2026 eBPF Vienna 38 References Talks & Papers: BPF Signing

    + IMA (LPC 2022) Two-Phase Signing (BPF Conf 2025) eBPF in Lockdown Mode (LPC 2020) eBPF Loader Deep Dive (OSSNA 2025) Code: Hornet RFC Patches 2-Phase Signing PoC