一个 不受限域(unconfined domain)是一个具有宽泛权限的 SELinux 域,仅对该域的非常小范围的操作进行了限制。技术上来说,不受限域并非一个 SELinux 概念。它是 SELinux 策略制定者认为的一组权限,具有该组权限的域则被认为是“不受限”的。
通常来说,Linux 终端用户会具有 unconfined_t 类型,这种类型自然是不受限域的一种。在 SELinux 中,还有更多比 unconfined_t 更不受限的域。
SELinux 属性(attribute)关联了更多更细分的不受限“类型”,我们可以通过 seinfo -a | grep unconfined
来查找。
即便父进程具有不受限类型,也不代表被调用的子进程具有同样的不受限类型,由于 type_transition 的存在,子进程依旧可能进入一个受限的类型中。
当软件被运行时,在执行 domain transition 前,需要执行一系列检查:
-
源 SELinux 域必须对该程序(的文件)具有可执行权限(也就是 execute 权限)
-
目标 SELinux 域必须将该程序(的文件)标记为域的入口点(也就是 entrypoint 权限)
-
有一个 type_transition 条目指出源 SELinux 域运行程序(文件)时,需要向目标 SELinux 域切换
-
有一条规则指出源 SELinux 域对目标 SELinux 域具有 transition 权限
-
目标 SELinux 域可以与源 SELinux 角色组成有效的 SELinux 上下文(或者有一条规则允许转换到合适的 SELinux 角色)。
假设某终端用户 test_account 具有 wheel 组,且其被映射至 stuff_u SELinux 用户。现在要求,当 test_account 运行特定程序(比如 id
)时,以 unconfined_t 域运行。
当然,我们不能直接让 test_account 运行的任何程序都处于 unconfined_t 下,否则为 test_account 设置 staff_u 就没有意义的。因此,我们可以借用 sudo
,让 test_account 运行 sudo
时,切换 SELinux 类型,同时限制这个切换能使用的目标程序:
-
首先的首先,让我们确认一下 staff_u 可以使用 unconfined_r:
seinfo --user staff_u --expand
返回
Users: 1 user staff_u roles { staff_r sysadm_r system_r unconfined_r } level s0 range s0 - s0:c0.c1023;
我们可以确认,staff_u 是可以使用 unconfined_r 的。
-
添加 /etc/sudoers.d/test_account_id
visudo -f /etc/sudoers.d/test_account_id
内容
/etc/sudoers.d/test_account_idtest_account ALL=(root) ROLE=unconfined_r TYPE=unconfined_t /usr/bin/id
这条内容表示,在 test_account 运行 sudo,切换到 root 用户,且目标程序是 id 的时候,设置 SELinux 的用户和类型。这条 sudoers 规则对发起 sudo 的用户、目标用户、目标程序都做出了限制,以最小程度地限制“越权操作”的范围。
-
在 test_account 账户下运行
sudo id
,并检查输出,可以看见,其 SELinux Role 和 Type 已经分别切换至 unconfined_r 和 unconfined_u 了。另外,我们可以拷贝一份id
至其它目录下,再次运行sudo <拷贝出来的文件>
,就会发现其 SELinux Role 和 Type 并没有得到改变。这样就证明了我们书写的 sudoers 规则是按预期起效的。
若一个二进制文件不具有特定的标签(也就是被标记为 bin_t),那么它在被 systemd 调用为守护进程(也就是 systemd 以 init_t 域调用我们的程序)的时候,新生成的守护进程就会具有不受限的守护进程域 unconfined_service_t。
假设我们有一个 my_daemon 二进制程序文件,其 SELinux 类型为 bin_t。之后我们通过 systemd-run <my_daemon>
将该二进制文件加载为守护进程,当我们使用 ps -Z -C my_daemon
查询其 SELinux 上下文的时候,就能发现其 SELinux 类型为 unconfined_service_t。
虽然 unconfined_service_t 域已经包含了绝大部分可能的权限了,但是我们依旧可能遇到部分权限问题。实际上,SELinux 内置了一组名为 *_unconfined_type 的属性,它们依照其名称分别涵盖了一部分权限,当我们将这些属性中的一个或多个关联上对应的类型的时候,我们就给予这个类型几乎全部的权限了。
另外还有另一组名为 can_* 的属性,能微调某个特定的权限。部分 can_* 也可能是约束(constrain)。
这是一种局部的 permissive,它保证在全局 enforcing 的情况下,仅为少部分域开启 permissive 模式。
# 列举系统上现有的 permissive 域
semanage permissive --list
# 将特定的域设置为 permissive
semanage permissive --add <域名称>
但是,使用 permissive 域的时候,有几点需要注意:
-
一旦将某个域设置为 permissive,则所有处于该域的程序都会处于 permissive 模式。若一个已经处于不受限域的程序被阻挡,则切切不可将不受限域设置为 permissive 模式。我们应该为这个程序新建一个域,并将那个域设置为 permissive 模式。
-
一个域处于 permissive 模式,也依旧会触发相关的各种 transition。若这些 transition 没有正确设置,那么这个程序生成的文件的 SELinux 标签依旧不对。这并非 permissive 域正确的用法。系统管理员将某个域设置为 permissive 应该是一个临时举措,当 SELinux 配置完成后,这个域就应该退出 permissive 模式。
从安全角度考虑,我们不应该随意信任一个新应用,新应用应该在非常受限的情况下运行,换句话说,将其从与系统隔离开。使用 SELinux 进行的这类高度的隔离措施,就被成为 SELinux sandbox。终端用户程序就可以运行在这类域中。
SELinux 沙箱(SELinux sandbox)是一系列技术和保护手段的结合。SELinux 是其重要的组成部分,但其它隔离手段也需要一同使用,为应用和用户创建沙箱体验。
沙箱会阻止进程危害任何系统安全或用户数据。且默认情况下会阻断任何网络交互(放置数据外泄)。且很多系统资源对沙箱化的进程是不可见的。
沙箱化进程具有 sandbox_t 域,或其它类型的域(比如 sandbox_xserver_t)。而且沙箱化进程之间也会相互隔离。
在运行 sandbox
命令之前,我们要保证用户可以访问多个 SELinux category。SELinux 会使用这些 category 执行沙箱化。
# 检查用户可以访问多个 SELinux category
semanage login --list
Note
|
Fedora 用户: |
之后我们可以通过
sandbox <受限程序文件>
来启动程序。此时,我们的程序会直接以 sandbox_t 域运行,受到 SELinux 的诸多限制。
Note
|
需要注意的是, 假设我们的程序名为 my_daemon。若在程序运行时检查进程的 SELinux 上下文( |
sepolicy generate
可以为指定的程序生成初始的 SELinux 模块模板。
假设现在已经有一个程序文件 /usr/local/bin/my_daemon,我们可以使用
sepolicy generate --init /usr/local/bin/my_daemon
来为该程序生成一个基础的 SELinux 策略模块模板。命令中的 --init
表示目标程序是一个标准 Init 守护程序,当然还有其它的选项,可以为其它种类的程序生成策略模块模板。
sepolicy generate
会生成多个文本文件,而我们可以使用 selinux-policy-devel 包给出的 Makefile,将这些文件编译为 SELinux 二进制模块。
make -f /usr/share/selinux/devel/Makefile my_daemon.pp
之后我们救可以通过
semodule -i my_daemon.pp
将模块安装至 SELinux 策略库中。