红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 专栏
  3. Linux之旅
  4. 正文

Linux 之旅 16:SELinux 初探

2021年8月30日 1377点热度 0人点赞 0条评论

image-20210830104026067

图源:pexels

什么是SELinux

SELinux全称Security Enhanced LInux,它是一个由NSA(美国国家安全局)发起的,旨在增强Linux安全特性的一个额外的内核模块。

最初SELinux诞生的原因是NSA发现虽然Linux本身的权限管理已经做的很不错了,如果是有经验的系统管理员负责Linux主机的日常使用和维护,并不会出现致命的漏洞。但是如果是一般用户进行管理和维护,很可能就不是那么一回事了,比如之前不是很懂Linux的时候,我就随意更改测试服务器上的文件权限,尤其是为了解决权限问题,经常会把代码文件的权限设置为777,这显然是一种错误和会带来潜在安全风险的操作。

为了解决这个问题,NSA在原来Linux的文件权限控制的基础之上,增加了一层安全特性,以为文件权限管理提供额外的安全保护,这就是SELinux。

更多关于SELinux的内容可以阅读 http://www.nsa.gov/research/selinux/

事实上,对于文件系统的权限管理,大致可以分为两种不同的形式:

  • DAC

    传统对文件系统的权限管理方式为DAC(Discretionary Access Control,自主式存取控制),就是由进程的拥有者对文件的访问权限RWX决定是否有相应的操作权限。这种方式有几个缺点:

    • root有最高权限,可以随意执行任何操作。如果使用root账号的人经验丰富就是一种优点,但如果不是,则可能会因为某个疏忽的操作带来一些潜在的风险。

    • 可以通过命令(如chmod)很容易地改变资源的权限。和上边的原因一样,可能会带来潜在风险。

  • MAC

    SELinux使用的是MAC(mandatory access control,强制访问控制),在这种访问控制模式下,限制的主体会从用户变为进程或线程,限制的对象是文件、目录、端口等资源。主体和对象各自具有一组安全属性,每当需要进行授权检测时,操作系统会检查主体和对象的安全属性,以决定是否应当授权。

    这样就会避免上边的问题,即使是root用户,即使将目录修改为777,只要SELinux处于启用状态,依然可以杜绝某些安全漏洞。

    更多关于MAC的背景内容可以阅读强制访问控制

MAC和DAC可以用下边的图表表示:

使用 DAC/MAC 產生的不同結果,以 Apache 為例說明

图源:鸟哥的私房菜

SELinux的运作模式

SELinux由这几部分组成:

  • 主体(Subject)

    SELinux中需要管理(限制)的主体是进程或线程。

  • 目标(Object)

    目标就是文件系统上的资源,比如文件、目录、端口等。

  • 策略(Policy)

    由于操作系统中存在大量的进程和文件,无法一一管理,所以SELinux通过设定一些策略来进行统一管理,由策略决定哪些进程需要管理,以及如何管理。一个策略中会有多条规则,每条规则可以关闭或开启。策略通过这些规则来详细规定某个服务是否对某些资源具有相关权限。

    规则是模块化和可扩展的,新安装的程序可以通过添加新模块的方式添加新的规则。同样的,用户也可以手动添加或删除规则。

    在CentOS7中,默认提供三个策略:

    • targeted:默认使用的策略,针对网络服务限制较多,对于本地服务限制较少。

    • minimum:以targeted为基础,仅针对选定的网络服务进行限制。

    • mls:多级安全保护,会对所有进程进行限制。是最严格最完整的SELinux规则,安全度最高,但不易使用。

  • 安全上下文(Security Context)

    安全上下文分为两部分:主体的和目标的,只有主体和目标的安全上下文对应上,才能具有相应的操作权限。

上面几个组成部分之间的关系可以用下边的图表说明:

SELinux 運作的各元件之相關性

图源:鸟哥的私房菜

整个流程是这样的:

  1. 进程会请求SELinux赋予访问权限

  2. SELinux会在当前使用的策略中查找进程对应的规则,如果有,就按照规则去检查安全上下文

  3. 如果安全上下文能对应上,SELinux层面就会放行,进程就可以按照Linux上传统的RWX方式判断是否有操作权限。如果对应不上,SELinux就会拒绝访问。

安全上下文

使用命令ls -Z可以查看文件和目录的安全上下文:

[icexmoon@xyz ~]$ ls -Z
drwxrwxr-x. icexmoon icexmoon unconfined_u:object_r:home_bin_t:s0 bin
drwxrwxr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 shell_scripts
-rw-rw-r--. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 test.py
-rw-rw-r--. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 tmp.bak.log
-rw-rw-r--. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 tmp.tar
-rw-rw-r--. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 tmp.tar.log
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 公共
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 模板
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 视频
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 图片
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 文档
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 下载
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 音乐
drwxr-xr-x. icexmoon icexmoon unconfined_u:object_r:user_home_t:s0 桌面

