Blog series: Part 20 — BPF LSM security hooks
Javadoc: LSMHook · CGroupHook
Source: LSMHook.java · CGroupHook.java
See also: TC Hook · XDP Hook · BPF Maps
BPF LSM (Linux Security Module) lets you attach BPF programs to the same hook points as traditional LSM modules like SELinux and AppArmor. This gives you fine-grained, programmable security policies without recompiling the kernel.
CONFIG_BPF_LSM=yin kernel configlsm=bpfin kernel boot parameters (orlsm=...,bpfappended to existing list)
Verify:
cat /sys/kernel/security/lsm # should include "bpf"
grep CONFIG_BPF_LSM /boot/config-$(uname -r)import me.bechberger.ebpf.annotations.bpf.BPF;
import me.bechberger.ebpf.annotations.bpf.BPFFunction;
import me.bechberger.ebpf.bpf.BPFProgram;
import me.bechberger.ebpf.bpf.LSMHook;
import me.bechberger.ebpf.type.Ptr;
import static me.bechberger.ebpf.bpf.raw.Lib_1.*;
@BPF(license = "GPL")
public abstract class FileOpenRestrict extends BPFProgram implements LSMHook {
/** PIDs to block (set from Java). */
@BPFMapDefinition(maxEntries = 256)
final BPFHashMap<Integer, Integer> blockedPids = BPFHashMap.newInstance();
@Override
@BPFFunction
public int onFileOpen(Ptr<file> file) {
int pid = BPFJ.currentPid();
if (blockedPids.bpf_get(pid) != null) {
return -EACCES; // deny
}
return 0; // allow
}
}LSM hooks correspond to the security_* functions in the kernel. Common ones:
| Method | Fires when |
|---|---|
onFileOpen(Ptr<file>) |
A process opens a file |
onFilePermission(Ptr<file>, int mask) |
A file permission check (read/write/exec) |
onBpf(int cmd, Ptr<?> attr, int size) |
A process calls the bpf() syscall |
onBprmCheckSecurity(Ptr<linux_binprm>) |
A process calls execve |
onSocketCreate(int family, int type, int protocol, int kern) |
A process creates a socket |
onSocketConnect(Ptr<socket>, Ptr<sockaddr>, int) |
A socket connects |
Return 0 to allow, a negative errno to deny (e.g. -EACCES, -EPERM).
Standard errno constants are available via import static me.bechberger.ebpf.bpf.raw.Lib_1.*.
public static void main(String[] args) throws Exception {
try (FileOpenRestrict prog = BPFProgram.load(FileOpenRestrict.class)) {
prog.autoAttachPrograms();
// Block PID 12345
prog.blockedPids.put(12345, 1);
System.out.println("LSM active. Ctrl-C to stop.");
Thread.currentThread().join();
}
}!!! warning "CAP_MAC_ADMIN required"
Loading BPF LSM programs requires CAP_MAC_ADMIN in addition to CAP_BPF. Run as root
or grant the capability explicitly.
Cgroup BPF programs attach to cgroup v2 hierarchies and filter traffic for all processes in a cgroup. They are useful for per-container network policies.
@BPF(license = "GPL")
public abstract class CGroupFilter extends BPFProgram implements CGroupHook {
@BPFMapDefinition(maxEntries = 1024)
final BPFHashMap<Integer, Long> byteCount = BPFHashMap.newInstance();
@Override
@BPFFunction
public CGroupAction cgroupHandleIngress(Ptr<__sk_buff> skb) {
int mark = skb.val().mark;
Ptr<Long> c = byteCount.bpf_get(mark);
if (c != null) {
c.set(c.val() + skb.val().len);
} else {
long len = skb.val().len;
byteCount.bpf_put(mark, len);
}
return CGroupAction.PASS;
}
@Override
@BPFFunction
public CGroupAction cgroupHandleEgress(Ptr<__sk_buff> skb) {
return CGroupAction.PASS;
}
}The context type is __sk_buff (same as TC). Return CGroupAction.PASS to allow or
CGroupAction.DROP to drop.
public static void main(String[] args) throws Exception {
try (CGroupFilter prog = BPFProgram.load(CGroupFilter.class)) {
// Attach to root cgroup (affects all processes)
prog.cgroupAttachIngress("/sys/fs/cgroup");
prog.cgroupAttachEgress("/sys/fs/cgroup");
// Or a specific cgroup
prog.cgroupAttachIngress("/sys/fs/cgroup/mycontainer");
System.out.println("Cgroup hook active.");
Thread.currentThread().join();
}
}!!! note "Cgroup v2 required"
Cgroup BPF hooks require cgroup v2 (unified hierarchy). Check with:
bash mount | grep cgroup2
| Method | BPF attach type | Description |
|---|---|---|
cgroupHandleIngress |
BPF_CGROUP_INET_INGRESS |
Incoming packets for cgroup sockets |
cgroupHandleEgress |
BPF_CGROUP_INET_EGRESS |
Outgoing packets from cgroup sockets |
cgroupSockCreate |
BPF_CGROUP_INET_SOCK_CREATE |
Socket creation |
cgroupSockRelease |
BPF_CGROUP_INET_SOCK_RELEASE |
Socket close |
cgroupConnect4 |
BPF_CGROUP_INET4_CONNECT |
IPv4 connect() |
cgroupConnect6 |
BPF_CGROUP_INET6_CONNECT |
IPv6 connect() |
LSMDemo.java— BPF LSM hook demoCGroupBlockHTTPEgress.java— cgroup egress filter