SELinux问题排查

SELinux的权限检查在传统的UNIX文件权限rwx检查(DAC)之后,如果DAC的权限检查已经不通过了,就不会检查SELinux权限。

本文使用CentOS环境进行验证。

日志文件及分析

Linux系统中有两个日志文件:/var/log/audit/audit.log/var/log/message,根据启用服务的不同,SELinux记录日志文件也不同。

  • 启用了auditd服务,记录到/var/log/audit/audit.log
  • 未启用auditd,启用了rsyslogd服务,记录到/var/log/message
  • 启用了setroubleshootd, auditd, rsyslogd服务,记录到/var/log/audit/audit.log/var/log/message(此时setroubleshootd会往其中写入更易读的日志)

为了防止日志过多,有些拒绝并不会写入到日志文件中(称为dontaudit)。 此时需要通过semodule进行重建策略,在重建的时候可以通过选项-D进行排除dontaudit策略。

# semodule -DB      # 重建除dontaudit外的策略
# semodule -B       # 重建所有策略

日志文件分析

  1. audit日志工具

    auditd服务附带有两个分析日志文件audit.log的工具:aureport和ausearch,如果输出的信息很多, 都可以通过-ts/--start-te/--end指定输出的起始和结束时间, 同时预定义了几个常量now, recent, today, yesterday, this-week, this-month, this-year,具体的含义可以看man手册。

    # cat audit.log | aureport --avc
    AVC Report
    ========================================================
    # date time comm subj syscall class permission obj event
    ========================================================
    1. 11/12/2014 00:12:38 httpd unconfined_u:system_r:httpd_t:s0 0 file append system_u:object_r:default_t:s0 denied 19301
    2. 11/12/2014 00:12:38 httpd unconfined_u:system_r:httpd_t:s0 0 file append system_u:object_r:default_t:s0 denied 19301
    3. 11/12/2014 00:12:38 httpd system_u:system_r:httpd_t:s0 0 tcp_socket name_connect system_u:object_r:http_cache_port_t:s0 denied 19302
    4. 11/12/2014 00:12:43 restorecon unconfined_u:system_r:setfiles_t:s0 0 file append system_u:object_r:default_t:s0 denied 19303
    5. 11/12/2014 00:12:43 restorecon unconfined_u:system_r:setfiles_t:s0 0 file append system_u:object_r:default_t:s0 denied 19303
    6. 01/29/2015 19:17:57 httpd system_u:system_r:httpd_t:s0 0 tcp_socket name_connect system_u:object_r:port_t:s0 denied 97
    

    ausearch用于查找特定的audit日志,比如查找httpd的avc日志

    # ausearch -m avc -c httpd
    
  2. audit2why

    audit2why会解析日志文件,给出问题原因和解决方法。

    # cat audit.log | audit2why
    type=AVC msg=audit(1415722358.099:19301): avc:  denied  { append } for  pid=24374 comm="httpd" path="/app/foo.log" dev=sda2 ino=535288 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:default_t:s0 tclass=file
    
            Was caused by:
                    Missing type enforcement (TE) allow rule.
    
                    You can use audit2allow to generate a loadable module to allow this access.
    
    type=AVC msg=audit(1415722358.099:19301): avc:  denied  { append } for  pid=24374 comm="httpd" path="/app/foo.log" dev=sda2 ino=535288 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:default_t:s0 tclass=file
    
            Was caused by:
                    Missing type enforcement (TE) allow rule.
    
                    You can use audit2allow to generate a loadable module to allow this access.
    
    type=AVC msg=audit(1415722358.111:19302): avc:  denied  { name_connect } for  pid=2915 comm="httpd" dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
    
            Was caused by:
            One of the following booleans was set incorrectly.
            Description:
            Allow httpd to act as a relay
    
            Allow access by executing:
            # setsebool -P httpd_can_network_relay 1
            Description:
            Allow HTTPD scripts and modules to connect to the network using TCP.
    
            Allow access by executing:
            # setsebool -P httpd_can_network_connect 1
    
  3. sealert

    如果启用了setroubleshootd服务,还可以通过查看/var/log/message中给的拒绝ID,使用sealert进行查看详细的拒绝信息(一般比较适用于桌面系统), 日志如下:

    setroubleshoot: SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket. For complete SELinux messages. run sealert -l 8c123656-5dda-4e5d-8791-9e3bd03786b7
    

    根据日志中的提示,使用以下命令查看:

    # sealert -l 8c123656-5dda-4e5d-8791-9e3bd03786b7
    
  4. 手动查看

    熟悉了SELinux之后,也可以考虑直接手动查看日志文件

    # grep TYPE=AVC /var/log/audit/audit.log
    # grep "SELinux is preventing" /var/log/messages
    

