传统 Linux 与 SELinux 采用不同的权限控制机制:
传统 Linux(自主访问控制,DAC):基于文件或目录的所有者、所属组和其他人三类身份,控制读、写和执行权限。
SELinux(强制访问控制,MAC):由 SELinux 策略规则决定进程能否访问具体的文件或目录。
SELinux 会拦截到达内核的所有系统调用并默认拒绝,仅当策略中存在允许规则时才放行。因此,在仅启用 SELinux 而未配置策略的系统上,服务可能无法正常工作,管理员需要编写规则并将其加入策略。由于策略规则数量庞大,通常将其划分为多个模块,便于按系统组成部分单独启用或禁用。
在启用了 SELinux 的系统上,进程和文件都会被赋予安全上下文,作为访问控制的判定依据。安全上下文包含 SELinux 用户、角色、类型、类别等标签信息。
安全上下文
说明:您需要启用 SELinux 后,方可执行以下指令操作。
您可以执行以下操作,分别查看根目录本身、目录中文件以及进程的安全上下文:
查看根目录本身的安全上下文:
ls -Zd /# system_u:object_r:root_t:s0 /
查看根目录中文件的安全上下文:
ls -Z /# system_u:object_r:bin_t:s0 bin# system_u:object_r:boot_t:s0 boot# system_u:object_r:default_t:s0 data
查看进程上下文:
ps -Zaux | grep systemd-udevd# system_u:system_r:udev_t:s0-s0:c0.c1023 root 565 0.0 0.1 36964 15172 ? Ss Mar17 0:01 /usr/lib/systemd/systemd-udevd
其中,安全上下文以冒号(
:)进行字段分隔,各字段含义如下:字段 | 含义 |
system_u | SELinux 用户 |
object_r | 角色 |
root_t | 类型 |
s0 | 灵敏度等级 |
SELinux 用户(user)
每个 Linux 用户都通过 SELinux 策略映射到一个 SELinux 用户。这允许 Linux 用户继承对 SELinux 用户的限制。映射的 SELinux 用户身份在该会话中的进程的 SELinux 上下文中使用,以便定义他们可以进入的角色和级别。
角色(role)
SELinux 基于角色的进行访问控制(Role-Based Access Control,RBAC)安全模型。主要用来表示此数据是进程还是文件或目录。常见的角色有两种:
object_r(代表文件或目录),system_r(代表进程)。类型(type)
类型字段是安全上下文中最重要的字段,进程是否可以访问文件,主要就是看进程的安全上下文类型字段是否和文件的安全上下文类型字段相匹配,如果匹配则可以访问。
说明:
类型字段在文件或目录的安全上下文中被称作类型(type),但是在进程的安全上下文中被称作域(domain)。
灵敏度等级(level)
level 为 MLS 和 MCS 的一个属性。MLS 范围是一对级别,如果级别不同,则写为
lowlevel-highlevel,如果级别相同,则写为 s0(同 sensitivity)。每个级别都是一个灵敏度类别对,类别是可选的。如果有类别,则级别写为 sensitivity:category-set。如果没有类别,则写为 sensitivity。安装 SELinux 工具包
在 TencentOS Server 中,SELinux 默认设置为 enforcing 模式,并使用 targeted 策略。此外,在完整安装中会默认安装 SELinux 相关包,除非您在安装过程中手动排除。安装包名单及其作用介绍如下:
policycoreutils:提供用于操作和管理 SELinux 的实用程序,例如 restorecon、setfiles、semodule、load_policy 和 setsebool。
selinux-policy:为 SELinux 引用策略提供配置。SELinux Policy 是一个完整的 SELinux 策略,并作为 targeted 等其他策略的基础。此软件包包含 selinux-policy.conf 文件和 RPM 宏。
selinux-policy-targeted:提供 SELinux targeted 策略。
libselinux:为 SELinux 应用程序提供 API。
libselinux-utils:提供 avcstat、getenforce、getsebool、matchpathcon、selinuxconlist、selinuxdefcon、selinuxenabled 和 setenforce 实用程序。
除以上默认安装软件包,在 SELinux 使用过程中,您可能会用到默认安装范围以外的 SELinux 包,包括但不限于:
selinux-policy-devel:提供用于创建自定义 SELinux 策略和策略模块的实用程序。
selinux-policy-doc:提供了描述配置 SELinux 及其服务的手册页。
selinux-policy-mls:提供 MLS(多级安全)SELinux 策略。
setroubleshoot-server:将 SELinux 拒绝访问时产生的拒绝消息转换为可以使用 sealert 实用程序查看的详细描述,该实用程序也在该包中提供。
setools-console:提供用于分析和查询策略、审计日志监控和报告以及文件上下文管理的实用程序和库。
对于上述软件包,您可以按照自己的需求,通过以下命令自行安装:
# 安装某个包sudo dnf install <package># 查看已安装的包rpm -q <package>
安装 SELinux 策略
SELinux 提供 3 种策略,分别是 targeted(默认)、minimum 和 mls。不同的策略类型制定了不同的访问规则、甚至是 SELinux 上下文,直接影响了对应的主体(进程)是否可以访问的目标(文件或目录资源)。
targeted 策略
targeted 策略主要对系统中的服务进程进行访问控制,同时,它还可以限制其他进程和用户。服务进程都被放入沙盒,在此环境中,服务进程会被严格限制,以便使通过此类进程所引发的恶意攻击不会影响到其他服务或 Linux 系统。
沙盒(sandbox)是一种环境,在此环境中的进程可以运行,但对其他进程或资源的访问会被严格控制。换句话说,位于沙盒中的各个进程,都只是运行在自己的域(进程所运行的区域被称为域)内,它们无法访问其他进程或资源(除非被授予特殊的权限)。
通过使用此策略,可以更加安全地共享打印服务器、文件服务器、Web 服务器或其他服务,同时降低因访问这些服务而对系统中其他资源造成不利影响的风险。
minimum 策略
minimum 策略最初是针对低内存计算机或者设备(例如智能手机)而创建的。从本质上来说,minimum 和 targeted 类似,不同之处在于,它仅使用基本的策略规则包。对于低内存设备来说,minimum 策略允许 SELinux 在不消耗过多资源的情况下运行。
MLS 策略
MLS(全称 Multi-Level Security),该策略会对系统中的所有进程进行控制。启用 MLS 之后,用户即便执行最简单的指令(如
ls),都会报错。在使用过程中,我们可以通过
sestatus 指令查看 SELinux 策略类型,示例如下:sestatus# SELinux status: enabled# SELinuxfs mount: /sys/fs/selinux# SELinux root directory: /etc/selinux# Loaded policy name: targeted # 当前策略类型# Current mode: enforcing # 当前运行模式是 enforcing 模式
SELinux 配置与启用
SELinux 配置通过
/etc/selinux/config 文件进行修改,它控制 SELinux 工作模式和策略类型。SELinux 实现了 3 种工作模式,不同的 SELinux 模式具有不同特性:
enforcing 模式:启用 SELinux,并强制执行所有的安全策略规则。不符合策略规则的访问动作会被拒绝,并记录于日志。
permissive 模式:启用 SELinux,但并不强制执行安全策略规则。不符合策略规则的访问动作不会被拒绝,只是单纯的记录于日志。
disabled 模式:系统启动时不加载策略,因此不会访问策略规则,但并非完全关闭 SELinux。如果想要完全禁用 SELinux,需要通过内核命令行设置
selinux=0。工作模式查看
SELinux 的工作模式可以通过
getenforce 指令进行简明查看,如果想要获取更多信息,可以使用 sestatus 指令查看。getenforce# Enforcing
工作模式设置
setenforce 指令可以便捷地实现 permissive、enforcing 模式间的切换,但不能实现 disabled 模式的切换。# 切换成宽容模式setenforce 0# 切换成强制模式setenforce 1
通过编辑
/etc/selinux/config 配置文件,可以实现 disabled、permissive、enforcing 三种模式自由切换。说明:
因为 SELinux 工作在 disabled 模式下不打标签、在 permissive 模式下可能出现不会正确打标的情况。而切换到 enforcing 模式下后,MAC 依赖正确的文件标签。所以当切换回 enforcing 模式时,如果出现了文件标签异常的情况,可在 permissive 模式下执行以下指令进行 relabel 修复:
fixfiles -F onbootreboot
SELinux 查看与管理
SELinux 安装后,默认会开启 enforcing 模式。enforcing 模式下众多的策略规则、用户映射、文件标签等,我们都可以进行查看和按需设置。部分常见查看、管理指令介绍如下:
seinfo
SELinux 默认工作在 targeted 模式下,包含众多策略规则,通过
seinfo 指令可以查询具体包含哪些规则。seinfo -b# Conditional Booleans: 247# allow_domain_fd_use# allow_ftpd_full_access# ...
getsebool / setsebool
查询到规则名称后,可以进一步通过
getsebool/setsebool 指令分别查看、设置该规则的开启状态。# 查看所有布尔值getsebool -a# 查看指定布尔值getsebool httpd_can_network_connect# 设置布尔值(临时生效)setsebool httpd_can_network_connect on# 设置布尔值(永久生效,写入策略)setsebool -P httpd_can_network_connect on
sesearch
通过
seinfo 命令,可以查询相关策略规则的名称,但如果想要知道规则的具体内容,就需要使用 sesearch 命令了。# 查看所有允许的规则sesearch --allow# allow NetworkManager_dispatcher_ddclient_t init_t:service status;# allow NetworkManager_dispatcher_ddclient_t init_t:system status;# allow NetworkManager_dispatcher_ddclient_t init_t:unix_stream_socket sendto;
semanage
semanage 是一个功能强大的指令,包括但不限于 SELinux 用户查询、用户管理、映射关系查询、规则查询、上下文查询等等。# 查看登录账户和 SELinux 用户映射关系sudo semanage login -l# 查看 SELinux 用户和角色映射关系sudo semanage user -l# 查询规则(布尔值)sudo semanage boolean -l# 安全上下文查询sudo semanage fcontext -l
SELinux 布尔值管理
SELinux 布尔值是策略中的开关,允许在运行时修改策略行为,无需重新编写或加载策略。通过调整布尔值,可以快速开启或关闭某些 SELinux 策略规则。
查看布尔值
使用
getsebool 命令查看当前布尔值状态:# 查看所有布尔值及其状态getsebool -a# 查看指定布尔值getsebool httpd_can_network_connect# httpd_can_network_connect --> off
使用
semanage boolean -l 可以查看布尔值的详细说明(包括默认值和当前值):sudo semanage boolean -l | grep httpd# httpd_can_network_connect (off , off) Allow httpd to network connect# httpd_can_network_connect_db (off , off) Allow httpd to network connect db# httpd_enable_cgi (on , on) Allow httpd to enable cgi
设置布尔值
使用
setsebool 命令设置布尔值:# 临时设置(重启后失效)setsebool httpd_can_network_connect on# 永久设置(写入策略文件,重启后仍生效)setsebool -P httpd_can_network_connect on
说明:
使用
-P 选项会将修改写入策略文件,可能需要几秒钟时间。不加 -P 则仅对当前运行时生效,重启后恢复默认。常用布尔值示例
布尔值 | 说明 |
httpd_can_network_connect | 允许 httpd 发起网络连接 |
httpd_can_network_connect_db | 允许 httpd 连接数据库端口 |
httpd_enable_cgi | 允许 httpd 执行 CGI 脚本 |
httpd_read_user_content | 允许 httpd 读取用户家目录内容 |
httpd_unified | 允许 httpd 统一访问所有 httpd 类型的文件 |
ftp_home_dir | 允许 vsftpd 访问用户家目录 |
nfs_export_all_rw | 允许 NFS 共享读写访问 |
samba_enable_home_dirs | 允许 Samba 共享用户家目录 |
ssh_sysadm_login | 允许 sshd 直接登录为 sysadm_t |
use_nfs_home_dirs | 允许使用 NFS 挂载的家目录 |
安全上下文修改
SELinux 安全上下文可以通过多种方式进行修改,包括临时修改和永久修改。
chcon 临时修改
chcon 命令用于临时修改文件或目录的安全上下文,该修改在文件系统重新标签(relabel)或执行 restorecon 后会恢复为默认值。# 修改文件的类型chcon -t httpd_sys_content_t /var/www/html/index.html# 递归修改目录及其子文件的类型chcon -R -t httpd_sys_content_t /var/www/html/# 修改文件的完整上下文(用户:角色:类型)chcon system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html# 查看修改结果ls -Z /var/www/html/index.html# system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
restorecon 恢复默认上下文
restorecon 命令用于将文件或目录的安全上下文恢复为策略中定义的默认值。# 恢复单个文件的默认上下文restorecon -v /var/www/html/index.html# 递归恢复目录下所有文件的默认上下文restorecon -Rv /var/www/html/# 强制重新标签(忽略现有标签)restorecon -R -F /var/www/html/
semanage fcontext 永久修改
semanage fcontext 命令用于永久修改文件或目录的默认安全上下文。修改后需要执行 restorecon 使其生效。# 添加新的文件上下文规则sudo semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"# 修改已存在的文件上下文规则sudo semanage fcontext -m -t httpd_sys_content_t "/custom/web(/.*)?"# 使规则生效restorecon -Rv /custom/web/# 查看自定义的文件上下文规则sudo semanage fcontext -l | grep /custom/web# 删除文件上下文规则sudo semanage fcontext -d "/custom/web(/.*)?"
说明:
semanage fcontext 添加的规则存储在 /etc/selinux/targeted/contexts/files/ 目录下的策略文件中,重启后依然有效。路径匹配使用正则表达式,(/.*)? 表示匹配该目录及其所有子内容。策略模块管理
SELinux 策略由多个模块组成,每个模块负责一部分规则。通过
semodule 命令可以管理这些策略模块。查看已安装的模块
# 列出所有已安装的模块semodule -l# 查看模块数量semodule -l | wc -l
安装策略模块
# 安装一个策略模块sudo semodule -i mymodule.pp
移除策略模块
# 移除一个策略模块sudo semodule -r mymodule
其他常用操作
# 升级策略模块sudo semodule -u mymodule.pp# 重新加载所有模块sudo semodule -R# 构建并安装模块(结合 semodule_compile)# 通常先使用 checkmodule 和 semodule_package 编译 .te 文件checkmodule -M -m -o mymodule.mod mymodule.tesemodule_package -o mymodule.pp -m mymodule.modsudo semodule -i mymodule.pp
说明:
自定义策略模块的编写通常从
.te(Type Enforcement)文件开始,经过 checkmodule 编译为 .mod 文件,再通过 semodule_package 打包为 .pp 文件,最后通过 semodule -i 安装。SELinux 故障排查
当 SELinux 拒绝了某个进程的访问请求时,会生成 AVC(Access Vector Cache)拒绝消息。通过以下工具和日志可以排查 SELinux 相关问题。
查看审计日志
SELinux 的拒绝消息默认记录在审计日志中:
# 查看审计日志中的 AVC 拒绝消息sudo ausearch -m AVC -ts recent# 实时监控 AVC 拒绝消息sudo tail -f /var/log/audit/audit.log | grep AVC# 使用 ausearch 查看最近的 SELinux 拒绝sudo ausearch -m AVC,USER_AVC -ts today
sealert 命令
sealert 命令(由 setroubleshoot-server 包提供)将 AVC 拒绝消息转换为人类可读的详细描述,并给出修复建议:# 分析审计日志并生成详细报告sudo sealert -a /var/log/audit/audit.log# 分析单条 AVC 消息sudo sealert -l <message_id>
sealert 输出示例:SELinux is preventing /usr/sbin/httpd from name_connect access on the tcp_socket.***** Plugin catchall_boolean (89.3 confidence) suggests ********************If you want to allow httpd to network connectThen you must tell SELinux about this by enabling the 'httpd_can_network_connect' boolean.Dosetsebool -P httpd_can_network_connect 1
setroubleshoot 服务
setroubleshoot 服务会持续监控审计日志,将 SELinux 拒绝消息转换为详细描述,并发送到系统日志和 DBus:# 查看 setroubleshoot 服务状态sudo systemctl status setroubleshoot# 启动并设置开机自启sudo systemctl enable --now setroubleshoot# 查看通过 setroubleshoot 生成的消息sudo journalctl -t setroubleshoot
常用排查流程
# 1. 查看 SELinux 当前状态getenforcesestatus# 2. 查看最近的 AVC 拒绝消息sudo ausearch -m AVC -ts recent# 3. 使用 sealert 分析并获取修复建议sudo sealert -a /var/log/audit/audit.log# 4. 如果确定需要放行某访问,根据 sealert 建议执行# 如设置布尔值setsebool -P <boolean_name> on# 或修改文件上下文sudo semanage fcontext -a -t <type> "<path>(/.*)?"restorecon -Rv <path>
实际场景示例
场景一:自定义 SSH 端口
当需要将 SSH 服务端口从默认的 22 修改为其他端口(如 2222)时,SELinux 会阻止 sshd 监听非标准端口,需要添加端口标签。
注意:
重启 sshd 后,原 22 端口不再接受连接。请您确认端口标签已添加成功、防火墙已放行新端口后,再执行重启;同时保持当前会话不断开,重启后先通过新端口验证 SSH 连接可正常建立,再断开当前会话。
1. 修改 sshd 配置文件,将
Port 22 改为 Port 2222:sudo vi /etc/ssh/sshd_config
2. 查看当前 ssh 端口标签:
sudo semanage port -l | grep ssh
输出示例:
ssh_port_t tcp 22
3. 为新端口添加 SELinux 端口标签:
sudo semanage port -a -t ssh_port_t -p tcp 2222
4. 验证端口标签已添加:
sudo semanage port -l | grep ssh
输出示例:
ssh_port_t tcp 2222, 22
5. 重启 sshd 服务(执行成功时无输出):
sudo systemctl restart sshd
6. 验证 sshd 正常监听:
sudo ss -tlnp | grep sshd
说明:
如果新端口已存在其他类型的标签,需要使用
-m(modify)而非 -a(add)。场景二:修改 Web 服务目录标签
当 Web 服务器(如 httpd/nginx)使用非默认目录(如
/custom/web)作为网站根目录时,SELinux 会阻止 Web 服务访问该目录,需要为其设置正确的安全上下文。1. 创建自定义 Web 目录(执行成功时无输出):
sudo mkdir -p /custom/web
2. 查看当前安全上下文:
ls -Zd /custom/web
输出示例:
unconfined_u:object_r:default_t:s0 /custom/web
3. 添加永久文件上下文规则:
sudo semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
4. 应用上下文规则:
sudo restorecon -Rv /custom/web/
5. 验证上下文已修改:
ls -Zd /custom/web
输出示例:
system_u:object_r:httpd_sys_content_t:s0 /custom/web
6. 修改 Web 服务器配置指向新目录,将
DocumentRoot "/var/www/html" 改为 DocumentRoot "/custom/web":sudo vi /etc/httpd/conf/httpd.conf
7. 重启 httpd 服务(执行成功时无输出):
sudo systemctl restart httpd
8. 验证 Web 服务可以正常访问:
curl http://localhost/
场景三:允许 Web 服务连接数据库
当 Web 应用需要连接远程数据库时,SELinux 默认会阻止 httpd 发起网络连接,需要开启相应布尔值。
1. 查看 httpd 相关布尔值:
getsebool -a | grep httpd_can_network
2. 开启 httpd 网络连接布尔值(
-P 参数使其永久生效):setsebool -P httpd_can_network_connect on
3. 如果是连接数据库,开启数据库连接布尔值:
setsebool -P httpd_can_network_connect_db on
4. 验证布尔值已开启:
getsebool httpd_can_network_connect
输出示例:
httpd_can_network_connect --> on
5. 重启 Web 服务使配置生效(执行成功时无输出):
sudo systemctl restart httpd
场景四:允许 NFS 共享家目录访问
当使用 NFS 挂载家目录时,SELinux 默认会阻止访问 NFS 文件,需要开启相应布尔值。
1. 查看当前 NFS 相关布尔值:
getsebool -a | grep nfs
2. 开启 NFS 家目录访问布尔值(
-P 参数使其永久生效):setsebool -P use_nfs_home_dirs on
3. 验证布尔值:
getsebool use_nfs_home_dirs
输出示例:
use_nfs_home_dirs --> on
4. 挂载 NFS 家目录:
sudo mount -t nfs nfs-server:/home /home
5. 验证家目录可正常访问:
ls -Z /home/user/
场景五:临时切换到 permissive 模式排查问题
当服务因 SELinux 拒绝无法正常工作时,可以先切换到 permissive 模式验证问题是否由 SELinux 引起。
1. 查看当前模式:
getenforce
输出示例:
Enforcing
2. 切换到 permissive 模式(执行成功时无输出):
setenforce 0
3. 复现问题,查看是否有 AVC 拒绝记录:
sudo ausearch -m AVC -ts recent
4. 使用 sealert 分析拒绝原因:
sudo sealert -a /var/log/audit/audit.log
5. 根据 sealert 建议,设置相应布尔值或上下文。设置布尔值:
setsebool -P <boolean_name> on
或修改文件上下文(将
<type>、<path> 替换为实际的类型和路径):sudo semanage fcontext -a -t <type> "<path>(/.*)?"sudo restorecon -Rv <path>
6. 切回 enforcing 模式(执行成功时无输出):
setenforce 1
7. 重启服务并验证其恢复正常工作(将
<service> 替换为实际的服务名):sudo systemctl restart <service>