SELinux 不是防火墙,大致的目的是在保护由于自己 server 的用户耍白痴可能造成的错误!
说到 SELinux 许多人都很恨它,因为设置方面真的是很点困扰,而且经常还有许多的『例外』状况需要处理。 不过,近期以来的 SELinux 应该算是比较稳定些,而且,大概只要知道几个小细节,就很容易找到处理方向。 只是,如果想要用 Linux 进行类似专题、项目开发的话,其实,暂时转成宽容模式,大概还是需要的...。 总之,对于正规互联网服务或者是管理企业内部员工的权限来说,这东西目前还是很有用途的! 所以,还是得要让我们了解一下这东西才行!
请注意,这一章的所有练习环境,都在前一章节创建的 VM 里面实做喔!不要在 KVM host 里面动作喔!
SELinux 全名其实是『 Security Enhanced Linux 』的意思,这家伙最早是由美国国家安全局开发出来的, 会想要做这个东西的原因,其实是早期 Unix 的系统中,如果你将某个目录设置成为 777 (drwxrwxrwx) 之后, 那么该目录就变成所有人都可以访问的情境!对于内部某些敏感数据来说,很可能由于人为的设置错误, 导致数据可能被第三人窃取或删除...管理员想哭都哭不出来的啦!
所以,SELinux 开发的目的,就是在防止上述的自由选定访问控制 (Discretionary access control, DAC) 造成的影响, 取而代之的,是利用强制访问控制 (Mandatory access control, MAC) 的方法来进行文件的访问。 在 MAC 的访问方法中,并没有所谓的 root 的用户概念,而是通过安全本文来限制进程的读写能力。
另外,SELinux 并不是取代了传统的 rwx 权限,而是在通过文件权限的判定之后,『再加』一层防护, 该层 SELinux 的防护,可以『针对某些网络进程可以读取的文件之安全本文类型』进行限制, 因此,如果被读写目标文件的安全本文设计无法与相关进程匹配,那么该进程就无法读写目标文件了。
上面的说法其实很抽象,我们来画张图解释解释好了。如下图所示,要启动网络服务,总是得要运行某些脚本或程序。 这时 SELinux 就开始进行防护!如果你的设置档设计错误,无法符合当初 SELinux 指定的缺省规则 (rule), 那该脚本或程序就无法顺利加载到内存当中了 (蓝色箭头部份,很可能被 SELinux 抵挡)。若通过缺省规则而加载软件成为网络进程, 那该网络进程想要读取某些系统上面的文件时,如果网络进程与文件的安全本文设计不符,那也无法读到该文件! 这也是 SELinux 最主要限制的地方喔!(红色箭头部份)
这么限制的好处是,因为没有 root 的用户概念,因此,上图当中,你的 httpd process 被攻击而被破解了! 那也没关系,因为除了原本 httpd process 可以读写的位置之外,系统的其他目录,httpd 是没有访问权的! 举例来说, /etc 目录的安全本文与 httpd process 能读取的并不相同,因此你的 /etc/ 目录,就不可能被有问题的 httpd 访问了!这样起码能够作到一定程度的保护。
从上面的简单介绍,我们大概知道每个网络程序应该会有相对的 SELinux 安全本文,然后加载到内存之后, 会取得其相对的进程 SELinux 类型,而这个进程能不能读某个文件,也得要看该文件的安全本文类型才行。 那么,这些安全本文与进程的 SELinux 类型,是纪录在哪里呢?基本上,就是纪录在文件的 i-node 里面啦! 回想一下,当初你在救援系统时,如果有进入到 rd.break 这个救援环境,离开 chroot 时,是不是需要『 touch /.autorelabel 』呢? 这个动作不是会搞很久嘛?那就是 SELinux 要重建系统内所有文件的 SELinux 表头数据,而需要于各文件的 i-node 内重新创建 SELinux 安全本文的缘故! 现在懂了吧!
# 前往 VM 系统,查看一下 /usr/sbin/chronyd 的 SELinux 类型 [root@localhost ~]# stat /usr/sbin/chronyd File: /usr/sbin/chronyd Size: 353408 Blocks: 696 IO Block: 4096 regular file Device: fd03h/64771d Inode: 17318583 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Context: system_u:object_r:chronyd_exec_t:s0 Access: 2023-04-17 07:40:35.000000000 +0800 Modify: 2023-04-17 07:40:35.000000000 +0800 Change: 2023-08-04 13:37:38.809963808 +0800 Birth: 2023-08-04 13:37:38.806963845 +0800
如上所示,通过 stat 取出该文件名相关的纪录,最重要就是那个 Context 行,Context 就是安全本文啰! 因此,这个 chronyd 运行档,安全本文就是『system_u:object_r:chronyd_exec_t:s0』用冒号 (:) 隔开, 共分数个字段,现行主要的限制,其实仅有针对第三字段,也就是 chronyd_exec_t 那个项目而已! 所以,我们知道这个运行档的安全本文类型就是 chronyd_exec_t 的意思。除了这个 stat 之外,我们其实可以通过 ls 搭配 -Z 同样可以看到这个安全本文数据:
[root@localhost ~]# ll -Z /usr/sbin/chronyd -rwxr-xr-x. 1 root root system_u:object_r:chronyd_exec_t:s0 353408 Apr 17 07:40 /usr/sbin/chronyd
从图 3.1-1 可以看到,文件有安全本文,那么进程有嘛?其实也是有的! 观察进程的安全本文,可以通过 ps -Z 的参数来查找得到!
# 查出 chronyd 进程的 PID 含有的安全本文 [root@localhost ~]# ps auxZ | egrep 'chrony|LABEL' LABEL USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND system_u:system_r:chronyd_t:s0 chrony 616 0.0 0.1 84436 3252 ? S 16:02 0:00 /usr/sbin/chronyd -F 2
可以看到进程也是有安全本文相关数据,只是在 ps 里面被称为是标签 (LABEL) 而已。同样的, chronyd 进程的 SELinux 类型为 chronyd_t 喔!
上面讲的是比较概念性的运作方法,实际上,SELinux 的运作,可以参考下列的流程表。 在传统的 DAC,亦即是传统的 rwx 权限访问控制的分析后,若进程可以进行访问,则开始进入到 SELinux 的控制流程中。 若进程无法访问,那当然就不会进入 SELinux 的控制流程啦!
在 SELinux 的控制流程中,一个主体 (subject) 想要取用某个目标 (Object) 时,得要经过完整的 SELinux 流程之后, 最终才能够实际读写到文件的数据 (file data)。所以,MAC 的目的是增加 DAC 的不足,而不是用来取代 DAC 喔! 事实上,还是得要先通过 rwx 传统权限的控制才行!
在上面整体的说明中,重点在『主体』如何取得『目标』的资源访问权限!由上图我们可以发现, 进程的读写,还是需要先经过 rwx 的权限分析,如果 rwx 权限原本就被抵挡,那么后续 SELinux 流程就不会启动。 若通过 rwx 的权限且确定可以读写之后,则:SELinux 在取得主体进程与预备访问的目标资源之安全本文类型后, 开始搜索两者在 SELinux 当中的规则,若确认匹配可行,就予以放行,否则就予以抵挡。
再次强调,安全本文的纪录主要在文件的 inode 当中喔~进程也会有安全本文的纪录~让我们再次回想一下, 如何取得安全本文的数据呢?
# 进程的安全本文取得 [root@localhost ~]# ps -auxZ | egrep 'bash|chronyd|LABEL' LABEL USER ... COMMAND system_u:system_r:chronyd_t:s0 chrony ... /usr/sbin/chronyd -F 2 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root ... -bash # 文件的安全本文观察 [root@localhost ~]# ll -Z /usr/sbin/chronyd /etc/chrony.keys -rw-r-----. 1 root chrony system_u:object_r:chronyd_keys_t:s0 540 Aug 29 2022 /etc/chrony.keys -rwxr-xr-x. 1 root root system_u:object_r:chronyd_exec_t:s0 353408 Apr 17 07:40 /usr/sbin/chronyd [root@localhost ~]# ll -Zd /root/anaconda-ks.cfg /home/vbird drwx------. 2 vbird vbird unconfined_u:object_r:user_home_dir_t:s0 113 Aug 4 13:43 /home/vbird -rw-------. 1 root root system_u:object_r:admin_home_t:s0 1204 Aug 4 13:05 /root/anaconda-ks.cfg
大部分只要记得前面三个字段就好!这三个字段的功能分别是:
User_Identify:role:type SELinux用户别:角色:类型
安全本文的类型数据相当多!稍等我们再来查找安全本文的限制!大致上先了解到这里即可。
从图 3.2-1 当中,我们可以知道 SELinux 共有三种模式,分别是 disabled, permissive 与 enforcing 三种,根据图标的样子,我们大概可以这样看这三种模式:
宽容模式与强制模式,可以在不重新开机的情况下直接进行转换!因此,想要判断某个主体无法访问目标的原因是 SELinux 或是传统权限时,就可以将 SELinux 模式暂时转到宽容模式去测试即可。那如何观察目前的 SELinux 模式呢? 使用 getenforce 即可。
# 观察目前的 SELinux 模式 [root@localhost ~]# getenforce Enforcing # 暂时将 SElinux 模式调整成为 permissive [root@localhost ~]# setenforce [0|1] [root@localhost ~]# setenforce 0 [root@localhost ~]# getenforce Permissive # 显示详细的 SELinux 设置值 [root@localhost ~]# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: permissive Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 33 # 赶紧将模式转回 Enforcing [root@localhost ~]# setenforce 1
缺省的 SELinux 设置档为 /etc/selinux/config,但是,我们也可以在开机阶段,在 Linux 内核强迫打开或关闭 SELinux 的。 所以,要观察 SELinux 的初始设置,可能得要观察两个地方呢!分别是上述的设置档,还有 grub 的设置。 当然,你也可以直接观察目前的内核参数,以确认 SELinux 是否由内核参数所影响。
# 修改缺省的设置档,指定开机为 SELinux 模式 [root@localhost ~]# vim /etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted # 其实,这个设置档就是指定 SELinux 模式与政策的文件 # 检查目前的内核是否有 SELinux 的参数 [root@localhost ~]# cat /proc/cmdline BOOT_IMAGE=(hd0,gpt3)/boot/vmlinuz-5.14.0-284.18.1.el9_2.x86_64 root=UUID=e81ba... # 查找看看有没有 selinux=[0|1] 这个关键字符串! # 检查 grub.cfg 有没有 selinux 的关键字 [root@localhost ~]# grep selinux /boot/grub2/grub.cfg
在图 3.2-1当中,进入整个 SELinux 的进程当中的第二关,除了得要参考政策内的规则外, 某些特定功能 (SELinux boolean) 是否启动,也是相当重要的!举例来说,看看网页服务器能不能提供一般帐号家目录的读取权限, 就是通过这里的规范来额外指定的。如果这里的功能规范当中,不允许网页服务器读取个人家目录, 那么,即使个人家目录的安全本文类型是正确的,网页服务器也会无法读取喔!
查找目前所有 SELinux boolean 功能的状态是打开还是关闭,最简单可以通过 getsebool 来查找即可:
[root@localhost ~]# getsebool -a abrt_anon_write --> off abrt_handle_event --> off ..... zoneminder_anon_write --> off zoneminder_run_sudo --> off # 功能规范真的太多了!通过 grep 来截取看看 # 查找看看有没有 http 开头,home 后续存在的功能规范 [root@localhost ~]# getsebool -a | grep 'http.*home' httpd_enable_homedirs --> off # 已经知道 httpd_enable_homedirs 功能规范名称时 [root@localhost ~]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> off
除了 getsebool 之外,我们可以通过万用工具,就是 semanage 这个软件,里面的 boolean 指令来查找!
# 需要先安装 semanage 的软件!名称为 policycoreutils-pythone-utils [root@localhost ~]# yum whatprovides '*bin/semanage' policycoreutils-python-utils-3.5-1.el9.noarch : SELinux policy core python utilities Repo : appstream Matched from: Other : *bin/semanage [root@localhost ~]# yum -y install policycoreutils-python-utils # semanage boolean 的简单 help [root@localhost ~]# semanage boolean -h .... -l, --list List records of the boolean object type -1, --on Enable the boolean -0, --off Disable the boolean [root@localhost ~]# semanage boolean --list SELinux boolean State Default Description .... httpd_enable_homedirs (off , off) Allow httpd to enable homedirs ....
这样也能很轻松的找到需要的 SELinux boolean 说明!基本上,这些功能规范说明目前你可能还看不太懂, 这是因为可能你不具备某些特定的网络服务经验。没关系!等到后面许多服务器章节实做完之后, 对这些功能规范的名称,你大概就一看就懂了!所以,不用担心!慢慢来!
如果发现到某些功能规范没有激活,想要激活这些功能时,该怎么办呢?既然查看是 getsebool, 想当然尔,设置应该就是 setsebool 啰!
# 将刚刚查找到的 httpd_enable_homedirs 设置为 on [root@localhost ~]# setsebool -P httpd_enable_homedirs 1 [root@localhost ~]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> on # 设置值, 1 或 on 都可以激活, 0 或 off 都可以关闭!
特别注意的是,setsebool 缺省修改的是『目前的状况』,如果想要连同下次开机都使用相同的设置, 那直接加上 -P 的选项来处理即可!所以,将它背下来! setsebool 就是要 -P !!
# 使用 semanage boolean 关闭 httpd_enable_homedirs 测试看看 [root@localhost ~]# semanage boolean httpd_enable_homedirs --modify --off [root@localhost ~]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> off # 虽然比较麻烦,不过,还是稍微熟悉一下 semanage 较佳!
前面我们谈到 SELinux 安全本文,在进程上面,可以查看的指令有『 ps -Z 』之类的方式,而文件的安全本文, 则是通过『 ll -Z 』或者是『 stat 』这个指令来查找。那么修改呢?该如何进行安全本文的修改? 注意喔,修改时,请修改安全本文的类型即可,不要更动到身份识别或者是角色字段喔!
最简单的修改方式是通过 chcon 来修改即可!例如底下的范例:
[root@localhost ~]# chcon [OPTION]... [-t TYPE] FILE... [root@localhost ~]# chcon [OPTION]... --reference=RFILE FILE... 选项与参数: -t :后面接安全性本文的类型字段!例如 httpd_sys_content_t --reference=RFILE:拿文件名为 RFILE 当范例来修改后续接的文件的类型! # 将 /etc/hosts 拷贝 /dev/shm/hosts,并修改类型为 etc_t [root@localhost ~]# cd /dev/shm [root@localhost shm]# cp -a /etc/hosts . [root@localhost shm]# ll -Z hosts -rw-r--r--. 1 root root system_u:object_r:net_conf_t:s0 158 Jun 23 2020 hosts [root@localhost shm]# chcon -t etc_t hosts [root@localhost shm]# ll -Z hosts -rw-r--r--. 1 root root system_u:object_r:etc_t:s0 158 Jun 23 2020 hosts # 将类型改成与 /var/spool/mail 相同 [root@localhost shm]# ll -Zd /var/spool/mail/ drwxrwxr-x. 2 root mail system_u:object_r:mail_spool_t:s0 19 Jul 21 15:01 /var/spool/mail/ [root@localhost shm]# chcon --reference=/var/spool/mail hosts [root@localhost shm]# ll -Z hosts -rw-r--r--. 1 root root system_u:object_r:mail_spool_t:s0 158 Jun 23 2020 hosts
不知道你会不会好奇,既然 SELinux 模式可以在 disable 与 enforcing 之间切换,只是转为 enforcing 时, 可能会花费一段时间,让内核对全系统的文件进行重新设置 (relabel) 的动作!那么表示,每个文件/目录, 可能都会有默认值啰!那如何查找默认值呢?通过 semanage 吧!这样做看看:
# 找到 /etc/sysconfig 相关的缺省 SELinux 安全本文类型 [root@localhost ~]# semanage fcontext --list | grep /etc/sysconfig SELinux fcontext type Context /etc/sysconfig/.*l2tpd regular file system_u:object_r:l2tp_conf_t:s0 /etc/sysconfig/MailScanner regular file system_u:object_r:mscan_etc_t:s0 .....
很简单就可以查看到某个文件/目录的缺省 SELinux 安全本文类型了。好!那如果不是在正规目录, 缺省的安全本文类型又是什么呢?基本上,看一下上述数据最前头输出的几行就知道了!
[root@localhost ~]# semanage fcontext --list | head SELinux fcontext type Context / directory system_u:object_r:root_t:s0 /.* all files system_u:object_r:default_t:s0 /[^/]+ regular file system_u:object_r:etc_runtime_t:s0 .....
原来缺省会是 default_t 喔!
假设我们预计创建一个名为 /www 的目录,这个目录的内容主要就是给网页服务器使用的!因此, 主要的 SELinux 类型应该指定为与 /var/www 这个目录相同。那该如何处理呢? 基本上,你可以这样处理看看。
# 基本的设置语法,如下所示,只要改 type 与目录位置即可 [root@localhost ~]# semanage fcontext -a -t type "/some/dir(/.*)?" # 1. 先找到 /var/www 的类型为何 [root@localhost ~]# semanage fcontext -l | grep '/var/www(' /var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 /var/www(/.*)?/logs(/.*)? all files system_u:object_r:httpd_log_t:s0 # 2. 创建所需目录,并且查看默认值 [root@localhost ~]# mkdir /www [root@localhost ~]# echo check > /www/index.html [root@localhost ~]# ll -Zd /www /www/index.html drwxr-xr-x. 2 root root unconfined_u:object_r:default_t:s0 24 Jul 22 14:40 /www -rw-r--r--. 1 root root unconfined_u:object_r:default_t:s0 6 Jul 22 14:40 /www/index.html # 果然默认值是 default_t 呢! # 3. 增加 /www 缺省为 httpd_sys_content_t 的类型 [root@localhost ~]# semanage fcontext -a -t httpd_sys_content_t "/www(/.*)?" [root@localhost ~]# semanage fcontext -l | grep '^/www' /www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
通过这些步骤,很轻松的就完成了非正规目录的缺省 SELinux 安全本文类型设置值!
现在,我们已经规划好了 /www 的缺省类型,那,我们还需要使用 chcon 一个一个慢慢调整 SELinux 的规范嘛? 似乎不需要呢!直接通过 restorecon 来复原即可!很轻松愉快喔!
# 将刚刚的 /www 安全本文类型重置一下! [root@localhost ~]# restorecon -Rv /www Relabeled /www from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 Relabeled /www/index.html from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
加上 -v 之后,连修改的过程都跟你说了!很简单愉快!如果在某个特别的情况下,你想要复原全系统的 SELinux 类型, 不必进入内核功能,直接使用 restorecon 也是办得到的!
[root@localhost ~]# restorecon -Rv /
如同图 3.1-1 里面提到的,除了主体进程要访问目标文件需要通过 SELinux 管理之外, 程序要触发成为进程时,可能也会经过 SELinux 的修改。系统很可能因为被值入木马,或者是用户不小心安装了有问题的服务, 而这些服务很可能会打开不明的网络端口口!因此,SELinux 确实有针对某些服务来管理缺省端口口, 如果想要启动非正规端口口,还需要这个端口口对应的功能修改正确才行。
虽然我们还没有谈到网络基础,不过,一般常识来说,你可能会知道,网页服务器一般启动的端口口会是 port 80, port 443, 因此,SELinux 可能会管制你的服务器端口口,限制 httpd 这个网络服务程序只能开放在 port 80, 443 而已。 让我们来查找一下:
[root@localhost ~]# semanage port --list | grep http http_cache_port_t tcp 8080, 8118, 8123, 10001-10010 http_cache_port_t udp 3130 http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
就是 http_port_t 那个项目!基本上使用的端口口好几个!那如果你想要增加一个 port 98 怎么办? 同样使用 semanage port 来处理!指令方式也不算太困难:
[root@localhost ~]# semanage port -a -t TYPE -p [tcp|udp] port_range -a 添加一笔纪录 -t 修改的端口口名称,例如 httpd_port_t -p 使用 tcp 或 udp 协定 port_range 使用的端口口号码 # 加入 port 98 的支持到 http 当中 [root@localhost ~]# semanage port -a -t http_port_t -p tcp 98 [root@localhost ~]# semanage port --list | grep http http_port_t tcp 98, 80, 81, 443, 488, 8008, 8009, 8443, 9000
很快可以看到 port 98 也加入可让 httpd 服务启动的端口口了!
基本上,通过了解 SELinux 的三种模式 (disabled, permissive, enforcing)、功能规范开放与否 (getsebool, setsebool)、 安全本文的修改 (chcon, restorecon, semanage fcontext),以及端口口规范 (semanage port) 的方法, 对于 SELinux 的管理,大概就不会差太多了!不过,有没有更简单的方法呢?是有的喔!
事实上,如果你的 SELinux 运作错误时,我们可以通过 setroubleshoot 这个软件的功能, 它会自动分析可能的错误,并且将可能的解决方案直接纪录到 /var/log/messages 里面! 如此一来,你只要重复犯错的动作,然后查阅 messages 文件内容,就可以知道如何解决了!相当愉快!
要使用 SELinux 自动错误克服的功能,就得要安装 setroubleshoot 软件才行!而且, 初次安装完毕时,可能得要重新开机才会有作用。另外,RockyLinux 8 缺省似乎没有启动 rsyslog,不过,RockyLinux 9 倒是缺省安装的。 如果你无法确认 rsyslog 有没有启动以及 setroubleshoot 有没有安装,没关系,就让我们来手动测试看看即可。
[root@localhost ~]# yum -y install setroubleshoot* [root@localhost ~]# rpm -qa | grep setrouble setroubleshoot-plugins-3.3.14-4.el9.noarch setroubleshoot-server-3.3.31-2.el9_2.x86_64 setroubleshoot-3.3.31-2.el9_2.x86_64 [root@localhost ~]# systemctl status rsyslog ● rsyslog.service - System Logging Service Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; preset: enabled) Active: active (running) since Fri 2023-08-04 16:02:11 CST; 6h ago Docs: man:rsyslogd(8) https://www.rsyslog.com/doc/ Main PID: 609 (rsyslogd) Tasks: 3 (limit: 12243) Memory: 3.0M CPU: 521ms CGroup: /system.slice/rsyslog.service └─609 /usr/sbin/rsyslogd -n
你可能会觉得很怪异,上面安装的软件名称当中有 setroubleshoot-server 这个关键字,但是,使用 systemctl 去检查相关的服务时,却找不到任何 setrouble 相关的服务名称!这是因为 setrouble 已经集成到稽核模块 auditd 服务中! 因此, setroubleshoot 的运作方式是这样的:
总之,鸟哥这种老人家,还是比较习惯查找 /var/log/messages 内的数据,而不是让日志直接写入 systemd-journald 当中! 因为只写入 systemd-journald 时,当系统重新开机,日志可能是会遗失的呢!
基本上,http, https 的端口口分别是 port 80, port 443 的 tcp 端口口。那么当我将这个端口口打开到非正规的 377 端口口呢? 很可能会无法启动喔!先来测试看看。我们依序可以这样做:
我们就实际在虚拟机上面恶搞一下啰!
# 1. 先安装软件 [root@localhost ~]# yum -y install httpd # 2. 修改设置档,大约在 47 行处,修改端口口号码 [root@localhost ~]# vim /etc/httpd/conf/httpd.conf Listen 377 # 3. 尝试启动 httpd 服务 [root@localhost ~]# systemctl start httpd Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xeu httpd.service" for details. # 如上所示,系统会提示出现错误了! # 4. 尝试将 SELinux 模式改为 permissive [root@localhost ~]# getenforce Enforcing [root@localhost ~]# setenforce 0 [root@localhost ~]# getenforce Permissive # 5. 确认一下能不能顺利启动?用来判断问题是否出在 SELinux 的情况 [root@localhost ~]# systemctl start httpd [root@localhost ~]# netstat -tlunp | grep httpd tcp6 0 0 :::377 :::* LISTEN 8324/httpd # 出现 LISTEN 关键字!代表服务有正常启动了!所以,问题一定是 SELinux 造成的! # 6. 确认 /var/log/messages 有没有因为启动 httpd 而记载错误解决方案 [root@localhost ~]# grep setrouble /var/log/messages | grep sealert Aug 4 22:13:38 localhost setroubleshoot[3553]: SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 377. For complete SELinux messages run: sealert -l 525fe157-a16b-47c4-ad8d-11bad86c9e9a # 重点是找到 sealert 这个关键字!后续的指令直接运行就是答案! # 7. 将找到的 sealert 指令运行,并依据提示处理问题 [root@localhost ~]# sealert -l 525fe157-a16b-47c4-ad8d-11bad86c9e9a SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 377. ***** Plugin bind_ports (99.5 confidence) suggests ************************ If you want to allow /usr/sbin/httpd to bind to network port 377 Then you need to modify the port type. Do # semanage port -a -t PORT_TYPE -p tcp 377 where PORT_TYPE is one of the following: http_cache_port_t, http_port_t, ... ***** Plugin catchall (1.49 confidence) suggests ************************** ....
其实解决问题的方案不止一种,因此,上述的 sealert 提供的方式中,会有好几个解决方案,不过,解决方案总是有轻重缓急! 所以,最好选择信赖度最高的方案来解决较佳!所以,当然是选上面 99.5% 信赖度的啊!然后,又看到底下的指令, 就是『 semanage port -a -t PORT_TYPE -p tcp 377 』这一段,你应该会觉得很开心!因为刚刚才学过啊! 只是, PORT_TYPE 必须要选择正确的项目才行!因为我们是在处理 http 的端口口,当然最终选择 http_port_t! 所以,整个解决方案的处理会是这样:
[root@localhost ~]# semanage port -a -t http_port_t -p tcp 377 [root@localhost ~]# setenforce 1 [root@localhost ~]# getenforce Enforcing [root@localhost ~]# systemctl restart httpd # 最终,在 Enforcing 的模式中,再次重新启动服务!可正常启动才会是正确的!
最后,让我们使用文本型浏览器来看看我们的本机 (https://127.0.0.1) 有没有顺利提供服务呢?
[root@localhost ~]# curl https://127.0.0.1:377 2> /dev/null | head <!doctype html> <html> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1'> <title>HTTP Server Test Page powered by: Rocky Linux</title> <style type="text/css"> ..... # 有看到数据输出,就是正确的显示啦!文本型浏览器只能作到这样解析!
进行这个仿真时,先有个观念,那就是,整个网页服务器的数据缺省是放置到 /var/www/html 里面的! 所以,如果想要读取 https://127.0.0.1/test.txt 时,该文件需要放置成为 /var/www/html/test.txt 才对! 现在,让我们仿真一个错误!那就是,用户在自己家目录创建好网页数据,然后使用 cp -a 的方式拷贝到网页服务器上! 那个 -a 很厉害啊!可能会连同 SELinux type 都拷贝过去~如此一来,会变怎样呢?
# 1. 先创建名为 test.txt 的文件 [root@localhost ~]# vim ~/test.txt I am VBird Today: 2023/08/04 # 2. 用 cp -a,注意,记得加上 -a 喔!在这个练习底下!不要用 -r [root@localhost ~]# cp -a ~/test.txt /var/www/html # 3. 使用 curl 去浏览,记得我们的端口口在非正规喔! [root@localhost ~]# curl https://127.0.0.1:377/test.txt <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>403 Forbidden</title> </head><body> <h1>Forbidden</h1> <p>You don't have permission to access this resource.</p> </body></html> # 注意喔,错误消息是『没有权限』而不是『找不到文件』 # 意思是,有这个文件存在,但是你没有权限读取的意思!重要重要! [root@localhost ~]# ll /var/www/html/test.txt -rw-r--r--. 1 root root 29 Aug 4 22:20 /var/www/html/test.txt # 但是权限是合理的!所有人都可以读取(r)!所以,直接怀疑是 SELinux 啰! # 4. 检查有没有 SELinux 的 log 错误!? [root@localhost ~]# grep setrouble /var/log/messages | grep sealert Aug 4 22:20:44 localhost setroubleshoot[3942]: SELinux is preventing /usr/sbin/httpd from getattr access on the file /var/www/html/test.txt. For complete SELinux messages run: sealert -l 95e5f3a3-e946-4f45-9560-5787ff5083f9 # 果然在最接近的时间就有一个 SELinux 的警告消息出现了! # 5. 运行看看 sealert 之后,设法解决问题! [root@localhost ~]# sealert -l 95e5f3a3-e946-4f45-9560-5787ff5083f9 SELinux is preventing /usr/sbin/httpd from getattr access on the file /var/www/html/test.txt. ***** Plugin restorecon (99.5 confidence) suggests ************************ If you want to fix the label. /var/www/html/test.txt default label should be httpd_sys_content_t. Then you can run restorecon. The access attempt may have been stopped due to insufficient permissions to access a parent directory in which case try to change the following command accordingly. Do # /sbin/restorecon -v /var/www/html/test.txt .... [root@localhost ~]# /sbin/restorecon -v /var/www/html/test.txt Relabeled /var/www/html/test.txt from unconfined_u:object_r:admin_home_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 # 6. 最后,让我们测试一下,到底能不能成功浏览到信息了? [root@localhost ~]# curl https://127.0.0.1:377/test.txt I am VBird Today: 2023/08/04 # 果然就正常啦!
这边的两个练习都是常见的问题!请大家务必实做一次以上啊!会很有帮助喔!加油加油!
从前一章的后半段开始,我们都用 test1.img 做测试,但其实许多的软件与服务,似乎应该都要在 demo1.img 里面存在比较好! 否则,未来我们都会以为好像系统都安装妥当了...结果却是在测试环境中进行的...那就比较伤脑筋!呵呵! 所以,现在请将 test1 系统的 history 倒出来备份好,将 test1 关机之后,再启动 demo1 看看!
# 确认 test1 关闭之后,就启动 demo1 吧! [root@cloud ~]# virsh create /kvm/xml/demo1.xml Domain 'demo1' created from /kvm/xml/demo1.xml [root@cloud ~]# arp -n -i templan Address HWtype HWaddress Flags Mask Iface 192.168.10.53 ether 52:54:00:ed:63:1f C templan # 登录 192.168.10.53,取得控制权之后,就开始将 SELinux 以及前一章需要的软件装上吧! [root@cloud ~]# ssh vbird@192.168.10.53 [vbird@localhost ~]$ sudo su - [root@localhost ~]# yum -y install fio iperf3 policycoreutils-python-utils setroubleshoot* [root@localhost ~]# poweroff
很简单!这样就好了!未来使用这个 demo1.img 作为 backing_file 所设计出来的 overlay, 里头就拥有 SELinux 运作所需要的 debug 软件了!喔耶!