主机安全之SELinux

Posted by suleo on February 27, 2021

SELinux研究与测试报告

1、主机安全之SELinux理论部分

1.1、主机安全简介

主机安全核心内容应包括应用交付系统、应用监管系统、安全增强操作系统和运维安全管控系统。具体功能是为了保障主机在数据存储和处理的保密性、完整性和可用性,它包括硬件、固件、系统软件的自身安全以及一系列附加的安全技术与安全管理措施,从而建立一个完整的主机安全的环境。

1.1.1、基础功能应包括以下

  1. 系统安全基线 (一个操作系统最小的安全保证)
  2. 强制访问控制 (对系统上资源配置不同访问策略加以保护)
  3. 防格式化保护机制 (防止病毒或入侵者格式化磁盘)
  4. 完整性检测(检测文件改变异常时应告警)
  5. 系统资源监控 (对系统cpu、内存、磁盘、网络资源进行监控,超过阈值报警)
  6. 双因子认证 (usb-key + 密码)
  7. 日志审计 (记录尽可能详尽的违规日志、用户操作日志并上传到专用日志服务器)
  8. 关键事件 (定义严重时间特征,触发马上告警)
  9. 设备统一管理机制 (提高管理效率)
  10. 策略模板 (指定分等级的安全策略模板,降低用户维护难度)
  11. 信息收集 (对用户使用过程提出的疑问,集中探讨并解决)
  12. 维护模式 (此时配置的策略只记录,不拦截,以防止配错策略造成业务中断)
  13. 内部人员安全意识培训

1.1.2、价值功能如下

  1. 免疫木马病毒,从文件的创建执行到访问资源到结束层层把关,从根本免疫各种已知未知病毒、后门等恶意代码,确保系统和应用的安全
  2. 抵御0day, 才用白名单和强制访问控制,只允许可信账户与进程访问被保护资源,并对重要二进制文件实行完整性保护,即使用户找到突破口,影响能被控制在最小范围
  3. 分权管理,才用分权管理机制,避免单用户权限独大,将原系统管理员权限分为系统操作员、安全管理员与审计管理员,三个管理用户各司其职,相互制约
  4. 提升系统安全级别,系统在操作系统内核实现安全标记和系统级强制访问控制,与用户系统自身的自主访问控制相融合,为系统和用户重要应用提供更强的约束和更高级别的安全控制
  5. 系统暴露的高危漏洞,漏洞是攻击者发起攻击的最有效方法,最有效的方法就是保持与厂商同步的最新更新
  6. 入侵检测系统,所有用户一视同仁,没有超级用户的概念,监控主机进程与文件并提供细化的权限分配接口

1.2、SELinux (Security-Enhanced Linux)

SELinux是美国国家安全局对强制访问控制的实现,centos7中已经默认安装 ,是目前功能最全面,测试最充分的linux安全模块,基于域 (所有进程和文件都用类型标记,类型定义进程的域) 的强制访问控制模型,他会在自主访问控制(现有的基于用户的)策略之后生效,对系统所有进程和文件实施由管理人员设置的安全策略,从而限制了可能因系统或应用漏洞而导致的潜在损害范围。

IMG_9338

