如何使用 Landlock 对 Linux 进程进行沙箱隔离,无需 root 权限

如何使用 Landlock 对 Linux 进程进行沙箱隔离,无需 root 权限

💡 原文英文,约2400词,阅读约需9分钟。
📝

内容提要

Landlock 是 Linux 5.13 起内置的无特权安全模块,允许程序自我限制文件访问。它仅提供三个系统调用,无需 root 或配置。规则集只能收紧不能放宽,多次应用取交集,且限制由子进程继承。使用前必须设置 no_new_privs。读取限制会阻止 exec,需显式允许二进制路径。

🔎

延伸解读

Landlock 的权限模型:只减不增

Landlock 规则集只能收紧权限,不能放宽。每次调用 restrict_self 都会与已有规则取交集,因此多次应用会导致权限越来越小。若需同时允许多个目录,必须在单个规则集中一次性添加所有规则。这种设计确保了沙箱无法被绕过,但也意味着进程一旦自我限制,就无法在后续运行中恢复权限,除非重新启动进程。

no_new_privs 为何是强制前提

在调用 landlock_restrict_self 之前,必须设置 PR_SET_NO_NEW_PRIVS。否则系统调用会返回 EPERM 错误。这是因为 no_new_privs 确保进程及其子进程无法通过 execve 获得新特权(如 setuid 程序),从而防止沙箱逃逸。Landlock 拒绝在不满足此条件时应用规则,以避免留下安全漏洞。

exec 与读取限制的冲突

如果规则集处理了 LANDLOCK_ACCESS_FS_READ_FILE,那么执行任何二进制文件都需要显式允许其路径可读。否则 exec 会因权限不足而失败。这是因为内核在加载可执行文件前会检查读取权限。因此,沙箱必须包含程序运行所需的所有路径,包括二进制文件、共享库、解释器和配置文件。使用 ldd 和 strace 可以帮助找出这些路径。

ABI 版本与功能差异

Landlock 自 Linux 5.13 引入后不断扩展,不同内核版本支持的访问权限不同。可以通过 landlock_create_ruleset 传入 NULL 和 LANDLOCK_CREATE_RULESET_VERSION 来查询当前 ABI 版本。若程序使用了当前内核不支持的权限标志,创建规则集会失败并返回 EINVAL。因此,编写跨版本兼容的程序时,应查询版本并优雅降级,避免直接使用新特性。

Q&A

Landlock 是什么?它有什么主要特点?

Landlock 是 Linux 5.13 起内置的无特权安全模块,允许程序自我限制文件访问。它仅提供三个系统调用,无需 root 或配置。规则集只能收紧不能放宽,多次应用取交集,且限制由子进程继承。

使用 Landlock 时为什么必须设置 no_new_privs?

PR_SET_NO_NEW_PRIVS 告诉内核该进程及其后代不能通过 execve 获得特权,防止沙箱进程通过执行 setuid 程序(如 sudo)逃逸。如果不设置,landlock_restrict_self 会返回 EPERM 错误,拒绝应用沙箱。

多个 Landlock 规则集如何相互作用?

每个 restrict_self 会与已应用的规则集取交集,只能收紧权限,不能放宽。例如先允许 /etc 再允许 /tmp,结果两者都被拒绝,因为交集为空。因此所有需要的路径必须放在同一个规则集中一次性应用。

Landlock 的限制如何影响子进程和 exec 调用?

限制通过 fork 和 execve 自动继承,子进程无法退出沙箱。但读取限制会阻止 exec,因为执行二进制需要读取权限。必须显式允许二进制路径(如 /usr/bin/cat)及其依赖库,否则 exec 会失败并返回 Permission denied。

如何用 Landlock 包装一个未修改的程序?

编写一个包装器:先创建规则集,允许系统目录(如 /usr、/lib、/etc)和用户指定的工作目录,设置 no_new_privs,然后调用 restrict_self,最后 execvp 目标程序。目标程序将在沙箱中运行,无需修改或同意。

如何确定一个程序需要哪些路径才能正常运行?

使用 ldd 获取共享库依赖,使用 strace 跟踪 openat 调用收集所有打开的文件路径。例如 cat 需要 /etc/hostname、/etc/ld.so.cache、libc 和 locale 归档。受限后,strace 可显示 EACCES 拒绝,帮助迭代调整规则集。

如何检查当前内核支持的 Landlock ABI 版本?

调用 syscall(__NR_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION) 返回版本号。例如版本 1 对应 5.13,提供 13 种文件系统访问权限。新版本增加网络规则等,使用未知权限会导致 EINVAL 错误。

🏷️

标签

➡️

继续阅读