Linux 内核 Landlock 系统级管理深度解析:审计记录、事件过滤与 Tracepoint 可观测性
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
本文以 Linux 内核文档Documentation/admin-guide/LSM/landlock.rst为核心,系统讲解 Landlock 沙箱框架的系统级管理能力:被拒访问如何通过 audit 子系统生成AUDIT_LANDLOCK_ACCESS与AUDIT_LANDLOCK_DOMAIN记录、blockers字段的完整语义、如何用sys_landlock_restrict_self()标志与LANDLOCK_ADD_RULE_QUIET控制日志噪音,以及 Landlock tracepoint 与 audit 两种可观测性手段的取舍。读完后,你将能够完整解读 Landlock 审计日志、构建 ftrace/eBPF 监控流水线,并理解内核源码中记录生成的真实调用路径。
1. 为什么需要 Landlock 的系统级可观测性
Landlock 是 Linux 内核的强制访问控制(MAC)机制,允许任意进程在不依赖特权的前提下为自己创建访问策略(规则集),限制自身及子孙进程对文件系统和网络端口的访问。策略生效之后,一个核心运维问题是:当某个沙箱进程触发了被拒访问时,系统管理员如何发现、定位并解读这次拒绝?
Landlock 的答案是双轨制:
- Audit(审计):被拒访问默认通过 Linux audit 子系统记录。这是标准的安全事件通道,记录格式稳定,已被日志管理系统、SIEM 平台和 EDR 方案广泛支持,面向长期安全监控与合规。
- Tracepoint(跟踪点):与 audit 配置完全解耦,无条件触发,默认禁用(零开销),面向策略调试和常驻监控(包括 eBPF 程序)。
内核侧实现集中在 security/landlock/ 目录,其中审计记录由 security/landlock/audit.c 生成,日志策略判定在 security/landlock/log.c,用户空间标志的定义在 include/uapi/linux/landlock.h。用户空间 API 文档见 Documentation/userspace-api/landlock.rst,tracepoint 完整参考见 Documentation/trace/events-landlock.rst。
2. 审计记录类型
Landlock 可生成两种审计记录类型。从源码看,对应security/landlock/audit.c中的两个关键函数:landlock_audit_denial()(被拒访问)和landlock_audit_free_domain()(域释放),二者都通过audit_log_start()分别注册为AUDIT_LANDLOCK_ACCESS和AUDIT_LANDLOCK_DOMAIN类型。
2.1 AUDIT_LANDLOCK_ACCESS:被拒访问记录
该记录标识一次对内核资源的被拒访问,包含三类信息:
domain:发起拒绝的 Landlock 域 ID;blockers:拒绝原因,多个原因以逗号分隔;- 内核对象标识字段(与 SELinux 类似):文件系统访问为
path、dev、ino,信号类访问为opid、ocomm。
一次审计事件内可能包含多条该类型记录。例如一个文件 link 请求在同一事件中产生两条记录::
domain=195ba459b blockers=fs.refer path="/usr/bin" dev="vda2" ino=351 domain=195ba459b blockers=fs.make_reg,fs.refer path="/usr/local" dev="vda2" ino=365blockers字段使用点分前缀标示限制类别,完整清单如下:
fs.*— 文件系统访问权(ABI 1+):
fs.execute,fs.write_file,fs.read_file,fs.read_dirfs.remove_dir,fs.remove_filefs.make_char,fs.make_dir,fs.make_reg,fs.make_sockfs.make_fifo,fs.make_block,fs.make_symfs.refer(ABI 2+)fs.truncate(ABI 3+)fs.ioctl_dev(ABI 5+)fs.resolve_unix(ABI 9+)
net.*— 网络访问权(ABI 4+):
net.bind_tcp— TCP 端口绑定被拒net.connect_tcp— TCP 连接被拒net.bind_udp— UDP 端口绑定被拒net.connect_send_udp— UDP 连接与发送被拒
scope.*— IPC 作用域限制(ABI 6+):
scope.abstract_unix_socket— 抽象 UNIX 套接字连接被拒scope.signal— 信号发送被拒
当一次操作缺失多个访问权时,blockers会以逗号形式列出多个值。例如在既缺make_reg又缺refer权限的目录中创建普通文件,会看到blockers=fs.make_reg,fs.refer。
源码印证:请求类型由 security/landlock/log.h 中的enum landlock_request_type定义(LANDLOCK_REQUEST_PTRACE、LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY、LANDLOCK_REQUEST_FS_ACCESS、LANDLOCK_REQUEST_NET_ACCESS、LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET、LANDLOCK_REQUEST_SCOPE_SIGNAL)。blockers前缀由audit.c的blocker_prefix()按请求类型映射:文件系统类请求加fs.前缀,网络类加net.,scope 类加scope.,ptrace 独立不加前缀。多条 blocker 的逗号拼接在log_blockers()中完成——它遍历缺失访问权位图的每一位,逐位调用get_blocker()输出名称。
2.2 AUDIT_LANDLOCK_DOMAIN:域生命周期记录
该记录描述 Landlock 域的状态,status字段取值为allocated或deallocated。
allocated 状态与首个被记录的AUDIT_LANDLOCK_ACCESS记录属于同一审计事件,紧随其后出现,用于在sys_landlock_restrict_self()调用时快照域信息,字段包括:
domain:域 ID;mode:执行模式;pid:创建域的进程 PID;uid:创建域的用户 UID;exe:创建域的可执行文件路径;comm:创建域的命令名。
示例::
domain=195ba459b status=allocated mode=enforcing pid=300 uid=0 exe="/root/sandboxer" comm="sandboxer"deallocated 状态是一条独立事件,标识域的释放。此后内核保证该域 ID 在系统生命周期内永不复用。domain字段标识被释放的域,denials字段汇总该域拒绝的访问请求总数(这些拒绝不一定都被记录过,取决于审计规则与sys_landlock_restrict_self()的标志位)。
示例::
domain=195ba459b status=deallocated denials=3源码印证:log_domain()在 security/landlock/audit.c 中通过hierarchy->log_status与LANDLOCK_LOG_RECORDED状态保证每个域只输出一次 allocated 记录;landlock_audit_free_domain()(同文件 #L214-L237)则检查"该域是否曾出现在审计日志中",仅对已记录的域输出 deallocated 记录,denials值直接读取hierarchy->num_denials原子计数器。这解释了为什么denials可能大于实际记录的审计条数:它统计所有拒绝,而日志标志与 audit 侧限流/排除规则可能抑制其中一部分。
3. 完整事件样例解读
以下两个样例展示了审计事件的完整形态。事件以时间戳加唯一序列号标识(如msg=audit(1729738800.268:30))。
3.1 信号作用域拒绝:沙箱内 kill init 进程
命令::
$ LL_FS_RO=/ LL_FS_RW=/ LL_SCOPED=s LL_FORCE_LOG=1 ./sandboxer kill 1沙箱程序尝试向 init 进程发送信号,被信号作用域限制(LL_SCOPED=s)拒绝。该操作产生两个事件、共 5 条记录::
type=LANDLOCK_ACCESS msg=audit(1729738800.268:30): domain=1a6fdc66f blockers=scope.signal opid=1 ocomm="systemd" type=LANDLOCK_DOMAIN msg=audit(1729738800.268:30): domain=1a6fdc66f status=allocated mode=enforcing pid=286 uid=0 exe="/root/sandboxer" comm="sandboxer" type=SYSCALL msg=audit(1729738800.268:30): arch=c000003e syscall=62 success=no exit=-1 [..] ppid=272 pid=286 auid=0 uid=0 gid=0 [...] comm="kill" [...] type=PROCTITLE msg=audit(1729738800.268:30): proctitle=6B696C6C0031 type=LANDLOCK_DOMAIN msg=audit(1729738800.324:31): domain=1a6fdc66f status=deallocated denials=1逐条解读:
- 第一条记录(
type=LANDLOCK_ACCESS,序列 30):访问被域1a6fdc66f拒绝,原因为blockers=scope.signal;本应接收信号的进程是 init(opid=1 ocomm="systemd")。 - 第二条记录(
type=LANDLOCK_DOMAIN):描述1a6fdc66f域,status=allocated。该域由进程286执行/root/sandboxer程序、以 root 用户身份创建。 - 第三条记录(
type=SYSCALL):系统调用详情——参数、结果(success=no exit=-1)以及调用进程。 - 第四条记录(
type=PROCTITLE):命令行以十六进制呈现,可用python -c 'print(bytes.fromhex("6B696C6C0031"))'还原为kill\01。 - 最后一条记录(序列 31,唯一的第二条事件):
type=LANDLOCK_DOMAIN且status=deallocated,属于异步事件,与用户空间直接操作无关,而是释放域关联资源时触发。它的价值在于明确告知:后续日志不会再涉及域1a6fdc66f;同时denials=1汇总了该域拒绝过的全部请求(无论是否被记录)。
3.2 文件系统拒绝:只读根 + 可写 /tmp 下写 /etc/passwd
命令::
$ LL_FS_RO=/ LL_FS_RW=/tmp LL_FORCE_LOG=1 ./sandboxer sh -c "echo > /etc/passwd"关联审计日志包含来自 3 个事件(序列 33、34、35)的 8 条记录,全部源于同一域1a6fdc679::
type=LANDLOCK_ACCESS msg=audit(1729738800.221:33): domain=1a6fdc679 blockers=fs.write_file path="/dev/tty" dev="devtmpfs" ino=9 type=LANDLOCK_DOMAIN msg=audit(1729738800.221:33): domain=1a6fdc679 status=allocated mode=enforcing pid=289 uid=0 exe="/root/sandboxer" comm="sandboxer" type=SYSCALL msg=audit(1729738800.221:33): arch=c000003e syscall=257 success=no exit=-13 [...] ppid=272 pid=289 auid=0 uid=0 gid=0 [...] comm="sh" [...] type=PROCTITLE msg=audit(1729738800.221:33): proctitle=7368002D63006563686F203E202F6574632F706173737764 type=LANDLOCK_ACCESS msg=audit(1729738800.221:34): domain=1a6fdc679 blockers=fs.write_file path="/etc/passwd" dev="vda2" ino=143821 type=SYSCALL msg=audit(1729738800.221:34): arch=c000003e syscall=257 success=no exit=-13 [...] ppid=272 pid=289 auid=0 uid=0 gid=0 [...] comm="sh" [...] type=PROCTITLE msg=audit(1729738800.221:34): proctitle=7368002D63006563686F203E202F6574632F706173737764 type=LANDLOCK_DOMAIN msg=audit(1729738800.261:35): domain=1a6fdc679 status=deallocated denials=2值得注意的细节:echo > /etc/passwd在 shell 语义下先尝试打开重定向目标,因此产生了两次fs.write_file拒绝(序列 33 对应/dev/tty,序列 34 对应/etc/passwd),而 deallocated 记录如实汇总denials=2。proctitle十六进制7368002D63006563686F203E202F6574632F706173737764还原后即sh\0-c\0echo > /etc/passwd。
4. 事件过滤:控制审计日志噪音
当系统被 Landlock 审计日志"刷屏"时,这通常意味着攻击尝试或安全策略 bug。文档给出两种互补的降噪手段:
4.1 通过 sys_landlock_restrict_self() 标志调整日志策略
标志位定义于 include/uapi/linux/landlock.h:
| 标志 | 值 | 语义 |
|---|---|---|
LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF | 1U << 0 | 关闭"创建域线程及其子孙在未发生execve(2)前"的被拒访问日志。面向解释器类程序(执行未知代码但不 execve)。仅沙箱自身的程序不应设置此标志,以便用户能从系统日志获知越权尝试 |
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | 1U << 1 | 在execve(2)之后启用日志,提供域内新执行程序的可见性。仅当域内所有可执行文件都预期遵守限制时才建议设置,否则日志过多反而淹没关键事件 |
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | 1U << 2 | 关闭调用者或其子孙创建的嵌套 Landlock 域的日志。适合容器运行时等可能启动自沙箱程序的场景;可按运行配置动态设置,不建议硬编码。可配合ruleset_fd = -1仅静音子域日志而不创建域,配合TSYNC可传播到进程所有线程 |
LANDLOCK_RESTRICT_SELF_TSYNC | 1U << 3 | 将新 Landlock 配置(域 + 日志配置)原子地应用到当前进程的所有线程 |
LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS | 1U << 4 | 仅当规则集执行成功时才设置 no_new_privs,免除预先prctl(PR_SET_NO_NEW_PRIVS)或 CAP_SYS_ADMIN 的开销 |
默认行为是:对自沙箱程序,被拒访问默认记录(此类事件通常代表异常:bug 或利用尝试);而由他人创建的域拒绝的访问默认不记录——程序应当了解自己的行为,但不必了解其他程序的行为。
4.2 通过 LANDLOCK_ADD_RULE_QUIET 按对象静音(ABI 10+)
若沙箱程序可控,可在添加规则时传入LANDLOCK_ADD_RULE_QUIET标志,配合struct landlock_ruleset_attr中的quiet_*字段,对特定对象逐个对象抑制日志。uapi 文档(见 include/uapi/linux/landlock.h 的landlock_add_rule_flags说明)明确了日志被抑制的三条件:该层是拒绝该访问的最内层;被拒访问全部落在对应quiet_*字段范围内;对象(文件系统规则还包括其父级对象)通过LANDLOCK_ADD_RULE_QUIET标记为 quiet。
安全性保证:由于日志抑制只发生在"该层自身拒绝访问"的前提下,沙箱程序无法用该标志"隐藏"拒绝——它必须先拒绝自己才能静音。且该标志的效果不依赖allowed_access的取值,携带该标志时允许传入空的allowed_access。
内核校验逻辑可见 security/landlock/syscalls.c:LANDLOCK_ADD_RULE_QUIET仅在规则集的quiet_masks对应类别(fs/net)非空时合法,否则返回-EINVAL;标志位本身只有 0 或LANDLOCK_ADD_RULE_QUIET两种取值。
4.3 通过 audit 规则过滤
也可在 audit 侧用规则掩蔽 Landlock 日志(参见auditctl(8)手册页),例如按记录类型、域或对象字段做排除。
5. Tracepoint:面向调试与常驻监控的替代通道
Landlock 同时提供 tracepoint 作为 audit 的替代与补充。关键特性:
- 无条件触发:不依赖 audit 配置、
audit_enabled开关或域日志标志; - 默认禁用:禁用状态时 tracepoint 是零开销的 no-op;
- 适用场景:eBPF 常驻监控,或
trace_pipe临时调试。
完整事件参考见 Documentation/trace/events-landlock.rst,事件按四类组织:
| 类别 | 事件 |
|---|---|
| 系统调用事件 | landlock_create_ruleset、landlock_add_rule_fs、landlock_add_rule_net、landlock_create_domain、landlock_enforce_domain |
| 拒绝事件 | landlock_deny_access_fs、landlock_deny_access_net、landlock_deny_ptrace、landlock_deny_scope_signal、landlock_deny_scope_abstract_unix_socket |
| 规则求值事件 | landlock_check_rule_fs、landlock_check_rule_net |
| 生命周期事件 | landlock_free_domain、landlock_free_ruleset |
启用与读取方式::
# 启用全部 Landlock 事件 echo 1 > /sys/kernel/tracing/events/landlock/enable # 启用单个事件 echo 1 > /sys/kernel/tracing/events/landlock/landlock_deny_access_fs/enable # 读取跟踪输出 cat /sys/kernel/tracing/trace_pipe一个典型跟踪样例(沙箱程序被拒读取/etc/passwd):
cat-127 [...] landlock_create_ruleset: ruleset=195cc6b76.0 handled_fs=execute|write_file|read_file|read_dir|... handled_net= scoped= cat-127 [...] landlock_create_domain: domain=195cc6b7c parent=0 ruleset=195cc6b76.6 cat-127 [...] landlock_enforce_domain: domain=195cc6b7c complete=1 process_wide=1 no_new_privs=1 cat-127 [...] landlock_deny_access_fs: domain=195cc6b7c same_exec=0 logged=0 blockers=read_file dev=0:17 ino=5901179 path=/etc/passwd kworker/0:1-11 [...] landlock_free_domain: domain=195cc6b7c denials=1其中logged=0体现了关键差异:该跨 exec 拒绝在默认标志下不会进入审计日志,但 trace 事件依然产生——这正是"tracepoint 无条件触发"的实际表现。
6. Tracing 与 Audit 如何选择
文档对两者做了明确的职责划分:
Audit记录被拒访问的 blockers、域与对象标识(path、端口)。它是 Linux 安全事件的标准机制,记录格式稳定,已被日志管理、SIEM 与 EDR 方案支持;在内核启用 audit 时始终活跃,受 Landlock 日志标志过滤以减少生产噪音,面向长期安全监控与合规。
Tracing面向策略调试的深度内省。除了被拒访问,trace 事件还覆盖 Landlock 对象的完整生命周期(规则集、域)以及访问检查中的中间规则匹配。eBPF 程序可经 BTF 访问完整内核上下文(规则集规则、域层级、进程凭证),实现比 audit 平面字段更丰富的分析——例如关联创建、加规则与拒绝事件,构建所有活跃域及其策略的实时视图。但有两点限制:
- BTF 访问依赖内核内部结构体布局,无稳定性保证,CO-RE(Compile Once, Run Everywhere)提供尽力而为的字段重定位;
- ftrace 的 printk 格式不是稳定 ABI,但通过每事件的
format文件自描述,工具可动态适配。
此外,trace 与 audit 的字段格式存在系统性差异(详见 trace 文档 "Differences from audit records" 一节):trace 用d_absolute_path()解析路径(audit 用相对进程 chroot 的d_path());trace 用数字dev=<major>:<minor>,audit 用字符串dev="<s_id>";blockers=在 audit 中带类别前缀、逗号分隔,在 trace 中省略类别(由事件名承载)、以|分隔;scope/ptrace 的 trace 事件还会报告对端域 ID(tracee_domain=、target_domain=、peer_domain=),而 audit 记录不带该信息。
7. 可观测性保证与边界
audit 记录与 trace 事件对每一次被拒访问都会输出,存在以下例外:
- Landlock 日志标志(仅 audit):
LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF、LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON、LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF控制哪些拒绝生成审计记录;trace 事件不受这些标志影响。 - NOAUDIT 钩子:部分 LSM 钩子对推测性权限探测抑制日志(例如读取
/proc/<pid>/status使用PTRACE_MODE_NOAUDIT)。NOAUDIT 场景下审计记录与 trace 事件都不输出,拒绝也不计入denials——但拒绝本身仍然生效。这避免了试探性探测的性能开销与噪音。 - 审计限流:audit 子系统在审计队列满时可能静默丢弃记录;trace 事件无限流。
- tracepoint 禁用:默认状态下是零开销 no-op。
当 audit 与 tracing 同时激活时,每次拒绝都产生 trace 事件;被域日志策略选中的拒绝额外产生审计记录(仍受日志标志约束)。free_domain事件中的denials计数对每次拒绝都递增,与日志标志无关,因此可能大于实际审计记录数。
8. 可观测性的安全考量
audit 记录与 trace 事件都暴露了系统内所有受 Landlock 沙箱进程的信息:正在访问的文件路径、网络端口、进程身份。管理员必须确保:
- 审计日志的访问受 audit 子系统配置约束,仅限可信用户;
- 跟踪事件的访问需要
CAP_SYS_ADMIN,或CAP_BPF+CAP_PERFMON,同样仅限可信用户。
eBPF 程序挂在 Landlock 跟踪点上可经 BTF 读取每个事件的完整内核上下文(规则集规则、域层级、进程凭证),这暴露了所有受沙箱进程的敏感状态,访问权限管控应与审计日志同等对待。
最后要注意:审计日志与内核跟踪事件都需要提权且是系统级的,二者并非为每个沙箱的非特权监控设计。
9. 参考与延伸阅读
- Landlock 用户空间 API:
Documentation/userspace-api/landlock.rst - Landlock 跟踪事件完整参考:
Documentation/trace/events-landlock.rst - Landlock 安全子系统总览:
Documentation/security/landlock.rst - 审计记录实现:
security/landlock/audit.c、security/landlock/log.c、security/landlock/log.h - 用户空间标志与规则定义:
include/uapi/linux/landlock.h
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考