其中unconfined_u:object_r:home_bin_t:s0就是SELinux中目标的上下文,字段以:分隔,含义为:

  • user(用户):

    主要分为两类:system_u为系统服务创建的文件,unconflined_u为用户进程创建的文件。

  • role(角色):

    均为object_r表示一个文件

  • type(类型):

    这是在targeted模式下唯一需要关注的字段,只有主体的类型和目标的类型对应上,才会认为安全上下文匹配上了,进而赋予权限。

进程的安全上下文

类似的,我们也可以查看进程的安全上下文字段:

[icexmoon@xyz ~]$ ps -eZ
LABEL                              PID TTY          TIME CMD
system_u:system_r:init_t:s0          1 ?        00:00:04 systemd
system_u:system_r:kernel_t:s0        2 ?        00:00:00 kthreadd
system_u:system_r:kernel_t:s0        4 ?        00:00:00 kworker/0:0H
system_u:system_r:kernel_t:s0        6 ?        00:00:01 ksoftirqd/0
system_u:system_r:kernel_t:s0        7 ?        00:00:00 migration/0
system_u:system_r:kernel_t:s0        8 ?        00:00:00 rcu_bh
system_u:system_r:kernel_t:s0        9 ?        00:00:01 rcu_sched
system_u:system_r:kernel_t:s0       10 ?        00:00:00 lru-add-drain
system_u:system_r:kernel_t:s0       11 ?        00:00:00 watchdog/0
system_u:system_r:kernel_t:s0       13 ?        00:00:00 kdevtmpfs
system_u:system_r:kernel_t:s0       14 ?        00:00:00 netns
system_u:system_r:kernel_t:s0       15 ?        00:00:00 khungtaskd
...省略...
system_u:system_r:kernel_t:s0     2265 ?        00:00:01 kworker/0:2
system_u:system_r:kernel_t:s0     2307 ?        00:00:00 kworker/0:3
system_u:system_r:sshd_t:s0-s0:c0.c1023 2308 ?  00:00:00 sshd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2316 ? 00:00:00 sshd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2317 pts/0 00:00:00 bash
system_u:system_r:abrt_t:s0-s0:c0.c1023 2339 ?  00:00:00 abrt-dbus
system_u:system_r:ksmtuned_t:s0   2388 ?        00:00:00 sleep
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2389 pts/0 00:00:00 ps

主体(进程)的上下文字段与目标的类似:

  • user(用户):

    system_u为系统服务进程,受到限制。unconfined_u为不会受到限制的进程,通常为用户开启的(如bash)。

  • role(角色):

    system_r为系统服务进程,受到限制。unconfined_r为不会受到限制的进程,通常为用户开启的。

  • type(类型):

    这是在targeted模式下唯一需要关注的字段,只有主体的类型和目标的类型对应上,才会认为安全上下文匹配上了,进而赋予权限。

可以使用crond这个服务进程进行说明:

[icexmoon@xyz ~]$ ps -eZ | grep cron
system_u:system_r:crond_t:s0-s0:c0.c1023 1257 ? 00:00:01 crond
system_u:system_r:crond_t:s0-s0:c0.c1023 1259 ? 00:00:00 atd
[icexmoon@xyz ~]$ ll -dZ /usr/sbin/crond /etc/crontab /etc/cron.d /var/spool/cron
drwxr-xr-x. root root system_u:object_r:system_cron_spool_t:s0 /etc/cron.d
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 /etc/crontab
-rwxr-xr-x. root root system_u:object_r:crond_exec_t:s0 /usr/sbin/crond
drwx------. root root system_u:object_r:user_cron_spool_t:s0 /var/spool/cron

可以看到,crond的type是crond_t,而理论上crond会操作的相关文件(/etc/crontab)等的相关type是system_cron_spool_t和crond_exec_t,所以在SELinux中必然有相关规则可以让crond_t这个类型对应到system_cron_spool_t和crond_exec_t这两个类型上。

这个过程可以用下图进行表示:

主體程序取得的 domain 與目標檔案資源的 type 相互關係

图源:鸟哥的私房菜

这可以通过下面的实验进行测试:

