Linux Landlock 怎样限制普通进程访问文件目录
来源:17golang原创
时间:2026-10-09 21:20:56 151浏览 收藏
普通 Linux 进程可以用 Landlock 主动限制自己能访问的文件目录,而且不必先取得 root 权限。做法是:声明要由 Landlock 处理的文件操作,创建规则集,为允许访问的目录添加 PATH_BENEATH 规则,再设置 no_new_privs 并调用 landlock_restrict_self()。规则生效后无法撤销,只能继续叠加更严格的限制。
官方文档:https://docs.kernel.org/userspace-api/landlock.html
- Landlock 是进程“自我收紧”的安全层,不会绕过 DAC、ACL 或其他 LSM,也不会授予新权限。
handled_access_fs决定哪些操作进入规则集的默认拒绝范围,目录规则只为指定层级放行其中一部分权限。- 普通进程通常先设置
PR_SET_NO_NEW_PRIVS,再对当前线程施加规则集。 - 策略会被后续创建的子线程和子进程继承;已有兄弟线程不会自动获得经典单线程调用施加的策略。
我更愿意把 Landlock 看成“进程内最小权限层”
第一次接触 Landlock 时,最容易把它理解成另一个文件权限系统。实际更准确的定位是:应用已经通过用户、组、ACL、SELinux 或 AppArmor 获得一组环境权限,Landlock 再从这组权限里减去应用当前阶段不需要的部分。它不能让进程读取原本无权读取的文件,也不会替代系统管理员配置的全局策略。
这种模式特别适合内容解析器、构建工具、插件宿主、文档转换器和网络服务中的受限工作阶段。例如,一个转换器需要只读系统运行库、读写自己的工作目录,却完全没有必要访问用户的 SSH 密钥。与其依赖“代码绝不会碰到”,不如让内核拒绝不在白名单层级内的已处理操作。

规则集为什么要先声明 handled_access_fs
Landlock 不是把所有文件操作一次性变成默认拒绝,而是先由应用声明“哪些访问权由本规则集处理”。这些位组成 handled_access_fs。一旦某项权利被处理,没有匹配允许规则的文件层级就会拒绝该操作;没有纳入 handled 集合的操作则不受这一层规则集限制。
这也是为什么只写 READ_FILE 不能得到完整的“只读沙箱”。要限制目录遍历,还要考虑 READ_DIR;要限制覆盖文件,通常要同时处理 WRITE_FILE 与 TRUNCATE;要限制新建和删除,则要纳入相应的 MAKE_*、REMOVE_FILE 与 REMOVE_DIR。具体可用位受运行内核的 Landlock ABI 约束,应用应先查询 ABI,再按支持范围裁剪。
一个普通进程怎样建立只读和可写目录
下面的 C 示例把一个目录设为只读,把另一个目录设为可读写。它故意采用“内核不支持就退出”的闭合策略,适合把沙箱视为安全前提的程序;如果你的应用允许降级运行,应明确记录降级并让部署者选择,而不是悄悄假装已受保护。
#define _GNU_SOURCE #include#include #include #include #include #include #include #include static int ll_create(const struct landlock_ruleset_attr *attr, size_t size, __u32 flags) { /* 直接调用 Landlock 系统调用,返回规则集文件描述符或 ABI 版本 */ return (int)syscall(SYS_landlock_create_ruleset, attr, size, flags); } static int ll_add(int ruleset_fd, const struct landlock_path_beneath_attr *rule) { /* PATH_BENEATH 规则把允许权限绑定到一个目录层级 */ return (int)syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, rule, 0); } static int ll_restrict(int ruleset_fd) { /* flags 为 0,采用广泛兼容的经典自我限制方式 */ return (int)syscall(SYS_landlock_restrict_self, ruleset_fd, 0); } static int add_path_rule(int ruleset_fd, const char *path, __u64 allowed_access) { int path_fd = open(path, O_PATH | O_CLOEXEC); if (path_fd \n", argv[0]); return 2; } /* 先查询运行内核支持的 Landlock ABI,避免盲用新权限位 */ int abi = ll_create(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (abi = 2) write_access |= LANDLOCK_ACCESS_FS_REFER; if (abi >= 3) write_access |= LANDLOCK_ACCESS_FS_TRUNCATE; struct landlock_ruleset_attr attr = { .handled_access_fs = read_access | write_access, }; int ruleset_fd = ll_create(&attr, sizeof(attr), 0); if (ruleset_fd
代码中的两个目录都只是调用者传入的示例边界。真实应用还要把动态库、配置、证书、时区数据、插件目录等运行依赖纳入设计。我的取舍是先列出业务阶段实际需要的目录,再逐个授予最小权限;若一开始直接允许整个主目录,Landlock 的收益会迅速变小。
限制自身之前,三个调用各自负责什么
| 调用 | 职责 | 失败时的含义 |
|---|---|---|
landlock_create_ruleset() | 查询 ABI 或创建包含 handled 权限的规则集 | 内核不支持、功能被禁用或参数与 ABI 不匹配 |
landlock_add_rule() | 把允许权限关联到打开的目录层级 | 目录锚点、权限位或规则类型无效 |
landlock_restrict_self() | 把规则集叠加到当前线程的 Landlock 域 | no_new_privs、能力、规则集或 flags 条件不满足 |
策略成功施加后没有“撤销”按钮。后续规则层只能继续收紧,子线程和子进程会继承父线程已经进入的 Landlock 域。这个不可逆特性正是安全价值的一部分,也意味着初始化顺序必须在架构设计阶段确定。
Landlock 只会收紧,不能抬高原有权限
某次文件访问最终能否成功,不只取决于 Landlock。传统 DAC、ACL、其他 LSM 与每一层 Landlock 策略都必须允许。Landlock 规则允许读取某目录,并不代表进程可以越过文件所有者和模式位;反过来,即使 DAC 允许,Landlock 也可以拒绝。

