SELinux

From Segfault
Jump to: navigation, search

Installation

Kernel support:

CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
CONFIG_SECURITY_SELINUX_DISABLE=y
CONFIG_SECURITY_SELINUX_DEVELOP=y
CONFIG_SECURITY_SELINUX_AVC_STATS=y
CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
CONFIG_DEFAULT_SECURITY_SELINUX=y
CONFIG_DEFAULT_SECURITY="selinux"

Debian

A short summary of Debian's setup manual[1]

apt-get install auditd checkpolicy policycoreutils selinux-basics selinux-utils       # Debian, Ubuntu

Configure GRUB and PAM and create an /.autorelabel file:

selinux-activate

After rebooting the machine, run check-selinux-installation to verify if everything is setup correctly.

Fedora

    yum install policycoreutils policycoreutils-python audit checkpolicy # Fedora
systemctl enable auditd.service
systemctl  start auditd.service
TBD...

Configuration

When booting, SELinux can be enabled/disabled with the following boot options:

security=selinux        # Choose SELinux as the security module
selinux=1               # Use 0 to disable SELinux
enforcing=1             # Use 0 to enable permissive mode

When enabled, /etc/selinux/config is evaluated during bootup:

SELINUX=permissive          # enforcing, permissive, disabled
SELINUXTYPE=default         # default, mls, src
SETLOCALDEFS=0              # (don't) check for local definition changes

To check if SELinux is enabled:

$ selinuxenabled; echo $?
0                           # 0: enabled, 1: not enabled
$ getenforce 
Permissive                  # Enforcing, Permissive, Disabled

$ setenforce Enforcing      # Enforcing, Permissive

View/set SELinux boolean values:

$ getsebool -a
[...]

$ setsebook user_ping on
$ getsebool user_ping
user_ping --> on

Usage

Quick Start

While still in permissive mode, look out for AVC (Access Vector Cache) messages in /var/log/audit/audit.log. We're going to produce a local policy manually.

audit2allow --dmesg -M local_dmesg
audit2allow  --boot -M local_boot

semodule -v -i local_dmesg.pp
semodule -v -i local_boot.pp

Details

Depending on the distribution support, there will be base policies available to allow certain kinds of programs:

$ semodule -l | head -3
apache  2.2.0   
apm     1.11.0  
clamav  1.8.0  

SELinux has strong support in RedHat & Fedora and only basic support in Debian or Ubuntu based distributions:

fedora$ semodule -l | wc -l
382

debian$ semodule -l | wc -l
48

Considering the vast amount of different applications out there, we may have to adjust the basic rulesets and create a local policy file. Let's print out all violations that occured since the last reboot but are only present in dmesg, as auditd was not yet running:

$ audit2allow --dmesg --module local

module local 1.0;
require {
       type udev_t;
       type modules_conf_t;
       class file read;
}
allow udev_t modules_conf_t:file read;
[...]

Print out all violations that occured since the last reboot and were logged by auditd:

$ audit2allow --boot --module local

module local 1.0;
require {
       type spamd_var_lib_t;
       type gpg_t;
       [...]
       class dir { read search create };
}

#============= gpg_t ==============
allow gpg_t crond_tmp_t:dir search;
allow gpg_t spamd_var_lib_t:dir search;

FIXME: the --boot and --dmesg options cannot be combined but we have to somehow merge both policy files into one. Doing that manually is error prone and leads to duplicate statements that could be consolidated[2] easily. I.e. instead of writing

allow httpd_t user_home_t:dir getattr;
allow httpd_t user_home_t:dir search;

one could write:

allow httpd_t user_home_t:dir { getattr search };

Let's look which actions got denied so far:

$ ausearch --interpret --success no | awk '/avc.*denied/  {print $12}' | sort | uniq -c | sort -n
     1 comm=master
     2 comm=pam-tmpdir-help
     4 comm=grep
     8 comm=rsync
    12 comm=lighttpd
    14 comm=gpg

We'll build a local policy for the rsync process:

$ grep -h "avc:  denied" /var/log/audit/audit.log* | grep /usr/bin/perl | audit2allow -m local_munin > local_munin.te

If the AVC message has been logged to syslog (because auditd was not running), we have to decode the alert first.

