RBAC

RBAC, Role Based Access Control, 是SELinux的另一个重要的特性。但他并不是一种独立的控制方式,而是对TEAC的一种补充。其主要的作用是,构建SELinux提供的进程级的MAC机制与Linux的用户系统的映射关系。

RBAC工作原理

RBAC并不提供强制访问控制,而是通过user,role,type之间的对应关系,来控制type_transition策略能否成功。

role vs. type

role based最基本的就是定义role到type到对应关系。使用role语句可以实现:role role_name [types type_set];。例如:

1
2
role user_r types user_t;
role user_r types { staff_t, admin_t };

  • role定义是累积的,即后面的对应关系不会覆盖前面语句定义的对应关系,而只会追加这种关系
  • role语句通常定义在type声明附近,以确保该对应关系被及时定义

定义子role (period)
A period is used to indicate restrictions on the set of types that may be assigned to a role. For example, the set of types for a role called apache.cgi must be a subset of the type set of a role called apache.

role语句定义的基本逻辑就是,定义了role=>{ types }集合的映射关系。当发生type transition时,如果转换后的{role,type}组合未定义,则视为权限错误。从这个层面看,role对应的是主体type,即domain type,而非object type。

特殊的role: object_r

object_r是内核SELinux模块预定义的一个role,无需在policy中声明。而这个object_r被专门用来定义客体的安全上下文。

user vs. role

user是对role的再一次扩充,通过user将role与Linux user的映射关系建立起来。SELinux的用户系统独立于Linux用户系统。通过seusers文件或者semanage user命令可以定义他们之间的映射关系。但实际可定义的自由度并没有那么高。

The design decision for SELinux to have a distinct user identifier (rather than share that of Linux) is motivated by the desire to create an immutable SELinux user identifier.

定义一个user与role的映射关系,可以使用user语句:user user_name roles { role_set };
user语句所体现的逻辑和role语句类似,即定义了user=>{ roles }集合的映射关系。当应用启动时发生role transition时,如果转换后的{user, role}未定义,则视为权限错误。

当SELinux通过RBAC,建立起user=>role=>type的映射关系以后,还获得了一个好处。Linux系统的用户数可能很多,而用户的类型很少。用户并不是为了定义权限的,不同的用户之间的使用数据需要隔离。但往往一类用户的权限是相同的。例如超级用户,管理员用户,普通用户等等。系统的权限集最小粒度由domain定义。可能每个进程的权限不同,也可能一组进程的权限相同。但往往domain的数量也很庞大。如果没有RBAC,我们需要定义user到domain到映射。这样也不是不能做,但却不太优雅。每次新增用户时,需要为新增用户绑定一堆domain。定义了role之后,role相当于较稳定的一层抽象,role到types的映射关系是在定义type时候就定义了的。增加新user的时候,只要定义user到role的映射即可。这就是引入RBAC的原因。

graph LR
A[user] --> B[role]
A --> C[role]
A --> D[role]
C --> E[domain]
C --> F[domain]
C --> G[domain]

Linux用户 vs. SELinux user

  • 当前系统的用户可以通过查看/etc/passwd文件获取,也可以参考列出 Linux 系统上所有用户的 3 种方法.
  • 查看SELinux的用户可以通过命令semanage user -l.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
                   标记中        MLS/       MLS/                          
    SELinux 用户 前缀 MCS 级别 MCS 范围 SELinux 角色

    guest_u user s0 s0 guest_r
    root user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r
    staff_u user s0 s0-s0:c0.c1023 staff_r sysadm_r unconfined_r
    sysadm_u user s0 s0-s0:c0.c1023 sysadm_r
    system_u user s0 s0-s0:c0.c1023 system_r unconfined_r
    unconfined_u user s0 s0-s0:c0.c1023 system_r unconfined_r
    user_u user s0 s0 user_r
    xguest_u user s0 s0 xguest_r
  • 查看两者的映射关系可以通过命令semanage login -l.
    1
    2
    3
    4
    5
    登录名                  SELinux 用户           MLS/MCS 范围           服务

    __default__ unconfined_u s0-s0:c0.c1023 *
    john user_u s0 *
    root unconfined_u s0-s0:c0.c1023 *