这种叠加模型让我觉得 Landlock 更像应用自带的“最后一道围栏”,而不是系统权限的替代方案。它很适合把安全意图随程序一起交付:应用最清楚自己在解析、构建或转换阶段需要哪些目录,管理员则继续控制主机级权限。
四个反例会让沙箱看起来比实际更强
施加策略前已经打开敏感文件
Landlock 文件访问检查主要发生在打开或操作对象的相关时点。官方文档明确提醒,沙箱前已打开的文件不受后续路径访问规则的同等限制。进入沙箱前应关闭不需要的文件描述符,并审查从父进程继承或通过 IPC 传入的描述符。
多线程程序只限制了其中一个线程
经典 landlock_restrict_self() 针对调用线程及其后代。已经存在的父线程或兄弟线程不会自动被同一调用限制。最稳妥的架构是在创建工作线程前施加策略,或者依据运行 ABI 研究并采用官方支持的线程同步限制能力,而不是假设“限制一个线程等于限制整个进程”。
忽略 ABI,直接使用当前头文件的所有权限位
编译环境的头文件可能比运行内核新。应用应查询 ABI,并仅把运行内核支持的权限纳入 handled 与 allowed 集合。官方推荐 best-effort 兼容模式;安全关键程序则可以在缺少必需能力时明确拒绝启动。无论选择哪种,都要让行为可观察。
白名单目录过大
把 /home、/etc 或根目录整体加入规则,虽然省事,却会让策略失去最小权限价值。官方建议尽可能把权限授予文件层级叶子:只读文档目录与可写临时目录分开,比给父目录宽权限再期待子目录“更严格”更容易维护。
什么时候值得采用 Landlock
- 应用能清楚列出当前阶段需要读取、执行、创建和修改的目录。
- 策略可以在打开敏感资源、创建线程或加载不可信输入前建立。
- 团队愿意按运行内核 ABI 处理兼容,并明确决定不支持时是降级还是拒绝运行。
- 目标是降低应用漏洞的文件系统影响面,而不是替代身份、容器、seccomp 或主机级 LSM。
- 可以为白名单外访问失败准备清晰日志,并把依赖目录变化纳入回归检查。
常见问题
普通用户真的可以使用 Landlock 吗?
可以。Landlock 的目标之一就是让无特权进程安全地限制自身。普通进程通常需要先设置 no_new_privs,而运行内核也必须启用并支持 Landlock。
Landlock 能让程序读取原本无权访问的目录吗?
不能。它只会收紧权限,现有 DAC、ACL 和其他 LSM 仍然生效,所有相关层都允许时访问才可能成功。
规则生效后还能删除或放宽吗?
不能放宽。进入 Landlock 域后只能继续叠加更严格的规则层,这也是初始化顺序需要提前设计的原因。
为什么白名单外仍能操作某个已打开文件?
先检查该文件描述符是否在施加策略前就已打开或从其他进程传入。Landlock 不是自动关闭已有能力的文件描述符清理器,敏感描述符必须在沙箱前处理。
如果把 Landlock 视为“应用自己声明的附加最小权限层”,设计会清楚很多:先确认运行 ABI,再声明要处理的访问权,为必要目录添加最小允许规则,关闭多余资源,最后在正确的线程时机不可逆地限制自身。它的价值不在于取代所有安全机制,而在于让普通进程也能主动缩小文件系统攻击面。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
190 收藏
-
348 收藏
-
193 收藏
-
220 收藏
-
227 收藏
-
180 收藏
-
331 收藏
-
386 收藏
-
261 收藏
-
233 收藏
-
179 收藏
-
文章 · linux | 1天前 | 容器 · Linux · 容器隔离 mount namespace PID namespace linux namespace network namespace268 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习