Why do I spend time blogging about security frameworks? Because, although there are some resources available on the Web, none apply to Percona XtraDB Cluster (PXC) directly. Actually, I rarely encounter a MySQL setup where SELinux is enforced and never when Percona XtraDB Cluster (PXC) or another Galera replication implementation is used. As we’ll see, there are good reasons for that. I originally thought this post would be a simple “how to” but it ended up with a push request to modify the SST script and a few other surprises.
Some context
These days, with all the major security breaches of the last few years, the importance of security in IT cannot be highlighted enough. For that reason, security in MySQL has been progressively tightened from version to version and the default parameters are much more restrictive than they used to be. That’s all good but it is only at the MySQL level if there is still a breach allowing access to MySQL, someone could in theory do everything the mysql user is allowed to do. To prevent such a situation, the operations that mysqld can do should be limited to only what it really needs to do. SELinux’ purpose is exactly that. You’ll find SELinux on RedHat/Centos and their derived distributions. Debian, Ubuntu and OpenSuse uses another framework, AppArmor, which is functionally similar to SELinux. I’ll talk about AppArmor in a future post, let’s focus for now on SELinux.
The default behavior of many DBAs and Sysadmins appears to be: “if it doesn’t work, disable SELinux”. Sure enough, it often solves the issue but it also removes an important security layer. I believe disabling SELinux is the wrong cure so let’s walk through the steps of configuring a PXC cluster with SELinux enforced.
Starting point
As a starting point, I’ll assume you have a running PXC cluster operating with SELinux in permissive mode. That likely means the file “/etc/sysconfig/selinux” looks like this:
1 2 3 4 5 6 7 8 9 10 11 |
# This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. SELINUX=permissive # SELINUXTYPE= can take one of three two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted |
For the purpose of writing this article, I created a 3 nodes PXC cluster with the hosts: BlogSELinux1, BlogSELinux2 and BlogSELinux3. On BlogSELinux1, I set SELinux in permissive mode, I truncated the audit.log. SELinux violations are logged in the audit.log file.
1 2 3 |
[root@BlogSELinux1 ~]# getenforce Permissive [root@BlogSELinux1 ~]# echo '' > /var/log/audit/audit.log |
Let’s begin by covering the regular PXC operation items like start, stop, SST Donor, SST Joiner, IST Donor and IST Joiner. As we execute the steps in the list, the audit.log file will record SELinux related elements.
Stop and start
Those are easy:
1 2 |
[root@BlogSELinux1 ~]# systemctl stop mysql [root@BlogSELinux1 ~]# systemctl start mysql |
SST Donor
On BlogSELinux3:
1 |
[root@BlogSELinux3 ~]# systemctl stop mysql |
then on BlogSELinux2:
1 2 3 |
[root@BlogSELinux2 ~]# systemctl stop mysql [root@BlogSELinux2 ~]# rm -f /var/lib/mysql/grastate.dat [root@BlogSELinux2 ~]# systemctl start mysql |
SST Joiner
We have BlogSELinux1 and BlogSELinux2 up and running, we just do:
1 2 3 |
[root@BlogSELinux1 ~]# systemctl stop mysql [root@BlogSELinux1 ~]# rm -f /var/lib/mysql/grastate.dat [root@BlogSELinux1 ~]# systemctl start mysql |
IST Donor
We have BlogSELinux1 and BlogSELinux2 up and running, we just do:
1 |
[root@BlogSELinux2 ~]# systemctl stop mysql |
Then on the first node:
1 2 3 |
[root@BlogSELinux1 ~]# mysql -e 'create database test;'; [root@BlogSELinux1 ~]# mysql -e 'create table test.testtable (id int not null, primary key (id)) engine=innodb;' [root@BlogSELinux1 ~]# mysql -e 'insert into test.testtable (id) values (1);' |
Those statements put some data in the gcache, now we just restart the second node:
1 |
[root@BlogSELinux2 ~]# systemctl start mysql |
IST Joiner
We have BlogSELinux1 and BlogSELinux2 up and running, we just do:
1 |
[root@BlogSELinux1 ~]# systemctl stop mysql |
Then on the second node:
1 |
[root@BlogSELinux2 ~]# mysql -e 'insert into test.testtable (id) values (2);' |
to insert some data in the gcache and we restart the first node:
1 |
[root@BlogSELinux1 ~]# systemctl start mysql |
First run
Now that we performed the basic operations of a cluster while recording the security violations in permissive mode, we can look at the audit.log file and start building the SELinux policy. Let’s begin by installing the tools needed to manipulate the SELinux audit log and policy files with:
1 |
[root@BlogSELinux1 ~]# yum install policycoreutils-python.x86_64 |
Then, we’ll use the audit2allow tool to analyze the audit.log file:
1 2 3 4 5 |
[root@BlogSELinux1 ~]# grep -i denied /var/log/audit/audit.log | grep mysqld_t | audit2allow -M PXC ******************** IMPORTANT *********************** To make this policy package active, execute: semodule -i PXC.pp |
We end up with 2 files, PXC.te and PXC.pp. The pp file is a compiled version of the human readable te file. If we examine the content of the PXC.te file, at the beginning, we have the require section listing all the involved SELinux types and classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
module PXC 1.0; require { type unconfined_t; type init_t; type auditd_t; type mysqld_t; type syslogd_t; type NetworkManager_t; type unconfined_service_t; type system_dbusd_t; type tuned_t; type tmp_t; type dhcpc_t; type sysctl_net_t; type kerberos_port_t; type kernel_t; type unreserved_port_t; type firewalld_t; type systemd_logind_t; type chronyd_t; type policykit_t; type udev_t; type mysqld_safe_t; type postfix_pickup_t; type sshd_t; type crond_t; type getty_t; type lvm_t; |