1.2.1、术语解释

  • 主体:selinux中通常指的是进程

  • 客体:用于信息共享、存储和通讯的系统资源(如文件、目录、套接字、共享内存等)

    ​ 一个客体类别代表某个确定类型(文件或套接字)的所有资源

    ​ 一个客体类别的实例(该类型的某个特定文件)被称为一个客体(如/etc/passwd这个文件)

  • DAC/MAC

    • DAC (discretionary access control),自主访问控制,根据权限位+ACL设置访问权限,特点是基于用户标识的访问控制,如一个文件,文件所有者,同组用户,其它组用户。
    • MAC (mandatory access control),强制访问控制,用户(或其他主体)与文件(或其他客体)都被标记了固定的安全属性(如安全级、访问权限等),在每次访问发生时,系统检测安全属性以便确定一个用户是否有权访问该文件
  • AV/AVC

    • AV (Access vectors),用来表示策略的规则,如允许域访问各种系统客体,一个AV是一套许可。一个基本的AV规则是主体和客体的类型对,AV规则语法如下

      <av_kind><source_type(s)><target_type(s)>:<class(es)>
      <permission(s)>
      
    • 有四种设置: - allow,表示允许主体对客体执行允许的操作; - neverallow,表示不允许主体对客体执行指定的操作; - auditallow,表示允许操作并记录访问决策信息; - dontaudit,表示不记录违反规则的决策信息,且违反规则不影响运行; Allow规则由四部分组成: - 源类型(Source type(s))是尝试访问进程的域类型; - 目标类型(Target type(s))被进程访问的客体的类型; - 客体类别(Object class(es))是指定允许访问的客体的类型,如 file,dir,socket 等; - 许可(Pemission(s))象征目标类型允许源类型访问客体类型的访问种类。
    • AV 的例子如下:

      allow init_t apache_exec_t : file execute;
      allow userdomain shell_exec_t: file{ read getattr lock execute ioctl};
      # 拥有域类型user_t的进程可以读/执行或获取具体有bin_t类型的文件客体的属性
      allow user_t bin_t:file {read execute getattr}
      
    • AVC 提供了从安全服务器获得的访问策略的缓冲区(cache),提高了安全机制的运行性能。它提供了 hook 函数高效检查授权的接口,提供了安全服务器管理 cache 的接口。

1.2.2、 selinux运行模式

  1. Enforcing
    • 这个缺省模式会在系统上启用并实施selinux的安全策略,拒绝访问并记录行动
  2. Permissive
    • selinux会被启用但不会实施安全性策略,只会发出警告以及记录行动,常被用于调试模式排查是否为selinux问题
  3. Disabled
    • selinux已被停用
  4. 配置文件:/etc/selinux/config 或 /etc/sysconfig/selinux, SELINUX=enforcing

1.2.3、 安全策略类型

targeted为默认类型,minimum和mls 稳定性不足,应用较少

  1. targeted
    • 默认策略类型,采用mcs级别的安全策略。将系统进程分为两大类,一类运行在confined域,任何访问请求都会受到selinux的限制。另一类运行在unconfined域,不受selinux的约束
  2. minimum
    • 是targeted的子集,只有指定的confined域的进程受到selinux的限制
  3. mls
    • 所有进程都被划分为细粒度的安全域,受mls策略的限制。如果mls策略还采用了BLP(Bell And LaPadula模型,则所有进程还进一步收到数据安全的安全级别的限制),这是最严格的政策,配置难度非常大。(一般不用,除非对安全性有极高的要求。)
  4. 配置文件:/etc/selinux/config 或 /etc/sysconfig/selinux,SELINUXTYPE=targeted

1.2.4、 安全上下文

文件的安全上下文可以通过 ls -Z查看

进程的安全上下文可以通过 ps auxZ 查看

一个进程安全上下文一般对应多个文件安全上下文,只有两者的安全上下文对应上了,进程才能访问文件

  1. 安全上下文五元素示例
    • user:role:type:sensitivity
  2. user
    • 指登录系统的用户类型,如root、user_u、system_u,多数本地进程都属于自由(unconfined)进程
  3. role
    • 定义文件、进程和用户的用途,文件:object_r,进程和用户:system_r
  4. type
    • 指定数据类型,规则中定义何种进程类型访问何种文件target策略基于type实现,多服务共用:public_content_t, http服务:http_t
  5. sensitivity
    • 限制访问的需要,由组织定义的分层安全级别,如uncalssified, secret,top,secret,一个对象有且只有一个sensitivity,分0-15级别,s0最低,target策略默认使用s0

1.2.5、selinux的优势

  • 控制范围覆盖文件系统、目录、文件、文件启动描述符、端口、消息接口和网络接口。
  • 减少特权升级攻击的漏洞。进程在域中运行,并且每个进程都被隔离开来。
  • SELinux策略规则定义了进程如何访问文件和其他进程。如果一个进程受到攻击,攻击者只能访问该进程的正常功能。以及该进程已被配置为有权访问的文件。(例如,如果Apache HTTP服务器遭到入侵,攻击者无法使用该进程读取用户主目录中的文件,除非已添加或配置了特定的SELinux策略规则来允许此类访问)