[root@xyz ~]# cd ~
[root@xyz ~]# vim checktime
[root@xyz ~]# cat checktime
10 * * * * root sleep 60s
[root@xyz ~]# mv checktime /etc/cron.d
[root@xyz ~]# ll -Z /etc/cron.d
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 0hourly
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 checktime
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 raid-check
-rw-------. root root system_u:object_r:system_cron_spool_t:s0 sysstat
[root@xyz ~]# systemctl restart crond
[root@xyz ~]# tail /var/log/cron
Aug 30 12:30:02 xyz CROND[2107]: (root) CMD (/usr/lib64/sa/sa1 1 1)
Aug 30 15:30:01 xyz CROND[2247]: (root) CMD (/usr/lib64/sa/sa1 1 1)
Aug 30 15:40:01 xyz CROND[2420]: (root) CMD (/usr/lib64/sa/sa1 1 1)
Aug 30 15:50:02 xyz CROND[2583]: (root) CMD (/usr/lib64/sa/sa1 1 1)
Aug 30 15:51:00 xyz crond[1257]: (CRON) INFO (Shutting down)
Aug 30 15:51:00 xyz crond[2610]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 21% if used.)
Aug 30 15:51:01 xyz crond[2610]: ((null)) Unauthorized SELinux context=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 file_context=unconfined_u:object_r:admin_home_t:s0 (/etc/cron.d/checktime)
Aug 30 15:51:01 xyz crond[2610]: (root) FAILED (loading cron table)
Aug 30 15:51:01 xyz crond[2610]: (CRON) INFO (running with inotify support)
Aug 30 15:51:01 xyz crond[2610]: (CRON) INFO (@reboot jobs will be run at computer's startup.)

日志中说的很清楚,因为主体(crond这个服务进程)的类型system_cronjob_t和目标(/etc/cron.d/checktime这个配置文件)的类型admin_home_t对应不上,所以不能通过SELinux授权。

这是因为使用mv移动文件时会保留了原来的安全上下文而非/etc/cron.d目录应该有的安全上下文。

模式的启动、关闭与查看

SELinux共有3种模式:

  • Enforcing:强制模式,表示SELinux正常运行中,会对不符合安全上下文的授权请求进行限制

  • Permissive:宽容模式,虽然SELinux运行,但是对于不和要求的授权请求,仅会产生警告信息,不会进行实际限制。

  • Disabled:关闭模式。

这3种模式可以用下图表示:

SELinux 的三種類型與實際運作流程圖示意

图源:鸟哥的私房菜

如果需要查看当前系统的SELinux处于何种模式:

[root@xyz ~]# getenforce
Enforcing

要查看更多SELinux的相关信息:

[root@xyz ~]# sestatus
SELinux status:                 enabled # 已启用
SELinuxfs mount:                /sys/fs/selinux # selinux相关文件挂载点
SELinux root directory:         /etc/selinux # selinux根目录
Loaded policy name:             targeted # 加载的策略
Current mode:                   enforcing # 当前模式
Mode from config file:          enforcing # 模式对应的配置文件
Policy MLS status:              enabled
Policy deny_unknown status:     allowed # 是否阻止未知主体(进程)
Max kernel policy version:      31

SELinux的配置文件为/etc/selinux/config:

[root@xyz ~]# cat /etc/selinux/config
​
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing # 运行模式
# SELINUXTYPE= can take one of three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted # 策略

可以在配置文件种修改SELinux的运行模式和加载的策略。

SELinux 的启动与关闭

如果修改了SELInux加载的策略,则需要重新启动系统,如果将运行模式从Enforcing或Permissive修改为Disabled(或者反过来),同样需要重启系统。这是因为SELinux是整合在Linux系统内核中的。

此外,如果是从Disabled修改为Enforcing或Permissive,则在重启系统时SELinux需要花费比较长的一段时间重写系统中所需的SELinux安全上下文,而且在重写完毕后会自动重新启动系统。

SELinux在Enforcing和Permissive之间切换则不需要重启,只需要:

[root@xyz ~]# setenforce 0
[root@xyz ~]# getenforce
Permissive
[root@xyz ~]# setenforce 1
[root@xyz ~]# getenforce
Enforcing

0表示Permissive,1表示Enforcing。

在某些情况下,将SELinux从Disabled切换到Enforcing,可能会导致出现/lib/xxx中文件没有读写权限,进而导致系统重启失败。这是因为重启时SELinux重写安全上下文时缺少权限出错导致。这时候需要将SELinux切换到Permissve,并使用restorecon -Rv重写相关目录的安全上下文。

策略内的规则管理

getsebool

getsebool可以用来查询SELinux中各个规则的bool值:

[root@xyz ~]# getsebool -a
abrt_anon_write --> off
abrt_handle_event --> off
abrt_upload_watch_anon_write --> on
antivirus_can_scan_system --> off
antivirus_use_jit --> off
auditadm_exec_content --> on
authlogin_nsswitch_use_ldap --> off
authlogin_radius --> off
authlogin_yubikey --> off
...省略

-a参数可以输出所有规则的bool值。

seinfo

这个工具CentOS7没有默认安装,需要先安装:

[root@xyz ~]# yum install setools-console-*
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 ...省略

seinfo可以查询SELinux相关的概况信息,:

[root@xyz ~]# seinfo

Statistics for policy file: /sys/fs/selinux/policy
Policy Version & Type: v.31 (binary, mls)

   Classes:           130    Permissions:       272
   Sensitivities:       1    Categories:       1024
   Types:            4793    Attributes:        253
   Users:               8    Roles:              14
   Booleans:          316    Cond. Expr.:       362
   Allow:          107834    Neverallow:          0
   Auditallow:        158    Dontaudit:       10022
   Type_trans:      18153    Type_change:        74
   Type_member:        35    Role allow:         37
   Role_trans:        414    Range_trans:      5899
   Constraints:       143    Validatetrans:       0
   Initial SIDs:       27    Fs_use:             32
   Genfscon:          103    Portcon:           614
   Netifcon:            0    Nodecon:             0
   Permissives:         0    Polcap:              5

输出的信息中Types: 4793表明当前SELinux中有4793个安全上下文类型,Booleans: 316表明有316条规则。

sesearch

sesearch命令可以在SELinux加载的规则中检索具体的规则,并输出:

[root@xyz ~]# sesearch -A -s crond_t | grep spool
   allow crond_t system_cron_spool_t : dir { ioctl read getattr lock search open } ;
   allow crond_t var_spool_t : file { ioctl read getattr lock open } ;
   allow crond_t cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow crond_t cron_spool_t : dir { ioctl read write getattr lock add_name remove_name search open } ;
   allow crond_t user_cron_spool_t : dir { ioctl read write getattr lock add_name remove_name search open } ;
   allow daemon user_cron_spool_t : file { ioctl read write getattr lock append } ;
   allow crond_t var_spool_t : dir { ioctl read getattr lock search open } ;
   allow crond_t system_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow crond_t user_cron_spool_t : lnk_file { read getattr } ;
   allow crond_t user_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow crond_t system_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow crond_t user_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
[root@xyz ~]# ll /etc/cron.d/checktime

可以看到,这里的规则的确表明主体类型为crond_t的进程可以操作cron_spool_t类型的文件(read write create)。

这里-s参数表明在查询主体类型(subject),对应的,如果要查询目标类型,使用-t(target),如果要查询某个bool规则,使用-b(boolean)。-A(allow)参数用于查询允许规则。

下面我们看允许规则中有没有之前创建的配置文件checktime对应的读写权限:

[root@xyz ~]# ll -Z /etc/cron.d/checktime
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 /etc/cron.d/checktime
[root@xyz ~]# sesearch -A -s crond_t | grep admin_home_t
   allow crond_t admin_home_t : lnk_file { read getattr } ;
   allow domain admin_home_t : dir { getattr search open } ;
   allow userdom_filetrans_type admin_home_t : lnk_file { read getattr } ;
   allow domain admin_home_t : lnk_file { read getattr } ;
   allow crond_t admin_home_t : dir { ioctl read getattr lock search open } ;
   allow userdom_filetrans_type admin_home_t : dir { ioctl read write getattr lock add_name remove_name search open } ;

可以看到,只有dir和lnk_file的相关规则,并没有file的规则,也就是说crond并不能操作类型为admin_home_t的文件。

再看一个例子,这个例子用于查看httpd_enable_homedirs这个bool规则相关的信息:

[root@xyz ~]# getsebool -a | grep httpd_enable_home
httpd_enable_homedirs --> off
[root@xyz ~]# semanage boolean -l | grep httpd_enable_homedirs
httpd_enable_homedirs          (关    ,    关)  Allow httpd to enable homedirs
[root@xyz ~]# sesearch -A -b httpd_enable_homedirs
Found 77 semantic av rules:
   allow httpd_t user_home_dir_t : lnk_file { read getattr } ;
   allow httpd_suexec_t user_home_dir_t : dir { getattr search open } ;
   allow httpd_t nfs_t : lnk_file { read getattr } ;
   allow httpd_sys_script_t nfs_t : file { ioctl read getattr lock open } ;
   allow httpd_sys_script_t cifs_t : lnk_file { read getattr } ;
   allow httpd_suexec_t user_home_dir_t : lnk_file { read getattr } ;
   allow httpd_t cifs_t : file { ioctl read getattr lock open } ;
   ...省略

setsebool

setsebool可以修改bool规则的值:

[root@xyz ~]# setsebool -P httpd_enable_homedirs 1
[root@xyz ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on
[root@xyz ~]# setsebool -P httpd_enable_homedirs 0
[root@xyz ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off

-P(policy)参数用于将相关数据写入规则文件,所以修改的时候最好加上这个参数。

安全上下文的修改

在涉及到SELinux相关的问题的时候,我们需要先考虑SELinux的运行模式,再考虑加载的策略,以及具体的规则是否放行,最后还要考虑安全上下文是否匹配。

chcon

使用chcon(change context)可以修改指定文件的安全上下文类型:

[root@xyz ~]# ll -Z /etc/hosts
-rw-r--r--. root root system_u:object_r:net_conf_t:s0  /etc/hosts
[root@xyz ~]# chcon -vt net_conf_t /etc/cron.d/checktime
正在更改"/etc/cron.d/checktime" 的安全环境
[root@xyz ~]# ll -Z /etc/cron.d/checktime
-rw-r--r--. root root unconfined_u:object_r:net_conf_t:s0 /etc/cron.d/checktime

-v参数可以输出修改结果,-t参数表明是要修改安全上下文类型。

除了手动填写安全上下文类型,还可以指定一个文件,直接修改成和文件一样的安全上下文信息:

[root@xyz ~]# chcon -v --reference=/etc/shadow /etc/cron.d/checktime
正在更改"/etc/cron.d/checktime" 的安全环境
[root@xyz ~]# ll -Z /etc/shadow /etc/cron.d/checktime
-rw-r--r--. root root system_u:object_r:shadow_t:s0    /etc/cron.d/checktime
----------. root root system_u:object_r:shadow_t:s0    /etc/shadow

参数--reference用于指定作为参照的文件。

需要注意的是这里不仅类型,其它的安全上下文字段也被修改了。

restorecon

使用restorecon(restore context)可以“自动修复”文件的安全上下文信息:

[root@xyz ~]# restorecon -Rv /etc/cron.d
restorecon reset /etc/cron.d/checktime context system_u:object_r:shadow_t:s0->system_u:object_r:system_cron_spool_t:s0
[root@xyz ~]# ll -Z /etc/cron.d
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 0hourly
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 checktime
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 raid-check
-rw-------. root root system_u:object_r:system_cron_spool_t:s0 sysstat
[root@xyz ~]# systemctl restart crond
[root@xyz ~]# tail /var/log/cron
Aug 30 17:01:01 xyz run-parts(/etc/cron.hourly)[3532]: starting 0anacron
Aug 30 17:01:01 xyz run-parts(/etc/cron.hourly)[3544]: finished 0anacron
Aug 30 17:01:01 xyz run-parts(/etc/cron.hourly)[3532]: starting mcelog.cron
Aug 30 17:01:01 xyz run-parts(/etc/cron.hourly)[3550]: finished mcelog.cron
Aug 30 17:02:02 xyz crond[2610]: ((null)) Unauthorized SELinux context=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 file_context=system_u:object_r:shadow_t:s0 (/etc/cron.d/checktime)
Aug 30 17:02:02 xyz crond[2610]: (root) FAILED (loading cron table)
Aug 30 17:09:03 xyz crond[2610]: (CRON) INFO (Shutting down)
Aug 30 17:09:03 xyz crond[3662]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 20% if used.)
Aug 30 17:09:04 xyz crond[3662]: (CRON) INFO (running with inotify support)
Aug 30 17:09:04 xyz crond[3662]: (CRON) INFO (@reboot jobs will be run at computer's startup.)

参数-R用于递归处理子目录和子文件,类似于rm -r,-v用于输出相关信息。

可以看到,使用restorecon命令后checktime文件的安全上下文信息和其它同级文件一样了,而且crond服务进程也可以正常重启了。

semanage

之所以restorecon命令可以“自动恢复”错乱的SELinux安全上下文信息,是因为系统中保存了相关目录下默认的安全上下文信息应该是什么的规则,可以使用semanage命令查看:

[root@xyz ~]# semanage fcontext -l | grep -E '^/etc |^/etc/cron*'
...省略
/etc/cron\.d(/.*)?                                 all files          system_u:object_r:system_cron_spool_t:s0
/etc/cron\.daily/[sm]locate                        regular file       system_u:object_r:locate_exec_t:s0
/etc/cron\.weekly/(c)?fingerd                      regular file       system_u:object_r:fingerd_exec_t:s0
...省略

这里显示的很清楚,对于符合/etc/cron\.d(/.*)?这个路径规则(即/etc/cron.d目录下的所有文件和子目录),其安全上下文默认都是system_u:object_r:system_cron_spool_t:s0。

类似的,如果我们希望我们创建的某个目录下的文件建立的时候也有一个默认的安全上下文,则也需要通过semanage添加相应的规则:

[root@xyz ~]# mkdir /srv/mycron
[root@xyz ~]# ll -dZ /srv /srv/mycron
drwxr-xr-x. root root system_u:object_r:var_t:s0       /srv
drwxr-xr-x. root root unconfined_u:object_r:var_t:s0   /srv/mycron
[root@xyz ~]# semanage fcontext -l | grep -E '^/srv |^/srv/.*'
/srv/.*                                            all files          system_u:object_r:var_t:s0
/srv/([^/]*/)?www(/.*)?                            all files          system_u:object_r:httpd_sys_content_t:s0
/srv/([^/]*/)?ftp(/.*)?                            all files          system_u:object_r:public_content_t:s0
/srv/([^/]*/)?rsync(/.*)?                          all files          system_u:object_r:public_content_t:s0
/srv/([^/]*/)?www/logs(/.*)?                       all files          system_u:object_r:httpd_log_t:s0
/srv/node(/.*)?                                    all files          system_u:object_r:swift_data_t:s0
/srv/gallery2(/.*)?                                all files          system_u:object_r:httpd_sys_content_t:s0
/srv/lib/gitosis(/.*)?                             all files          system_u:object_r:gitosis_var_lib_t:s0
/srv/gallery2/smarty(/.*)?                         all files          system_u:object_r:httpd_sys_rw_content_t:s0
/srv/loopback-device(/.*)?                         all files          system_u:object_r:swift_data_t:s0
/srv                                               all files          system_u:object_r:var_t:s0
[root@xyz ~]# semanage fcontext -a -t system_cron_spool_t "/srv/mycron(/.*)?"
[root@xyz ~]# semanage fcontext -l | grep -E '^/srv |^/srv/.*'
/srv/.*                                            all files          system_u:object_r:var_t:s0
/srv/([^/]*/)?www(/.*)?                            all files          system_u:object_r:httpd_sys_content_t:s0
/srv/([^/]*/)?ftp(/.*)?                            all files          system_u:object_r:public_content_t:s0
/srv/([^/]*/)?rsync(/.*)?                          all files          system_u:object_r:public_content_t:s0
/srv/([^/]*/)?www/logs(/.*)?                       all files          system_u:object_r:httpd_log_t:s0
/srv/node(/.*)?                                    all files          system_u:object_r:swift_data_t:s0
/srv/gallery2(/.*)?                                all files          system_u:object_r:httpd_sys_content_t:s0
/srv/lib/gitosis(/.*)?                             all files          system_u:object_r:gitosis_var_lib_t:s0
/srv/gallery2/smarty(/.*)?                         all files          system_u:object_r:httpd_sys_rw_content_t:s0
/srv/loopback-device(/.*)?                         all files          system_u:object_r:swift_data_t:s0
/srv                                               all files          system_u:object_r:var_t:s0
/srv/mycron(/.*)?                                  all files          system_u:object_r:system_cron_spool_t:s0
[root@xyz ~]# cp /etc/cron.d/checktime /srv/mycron
[root@xyz ~]# ll -Z /srv/mycron
-rw-r--r--. root root unconfined_u:object_r:var_t:s0   checktime
[root@xyz ~]# restorecon -Rv /srv/mycron
restorecon reset /srv/mycron context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:system_cron_spool_t:s0
restorecon reset /srv/mycron/checktime context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:system_cron_spool_t:s0
[root@xyz ~]# ll -Z /srv/mycron
-rw-r--r--. root root unconfined_u:object_r:system_cron_spool_t:s0 checktime

其中-a参数用于添加规则,-m为修改规则,-d为删除规则。

在示例中,虽然已经添加了规则,但拷贝文件到/srv/mycron下的时候安全上下文并非我们希望的安全上下文,这是因为/srv/mycron目录本身的安全上下文不对,所以需要使用restorecon命令修复。

一个网络服务案例

遇到SELinux相关的问题,需要查看相关的日志。SELinux使用auditd与setroubleshootd两个服务产生错误日志。

setroubleshoot

setroubleshootd服务会将SELinux的相关错误及解决方法记录到/var/log/messages与/var/log/setroubleshoot/*中,这个服务需要两个应用:setroubleshoot与setroubleshoot-server。

在CentOS 6.X之前,SELinux的相关信息由auditd与setroubled两个进程记录,在CentOS 6.X之后,都整合到了auditd这个服务中,所以安装setroubleshoot-server之后需要重启auditd服务。

如果要确认系统中有没有安装setroubleshoot应用,可以:

[root@xyz ~]# rpm -qa | grep setroubleshoot
setroubleshoot-3.2.30-8.el7.x86_64
setroubleshoot-plugins-3.0.67-4.el7.noarch
setroubleshoot-server-3.2.30-8.el7.x86_64
[root@xyz ~]#

实例:FTP服务器

这里通过一个FTP服务来说明SELinux问题排查。

先创建一个给FTP服务使用的账号:

[root@xyz ~]# useradd -s /sbin/nologin ftptest
[root@xyz ~]# echo 'password' | passwd --stdin ftptest
更改用户 ftptest 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@xyz ~]# grep ftptest /etc/passwd
ftptest:x:1513:1513::/home/ftptest:/sbin/nologin

下面安装FTP软件vsftpd:

[root@xyz ~]# yum install vsftpd
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
 ...省略

启动服务:

[root@xyz ~]# systemctl restart vsftpd
[root@xyz ~]# systemctl enable vsftpd
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd.service to /usr/lib/systemd/system/vsftpd.service.
[root@xyz ~]# systemctl status vsftpd
● vsftpd.service - Vsftpd ftp daemon
   Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; enabled; vendor preset: disabled)
   Active: active (running) since 一 2021-08-30 17:48:04 CST; 33s ago
 Main PID: 4135 (vsftpd)
   CGroup: /system.slice/vsftpd.service
           └─4135 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf

8月 30 17:48:04 xyz.icexmoon.centos systemd[1]: Starting Vsftpd ftp daemon...
8月 30 17:48:04 xyz.icexmoon.centos systemd[1]: Started Vsftpd ftp daemon.
[root@xyz ~]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      1458/dnsmasq
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1234/sshd
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      1240/cupsd
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1562/master
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      733/rpcbind
tcp6       0      0 :::21                   :::*                    LISTEN      4135/vsftpd
tcp6       0      0 :::22                   :::*                    LISTEN      1234/sshd
tcp6       0      0 ::1:631                 :::*                    LISTEN      1240/cupsd
tcp6       0      0 ::1:25                  :::*                    LISTEN      1562/master
tcp6       0      0 :::111                  :::*                    LISTEN      733/rpcbind

可以看到vsftpd服务已经开始监听21端口了。

往ftp的公共目录拷贝两个文件作为测试文件:

[root@xyz ~]# cp -a /etc/securetty /etc/sysctl.conf /var/ftp/pub
[root@xyz ~]# ll /var/ftp/pub
总用量 8
-rw-------. 1 root root 221 4月   1 2020 securetty
-rw-r--r--. 1 root root 449 10月 13 2020 sysctl.conf

下面进行一些匿名用户访问测试:

[root@xyz ~]# cp -a /etc/securetty /etc/sysctl.conf /var/ftp/pub
[root@xyz ~]# ll /var/ftp/pub
总用量 8
-rw-------. 1 root root 221 4月   1 2020 securetty
-rw-r--r--. 1 root root 449 10月 13 2020 sysctl.conf
[root@xyz ~]# clear
[root@xyz ~]# curl ftp://localhost
drwxr-xr-x    2 0        0              42 Aug 30 09:55 pub
[root@xyz ~]# curl ftp://localhost/pub/
-rw-------    1 0        0             221 Apr 01  2020 securetty
-rw-r--r--    1 0        0             449 Oct 13  2020 sysctl.conf
[root@xyz ~]# curl ftp://localhost/pub/sysctl.conf
# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
[root@xyz ~]# curl ftp://localhost/pub/securetty
curl: (78) RETR response: 550
[root@xyz ~]# chmod a+r /var/ftp/pub/securetty
[root@xyz ~]# curl ftp://localhost/pub/securetty
console
vc/1
vc/2
vc/3
vc/4
vc/5

无法从家目录下载文件的问题分析与解决

在实际操作之前,需要先修改一个vsftpd的pam模块配置:

[root@xyz ~]# vim /etc/pam.d/vsftpd
[root@xyz ~]# cat /etc/pam.d/vsftpd
#%PAM-1.0
session    optional     pam_keyinit.so    force revoke
auth       required     pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed
#auth       required    pam_shells.so
auth       required     pam_nologin.so
auth       include      password-auth
account    include      password-auth
session    required     pam_loginuid.so
session    include      password-auth
[root@xyz ~]# systemctl restart vsftpd

需要将其中的pam_shells.so这个pam模块替换为pam_nologin.so模块,详细原因可以参考ubuntu vsftpd 530 Login incorrect 根本原因和解决方案。

经过测试发现,CentOS 7.9版本已经修复了这个问题,ftp用户是可以正常查看和下载自己家目录的文件的,所以就没有然后了。

一般账号从非正规目录上传/下载文件

[root@xyz ~]# mkdir /srv/gogogo
[root@xyz ~]# chgrp ftptest /srv/gogogo
[root@xyz ~]# echo 'test' > /srv/gogogo/test.txt
[root@xyz ~]# curl ftp://ftptest:password@localhost//srv/gogogo
curl: (78) RETR response: 550
[root@xyz ~]# curl ftp://ftptest:password@localhost//srv/gogogo/
[root@xyz ~]# curl ftp://ftptest:password@localhost//srv/gogogo/test.xt
curl: (78) RETR response: 550
[root@xyz ~]# curl ftp://ftptest:password@localhost//srv/gogogo/test.txt
curl: (78) RETR response: 550

可以查看日志文件:

[root@xyz ~]# grep sealert /var/log/messages | tail
Aug 30 18:25:50 xyz setroubleshoot: SELinux is preventing vsftpd from read access on the directory gogogo. For complete SELinux messages run: sealert -l ee06faec-2d36-459e-8eeb-f69eab6a8d4b
Aug 30 18:28:24 xyz setroubleshoot: SELinux is preventing vsftpd from read access on the directory gogogo. For complete SELinux messages run: sealert -l ee06faec-2d36-459e-8eeb-f69eab6a8d4b
Aug 30 18:28:31 xyz setroubleshoot: SELinux is preventing /usr/sbin/vsftpd from getattr access on the file /srv/gogogo/test.txt. For complete SELinux messages run: sealert -l 16f7093f-cf15-4e0f-b39c-497a8902dd4b
Aug 30 18:28:34 xyz setroubleshoot: SELinux is preventing vsftpd from read access on the file test.txt. For complete SELinux messages run: sealert -l 528bea46-effe-4f09-8b1f-4c2f1eb42b71

可以按照建议运行:

[root@xyz ~]# sealert -l 528bea46-effe-4f09-8b1f-4c2f1eb42b71
SELinux is preventing vsftpd from read access on the 文件 test.txt.

*****  插件 catchall_boolean (57.6 置信度) 建议  ************************************

如果你想 allow ftpd to full access
Then 必须启用 'ftpd_full_access' 布尔值告知 SELinux 此情况。

Do
setsebool -P ftpd_full_access 1

*****  插件 catchall_labels (36.2 置信度) 建议  *************************************

如果你想允许 vsftpd有 read 访问 test.txt $TARGET_类
Then 必须更改 test.txt 中的标签
...省略

这里会按照可信度依次显示建议的解决方案,其中第一个建议是通过启用ftpd_full_access规则让ftpd服务可以访问所有的目录和文件,这显然不是我们希望的。第二个建议是让我们给test.txt文件加一个安全上下文类型,这正是我们应该采用的:

[root@xyz ~]# ll -Zd /var/ftp/
drwxr-xr-x. root root system_u:object_r:public_content_t:s0 /var/ftp/
[root@xyz ~]# semanage fcontext -a -t public_content_t "/srv/gogogo(/.*)?"
[root@xyz ~]# restorecon -Rv /srv/gogogo
restorecon reset /srv/gogogo context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:public_content_t:s0
restorecon reset /srv/gogogo/test.txt context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:public_content_t:s0
[root@xyz ~]# curl ftp://ftptest:password@localhost//srv/gogogo/test.txt
test

这里我们是要把/srv/gogogo目录当作ftp公共目录/var/ftp一样使用,所以这里使用的安全上下文类型参考了/var/ftp的,修改后的确可以使用curl访问了。

无法修改FTP端口的问题和解决

有时候可能某个软件常用的端口会被其它应用占用,我们不得不改用不常用的端口,但是这可能会受到SELinux的限制而无法正常修改。

这里我们将FTP服务的端口修改为555试试看:

[root@xyz ~]# vim /etc/vsftpd/vsftpd.conf
[root@xyz ~]# tail /etc/vsftpd/vsftpd.conf
# sockets. If you want that (perhaps because you want to listen on specific
# addresses) then you must run two copies of vsftpd with two configuration
# files.
# Make sure, that one of the listen options is commented !!
listen_ipv6=YES

pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES
listen_port=555
[root@xyz ~]# systemctl restart vsftpd
Job for vsftpd.service failed because the control process exited with error code. See "systemctl status vsftpd.service" and "journalctl -xe" for details.

在vsftp的配置文件结尾加上端口指定为555的设置后重启服务,结果出现错误。

查看SELinux的错误日志:

[root@xyz ~]# grep sealert /var/log/messages | tail
Aug 30 18:25:50 xyz setroubleshoot: SELinux is preventing vsftpd from read access on the directory gogogo. For complete SELinux messages run: sealert -l ee06faec-2d36-459e-8eeb-f69eab6a8d4b
Aug 30 18:28:24 xyz setroubleshoot: SELinux is preventing vsftpd from read access on the directory gogogo. For complete SELinux messages run: sealert -l ee06faec-2d36-459e-8eeb-f69eab6a8d4b
Aug 30 18:28:31 xyz setroubleshoot: SELinux is preventing /usr/sbin/vsftpd from getattr access on the file /srv/gogogo/test.txt. For complete SELinux messages run: sealert -l 16f7093f-cf15-4e0f-b39c-497a8902dd4b
Aug 30 18:28:34 xyz setroubleshoot: SELinux is preventing vsftpd from read access on the file test.txt. For complete SELinux messages run: sealert -l 528bea46-effe-4f09-8b1f-4c2f1eb42b71
Aug 30 18:46:46 xyz setroubleshoot: SELinux is preventing vsftpd from name_bind access on the tcp_socket port 555. For complete SELinux messages run: sealert -l 33d121d1-0c3d-40a8-9d63-6e2b4ea70cb5

端口请求被拒绝了,运行建议内容:

[root@xyz ~]# sealert -l 33d121d1-0c3d-40a8-9d63-6e2b4ea70cb5 | less

其中第一条92%可信度的建议是通过semanage添加一个555端口的设置,这显然是我们需要的:

[root@xyz ~]# semanage port -a -t ftp_port_t -p tcp 555
[root@xyz ~]# systemctl restart vsftpd
[root@xyz ~]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      1458/dnsmasq
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1234/sshd
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      1240/cupsd
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1562/master
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      733/rpcbind
tcp6       0      0 :::22                   :::*                    LISTEN      1234/sshd
tcp6       0      0 ::1:631                 :::*                    LISTEN      1240/cupsd
tcp6       0      0 ::1:25                  :::*                    LISTEN      1562/master
tcp6       0      0 :::555                  :::*                    LISTEN      5175/vsftpd
tcp6       0      0 :::111                  :::*                    LISTEN      733/rpcbind
[root@xyz ~]# curl ftp://localhost:555/pub/
-rw-r--r--    1 0        0             221 Apr 01  2020 securetty
-rw-r--r--    1 0        0             449 Oct 13  2020 sysctl.conf

添加后可以正常运行了。

参考资料

  • 做人做事,安全第一! —— SELinux 入门

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: Linux SELiux
最后更新:2021年8月30日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

取消回复

*

code

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号