Working with SELinux

The Linux kernel, through the Linux Security Module (LSM), supports Security-Enhanced Linux (SELinux). This module provides a way to support mandatory access control policies. SELinux defines how confined processes interact with files, network ports, directories, other processes, and additional server components.

Rules determine how a process interacts with another resource. An SELinux policy defines the set of rules. SELinux makes decisions whether to allow or deny an action based on the subject’s context, what object initiates and what object is the action’s target. A label represents the context for administrators and users.

CentOS 7 and CentOS 8 contain a MySQL SELinux policy. Percona Server for MySQL is a drop-in replacement for MySQL and can use this policy without changes.

Example of an SELinux context

Here is an example of the context for mysqld:

$ system_u:system_r:mysqld_t:s0    3356 ?        00:00:01 mysqld

The context has the following information:

  • User - system_u
  • Role - system_r
  • Type or domain - mysqld_t
  • Sensitivity level - s0 3356

Most SELinux policy rules are based on the type or domain.

List SELinux Types or Domains associated with files

For Type Enforcement each policy module, are used for the interactions between processes and resources. SELinux type names often end with a _t.

To view the SELinux contexts, add the -Z switch to many of the utilities. For example, to view the SELinux types associated with MySQL directories and files, run the following command:

$ ls -laZ /var/lib/ | grep mysql
drwxr-x--x. mysql   mysql   system_u:object_r:mysqld_db_t:s0 mysql
drwxr-x---. mysql   mysql   system_u:object_r:mysqld_db_t:s0 mysql-files
drwxr-x---. mysql   mysql   system_u:object_r:mysqld_db_t:s0 mysql-keyring
$ grep ^SELINUX= /etc/selinux/config
SELINUX=enforcing

The /etc/selinux/config file controls if SELinux is disabled or enabled, and if enabled, whether SELinux operates in enforcing mode or permissive mode.

SELinux modes

SELinux has the following modes:

  • Disabled - No SELinux policy modules loaded, which disables policies. Nothing is reported.
  • Permissive - SELinux is active, but policy modules are not enforced. A policy violation is reported but does not stop the action.
  • Enforcing - SELinux is active, and violations are reported and denied. If there is no rule to allow access to a confined resource, SELinux denies the access.

Types of Policy

SELinux has two kinds of policies:

  • Targeted - Most processes operate without restriction. Specific services are contained in security domains and defined by policies.
  • Strict - All processes are contained in security domains and defined by policies.

SELinux has confined processes that run in a domain and restricts everything unless explicitly allowed. An unconfined process in an unconfined domain is allowed almost all access.

MySQL is a confined process, and the policy module defines which files are read, which ports are opened, and so on. SELinux assumes the Percona Server for MySQL installation uses the default file locations and default ports.

If you change the default, you must also edit the policy. If you do not update the policy, SELinux, in enforcing mode, denies access to all non-default resources.

Checking the SELinux mode

To check the current SELinux mode, use the following command:

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      31

Note

Add the -b option to sestatus to display the Policy booleans. The boolean values for each parameter is shown.

Disabling SELinux

If you plan to use the enforcing mode at another time, do not disable SELinux, instead use the Permissive mode. While SELinux is disabled, the system may contain mislabeled objects or objects with no label.

On boot, to disable SELinux, set the selinux=0 kernel option. The kernel does not load the SELinux infrastructure. This option has the same effect as changing the SELINUX=disabled instruction in the configuration file and then rebooting the system.

If you enable SELinux after disabling SELinux, you must follow the steps in Relabeling the entire file system.

Changing the mode in the configuration file

Switching between modes may help when troubleshooting or when modifying rules.

To permanently change the mode, edit the /etc/selinux/config file and change the SELINUX= value. You should also verify the change.

$ cat /etc/selinux/config | grep SELINUX= | grep -v ^#
SELINUX=enforcing
SELINUX=enforcing

$ sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config

$ cat /etc/selinux/config | grep SELINUX= | grep -v ^#
SELINUX=permissive
SELINUX=permissive

Reboot your system after the change.

If switching from either disabled mode or permissive mode to enforcing, see Relabeling the entire file system.

Changing the mode Until the next reboot

To change the mode until the next reboot, use either of the following commands as root:

$ setenforce Enforcing

or

$ setenforce 1
setenforce Options Also Permitted
0 Permissive
1 Enforcing

You can view the current mode by running the following command:

$ getenforce
Enforcing

Managing the SELinux environment

Install the SELinux management tools, such as semanage or sesearch, if needed.

On RHEL 7 or compatible operating systems, use the following command as root:

$ yum -y install policycoreutils-python

On RHEL 8 or compatible operating systems, use the following command as root:

$ yum -y install policycoreutils-python-utils

Note

You may need root privileges to run SELinux management commands.

Changing the mode for a service

You can move one or more services into a permissive domain. The other services remain in enforcing mode.

To add a service to the permissive domain, run the following as root:

$ sudo semanage permissive -a mysqld_t

To list any current permissive domains, run the following:

$ sudo semanage permissive -l

Customized Permissive Types

mysqld_t

Builtin Permissive Types

To delete a service from the permissive domain, run the following:

$ sudo semanage permissive -d mysqld_t

The service returns to the system’s SELinux mode. Be sure to follow the steps to Relabeling the entire file system.

Relabeling the entire file system

Changing from disabled or permissive to enforcing requires additional steps. The enforcing mode requires the correct contexts, or labels, to function. The permissive mode allows users and processes to label files and system objects incorrectly. The disabled mode does not load the SELinux infrastructure and does not label resources or processes.

RHEL and compatible systems, use the fixfiles application for relabeling. You can relabel the entire file system or the file contexts of an application.

For one application, run the following command:

$ fixfiles -R mysqld restore

To relabel the file system without rebooting the system, use the following command:

$ fixfiles -f -F relabel

Another option relabels the file system during a reboot. You can either add a touch file, read during the reboot operation, or configure a kernel boot parameter. The completion of the relabeling operation automatically removes the touch file.

Add the touch file as root:

$ touch /.autorelabel

To configure the kernel, add the autorelabel=1 kernel parameter to the boot parameter list. The parameter forces a system relabel. Reboot in permissive mode to allow the process to complete before changing to enforcing.

Note

Relabeling an entire filesystem takes time. When the relabeling is complete, the system reboots again.

Setting a Custom Data directory

If you do not use the default settings, SELinux, in enforcing mode, prevents access to the system.

For example, during installation, you have used the following configuration:

datadir=/var/lib/mysqlcustom
socket=/var/lib/mysqlcustom/mysql.sock

Restart the service.

$ service mysqld restart
Redirecting to /bin/systemctl restart mysqld.service
Job for mysqld.service failed because the control process exited with error code.
See "systemctl status mysqld.service" and "journalctl -xe" for details.

Check the journal log to see the error code.

$ journalctl -xe
...
SELinux is preventing mysqld from getattr access to the file /var/lib/mysqlcustom/ibdata1.
...

Check the SELinux types in /var/lib/mysqlcustom.

ls -1aZ /var/lib/mysqlcustom
total 164288
drwxr-x--x.  6 mysql mysql system_u:object_r:var_lib_t:s0       4096 Dec  2 07:58  .
drwxr-xr-x. 38 root  root  system_u:object_r:var_lib_t:s0       4096 Dec  1 14:29  ..
...
-rw-r-----.  1 mysql mysql system_u:object_r:var_lib_t:s0   12582912 Dec  1 14:29  ibdata1
...

To solve the issue, use the following methods:

  • Set the proper labels for mysqlcustom files
  • Change the mysqld SELinux policy to allow mysqld access to var_lib_t files.

The recommended solution is to set the labels. The following procedure assumes you have already created and set ownership to the custom data directory location:

  1. To change the SELinux context, use semanage fcontext. In this step, you define how SELinux deals with the custom paths:

    SELinux applies the same labeling schema, defined in the mysqld policy, for the /var/lib/mysql directory to the custom directory. Files created within the custom directory are labeled as if they were in /var/lib/mysql.

  2. To restorecon command applies the change.

    $ restorecon -R -v /var/lib/mysqlcustom
    
  3. Restart the mysqld service:

    $ service mysqld start
    

Setting a Custom Log Location

If you do not use the default settings, SELinux, in enforcing mode, prevents access to the system. If you move your logs to a custom location in my.cnf:

log-error=/logs/mysqld.log

Verify the logs with the following command:

$ ls -laZ /
...
drwxrwxrwx.   2 root root unconfined_u:object_r:default_t:s0    6 Dec  2 09:16 logs
...

Starting MySQL returns the following message:

$ service mysql start
Redirecting to /bin/systemctl start mysql.service
Job for mysqld.service failed because the control process exited with error code.
See "systemctl status mysqld.service" and "journalctl -xe" for details.

$ journalctl -xe
...
SELinux is preventing mysqld from write access to the directory logs.
...