2、SELinux强制访问控制-实践部分

2.1、工具安装

  • # semanage、audit2why、audit2allow
    yum install -y policycoreutils-python policycoreutils-devel
    #seinfo、sesearch
    yum install -y settools-console
    

2.2、selinux基本操作示例

  • 查询/设置selinux工作状态

    #查询工作状态
      getenforce
      #enforcing:强制模式。违反 SELinux规则的行为将被阻止并记录到日志中。
      #permissive:宽容模式。违反 SELinux 规则的行为只会记录到日志中,一般为调试用。
      #disabled:关闭 SELinux
      
    #设置工作状态
      setenforce 0/1 (0为permissive,1为enforcing)
      #设置禁用需要修改配置文件
      vim /etc/selinux/config
      更改:SELINUX=disabled
      重启系统
    
  • 查询文件或目录的安全上下文

    ls -Z <文件或目录>
    # 示例
    命令:ls -Z /etc/hosts
    结果:-rw-r--r--. root root system_u:object_r:net_conf_t:s0  /etc/hosts
    
  • 查询进程的安全上下文

    ps auxZ | grep <进程名>
    # 示例
    命令: ps auxZ | grep nginx
    结果: system_u:system_r:nginx_t:s0    root       7997  0.0  0.0 122784  2156 ?        Ss   14:31   0:00 nginx: master process /usr/sbin/nginx
    system_u:system_r:nginx_t:s0    nginx      7998  0.0  0.0 125332  7560 ?        S    14:31   0:00 nginx: worker process
    
  • 修改文件/目录安全上下文

    #修改文件安全上下文
    chcon -t 标签名 文件路径
    #修改目录安全上下文
    chcon -R -t 标签名 目录路径
    
  • 恢复文件/目录默认安全上下文

    restorecon 文件路径
    restorecon -R 目录路径
    
  • 查询/设置布尔型规则

    #查询所有httpd相关的布尔型规则
      getsebool -a |grep httpd
    #执行结果
      httpd_anon_write --> off
      httpd_builtin_scripting --> on
      httpd_can_check_spam --> off
      httpd_can_connect_ftp --> off
      ......  
    # 设置布尔型规则
    setsebool -P httpd_anon_write on (-P为重启仍然生效,如果临时应用可不加)
    
  • 设置/查询文件/目录默认安全上下文

    #设置文件默认安全上下文
      semanage fcontext -a -t <文件安全上下文中的类型字段> "<目录(后面不加斜杠)>(/.*)?"
      #示例:
      semanage fcontext -a -t httpd_sys_content_t "/usr/share/nginx/html2(/.*)?"
    #查询文件默认安全上下文
      semanage fcontext -l |grep 文件/目录路径
    
  • 新增/查询进程允许访问的端口

    # 新增端口标签可使用的端口
      semanage port -a -t <服务类型> -p <协议> <端口号>
      semanage port -a -t http_port_t -p tcp 10080
    #查询示例
      semanage port -l |grep http
    
  • 查询系统上所有selinux标签/用户/角色

    # 查询系统上所有selinux标签/用户/角色
    seinfo -a
    # 查询系统上所有selinux标签
    seinfo -t/-u/-r (-u:用户/-t:标签/-r:角色)
    # 查询系统上所有与http相关的selinux标签
    seinfo -t |grep http
    
  • 查询指定标签的类型强制访问规则

    sesearch --all -s httpd_t -t httpd_sys_content_t (-s 源标签 -t 目标标签)
    结果:
    Found 32 semantic av rules:
       allow httpd_t httpd_sys_content_t : lnk_file { read getattr } ; 
       allow httpd_t httpd_sys_content_t : dir { ioctl read getattr lock search open } ; 
       allow httpd_t httpd_sys_content_t : file { ioctl read getattr lock map open } ; 
       allow httpd_t httpd_content_type : file { ioctl read getattr lock map open } ; 
       allow httpd_t httpd_content_type : dir { getattr search open } ; 
       dontaudit httpd_t exec_type : file { execute execute_no_trans } ; 
       allow httpd_t file_type : dir { getattr search open } ; 
       allow daemon httpd_sys_content_t : dir { getattr search open } ; 
       ......
    

