Product SiteDocumentation Site

14.5. SELinux 简介

14.5.1. 原理

SELinux (安全增强-Security Enhanced Linux)是一种基于Linux LSM(Linux 安全模块)的强制访问控制系统。实际上,内核在执行系统调用前会询问 SELinux 进程是否获得了执行操作的授权。
SELinux 使用一套规则-合称为策略-来授权或禁止操作。这些规则较难创建。幸好,有两种标准策略(针对-targeted严格-strict)可以避免大量的配置工作。
在 SELinux 中,权限的管理完全不同于传统的 Unix 系统。进程的权限取决于它的安全上下文。上下文由唤起该进程的用户身份定义,即该时刻用户具有的角色。权限实际上取决于域,但是在域之间转换由角色控制。角色之间的转换取决于身份。
安全上下文和 Unix 用户

图 14.1. 安全上下文和 Unix 用户

In practice, during login, the user gets assigned a default security context (depending on the roles that they should be able to endorse). This defines the current domain, and thus the domain that all new child processes will carry. If you want to change the current role and its associated domain, you must call newrole -r role_r -t domain_t (there is usually only a single domain allowed for a given role, the -t parameter can thus often be left out). This command authenticates you by asking you to type your password. This feature forbids programs to automatically switch roles. Such changes can only happen if they are explicitly allowed in the SELinux policy.
Obviously the rights do not apply to all objects (files, directories, sockets, devices, etc.). They can vary from object to object. To achieve this, each object is associated to a type (this is known as labeling). Domains' rights are thus expressed with sets of (dis)allowed operations on those types (and, indirectly, on all objects which are labeled with the given type).
By default, a program inherits its domain from the user who started it, but the standard SELinux policies expect many important programs to run in dedicated domains. To achieve this, those executables are labeled with a dedicated type (for example ssh is labeled with ssh_exec_t, and when the program starts, it automatically switches to the ssh_t domain). This automatic domain transition mechanism makes it possible to grant only the rights required by each program. It is a fundamental principle of SELinux.
域之间的自动转换

图 14.2. 域之间的自动转换

14.5.2. 设置 SELinux

SELinux 支持嵌入到 Debian 提供的标准内核中。Unix 核心工具无需修改就支持 SELinux。因此,启用 SELinux 也相对容易。
The apt install selinux-basics selinux-policy-default command will automatically install the packages required to configure an SELinux system.
The selinux-policy-default package contains a set of standard rules. By default, this policy only restricts access for a few widely exposed services. The user sessions are not restricted and it is thus unlikely that SELinux would block legitimate user operations. However, this does enhance the security of system services running on the machine. To setup a policy equivalent to the old “strict” rules, you just have to disable the unconfined module (modules management is detailed further in this section).
一旦策略安装,就要标识全部有效文件(即给它们赋予类型)。该操作必须使用 fixfiles relabel 命令手工启动。
The SELinux system is now ready. To enable it, you should add the selinux=1 security=selinux parameter to the Linux kernel. The audit=1 parameter enables SELinux logging which records all the denied operations. Finally, the enforcing=1 parameter brings the rules into application: without it SELinux works in its default permissive mode where denied actions are logged but still executed. You should thus modify the GRUB bootloader configuration file to append the desired parameters. One easy way to do this is to modify the GRUB_CMDLINE_LINUX variable in /etc/default/grub and to run update-grub. SELinux will be active after a reboot.
注意在下次启动时,用 selinux-activate 脚本自动化这些操作并强制标识(避免无标识的文件在 SELinux 未生效或正在标识的时候被创建)。

14.5.3. 管理 SELinux 系统

SELinux 策略是一系列的模块化规则集合,安装时它会基于已经安装的服务自动探测并启用相关模块。系统立即可操作。然而,如果一个服务是在 SELinux 策略之后安装的,就要手动启用相应模块了。semodule 命令可以实现该目的。还必须用 semanage 命令,定义每个用户可用的角色。
这两个命令可以用于修改当前 SELinux 存储在 /etc/selinux/default/ 的配置。不像其他配置文件,可以在 /etc/ 中的其他配置文件,这些文件不能手工修改。应当使用相应的程序来修改。