The default SELinux policy allows mysqld to write logs into a directory tagged with var_log_t`, which is the ``/var/log. You can solve the issue with the following methods:

  • Tag the /logs directory properly
  • Edit the SELinux policy to allow mysqld access to the directories.

Tag the /logs directory is the recommended method since it locks down access. Run the following commands:

$ semanage fcontext -a -t var_log_t /logs
$ restorecon -v /logs

You may not be able to change the /logs directory label. For example, other applications, with their own rules use the same directory. To adjust the SELinux policy, follow these steps:

  1. Create a local policy:

    ausearch -c 'mysqld' --raw | audit2allow -M my-mysqld
    
  2. This command generates the my-mysqld.te and the my-mysqld.pp files. The mysqld.te file is a human-readable policy definition. The my-mysqld.pp is the policy module loaded into the SELinux subsystem.

    Open the my-myslqd.te file for editing:

    module my-mysqld 1.0;
    
    require {
        *type mysqld_t*;
        type var_lib_t;
        *type default_t*;
        class file getattr;
        *class dir write*;
    }
    
    #============= mysqld_t ==============
    *allow mysqld_t default_t:dir write*;
    allow mysqld_t var_lib_t:file getattr;
    

    The policy contains rules for the custom data directory and the custom logs directory. We have set the proper labels for the data directory, and applying this autogenerated policy would loosen our hardening by allowing mysqld to access var_lib_t tags.

  3. SELinux-generated events are converted to rules. A generated policy may contain rules for recent violations and include unrelated rules. Unrelated rules are generated from actions, such as changing the data directory location, that are not related to the logs directory.

Add the --start parameter to use log events after a specific time. The parameter captures events when the time stamp is equals to the specified time or later. SELinux generates a policy for the current actions.

$ ausearch --start 10:00:00 -c 'mysqld' --raw | audit2allow -M my-mysqld
  1. This policy allows mysqld writing into the tagged directories. Open the my_mysqld file:

    module my-mysqld 1.0;
    
    require {
        type mysqld_t;
        type default_t;
        class dir write;
    }
    
    #============= mysqld_t ==============
    allow mysqld_t default_t:dir write;
    
  2. Install the SELinux policy module:

    $ semodule -i my-mysqld.pp
    

After restarting the service, if you have a failure, check the journal log. You may view the following:

SELinux is preventing mysqld from add_name access on the directory mysqld.log.

SELinux prevents mysql from creating a log file inside the directory.

You can view all the violations by changing the SELinux mode to permissive and then running mysqld. All violations are logged in the journal log. After this run, you can generate a local policy module, install it, and switch SELinux back to enforcing mode.

  1. Unload the current local my-mysqld policy module:

    $ semodule -r my-mysqld
    
  2. You can put a single domain into permissive mode. Other domains on the system to remain in enforcing mode. Use semanage permissive with the -a parameter to change mysqld_t to permissive mode:

    $ semanage permissive -a mysqld_t
    
  3. Verify the mode change:

    semdule -l | grep permissive
    ...
    permissive_mysqld_t
    ...
    
  4. To make searching the log easier, return the time:

    $ date
    
  5. Start the service.

    $ service mysqld start
    
  6. MySQL starts, and SELinux logs the violations in the journal log. Check the journal log:

    $ journalctl -xe
    
  7. Stop the service:

    $ service mysqld stop
    
  8. Generate a local mysqld policy, using the time returned from step 4:

    $ ausearch --start <date> -c 'mysqld' --raw | audit2allow -M my-mysqld
    
  9. Review the policy (the policy you generate may be different):

    $ cat my-mysqld.te
    module my-mysqld 1.0;
    
    require {
    type default_t;
        type mysqld_t;
        class dir { add_name write };
        class file { append create open };
    }
    
    #============= mysqld_t ==============
    allow mysqld_t default_t:dir { add_name write };
    allow mysqld_t default_t:file { append create open };
    
  10. Install the policy:

    $ semodule -i my-mysqld.pp
    
  11. Use semanage permissive with the -d parameter, which deletes the permissive domain for the service:

    $ semanage permissive -d mysqld_t
    
  12. Restart the service:

    $ service mysqld start
    

Note

Use this procedure to adjust the local mysqld policy module. You should review the changes which are generated to ensure the rules are not too tolerant.

Setting secure_file_priv directory

Update the SELinux tags for the /var/lib/mysql-files/ directory, used for SELECT ... INTO OUTFILE or similar operations, if required. The server needs only read/write access to the destination directory.

To set secure_file_priv to use this directory, run the following commands to set the context:

$ semanage fcontext -a -t mysqld_db_t "/var/lib/mysql-files/(/.*)?"
$ restorecon -Rv /var/lib/mysql-files

Edit the path for a different location, if needed.

Contact Us

For free technical help, visit the Percona Community Forum.
To report bugs or submit feature requests, open a JIRA ticket.
For paid support and managed or professional services, contact Percona Sales.