2.3、selinux审计日志

  • SELinux 违规日志路径 /var/log/audit/audit.log

  • 日志示例分析

    avc:  denied  { read } for  pid=117269 comm="nginx" name="passwd" dev="dm-0" ino=16779295 scontext=system_u:system_r:nginx_t:s0 tcontext=system_u:object_r:passwd_file_t:s0 tclass=file permissive=0
    
    信息 描述
    avc: denied 操作已被拒绝
    { read } 此操作需要read权限
    pid=117269 PID117269的过程执行了该操作(或试图执行该操作)
    comm="nginx" 该过程执行者是nginx程序的一个实例
    name="passwd" 目标对象名为passwd(有时会显示完整路径)
    dev=dm-0 托管目标对象的设备是dm-0, 对于真实磁盘,您可以看到托管对象的分区(例如,“ sda3”)
    ino=16779295 文件索引节点号16779295
    scontext=system_u:system_r:nginx_t:s0 这是执行操作的进程的安全上下文
    tcontext=system_u:object_r:passwd_file_t:s0 这是目标对象的安全上下文
    tclass=file 目标对象是文件

    根据日志信息在te文件新增策略:allow nginx_t passwd_file_t:file { read };

    注:托管设备信息查询

    [root@localhost]# lsblk --output NAME,KNAME,TYPE,SIZE,MOUNTPOINT
    NAME            KNAME TYPE  SIZE MOUNTPOINT
    sda             sda   disk   20G 
    ├─sda1          sda1  part    1G /boot
    └─sda2          sda2  part   19G 
      ├─centos-root dm-0  lvm    17G /
      └─centos-swap dm-1  lvm     2G [SWAP]
    sr0             sr0   rom   918M 
    
  • 根据阻拦日志生成放行规则

    # 从日志生成可读性较好的阻挡原因
      grep "关键字" /var/log/audit/audit.log |audit2why
    # 生成放行策略
      grep "关键字" /var/log/audit/audit.log |audit2allow -M nginx_add
    # 安装新生成的策略
      semodule -i nginx_add.pp
    # 卸载策略
      semodule -r nginx_add
    # 查看策略列表
      semodule -l 
    

2.4、自定义标签示例(以源码安装的nginx为例)

