手把手教你为Linux内核新增一个LSM模块:以自定义文件访问控制为例

手把手教你为Linux内核新增一个LSM模块:以自定义文件访问控制为例 手把手教你为Linux内核新增一个LSM模块以自定义文件访问控制为例在Linux内核开发领域安全模块的实现一直是高级开发者关注的焦点。LSMLinux Security Modules框架作为内核安全的核心基础设施为开发者提供了扩展系统安全能力的标准化接口。不同于常见的SELinux或AppArmor等成熟方案本文将带您从零开始构建一个针对特定场景的轻量级安全模块实现基于用户组和文件路径的精细化访问控制。1. 环境准备与基础概念在开始编码前我们需要确保开发环境配置正确。推荐使用Ubuntu 20.04 LTS或更新版本作为开发系统内核版本最好选择5.4以上以获得完整的LSM支持。以下是必备组件sudo apt update sudo apt install build-essential libncurses-dev flex bison libssl-dev libelf-devLSM框架的核心思想是通过在内核关键路径插入Hook点来实现安全策略的强制执行。与传统的DAC自主访问控制不同我们的模块将实现MAC强制访问控制特性。当系统调用执行到关键操作时内核会依次调用原始功能检查DAC权限验证注册的LSM Hook函数这种设计使得安全模块可以无缝集成到现有系统中而无需修改内核核心代码。理解这点对后续开发至关重要——我们不是在修改内核而是在扩展它。2. 模块骨架搭建首先创建模块的基本文件结构custom_lsm/ ├── Makefile ├── custom_lsm.c └── Kconfig在custom_lsm.c中我们需要定义模块的元信息和初始化函数#include linux/lsm_hooks.h #include linux/module.h #include linux/init.h static int __init custom_lsm_init(void) { pr_info(Custom LSM module loaded\n); return 0; } static void __exit custom_lsm_exit(void) { pr_info(Custom LSM module unloaded\n); } security_initcall(custom_lsm_init); module_exit(custom_lsm_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Your Name); MODULE_DESCRIPTION(Custom LSM for file access control);对应的Makefile内容应为obj-$(CONFIG_SECURITY_CUSTOM_LSM) : custom_lsm.o这个基础框架虽然简单但已经包含了模块加载/卸载的基本逻辑。security_initcall宏确保我们的初始化函数在内核安全子系统初始化时被调用。3. 实现文件访问控制逻辑现在我们来实现核心功能——限制特定用户组对指定路径的访问。首先定义我们的策略数据结构struct file_restriction { const char *path_prefix; gid_t restricted_gid; int may_read; int may_write; int may_execute; struct list_head list; }; static LIST_HEAD(restriction_list);接着实现关键的file_openHook函数static int custom_file_open(struct file *file, const struct cred *cred) { struct file_restriction *entry; const char *pathname file-f_path.dentry-d_name.name; list_for_each_entry(entry, restriction_list, list) { if (strncmp(pathname, entry-path_prefix, strlen(entry-path_prefix)) 0) { if (in_group_p(entry-restricted_gid)) { if ((file-f_flags O_ACCMODE) O_RDONLY !entry-may_read) return -EACCES; if ((file-f_flags O_ACCMODE) O_WRONLY !entry-may_write) return -EACCES; if (file-f_flags O_EXEC !entry-may_execute) return -EACCES; } } } return 0; }这个实现展示了如何遍历预先定义的访问限制规则检查当前进程是否属于受限用户组根据文件打开模式验证相应权限返回-EACCES拒绝未经授权的访问4. 注册Hook与策略配置为了使我们的Hook生效需要将其注册到LSM框架中static struct security_hook_list custom_lsm_hooks[] __lsm_ro_after_init { LSM_HOOK_INIT(file_open, custom_file_open), }; void __init custom_lsm_add_hooks(void) { security_add_hooks(custom_lsm_hooks, ARRAY_SIZE(custom_lsm_hooks), custom_lsm); /* 添加示例策略 */ struct file_restriction *rule kzalloc(sizeof(*rule), GFP_KERNEL); rule-path_prefix /etc/secrets/; rule-restricted_gid 1001; // secret-access组 rule-may_read 1; rule-may_write 0; rule-may_execute 0; INIT_LIST_HEAD(rule-list); list_add_tail(rule-list, restriction_list); }在实际产品中策略配置应该通过更灵活的方式实现比如通过/proc或sysfs接口动态加载从配置文件读取规则使用netlink套接字接收策略更新5. 编译与调试技巧完成代码编写后编译模块需要特殊的内核构建系统支持。在模块目录下执行make -C /lib/modules/$(uname -r)/build M$(pwd) modules加载模块前建议先检查内核日志sudo dmesg -wH然后在另一个终端加载模块sudo insmod custom_lsm.ko调试时常见的几个问题权限不足确保测试用户确实在受限组中id -a验证路径匹配失败检查dentry-d_name.name是否如预期竞态条件对策略列表的访问需要适当的同步机制6. 进阶功能扩展基础功能实现后可以考虑以下增强特性动态策略更新static ssize_t policy_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { // 解析用户空间传入的策略规则 // 更新restriction_list return len; }审计日志static void log_denied_access(const char *path, uid_t uid, gid_t gid) { printk(KERN_INFO Denied access to %s by uid%d gid%d\n, path, uid, gid); // 也可以集成到内核审计子系统 }性能优化使用红黑树替代链表存储策略规则对频繁访问的路径实现缓存机制减少字符串比较操作7. 实际应用场景分析这种自定义LSM模块特别适合以下场景多租户环境隔离不同客户组对特定目录的访问合规需求确保敏感配置只能被授权进程读取特殊设备管理控制对/dev下特定设备的操作权限与完整方案如SELinux相比我们的模块具有轻量级仅包含必要功能无额外开销易理解策略规则简单明了可定制完全掌控安全逻辑在最近的一个金融项目中类似方案被用于保护交易系统的密钥存储区仅允许特定的清算进程访问而阻止其他所有用户和进程。