14.5.3.1. 管理 SELinux 模块

Available SELinux modules are stored in the /usr/share/selinux/default/ directory. To enable one of these modules in the current configuration, you should use semodule -i module.pp.bz2. The pp.bz2 extension stands for policy package (compressed with bzip2).
Removing a module from the current configuration is done with semodule -r module. Finally, the semodule -l command lists the modules which are currently installed. It also outputs their version numbers. Modules can be selectively enabled with semodule -e and disabled with semodule -d.
# semodule -i /usr/share/selinux/default/abrt.pp.bz2
libsemanage.semanage_direct_install_info: abrt module will be disabled after install as there is a disabled instance of this module present in the system.
# semodule -l
accountsd
acct
[...]
# semodule -e abrt
# semodule -d accountsd
# semodule -l
abrt
acct
[...]
# semodule -r abrt
libsemanage.semanage_direct_remove_key: abrt module at priority 100 is now active.semodule -l
semodule 立即加载新配置,除非使用了 -n 选项。需要注意的是在当前配置默认启用的程序(由在 /etc/selinux/config 文件中的 SELINUXTYPE 变量指明),可以使用 -s 选项来修改和储存其他的。注意不同版本支持的选项可能不同。

14.5.3.2. 管理身份

每次用户登录,就会被赋予一个 SELinux 身份。该身份定义了他们可以使用的角色。这两种映射(从用户到身份,从该身份到角色)使用 semanage 命令配置。
You should definitely read the semanage(8) manual page. All the managed concepts have their own manual page; for instance, semanage-login(8). Even if the command's syntax tends to be similar for all the concepts which are managed, it is recommended to read its manual page. You will find common options to most sub-commands: -a to add, -d to delete, -m to modify, -l to list, and -t to indicate a type (or domain).
semanage login -l 列出当用户身份和 SELinux 身份之间的映射。没有指明登记项的用户会使用 __default__ 登记项。 semanage login -a -s user_u user 命令会关联 user_u 身份到指定用户。semanage login -d user 会移除赋予用户的映射登记项。
# semanage login -a -s user_u rhertzog
# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
rhertzog             user_u               s0                   *
root                 unconfined_u         s0-s0:c0.c1023       *
# semanage login -d rhertzog
semanage user -l 列出 SELinux 用户身份和允许的角色之间的映射。添加一个新的身份需要定义相应的角色和用于给个人文件赋予类型的标识前缀(/home/user/*)。前缀必须从 userstaff,和 sysadm 之间挑选。staff 前缀生成 staff_home_dir_t 类型文件。创建新的用户身份使用 semanage user -a -R roles -P prefix identity。使用 semanage user -d identity 移除用户身份。
# semanage user -a -R 'staff_r user_r' -P staff test_u
# semanage user -l

                Labeling   MLS/       MLS/                          
SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles

root            sysadm     s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r
staff_u         staff      s0         s0-s0:c0.c1023                 staff_r sysadm_r
sysadm_u        sysadm     s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r
test_u          staff      s0         s0                             staff_r user_r
unconfined_u    unconfined s0         s0-s0:c0.c1023                 system_r unconfined_r
user_u          user       s0         s0                             user_r
# semanage user -d test_u

14.5.3.3. 管理文件上下文,端口和布尔值

每个 SELinux 模块提供了一套标识规则,但是也可以添加定制的标识规则来满足特殊情况。例如,如果想要网页服务器能读取 /srv/www/ 里面的文件,就要执行 semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"restorecon -R /srv/www/。前者注册新的标识规则,后者根据当前标识规则重置文件类型。
Similarly, TCP/UDP ports are labeled in a way that ensures that only the corresponding daemons can listen to them. For instance, if you want the web server to be able to listen on port 8080, you should run semanage port -m -t http_port_t -p tcp 8080.
有些 SELinux 模块导出布尔选项,可以用来改变默认规则。getsebool 程序可以用于检查此类选项(getsebool boolean 显示一个选项,getsebool -a 显示所有选项)。setsebool boolean value 命令改变当前布尔选项值。-P 选项使更改永久生效,就意味着新规则成为默认规则,重启之后也会保留。下面的例子授予网页服务器对用户主目录的访问权限(当用户在 ~/public_html/ 中有个人网页的时候,这是很有用的)。
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off
# setsebool -P httpd_enable_homedirs on
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on

14.5.4. 适应规则

既然 SELinux 策略是模块化的,那么为(定制化)新应用开发新模块是很有趣的。这些新模块会完善参考策略
要创建新模块,需要 selinux-policy-dev 软件包,还有 selinux-policy-doc。后者包含了标准规则的文档(/usr/share/doc/selinux-policy-doc/html/)和可用于创建新规则的模板示例文件。安装这些文件并更深入的研究:
$ cp /usr/share/doc/selinux-policy-doc/Makefile.example Makefile
$ cp /usr/share/doc/selinux-policy-doc/example.fc ./
$ cp /usr/share/doc/selinux-policy-doc/example.if ./
$ cp /usr/share/doc/selinux-policy-doc/example.te ./
The .te file is the most important one. It defines the rules. The .fc file defines the “file contexts”, that is the types assigned to files related to this module. The data within the .fc file are used during the file labeling step. Finally, the .if file defines the interface of the module: it is a set of “public functions” that other modules can use to properly interact with the module that you're creating.

14.5.4.1. 写一个 .fc 文件

阅读下面的例子足可以理解此类文件的结构。可以使用正则表达式给多个文件,甚至是整个目录树赋予相同的安全上下文。

例 14.2. example.fc 文件

# myapp executable will have:
# label: system_u:object_r:myapp_exec_t
# MLS sensitivity: s0
# MCS categories: <none>

/usr/sbin/myapp         --      gen_context(system_u:object_r:myapp_exec_t,s0)

14.5.4.2. 写一个 .if 文件

在下面的例子中,第一个接口(myapp_domtrans)控制谁可以执行程序。第二个(myapp_read_log)授予程序日志文件的读权限。
每个接口必须有一套规则并嵌入到 .te 文件。因此必须声明所有使用的类型(使用 gen_require 宏),并使用标准指令授予权限。也可以使用其他模块提供的接口。如何表示这些权限将在下节给出更详细的解释。

例 14.3. example.if 文件

## <summary>Myapp example policy</summary>
## <desc>
##      <p>
##              More descriptive text about myapp.  The <desc>
##              tag can also use <p>, <ul>, and <ol>
##              html tags for formatting.
##      </p>
##      <p>
##              This policy supports the following myapp features:
##              <ul>
##              <li>Feature A</li>
##              <li>Feature B</li>
##              <li>Feature C</li>
##              </ul>
##      </p>
## </desc>
#

########################################
## <summary>
##      Execute a domain transition to run myapp.
## </summary>
## <param name="domain">
##      Domain allowed to transition.
## </param>
#
interface(`myapp_domtrans',`
        gen_require(`
                type myapp_t, myapp_exec_t;
        ')

        domtrans_pattern($1,myapp_exec_t,myapp_t)
')

########################################
## <summary>
##      Read myapp log files.
## </summary>
## <param name="domain">
##      Domain allowed to read the log files.
## </param>
#
interface(`myapp_read_log',`
        gen_require(`
                type myapp_log_t;
        ')

        logging_search_logs($1)
        allow $1 myapp_log_t:file r_file_perms;
')

14.5.4.3. 写一个 .te 文件

看一看 example.te 文件:
policy_module(myapp,1.0.0) 1

########################################
#
# Declarations
#

type myapp_t; 2
type myapp_exec_t;
domain_type(myapp_t)
domain_entry_file(myapp_t, myapp_exec_t) 3

type myapp_log_t;
logging_log_file(myapp_log_t) 4

type myapp_tmp_t;
files_tmp_file(myapp_tmp_t)

########################################
#
# Myapp local policy
#

allow myapp_t myapp_log_t:file { read_file_perms append_file_perms }; 5

allow myapp_t myapp_tmp_t:file manage_file_perms;
files_tmp_filetrans(myapp_t,myapp_tmp_t,file)

1

模块必须通过名字和版本号标识。此指令是必须的。

2

如果模块引入了新类型,它们必须使用类似的指令声明。不要害怕需要创建的类型过多,这总胜过赋予过多无用权限。

3

这些接口定义 myapp_t 类型为进程域,它可以被标识为 myapp_exec_t 的可执行程序使用。这隐式地给这些对象添加 exec_type 属性,反过来允许其他模块授予权限执行这些程序:例如,userdomain 模块允许具有 user_tstaff_t,和 sysadm_t 域的进程执行。其他受限域将无权运行,除非规则赋予它们类似的权限(在此例中,dpkg 和域 dpkg_t)。

4

logging_log_file is an interface provided by the reference policy. It indicates that files labeled with the given type are log files which ought to benefit from the associated rules (for example, granting rights to logrotate so that it can manipulate them).

5

allow 指令是授予操作权限的基本指令。第一个参数是允许执行操作的进程域。第二个定义前面的域进程可以操作的对象。参数的形式是:“type:class”此处 type 是 SELinux 类型,class 描述对象(文件,目录,套接字,fifo,等等)属性。最后的参数描述许可权限(即允许的操作)。
Permissions are defined as the set of allowed operations and follow this template: { operation1 operation2 }. However, you can also use macros representing the most useful permissions. The /usr/share/selinux/devel/include/support/obj_perm_sets.spt lists them.
The following web page provides a relatively exhaustive list of object classes, and permissions that can be granted.
现在需要找到能确保目标程序或服务正常工作的最小规则集合。要做到这一点,就要很好的理解应用程序是如何工作的,它需要处理或生成什么样的数据。
然而,实验方法也可以。一旦相关的对象被正确标识,可以在许可模式下使用应用程序:应该禁止的操作会被记录但是仍然会运行。通过分析日志,就能识别那些操作应该允许。这里有一个此类日志条目示例:
avc:  denied  { read write } for  pid=1876 comm="syslogd" name="xconsole" dev=tmpfs ino=5510 scontext=system_u:system_r:syslogd_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=fifo_file permissive=1
为了更好的理解这条信息,我们逐一分析。

表 14.1. SELinux 追踪记录分析

消息描述
avc: denied一个操作被拒绝。
{ read write }该操作需要 读-read写-write 许可。
pid=1876进程号为1876的进程执行的操作(或者试图执行)。
comm="syslogd"该进程是 syslogd 程序的一个实例。
name="xconsole"The target object was named xconsole. Sometimes you can also have a “path” variable — with the full path — instead.
dev=tmpfsThe device hosting the target object is a tmpfs (an in-memory filesystem). For a real disk, you could see the partition hosting the object (for example, “sda3”).
ino=5510对象的节点(inode)号码是5510。
scontext=system_u:system_r:syslogd_t:s0这是执行操作进程的安全上下文。
tcontext=system_u:object_r:device_t:s0这是执行操作对象的安全上下文。
tclass=fifo_file目标对象是一个 FIFO 文件。
By observing this log entry, it is possible to build a rule that would allow this operation. For example, allow syslogd_t device_t:fifo_file { read write }. This process can be automated, and it is exactly what the audit2allow command (of the policycoreutils package) offers. This approach is only useful if the various objects are already correctly labeled according to what must be confined. In any case, you will have to carefully review the generated rules and validate them according to your knowledge of the application. Effectively, this approach tends to grant more rights than are really required. The proper solution is often to create new types and to grant rights on those types only. It also happens that a denied operation isn't fatal to the application, in which case it might be better to just add a “dontaudit” rule to avoid the log entry despite the effective denial.

14.5.4.4. 编译文件

Once the 3 files (example.if, example.fc, and example.te) match your expectations for the new rules, rename them to myapp.extension and run make NAME=devel to generate a module in the myapp.pp file (you can immediately load it with semodule -i myapp.pp). If several modules are defined, make will create all the corresponding .pp files.