当我们安装自己编写的程序或者第三方的程序时,安装之后默认为无限制标签,这时候该进程的权限等同于该用户的权限,这样是非常威胁的,所以我们需要自定义标签,将进程能访问的资源限定在我们配置的范围

  • 源码安装nginx并编写service文件

    vim /usr/lib/systemd/system/nginx.service
    #添加以下内容
    [Unit]
    Description=nginx
    After=network.target
      
    [Service]
    Type=forking
    ExecStart=/usr/local/nginx/sbin/nginx
    ExecReload=/usr/local/nginx/sbin/nginx -s reload
    ExecStop=/usr/local/nginx/sbin/nginx -s quit
    PrivateTmp=false
      
    [Install]
    WantedBy=multi-user.target
    
  • 创建自定义标签基础模型

    # 拷贝模版
    mkdir nginx_policy
    cd nginx_policy
    cp /usr/share/selinux/devel/example.te ./nginx.te
    cp /usr/share/selinux/devel/example.if ./nginx.if
    cp /usr/share/selinux/devel/example.fc ./nginx.fc
    cp /usr/share/selinux/devel/Makefile .
    
  • 编写.fc文件

    分配文件安全上下文,可以使用正则表达式为多个文件甚至整个目录树分配相同的安全上下文

    /usr/local/nginx/sbin/nginx     --  gen_context(system_u:object_r:nginx_exec_t,s0)
    /var/run/nginx.pid     --  gen_context(system_u:object_r:nginx_pid_t,s0)
    /usr/local/nginx/logs/*     --  gen_context(system_u:object_r:nginx_log_t,s0)
    /usr/local/nginx/html/*     --  gen_context(system_u:object_r:nginx_html_t,s0)
    /usr/local/nginx/conf/*     --  gen_context(system_u:object_r:nginx_conf_t,s0)
    /usr/lib/systemd/system/nginx.service     --  gen_context(system_u:object_r:nginx_unit_file_t,s0)
    
  • 编写.if文件

    在下面的示例中

    1. (“ nginx_domtrans”) 控制谁可以执行该应用程序
    2. (“ myapp_read_log”) 授予对应用程序日志文件的读取权限
    ########################################
    ## <summary>
    ##      Execute httpd_nginxhttpd_script_exec_t in the httpd_nginxhttpd_script domain.
    ## </summary>
    ## <param name="domain">
    ## <summary>
    ##      Domain allowed to transition.
    ## </summary>
    ## </param>
    #
    interface(`nginx_domtrans',`
            gen_require(`
                    type nginx_t, nginx_exec_t;
            ')
      
            domtrans_pattern($1,nginx_exec_t,nginx_t)
    ')
      
    ######################################
    ## <summary>
    ##      Execute httpd_nginxhttpd_script in the caller domain.
    ## </summary>
    ## <param name="domain">
    ##      <summary>
    ##      Domain allowed access.
    ##      </summary>
    ## </param>
    interface(`nginx_read_log',`
            gen_require(`
                    type nginx_log_t;
            ')
      
            logging_search_logs($1)
            allow $1 nginx_log_t:file r_file_perms;
    ')
    
  • 编写.te文件 ( 你能先起来的权限策略,可以自己编写)

    policy_module(nginx, 1.0.0)
      
    require {
            class capability { setgid setuid };
            class tcp_socket { accept bind create listen name_bind node_bind read setopt write };
            class dir { add_name remove_name search write };
            class file { append create getattr open read unlink write };
    }
    ########################################
    #
    # Declarations
    #
    type nginx_t;
    type nginx_exec_t;
    domain_type(nginx_t)
    domain_entry_file(nginx_t, nginx_exec_t)
    init_daemon_domain(nginx_t, nginx_exec_t)
      
    type nginx_log_t;
    logging_log_file(nginx_log_t)
      
    type nginx_var_pid_t;
    files_type(nginx_pid_t)
      
    type nginx_port_t;
    corenet_port(nginx_port_t)
      
    type nginx_unit_file_t;
    systemd_unit_file(nginx_unit_file_t)
      
    type nginx_conf_t;
    files_type(nginx_conf_t)
      
    type nginx_html_t;
    files_type(nginx_html_t)
      
    ########################################
    #
    # nginx local policy
    #
    allow nginx_t nginx_log_t:dir { getattr search add_name};
    allow nginx_t nginx_log_t:file { create open read write };
      
    allow nginx_t nginx_log_t:dir { getattr search add_name };
    allow nginx_t nginx_pid_t:file { read open write };
      
    allow nginx_t nginx_conf_t:dir { getattr search };
    allow nginx_t nginx_conf_t:file { getattr open read write };
      
    allow nginx_t nginx_html_t:dir { search getattr };
    allow nginx_t nginx_html_t:file { getattr open read };
      
    allow init_t nginx_unit_file_t:file { getattr open read };
    
  • 安装新策略

    # 生成策略
    make
    # 安装策略
    semodule -i nginx.pp
    # 查看策略是否是已经安装
    semodule -l |grep nginx
    
  • 修改对应文件标签

    # 修改文件和目录的selinux标签
    chcon -t nginx_log_t /usr/lib/systemd/system/nginx.service
    chcon -R -t nginx_exec_t /usr/local/nginx/sbin/
    chcon -R -t nginx_log_t /usr/local/nginx/conf/
    chcon -R -t nginx_log_t /usr/local/nginx/html/
    chcon -R -t nginx_log_t /usr/local/nginx/logs/
    chcon -t nginx_pid_t /var/run/nginx.pid
    # 查看标签是否正确被修改
    ls -l -Z /usr/local/nginx/html/
    ls -l -Z /usr/local/nginx/conf/
    ls -l -Z /usr/local/nginx/logs/
    ls -l -Z /usr/local/nginx/sbin/
    ls -l -Z /usr/lib/systemd/system/nginx.service
    # 重启nginx
    systemctl restart nginx
    
  • 根据selinux增加新策略(最简单的就是使用audit2allow工具+selinux审计日志)

    # 开启selinux调试模式
    setenforce 0
    # 查看是否成功切换为调试模式
    getenforce   (期望值:Permissive)
    # 启动nginx
    systemctl start nginx
    # 测试正常业务访问,应测尽测,当没有对应的selinux规则存在时,会记录在/var/log/audit/audit.log
    # 使用audit2allow工具生成对应策略,也可手动配置调试,更加理解策略含义
    grep "nginx" /var/log/audit/audit.log |audit2allow -M nginx_add
    # 会生成nginx_add.pp、nginx_add.te(此文件可以查阅根据拦截日志生成的新增策略)
    # 安装新生成的策略
    semodule -i nginx_add.pp
    # 恢复selinux拦截模式
    setenforce 1
    getenforce (期望值:Enforcing)
    
  • 策略配置文件展示

    • 初始配置(nginx.te)
    policy_module(nginx,1.0.0)
      
    ########################################
    #
    # Declarations
    #
      
    type nginx_t;
    type nginx_exec_t;
    domain_type(nginx_t)
    domain_entry_file(nginx_t, nginx_exec_t)
    init_daemon_domain(nginx_t, nginx_exec_t)
      
    type nginx_log_t;
    logging_log_file(nginx_log_t)
      
    #type myapp_tmp_t;
    #files_tmp_file(myapp_tmp_t)
      
    ########################################
    #
    # Myapp local policy
    #
      
    allow nginx_t nginx_log_t:file { read_file_perms append_file_perms };
      
    #allow myapp_t myapp_tmp_t:file manage_file_perms;
    #files_tmp_filetrans(myapp_t,myapp_tmp_t,file)
    
    • 根据selinux拦截日志新增的配置文件 (nginx_add.te), 融合而成的最终的策略文件
    module nginx_add 1.0;
      
    require {
            type passwd_file_t;
            type node_t;
            type setroubleshootd_t;
            type tmpfs_t;
            class capability { setgid setuid };
            class tcp_socket { accept bind create listen name_bind node_bind read setopt write };
            class dir { add_name remove_name search write };
            class file { append create getattr open read unlink write };
    }
      
    #============= nginx_t ==============
    allow nginx_t nginx_log_t:dir { add_name search write };
    allow nginx_t nginx_log_t:file { create write };
    allow nginx_t node_t:tcp_socket node_bind;
    allow nginx_t passwd_file_t:file { getattr open read };
    allow nginx_t self:capability { setgid setuid };
    allow nginx_t self:tcp_socket { accept bind create listen read setopt write };
    allow nginx_t unreserved_port_t:tcp_socket name_bind;
      
    allow nginx_t nginx_log_t:dir { getattr search add_name};
    allow nginx_t nginx_log_t:file { create open read write };
      
    allow setroubleshootd_t nginx_var_run_t:file getattr;
    allow nginx_t nginx_var_run_t:dir { add_name remove_name write };
    allow nginx_t nginx_var_run_t:file { read open write getattr create unlink };
      
    allow nginx_t nginx_conf_t:dir { getattr search };
    allow nginx_t nginx_conf_t:file { getattr open read write ioctl };
      
    allow nginx_t nginx_modules_t:dir { getattr search };
    allow nginx_t nginx_modules_t:file { getattr open read execute };
      
    allow nginx_t nginx_html_t:dir { search getattr };
    allow nginx_t nginx_html_t:file { getattr open read };
      
    allow init_t nginx_unit_file_t:file { getattr open read };
      
    allow nginx_t nginx_port_t:tcp_socket name_bind;
      
    #============= passwd_file_t ==========
    allow nginx_t passwd_file_t:file { getattr open read };
      
    #============= socket =============
    allow nginx_t node_t:tcp_socket node_bind;
    allow nginx_t self:capability { setgid setuid };
    allow nginx_t self:tcp_socket { getattr accept bind create listen read setopt write };
      
    #============ tmpfs_t ===============
    allow nginx_t tmpfs_t:file { read write }