SELinux server management

SELinux is an implementation of the FLASK security model, and can be used on most recent RHEL and Fedora Core versions. It provides Über powerful access control. Machine can still be pwned with a kernel exploit though <:(


You have three options for the state of SELinux on the server, and two options for the type of policy enforced:

  • State:
    • (recommended) enforcing - runs SELinux logging and enforcing the active policy

    • permissive (default) - runs SELinux but only logs policy breaches, does not enforce them

    • disabled - does not run SELinux at all from init. Switching away from disabled requires the filesystems to be relabelled on next bootup.

  • Policy:
    • (recommended) targeted - only contains policies that affect system and daemons

    • strict - policies affect entire system, daemons, filesystem, network etc. More complicated.

To see the current status of the server, run the command:

sestatus -v

State and policy can be configured at install or by editing /etc/selinux/config.

To switch between enforcing/permissive and disabled, the system will need to relabel all files and this will require a reboot.

To switch between permissive and enforcing use the setenforce command:

setenforce [ Enforcing | Permissive | 1 | 0 ]

There are also a bunch of useful things in /selinux (similar to /proc) that can be used.


If you have the system in the enforcing state, it will actively enforce the current policy (i.e. only after standard Linux permission checks pass, SELinux will then either allow or deny the access).

To find out if SELinux is denying access, check out:

  • /var/log/audit/audit.log, if auditd is running, otherwise:

  • dmesg output, or /var/log/messages

and have a look for entries containing avc (Access Vector Cache) and denied. It is possible (but rare) for a policy to specify that an access is to be denied without an audit record being generated. If you suspect that this is the case you should test the system by switching the system to permissive mode as documented above and performing your tests and then setting the system back to enforcing mode.

SELinux will fail to work correctly either because of:

  1. Incorrect file/directory security context labeling;
  2. The policy needs adjusting.

File and directory security contexts

Every object (file/directory/socket/etc) on the system has a security context attribute. You can use the -Z switch to ls to display the security context for an object (similarly the -Z switch to ps will display the type (called the domain) for a process). For example:

[root@dreadnought lib]# ls -Z
drwxr-xr-x  root     root     system_u:object_r:rpm_var_lib_t  alternatives
drwxr-xr-x  amanda   disk     system_u:object_r:var_lib_t      amanda
drwx------  apache   apache   system_u:object_r:var_lib_t      dav
drwxr-xr-x  root     root     system_u:object_r:dhcp_state_t   dhcp
drwxr-x---  root     root     system_u:object_r:var_lib_t      dhcpv6
drwxr-xr-x  root     root     system_u:object_r:var_lib_t      games
drwxr-xr-x  root     root     system_u:object_r:httpd_sys_content_t htdig
-rw-r--r--  root     root     system_u:object_r:var_lib_t      logrotate.status
drwxr-xr-x  root     root     system_u:object_r:var_lib_t      misc
drwxr-xr-x  mysql    mysql    system_u:object_r:mysqld_db_t    mysql
drwxr-x---  root     root     root:object_r:var_lib_t          mysqlbackup
drwxr-xr-x  root     root     system_u:object_r:var_lib_nfs_t  nfs
drwxr-xr-x  ntp      ntp      system_u:object_r:ntp_drift_t    ntp
drwx------  osiris   osiris   system_u:object_r:var_lib_t      osiris
drwx-----x  postgres postgres system_u:object_r:postgresql_db_t pgsql
drwxr-xr-x  root     root     system_u:object_r:var_lib_t      php
-rw-------  root     root     user_u:object_r:var_lib_t        random-seed
drwxr-xr-x  rpm      rpm      system_u:object_r:rpm_var_lib_t  rpm
drwxr-x---  root     slocate  system_u:object_r:var_lib_t      slocate
-rw-r--r--  root     root     system_u:object_r:var_lib_t      supportinfo
drwxr-xr-x  root     root     system_u:object_r:var_lib_t      tomcat5
drwxr-xr-x  root     root     system_u:object_r:var_lib_t      up2date
drwxr-xr-x  webalize root     system_u:object_r:var_lib_t      webalizer

These security contexts are persistently stored in the filesystem using extended attributes. They are an attribute of the object (inode) and not dependent on its path/name.

In the above output the directory alternatives has a security context of system_u:object_r:rpm_var_lib_t. This is read as:

  • User of system_u

  • Role of object_r

  • Type of rpm_var_lib_t

Under SELinux with the targeted policy you can ignore everything except the Type.

When a file/directory (or any other object) is created, its security context will be copied from its environment (i.e for a file/directory its security context will be copied from the directory in which it was created). Utilities that are SELinux aware and move a file should preserve the security context. Utilities that are SELinux aware and copy a file will only preserve the security context if they are told to preserve permissions. Unfortunately this means that there is the possibility of a file having an incorrect security context resulting in the security policy not acting as expected.

After reading the avc denied message and finding out which operation was being attempted on which particular file/directory you then need to figure out if the denial was due to the security context being incorrect. How can you determine what the correct context should be ? Luckily the security policy has a database of default security contexts based on a path regular expression. The easiest way to check if the type is incorrect is to run fixfiles.

fixfiles check PATH

This will tell you if the security context of PATH is not equal to the default and not one of the customisable types listed in /etc/selinux/targeted/contexts/customizable_types. If the security context is set to one of the customisable types and you want to find out what the default security context should be then you can either use the matchpathcon utility, run semanage fcontext -l, or consult the policy values in /etc/selinux/targeted/contexts/files/. If you have files installed in an unusual location you should change the default file security context (see below).

If you determine there really are discrepancies, you should reset the security context to the default by using:

fixfiles relabel PATH

You can also use the restorecon utility.

You can use the chcon command to manually set the security context on the file or directory:

chcon -R system_u:object_r:mysqld_db_t mysql

It behaves like chown or chmod in most respects. Try to start the daemon again, and if there are any more avc denied messages, repeat the process iteratively until the daemon starts and there are no more avc denied messages.

NB: Our use of bind mounts can cause files in /data to be mislabelled. It is best to run fixfiles on individual directory structures and ignore /data

Changing the default security context of a file/directory

If the policy has an incorrect default security context for a file/directory you should change the defaults so that fixfiles or restorecon don't complain. First you need to determine what the security context should be. You can probably figure it out from seeing what the default security context is for a file in the default location. Otherwise you can have a read through /etc/selinux/targeted/src/policy/file_contexts/program (you will need the policy source installed).

semanage can be used to adjust the default file contexts for a file:

[root@galleon files]# matchpathcon /crap
/crap   system_u:object_r:default_t
[root@galleon files]# semanage fcontext -a -t home_root_t '/crap'
[root@galleon files]# matchpathcon /crap
/crap   system_u:object_r:home_root_t

There are also defaults made for home directories in /etc/selinux/targeted/contexts/files/file_contexts.homedirs . This file is generated by running genhomedircon based on /etc/selinux/targeted/contexts/files/homedir_template (entries are only made for user account with a different home directory to /home).

Automatic relabeling

restorecond can be used to magically set the security context of a new file to the default. See the man page for restorecond and restorecon for details.

Tuning the policy

Some parts of the policy are controlled by booleans. Changing a boolean is a quick and proper way of fixing some problems that you may encounter.

To get a list of booleans that you can change:

getsebool -a

To change a particular boolean:

setsebool -P BOOLEAN {true|false}

The -P flag to setsebool makes the change persistent.

Changing the policies


File type



Type enforcement

See for details


File contexts

Default file contexts


Policy module

Binary of compiled type enforcment policy


Policy package

The module and optional additonal files (contexts, users, etc)

In order to change the customise the running policy you can either:

  1. (Recommended) Generate a module policy and load the new module. The selinux sources are not required. This can be done via new versions of audit2allow

    $ cat /var/log/audit/audit.log | audit2allow -M local
    Generating type enforcment file: local.te
    Compiling policy: checkmodule -M -m -o local.mod local.te
    Building package: semodule_package -o local.pp -m local.mod
    ******************** IMPORTANT ***********************
    In order to load this newly created policy package into the kernel,
    you are required to execute
    semodule -i local.pp
    $ semodule -i local.pp

    The policy module will be stored in /etc/selinux/targeted/modules/active/modules and should still work after reboots. It can also be done manually

    $ cat /var/log/audit/audit.log | audit2allow -m local > local.te
    $ cat local.te
    module local 1.0;
    require {
           role system_r;
           class fifo_file {  getattr ioctl };
           type cupsd_config_t;
           type unconfined_t;
    allow cupsd_config_t unconfined_t:fifo_file { getattr ioctl };
    <review local.te and customize as desired>
    Building module policy manually
    # Compile the module
    $ checkmodule -M -m -o local.mod local.te
    # Create the package
    $ semodule_package -o local.pp -m local.mod
    # Load the module into the kernel
    $ semodule -i local.pp
  2. Generate a new monolithic policy: You will need to load the selinux sources, e.g. selinux-policy-targeted-sources (or selinux-policy-devel). This allows you to make changes, roll them out and activate the new policy straight away. They will be located in /etc/selinux/targeted/src. The audit2allow program reads the dmesg output and creates exceptions for any avc denied messages it finds. It can be run without any parameters, but it is best if you copy and paste the few entries into a temporary file and run audit2allow over it:

    cd /etc/selinux/targeted/src/policy/domains/misc/
    less /var/log/messages
    vi temp
    audit2allow -i temp -o local.te

    The local.te file will be integrated into the policy when you build it:

    cd /etc/selinux/targeted/src/policy
    make load

The new customised policy will now be active. Try to start the daemon, and again follow an iterative process to eliminate deny messages. Customising the running policy should only be used as a last resort measure, it is not a good idea generally to create a lot of policy exceptions.

Handling AVC denials efficiently

Integration with cfengine

  • Cfengine:
    • Automatically sets the mode to enforcing
    • Sets defaults to targeted/enforcing in /etc/selinux/config

    • Rolls out a makefile to /etc/selinux/anchor that is capable of compiling and installing new policy

    • Rolls out any TE policy files from cfinputs/$(host)/etc/selinux/anchor

    • Calls the makefile to install the policy customisations any time files are updated in /etc/selinux/anchor

  • Additions to the local policy files rolled out by cfengine need to be made manually in the cfengine-stored file.
  • The machine must be made a member of SELINUX_SERVICABLE for any of these changes to be made.

Shared Hosting security assumptions

  • Content is in ~/public_html/

  • CGIs are in ~/public_html/cgi-bin/

  • CGIs and PHP will read arbitrary files
  • New files uploaded via FTP/SSH need to be correctly labelled and working

Automatically restored security contexts

The default contexts below are


Default type


File ~/.htpasswd


Files matching ~/public_html/*.cgi


Files in ~/public_html/cgi-bin/


Directories can't be exec_t otherwise suexec won't work. You will get errors like cannot get docroot information

Files under ~/public_html/ that match .ht.*


Anything else in ~/public_html/


httpd_user_content_t is an alias for this type

Example paths for testing:


Uploading new PHP content via FTP/SSH


Uploading new static content via FTP/SSH


Uploading new CGIs via FTP/SSH



Vim syntax highlighting

Download into ~/.vim/syntax/ and then add to ~/.vim/filetype.vim

augroup filetypedetect
    au! BufRead,BufNewFile *.te     setfiletype te
augroup END

Further documentation

If you can't find what you are looking for on this page, try: