13、文件系统SELinux上下文定制:file_contexts编写规则,文件上下文恢复(restorecon),动态文件上下文设置。

好,咱们今天聊点实在的。文件系统的SELinux上下文定制,说白了就是给每个文件贴上一张“安全标签”。没有这张标签,你的进程就不知道该不该碰这个文件。我在项目里见过太多因为上下文没配好,导致服务起不来、日志写不进去的坑了。今天咱们就把这块彻底捋清楚。

13.1 file_contexts 编写规则

file_contexts 文件是SELinux策略中用来定义文件系统上下文的核心配置文件。它告诉系统:某个路径下的文件,应该被赋予什么安全上下文。

它的基本格式很简单:

路径  类型  上下文

举个例子:

/system/bin/zygote     u:object_r:zygote_exec:s0
/data/local/tmp(/.*)?  u:object_r:shell_data_file:s0

这里有几个关键点,我一个个说。

13.1.1 路径匹配规则

  • 精确匹配:直接写路径,比如 /system/bin/sh,只匹配这个文件。
  • 通配符匹配:用 (/.*)? 表示匹配目录下所有文件和子目录。比如 /data/local/tmp(/.*)? 就匹配整个 /data/local/tmp 目录树。
  • 正则表达式:支持标准的正则,比如 /data/app/.* 匹配所有应用安装目录。

注意:路径匹配是有顺序的!file_contexts 按行从上到下匹配,第一个匹配到的规则生效。所以,更具体的规则要写在前面,更通用的写在后面。我曾经见过有人把通配符写在了精确匹配前面,结果所有文件都被打上了同一个标签,系统直接崩了。

13.1.2 类型字段

类型字段有几种常见值:

类型 含义 示例
-- 普通文件 /system/bin/ls -- u:object_r:system_file:s0
-d 目录 /data -d u:object_r:data_file:s0
-b 块设备 /dev/block/mmcblk0 -b u:object_r:block_device:s0
-c 字符设备 /dev/tty0 -c u:object_r:tty_device:s0
-l 符号链接 /system/etc -l u:object_r:system_file:s0
-p 命名管道 /data/pipe -p u:object_r:pipefs:s0
-s 套接字 /dev/socket/zygote -s u:object_r:zygote:s0

如果不指定类型,默认就是 --,也就是普通文件。我个人习惯是,只要路径可能包含目录,就明确写 -d,避免歧义。

13.1.3 上下文格式

上下文格式是:

u:object_r:类型名:s0
  • u:用户,SELinux里固定为 u
  • object_r:角色,文件系统固定为 object_r
  • 类型名:你自己定义的类型,比如 system_filedata_file
  • s0:安全级别,一般用 s0,多级安全场景下会用到 s1s2 等。

核心原则:每个文件只能有一个上下文。如果你想让不同进程访问同一个文件,那得通过类型转换规则(type_transition)来实现,而不是给文件打多个标签。

13.2 文件上下文恢复:restorecon

你写好了 file_contexts,但文件系统上已经存在的文件,它们的上下文并不会自动更新。这时候就需要 restorecon 来干活了。

restorecon 的作用就是:根据 file_contexts 中的规则,重新设置文件的安全上下文。

13.2.1 基本用法

# 恢复单个文件
restorecon /system/bin/zygote

# 恢复整个目录树
restorecon -R /data

# 恢复时显示详细信息
restorecon -Rv /data

-R 表示递归,-v 表示显示详细信息。我建议你每次恢复时都加上 -v,这样能看到哪些文件被修改了,方便排查问题。

13.2.2 强制恢复

有时候你改了 file_contexts,但 restorecon 发现文件当前的上下文和规则一致,就不会动它。这时候可以用 -F 强制恢复:

restorecon -RF /data

嗯,这里要注意:-F 会强制覆盖所有文件的上下文,哪怕它已经是正确的。这在调试时很有用,但生产环境慎用。

13.2.3 恢复指定类型

如果你只想恢复某种类型的文件,可以用 -f 指定:

