第一百六十五章:LSM 框架
渡劫期涉及内核源码:
一
林小源离开 SELinux 的城堡,来到一片广阔的骨架森林。这里的树木没有叶子,只有光秃秃的枝干,每一根枝干上都挂着一个钩子。
"这是 LSM,"security.c 老者再次出现,"Linux Security Modules 框架。SELinux、AppArmor、Smack——它们都生长在这副骨架上。"
"骨架?"
"LSM 是内核的安全框架,"老者说,"它在关键操作处插入钩子,让安全模块决定是否允许操作。文件打开、进程发信号、套接字连接——每一个关键操作都有一个钩子。"
林小源走近一棵树,看到枝干上刻着 "security_file_open"。他伸手触摸,感受到一股力量从钩子中传来。
"这就是安全检查的地方?"
"是的,"老者说,"当进程打开文件时,内核调用 security_file_open(),LSM 框架调用所有注册的钩子。如果任何一个钩子拒绝,操作就被阻止。"
"但骨架本身不等于防御。"老者指着没有叶子的枝干,"LSM 框架只提供 hook 和 security blob。真正的安全策略,要由 SELinux、AppArmor、Smack、Landlock 这些模块填进去。没挂模块,骨架只是骨架。"
林小源看到枝干内部有许多空腔,分别连着 、、inode、、。"这些空腔是什么?"
"security fields,也叫 blob。"老者说,"不同模块把自己的标签和状态放进去。hook 分两类:一类管理这些字段的分配和释放,一类在 inode permission、file open、task kill 等关键点做访问控制。"
骨架初试
LSM 框架本身提供 hook 和 security blob;真正策略由谁实现?
二
"有哪些安全模块?"林小源问。
老者指向远方几棵不同的树:"SELinux 实现类型强制,最复杂。AppArmor 基于路径,Ubuntu 默认使用。Smack 简化标记,用于嵌入式系统。TOMOYO 基于路径,日本开发。Yama 限制 ptrace,是最小的模块。"
"它们能共存吗?"
"可以,"老者说,"LSM 框架允许不同模块共存。但通常只启用一个主要模块,其他模块提供补充功能。"
"共存也有顺序。"老者折下一片枯叶,叶脉上浮现出一串名字。"hook 维护在列表里,调用顺序来自 CONFIG_LSM。系统运行时,可以读 /sys/kernel/security/lsm 看当前激活的模块。"
"那 capabilities 呢?"林小源想起上一章的宝石。
"它也和 LSM 有历史渊源。"老者说,"commoncap 是能力逻辑的实现之一,长期被放在安全框架的入口处。别把 capability 当成 LSM 之外的孤岛。"
林小源望着那些不同的树,心中感慨。守护的方式不止一种,每棵树都有自己的活法。
叠挂之试
查看当前启用 LSM 模块列表的 securityfs 路径是什么?
三
"LSM 的设计哲学是什么?"林小源问。
老者坐在一棵树下,说:"LSM 不实现具体的安全策略,只提供框架。具体策略由 SELinux、AppArmor 等模块实现。这让内核保持灵活——用户可以选择适合自己的安全模块,新的安全模块也可以轻松添加。"
"这就是为什么内核不硬编码安全策略?"
"正是,"老者说,"安全需求因场景而异。服务器需要 SELinux 的严格控制,桌面系统可能只需要 AppArmor 的简单保护。LSM 让这种选择成为可能。"
"还有一条边界。"老者把一卷旧文档递给林小源,封面上写着 outdated。"许多 LSM 文档记录的是框架思想和历史 API,现代内核的具体 hook 细节要回到 security/security.c 和头文件里看。修行最忌拿旧地图当现地形。"
林小源望着那片骨架森林,心中豁然开朗。在这片内核的深处,骨架不是脆弱,而是灵活。安全模块在上面生长,守护着整个系统。
边界之试
LSM 文档里明确提醒部分 API 描述已经怎样,具体 hook 细节应回源码确认?
/*
* LSM (Linux Security Modules) 框架:
*
* 设计目标:
* 允许不同的安全模块共存
* 不修改内核核心代码
* 通过钩子函数实现安全策略
*
* 钩子函数:
* security_file_open — 打开文件时
* security_file_read — 读取文件时
* security_inode_create — 创建 inode 时
* security_task_kill — 发送信号时
* security_socket_connect — 连接套接字时
* ...
*
* 钩子位置:
* 在内核关键操作处
* 调用 security_xxx() 函数
* LSM 框架调用注册的钩子
*
* 安全模块:
* SELinux — 类型强制
* AppArmor — 路径型
* Smack — 简化标记
* TOMOYO — 路径型
* Yama — ptrace 限制
*/
/* 模拟 LSM 钩子 */
typedef int (*lsm_hook_fn_t)(void *ctx);
struct security_hook_list {
const char *name;
lsm_hook_fn_t hook;
};
/* SELinux 的钩子实现 */
int selinux_file_open(void *ctx) {
printf(" [SELinux] 检查文件打开权限\n");
/* 检查安全上下文 */
return 0; /* 允许 */
}
/* AppArmor 的钩子实现 */
int apparmor_file_open(void *ctx) {
printf(" [AppArmor] 检查文件路径\n");
/* 检查路径是否在 profile 中 */
return 0; /* 允许 */
}
/* 模拟钩子调用 */
int security_file_open(void *ctx) {
printf("LSM 框架: security_file_open 被调用\n");
/* 调用 SELinux 钩子 */
selinux_file_open(ctx);
/* 调用 AppArmor 钩子 */
apparmor_file_open(ctx);
return 0;
}
printf("=== LSM 框架 — 安全的骨架 ===\n\n");
printf("LSM 是内核的安全框架:\n\n");
printf("--- 设计目标 ---\n");
printf("1. 允许不同安全模块共存\n");
printf("2. 不修改内核核心代码\n");
printf("3. 通过钩子函数实现策略\n\n");
printf("--- 钩子函数示例 ---\n");
printf("security_file_open\n");
printf("security_file_read\n");
printf("security_inode_create\n");
printf("security_task_kill\n");
printf("security_socket_connect\n");
printf("security_bprm_check\n\n");
printf("--- 模拟钩子调用 ---\n");
security_file_open(NULL);
printf("\n--- LSM 安全模块 ---\n");
printf("SELinux:\n");
printf(" 类型强制 (TE)\n");
printf(" 最复杂的模块\n\n");
printf("AppArmor:\n");
printf(" 路径型\n");
printf(" Ubuntu 默认\n\n");
printf("Smack:\n");
printf(" 简化标记\n");
printf(" 嵌入式系统\n\n");
printf("TOMOYO:\n");
printf(" 路径型\n");
printf(" 日本开发\n\n");
printf("Yama:\n");
printf(" ptrace 限制\n");
printf(" 最小模块\n\n");
printf("--- 查看当前 LSM ---\n");
printf("cat /sys/kernel/security/lsm\n\n");
printf("--- LSM 的意义 ---\n");
printf("1. 内核不再硬编码安全策略\n");
printf("2. 用户可以选择安全模块\n");
printf("3. 新的安全模块可以轻松添加\n");#include <stdio.h>
#include <string.h>
/*
* LSM (Linux Security Modules) 框架:
*
* 设计目标:
* 允许不同的安全模块共存
* 不修改内核核心代码
* 通过钩子函数实现安全策略
*
* 钩子函数:
* security_file_open — 打开文件时
* security_file_read — 读取文件时
* security_inode_create — 创建 inode 时
* security_task_kill — 发送信号时
* security_socket_connect — 连接套接字时
* ...
*
* 钩子位置:
* 在内核关键操作处
* 调用 security_xxx() 函数
* LSM 框架调用注册的钩子
*
* 安全模块:
* SELinux — 类型强制
* AppArmor — 路径型
* Smack — 简化标记
* TOMOYO — 路径型
* Yama — ptrace 限制
*/
/* 模拟 LSM 钩子 */
typedef int (*lsm_hook_fn_t)(void *ctx);
struct security_hook_list {
const char *name;
lsm_hook_fn_t hook;
};
/* SELinux 的钩子实现 */
int selinux_file_open(void *ctx) {
printf(" [SELinux] 检查文件打开权限\n");
/* 检查安全上下文 */
return 0; /* 允许 */
}
/* AppArmor 的钩子实现 */
int apparmor_file_open(void *ctx) {
printf(" [AppArmor] 检查文件路径\n");
/* 检查路径是否在 profile 中 */
return 0; /* 允许 */
}
/* 模拟钩子调用 */
int security_file_open(void *ctx) {
printf("LSM 框架: security_file_open 被调用\n");
/* 调用 SELinux 钩子 */
selinux_file_open(ctx);
/* 调用 AppArmor 钩子 */
apparmor_file_open(ctx);
return 0;
}
int main() {
printf("=== LSM 框架 — 安全的骨架 ===\n\n");
printf("LSM 是内核的安全框架:\n\n");
printf("--- 设计目标 ---\n");
printf("1. 允许不同安全模块共存\n");
printf("2. 不修改内核核心代码\n");
printf("3. 通过钩子函数实现策略\n\n");
printf("--- 钩子函数示例 ---\n");
printf("security_file_open\n");
printf("security_file_read\n");
printf("security_inode_create\n");
printf("security_task_kill\n");
printf("security_socket_connect\n");
printf("security_bprm_check\n\n");
printf("--- 模拟钩子调用 ---\n");
security_file_open(NULL);
printf("\n--- LSM 安全模块 ---\n");
printf("SELinux:\n");
printf(" 类型强制 (TE)\n");
printf(" 最复杂的模块\n\n");
printf("AppArmor:\n");
printf(" 路径型\n");
printf(" Ubuntu 默认\n\n");
printf("Smack:\n");
printf(" 简化标记\n");
printf(" 嵌入式系统\n\n");
printf("TOMOYO:\n");
printf(" 路径型\n");
printf(" 日本开发\n\n");
printf("Yama:\n");
printf(" ptrace 限制\n");
printf(" 最小模块\n\n");
printf("--- 查看当前 LSM ---\n");
printf("cat /sys/kernel/security/lsm\n\n");
printf("--- LSM 的意义 ---\n");
printf("1. 内核不再硬编码安全策略\n");
printf("2. 用户可以选择安全模块\n");
printf("3. 新的安全模块可以轻松添加\n");
return 0;
}道藏笔记
内核启示
LSM 不管具体安全策略,它只搭架子:hook 放在打开文件、发信号、连套接字、检查 inode 权限等关键路径上,security blob 挂在 task、cred、inode、file、socket 等对象上。SELinux、AppArmor、Smack、TOMOYO、Yama、Landlock 等模块在这副骨架上实现自己的策略。
这样设计的好处是灵活:服务器选 SELinux 的严格控制,桌面选 AppArmor 的简单保护,嵌入式选 Smack 的轻量标记。多个模块可以按 CONFIG_LSM 顺序叠挂,/sys/kernel/security/lsm 能看到当前激活列表。注意官方 lsm.rst 已标注部分 API 描述过时,写具体 hook 时要回源码确认。
cat /sys/kernel/security/lsm 能看到当前启用了哪些模块。
LSM 是骨架——安全模块在上面生长。
LSM 之试
LSM 框架允许多个安全模块在内核钩子上做访问决策,本章举的典型模块是哪一个?