常见问题排查

  1. 文件安全上下文设置不正确

    通过semanage设置文件和文件夹的安全上下文配置(/etc/selinux/targeted/contexts/files/目录下),然后使用这些配置恢复文件。 semanage是一个重量级命令,可以修改很多SELinux配置项。

    # semanage fcontext -a -t httpd_sys_content_t "/srv/myweb(/.*)?"
    # restorecon -R -v /srv/myweb
    

    当安装完系统之后,需要给某个目录节点(如/var/lib/php/session/)挂载一个单独的分区, 挂载之后,一般需要使用restore恢复下此挂载节点下目录和文件的属性。

    另一种方法是通过chcon修改文件安全上下文。但是通过restorecon之后,会恢复为SELinux配置目录下的属性

    # chcon -R -t httpd_sys_content_t "/srv/myweb(/.*)?"
    

    以下命令可以列出当前有哪些文件的安全上下文与配置文件中的上下文不同:

    # matchpathcon -V /var/www/html/*
    /var/www/html/index.html has context unconfined_u:object_r:user_home_t:s0, should be system_u:object_r:httpd_sys_content_t:s0
    /var/www/html/page1.html has context unconfined_u:object_r:user_home_t:s0, should be system_u:object_r:httpd_sys_content_t:s0
    
  2. Boolean未启用

    比如php需要连接网络获取http网页内容,在CentOS默认配置下是会被SELinux拒绝的:

    type=AVC msg=audit(1415722358.111:19302): avc:  denied  { name_connect } for  pid=2915 comm="httpd" dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
    

    查看httpd相关的Boolean:

    # getsebool -a | grep httpd
    

    设置Boolean值:

    # setsebool -P httpd_can_network_connect on # -P 把配置写入磁盘,可以先不加-P测试是否已经不再被拒绝
    
  3. 端口不允许绑定

    首先查看对应服务占用的端口:

    # semanage port -l | grep http
    http_cache_port_t              tcp      3128, 8080, 8118, 8123, 10001-10010
    http_cache_port_t              udp      3130
    http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
    pegasus_http_port_t            tcp      5988
    pegasus_https_port_t           tcp      5989
    

    添加某个端口到特定端口类型中:

    # semanage port -a -t http_port_t -p tcp 9876
    
  4. 大招——permissive模式

    如果一时半会儿排查不到SELinux故障,或者不知道该如何修改,可以先使用permissive模式, 该模式下只记录拒绝日志但是不会拒绝访问,可以允许整个系统启用或者个别域(进程运行在域中)进入此模式。

    setenforce和getenforce命令可以设置和查看整个系统的模式

    # getenforce 
    Enforcing
    # setenforce 0  # 使用permissive模式
    # getenforce 
    Permissive
    # setenforce 1
    # getenforce 
    Enforcing
    

    setenforce设置结果只是临时的,重启系统之后就不生效了,若想生效,需要修改配置文件:

    # sed -i\.bak 's/SELINUX=enforcing/SELINUX=permissive/' /etc/sysconfig/selinux
    

    查看系统的SELinux整体情况还可以使用sestatus命令:

    # sestatus 
    SELinux status:                 enabled
    SELinuxfs mount:                /selinux
    Current mode:                   enforcing
    Mode from config file:          enforcing
    Policy version:                 24
    Policy from config file:        targeted
    

    但是,让整个系统处于permissive状态,有比较大的安全风险。 SELinux还提供了域permissive功能,用于暂时放开单独的进程。

    # semodule -l | grep permissive
    permissivedomains   1.0.0   
    # semanage permissive -a httpd_t
    # semodule -l | grep permissive
    permissive_httpd_t  1.0 
    permissivedomains   1.0.0   
    # semanage permissive -d httpd_t
    # semodule -l | grep permissive
    permissivedomains   1.0.0
    

    如果以上方法都不能解决时,只能考虑先用audit2allow生成策略,然后加载此策略。 (根据红帽子文档,生成环境不要使用此策略,如果上面的方法都不能解决,那就可以给红帽子提BUG了。因此只有在迫不得已的情况下,才使用此方法) 以下例子展示从日志中提取出certwatch被拒绝的日志,然后生成并加载策略的过程。

    # grep certwatch /var/log/audit/audit.log | audit2allow -M mycertwatch2
    ******************** IMPORTANT ***********************
    To make this policy package active, execute:
    # semodule -i mycertwatch2.pp
    

参考

Chapter 8. Troubleshooting

SELinux Common Commands & Troubleshooting

How to troubleshoot SELinux problems

social