Featured image of post eBPF例程——基于LSM的拦截操作

eBPF例程——基于LSM的拦截操作

基于libbpf编写的eBPF程序,示例演示了如何通过LSM eBPF程序在内核的敏感位置挂载,从而拦截并限制潜在的敏感内核操作。

运行eBPF程序

LSM(Linux Security Modules)是一种Linux内核安全框架,它在内核处理系统调用的敏感路径(例如文件访问、网络连接、加载模块等)上提供静态的钩子函数,允许安全模块(如SELinux、AppArmor、eBPF LSM程序等)在关键决策点介入,从而实现访问控制、审计和策略等功能。Linux支持的LSM钩子可查询lsm_hook.h文件。例如与BPF系统调用相关的LSM钩子如下。

要使用LSM特性,需要首先检查系统内核版本高于5.7,且确认当前运行的内核是否编译了对BPF LSM的支持,图示结果即为支持。

1
grep CONFIG_BPF_LSM /boot/config-$(uname -r)

随后检查内核加载并启用了哪些LSM框架,图示结果表明为启动BPF LSM框架。

1
cat /sys/kernel/security/lsm

此时必须将LSM BPF添加到内核配置参数中来手动启用它。

1
2
3
4
5
6
sudo vim /etc/default/grub
# 在GRUB_CMDLINE_LINUX中添加
lsm=lockdown,capability,landlock,yama,apparmor,ima,evm,bpf
# 添加完成,退出VIM
sudo update-grub
sudo reboot

本示例演示的是bpf()系统调用的拦截,运行该eBPF程序。

1
2
make lsm
sudo ./lsm

例如bpftool涉及使用bpf()系统调用,可以观察到该工具已被禁止。

1
sudo bpftool prog list

eBPF程序:lsm.bpf.c

eBPF程序将通过BPF_MAP_TYPE_RINGBUF向用户态进程传递事件数据,因此需要定义事件数据的格式,本示例使用so_event结构体封装了源IP地址、目的IP地址、端口号、IP协议等数据。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char LICENSE[] SEC("license") = "GPL";

#define EPERM  1

// 指示eBPF程序lsm_bpf挂载到LSM钩子上,拦截对bpf()的调用
// 参数从cmd、attr、size为bpf()函数从参数
// ret表示前一个挂载在同一钩子上的LSM程序返回值,非0值表示已经有别的程序拒绝了该调用
SEC("lsm/bpf")
int BPF_PROG(lsm_bpf, int cmd, union bpf_attr *attr, unsigned int size, int ret)
{
    // 如果之前的LSM程序已经阻止了该调用,则当前程序不做修改
    if (ret != 0)
        return ret;

    // 打印信息
    bpf_printk("LSM: block bpf() worked");
    // 返回非0值,拒绝bpf()系统调用
    // 返回值需要使用Linux内核错误码(errno)规范中的通用错误码
    // -EPERM(1)表示操作不允许,拒绝访问
    // -EACCES(13)表示权限被拒绝
    // -EINVAL(22)表示参数无效
    return -EPERM;
}

用户态程序:lsm.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "lsm.skel.h"

// libbpf的日志回调函数
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
    return vfprintf(stderr, format, args);
}

int main(int argc, char **argv)
{
    struct lsm_bpf *skel;
    int err;

    // 设置libbpf的日志回调函数
    libbpf_set_print(libbpf_print_fn);

    // 加载eBPF程序
    skel = lsm_bpf__open_and_load();
    if (!skel) {
        fprintf(stderr, "Failed to open and load BPF skeleton\n");
        goto cleanup;
    }

    // 附加LSM eBPF程序到内核LSM钩子上
    err = lsm_bpf__attach(skel);
    if (err) {
        fprintf(stderr, "Failed to attach BPF skeleton\n");
        goto cleanup;
    }

    printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
           "to see output of the BPF programs.\n");

    // 进入循环以保持程序运行状态
    for (;;) {
        fprintf(stderr, ".");
        sleep(1);
    }

cleanup:
    // 资源清理
    lsm_bpf__destroy(skel);
    return -err;
}
Licensed under CC BY-NC-SA 4.0
皖ICP备2025083746号-1
公安备案 陕公网安备61019002003315号



使用 Hugo 构建
主题 StackJimmy 设计