$ awk '/For complete SELinux/ {print $9 " " $NF}' messages | sort | uniq -c | sort -n 
   463 /usr/bin/python2.7 780716dc-31dd-45d8-8482-f4e8f9006609
 10790 /usr/bin/perl 72981dad-8665-4fcf-b588-a2f1d0a89234
 36336 /usr/sbin/httpd e9762990-cfbf-4456-8579-880ef364e001
$ grep e9762990-cfbf-4456-8579-880ef364e001 messages | tail -1
Oct 20 00:30:47 len setroubleshoot: SELinux is preventing /usr/bin/perl from write access on the directory
/var/lib/munin/plugin-state. For complete SELinux messages. run sealert -l 72981dad-8665-4fcf-b588-a2f1d0a89234

Create a local policy file from this alert (using "munin" as the policy name):

$ sealert -l 72981dad-8665-4fcf-b588-a2f1d0a89234 | audit2allow -m local_munin > local_munin.te

These .te files ("Type Enforcement") are fairly readable, be sure to check them before using them in the following steps.

Continuing from the munin issue above, we can syntactically check local_munin.te and convert it into a .pp file ("Policy Module):

$ checkmodule -M -m local_munin.te -o local_munin.mod
checkmodule:  loading policy configuration from local_munin.te
checkmodule:  policy configuration loaded
checkmodule:  writing binary representation (version 14) to local_munin.mod

...and generate a .pp file ("Policy Package") from it:

$ semodule_package -m local_munin.mod -o local_munin.pp

And finally load the package file:

$ semodule -v -i local_munin.pp

Or, without the checkmodule call:

$ sealert -l 72981dad-8665-4fcf-b588-a2f1d0a89234 | audit2allow -M local_munin
$ semodule -i local_munin.pp

The semodule program will load the module into the kernel and will take quite some time and memory to complete. If successful, we see something like this:

$ semodule -l | grep local_
local_munin     1.0

This module is now loaded permanently across reboots.

To remove the module:

$ semodule -r local_munin

Applications

httpd

Allow httpd to query databases via PHP:[3]

setsebool -P httpd_can_network_connect_db on

Enable homedirs like ~user to be served from the webserver:

setsebool -P httpd_enable_homedirs on

clamd

The ClamAV daemon wouldn't start properly:

Starting ClamAV daemon: clamd
LibClamAV Warning: RWX mapping denied: Can't allocate RWX Memory: Permission denied
LibClamAV Warning: Bytecode: disabling JIT because SELinux is preventing 'execmem' access.
Run  'setsebool -P clamd_use_jit on'.

dovecot

Dovecot wants to read /etc/shadow - but an SELinux policy trying to allow this is running into a constraint[4]:

libsepol.check_assertion_helper: assertion on line 0 violated by allow dovecot_t shadow_t:file { read } and Expand module failed

As a workaround, we can set the can_read_shadow_passwords attribute:

require {
        attribute can_read_shadow_passwords;
}
typeattribute dovecot_t can_read_shadow_passwords;
allow dovecot_t shadow_t:file { read getattr open };

Tools

A few tools have already been mentioned above, let's recap and add some more:

$ sestatus 
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             default
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     denied
Max kernel policy version:      29

Check if SELinux is enabled:

$ selinuxenabled; echo $?
0

Check if SELinux is in "permissive" or "enforcing" mode:

$ getenforce                    # Use setenforce to change the mode 
Enforcing
$ getsebool -a | head -3        # Use setsebool to change these values
allow_cvs_read_shadow --> off
allow_execheap --> off
allow_execmem --> off

audit2why tries to explain AVC (Access Vector Cache) violations and provides hints how to resolve them:

$ ausearch --interpret --success no | tail -1 | audit2why 
type=AVC msg=audit(07/13/2014 02:10:12.122:59354) : avc:  denied  { getattr } for  pid=28917 comm=rsync path=/run/rsnapshot.ready dev="tmpfs" ino=32330 scontext=unconfined_u:system_r:rsync_t:s0-s0:c0.c1023 tcontext=system_u:object_r:var_run_t:s0 tclass=file 

       Was caused by:
       The boolean rsync_export_all_ro was set incorrectly. 
       Description:
       rsync_export_all_ro

       Allow access by executing:
       # setsebool -P rsync_export_all_ro 1

TODO

 setsebool
 semanage
 chcon

Links

References