restorecon -R -f /system/etc/selinux/plat_file_contexts /data

这个命令会使用 plat_file_contexts 文件中的规则来恢复 /data 目录下的文件。这在多文件上下文场景下很实用。

小技巧:在Android的init.rc中,经常能看到 restorecon_recursive /data 这样的命令。它其实就是调用了 restorecon -R。如果你在开机脚本里需要恢复上下文,直接用这个命令就行。

13.3 动态文件上下文设置

有些场景下,你没法提前在 file_contexts 里写死所有路径。比如:

  • 应用动态创建的文件
  • 临时文件
  • 用户自定义目录

这时候就需要动态设置上下文了。

13.3.1 使用 chcon 命令

chcon 是最直接的方式,手动设置文件上下文:

chcon u:object_r:my_app_data_file:s0 /data/myapp/temp.log

chcon 有个问题:它只修改当前文件的上下文,不会写入 file_contexts。如果文件被删除再创建,上下文就丢了。所以,chcon 只适合临时调试。

13.3.2 使用 setfilecon 系统调用

在C/C++代码中,可以用 setfilecon 来设置:

#include <selinux/selinux.h>

int ret = setfilecon("/data/myapp/temp.log", "u:object_r:my_app_data_file:s0");
if (ret != 0) {
    // 处理错误
}

Java层可以用 android.os.SELinux.setFileContext()

boolean success = android.os.SELinux.setFileContext(
    "/data/myapp/temp.log", 
    "u:object_r:my_app_data_file:s0"
);

我在项目中遇到过一个问题:用 setfilecon 设置上下文后,restorecon 又把上下文改回去了。这是因为 restorecon 会优先使用 file_contexts 中的规则。所以,如果你动态设置了上下文,记得确保 file_contexts 中没有更优先的规则覆盖它。

13.3.3 使用文件上下文转换规则

更优雅的方式是在策略中定义类型转换规则。比如:

type_transition my_app_domain my_app_data_dir:file my_app_data_file;

这条规则的意思是:当 my_app_domain 进程在 my_app_data_dir 目录下创建文件时,自动将文件的上下文设置为 my_app_data_file。这样就不用手动调 setfilecon 了。

但要注意,类型转换规则只对 新创建 的文件有效。已经存在的文件,还是得靠 restoreconchcon 来改。

13.4 知识体系总览

下面这张图帮你理清今天讲的内容:

文件系统SELinux上下文定制知识体系 file_contexts 编写 restorecon 恢复 动态上下文设置 • 路径匹配规则 • 类型字段含义 • 上下文格式 • 通配符与正则 • 匹配顺序优先级 • 多文件上下文 • 基本用法 -R -v • 强制恢复 -F • 指定规则文件 -f • 递归恢复目录树 • init.rc 中调用 • 调试与生产区别 • chcon 命令 • setfilecon 系统调用 • Java层 SELinux API • 类型转换规则 • 动态创建文件场景 • 与file_contexts冲突 核心原则:先静态定义,再动态调整,最后用 restorecon 兜底

13.5 避坑指南

最后,分享几个我踩过的坑:

坑1:我曾经在 file_contexts 里写了一个通配符 /data(/.*)?,结果所有 /data 下的文件都被匹配到了,包括 /data/system/data/app 等。正确的做法是先写精确路径,再写通配符。

坑2:restorecon -R 恢复 /data 时,如果 /data 下挂载了其他文件系统(比如 /data/media 是FUSE),恢复操作可能会失败。这时候需要先卸载再恢复,或者单独恢复子目录。

坑3:动态设置上下文时,如果文件已经存在且被其他进程打开,setfilecon 可能会返回 EACCES。这是因为SELinux不允许在文件被使用时修改上下文。解决办法是先关闭文件句柄,或者重启相关服务。

好了,文件系统上下文定制这块,核心就是这三板斧:写好 file_contexts,用好 restorecon,灵活处理动态场景。你想想看,是不是比想象中简单?


公众号:蓝海资料掘金营,微信deep3321