Linux用户和SELinux用户的初始映射关系由seusers文件指定。这个文件不属于策略二进制的一部分,而可以直接修改生效。当然更优雅的办法是通过semanage命令来修改。
另外,在修改了context以后,或者新建了用户以后,必须要使用注销登录的方法,才能使新的context生效,而只使用su命令切换用户,并不会切换context。推测注销登录使用的是PAM登录程序,而su命令中,并没有重新加载user context的功能。
Linux用户登录后,获取shell安全上下文的步骤,可以参考文献【2】。简述如下:

  1. 根据seusers文件,映射SELinux用户,如果没有seusers文件,系统就无法启动了。如果seusers文件没有对应的用户描述,则统一映射为__default__用户。如果__default__用户描述不存在,则无法登录。
  2. 根据SELinux的搜索优先级,找到对应SELinux用户的安全上下文。

勘误
在[1]中,对Linux用户和SELinux的映射关系有如下描述:
On login, if there is an SELinux user identifier that is exactly the same as the Linux user identifier, the matching SELinux user identifier becomes the user identifier in the security context for the initial shell process. In this way, if a Linux user identifier also exists as a user identifier in the SELinux policy, all login processes will set the initial shell process security context user identifier to that matching Linux identity.
经实验证明,并非如此:

  • 定义了一个guest_u用户,登录后,其shell的安全上下文仍为:
    1ca6909a350aa84947ea565bb4c8eaa2
  • 但事实证明,明明就有guest_u这个用户
    7737fbc74190005733b9f01b60178907
    推测,可能是因为SELinux也一直在发展改进中。[1]d 成书时间是2006年,彼时还是FC(Fedora Core)5的年代,现在最新版已经是Fedora 33,2020年10月份发布。

总结

现在可以引用[1]中的一幅图来总结一下RBAC的工作原理了。
115b4d2e2576fd1b93937d66425645d5

  • Linux系统有一个用户较joe
  • SELinuxcelue中规定joe和user_r绑定,user_r和user_t绑定
  • 当joe登录时,系统根据seusers(此处与图不同,参见上一章勘误),找到对应的SELinux user,找不到就是default, default没有就不能登录了
  • 根据以下context文件顺序,决定其shell的安全上下文,即joe:user_r:user_t
    • /etc/selinux/specified-policy/contexts/users
    • /etc/selinux/specified-policy/contexts/default_contexts
    • /etc/selinux/specified-policy/contexts/failsafe_context
    • sid kernel的安全上下文
  • joe执行了一个应用,开始进行domain_transition, 要从user_t切换到passwd_t
    • 如果定义了role user_r types passwd_trole user_r types user_t,则这次domain transition可以成功
    • 否则任意一个未定义,domain transition都会失败

高阶用法

role_transition

role transition和domain transition非常类似,也是在某个domain进程在执行某个可执行文件时,即exec系统调用时,切换进程主体的安全上下文。domain transition切换的时domain,role transtion切换的是role。实现方式通过以下两条语句:

1
2
allow staff_r sysadm_r; # 允许从staff_r切换到sysadm_r
role_transition sysadm_r http_exec_t system_r; # sysadm_r进程执行http_exec_t类型文件时,role切换为system_r

role_dominance

可以利用role来定义其他role,即role dominance。

1
dominance { role super_r {role sysadm_r; role secadm_r; }

上面例子中的super_r称为dominant role,它可以从它关联的role中即成types。但它只能继承该条语句之前关联的types。我们之前说role语句是可以累积的。那么在role_dominance语句之后定义的types,并不能动态的添加到dominant role上。

相关工具

跟role相关的主要是semnage的一些用法,另外[1]还介绍了一个apol的可视化工具,可以检索user和role的数据。我理解semanage通过命令行也都可以实现。

  1. 前文介绍过的semanage user -lsemanage login -l, 前者用来查看所有SELinux的用户,后者用来查看Linux用户和SELinux用户两者的映射关系.
  2. Modify the default user on the system to the guest_u user
    • semanage login -m -s guest_u __default__
  3. 修改user和role的对应关系
    • semanage user -m -R "message_filter_r unconfined_r" user_u
  4. 添加一个用户并指定SELinux用户
    • useradd joe -Z user_u

参考文献

[1] SELinux by Example_ Using Security Enhanced Linux
[2] SELinux初始化登录用户安全上下文的方法