Skip to content

Latest commit

 

History

History
172 lines (118 loc) · 9.82 KB

77.08、处理新应用.adoc

File metadata and controls

172 lines (118 loc) · 9.82 KB

处理新应用

在不设限的情况下运行程序

理解不受限域是如何工作的

一个 不受限域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 类型,同时限制这个切换能使用的目标程序:

  1. 首先的首先,让我们确认一下 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 的。

  2. 添加 /etc/sudoers.d/test_account_id

    visudo -f /etc/sudoers.d/test_account_id

    内容

    /etc/sudoers.d/test_account_id
    test_account ALL=(root) ROLE=unconfined_r TYPE=unconfined_t /usr/bin/id

    这条内容表示,在 test_account 运行 sudo,切换到 root 用户,且目标程序是 id 的时候,设置 SELinux 的用户和类型。这条 sudoers 规则对发起 sudo 的用户、目标用户、目标程序都做出了限制,以最小程度地限制“越权操作”的范围。

  3. 在 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

这是一种局部的 permissive,它保证在全局 enforcing 的情况下,仅为少部分域开启 permissive 模式。

# 列举系统上现有的 permissive 域
semanage permissive --list

# 将特定的域设置为 permissive
semanage permissive --add <域名称>

但是,使用 permissive 域的时候,有几点需要注意:

  • 一旦将某个域设置为 permissive,则所有处于该域的程序都会处于 permissive 模式。若一个已经处于不受限域的程序被阻挡,则切切不可将不受限域设置为 permissive 模式。我们应该为这个程序新建一个域,并将那个域设置为 permissive 模式。

  • 一个域处于 permissive 模式,也依旧会触发相关的各种 transition。若这些 transition 没有正确设置,那么这个程序生成的文件的 SELinux 标签依旧不对。这并非 permissive 域正确的用法。系统管理员将某个域设置为 permissive 应该是一个临时举措,当 SELinux 配置完成后,这个域就应该退出 permissive 模式。

使用沙箱化(sandboxed)应用

从安全角度考虑,我们不应该随意信任一个新应用,新应用应该在非常受限的情况下运行,换句话说,将其从与系统隔离开。使用 SELinux 进行的这类高度的隔离措施,就被成为 SELinux sandbox。终端用户程序就可以运行在这类域中。

理解 SELinux 沙箱

SELinux 沙箱SELinux sandbox)是一系列技术和保护手段的结合。SELinux 是其重要的组成部分,但其它隔离手段也需要一同使用,为应用和用户创建沙箱体验。

沙箱会阻止进程危害任何系统安全或用户数据。且默认情况下会阻断任何网络交互(放置数据外泄)。且很多系统资源对沙箱化的进程是不可见的。

沙箱化进程具有 sandbox_t 域,或其它类型的域(比如 sandbox_xserver_t)。而且沙箱化进程之间也会相互隔离。

使用 sandbox 命令

在运行 sandbox 命令之前,我们要保证用户可以访问多个 SELinux category。SELinux 会使用这些 category 执行沙箱化。

# 检查用户可以访问多个 SELinux category
semanage login --list
Note

Fedora 用户:sandbox 命令可以安装 policycoreutils-sandbox 包获取,同时还需要安装 selinux-policy-sandbox 包来支持 sandbox 的运行

之后我们可以通过

sandbox <受限程序文件>

来启动程序。此时,我们的程序会直接以 sandbox_t 域运行,受到 SELinux 的诸多限制。

Note

需要注意的是,sandbox 能执行的程序的 SELinux 类型是有一定限制的。比如 sandbox_t 是不可以将类型为 user_home_t 的二进制文件或脚本文件设置为进程入点的(sesearch --allow --source sandbox_t --target user_home_t --perm entrypoint 的返回值为空)。
这里我们要查找可以作为 sandbox_t 入点的文件类型 sesearch --allow --source sandbox_t --class file -Sp --perm entrypoint,execute(当然,我们不仅需要文件具有入点权限,还要具有可执行权限,参数 -Sp 表示求取的权限是同时包含两个权限的 SuPerset)。
此时我们查找到,任何具有 exec_type 属性的类型都可以作为 sandbox_t 的可执行程序。接着我们通过 seinfo --attribute exec_type --expand 来获取实际可以赋予文件的类型。
虽然我们可以随便挑一个类型,不过这里我们最终还是选择类似 sandbox_exec_t 会比较好。

假设我们的程序名为 my_daemon。若在程序运行时检查进程的 SELinux 上下文(ps -Z -C my_daemon),可以发现,该进程具有两个 SELinux category。这种赋予进程两个 SELinux categoty 的做法被称为“类 sVirt 的 MSC 对”(sVirt-like MCS pair)。由于 MCS 要求进程所具有的 category 必须包含目标对象的 category(进程的 category 是 目标 category 的超集),这样就限制了进程实际可以访问的目标对象(比如文件等)。(当然,默认情况下,文件是不含 category 的,也就是无论进程是否包含 category,也无论包含什么 category,进程对文件的访问都是不会被 MSC 阻挡的)。

扩展生成的策略

简介 sepolicy generate

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 策略库中。