
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.
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.
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 |
# This file controls the state of SELinux on the system.<br># SELINUX= can take one of these three values:<br># enforcing - SELinux security policy is enforced.<br># permissive - SELinux prints warnings instead of enforcing.<br># disabled - No SELinux policy is loaded.<br>SELINUX=permissive<br># SELINUXTYPE= can take one of three two values:<br># targeted - Targeted processes are protected,<br># minimum - Modification of targeted policy. Only selected processes are protected.<br># mls - Multi Level Security protection.<br>SELINUXTYPE=targeted<br> |
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 |
[root@BlogSELinux1 ~]# getenforce<br>Permissive<br>[root@BlogSELinux1 ~]# echo '' > /var/log/audit/audit.log<br> |
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.
Those are easy:
|
1 |
[root@BlogSELinux1 ~]# systemctl stop mysql<br>[root@BlogSELinux1 ~]# systemctl start mysql<br> |
On BlogSELinux3:
|
1 |
[root@BlogSELinux3 ~]# systemctl stop mysql<br> |
then on BlogSELinux2:
|
1 |
[root@BlogSELinux2 ~]# systemctl stop mysql<br>[root@BlogSELinux2 ~]# rm -f /var/lib/mysql/grastate.dat<br>[root@BlogSELinux2 ~]# systemctl start mysql<br> |
We have BlogSELinux1 and BlogSELinux2 up and running, we just do:
|
1 |
[root@BlogSELinux1 ~]# systemctl stop mysql<br>[root@BlogSELinux1 ~]# rm -f /var/lib/mysql/grastate.dat<br>[root@BlogSELinux1 ~]# systemctl start mysql<br> |
We have BlogSELinux1 and BlogSELinux2 up and running, we just do:
|
1 |
[root@BlogSELinux2 ~]# systemctl stop mysql<br> |
Then on the first node:
|
1 |
[root@BlogSELinux1 ~]# mysql -e 'create database test;';<br>[root@BlogSELinux1 ~]# mysql -e 'create table test.testtable (id int not null, primary key (id)) engine=innodb;'<br>[root@BlogSELinux1 ~]# mysql -e 'insert into test.testtable (id) values (1);'<br> |
Those statements put some data in the gcache, now we just restart the second node:
|
1 |
[root@BlogSELinux2 ~]# systemctl start mysql<br> |
We have BlogSELinux1 and BlogSELinux2 up and running, we just do:
|
1 |
[root@BlogSELinux1 ~]# systemctl stop mysql<br> |
Then on the second node:
|
1 |
[root@BlogSELinux2 ~]# mysql -e 'insert into test.testtable (id) values (2);'<br> |
to insert some data in the gcache and we restart the first node:
|
1 |
[root@BlogSELinux1 ~]# systemctl start mysql<br> |
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<br> |
Then, we’ll use the audit2allow tool to analyze the audit.log file:
|
1 |
[root@BlogSELinux1 ~]# grep -i denied /var/log/audit/audit.log | grep mysqld_t | audit2allow -M PXC<br>******************** IMPORTANT ***********************<br>To make this policy package active, execute:<br><br>semodule -i PXC.pp<br> |
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 |
module PXC 1.0;<br><br>require {<br> type unconfined_t;<br> type init_t;<br> type auditd_t;<br> type mysqld_t;<br> type syslogd_t;<br> type NetworkManager_t;<br> type unconfined_service_t;<br> type system_dbusd_t;<br> type tuned_t;<br> type tmp_t;<br> type dhcpc_t;<br> type sysctl_net_t;<br> type kerberos_port_t;<br> type kernel_t;<br> type unreserved_port_t;<br> type firewalld_t;<br> type systemd_logind_t;<br> type chronyd_t;<br> type policykit_t;<br> type udev_t;<br> type mysqld_safe_t;<br> type postfix_pickup_t;<br> type sshd_t;<br> type crond_t;<br> type getty_t;<br> type lvm_t;<br> type postfix_qmgr_t;<br> type postfix_master_t;<br> class process { getattr setpgid };<br> class unix_stream_socket connectto;<br> class system module_request;<br> class netlink_tcpdiag_socket { bind create getattr nlmsg_read setopt };<br> class tcp_socket { name_bind name_connect };<br> class file { getattr open read write };<br> class dir search;<br>}<br> |
Then, using these types and classes, the policy file adds a series of generic allow rules matching the denied found in the audit.log file. Here’s what I got:
|
1 |
#============= mysqld_t ==============<br>allow mysqld_t NetworkManager_t:process getattr;<br>allow mysqld_t auditd_t:process getattr;<br>allow mysqld_t chronyd_t:process getattr;<br>allow mysqld_t crond_t:process getattr;<br>allow mysqld_t dhcpc_t:process getattr;<br>allow mysqld_t firewalld_t:process getattr;<br>allow mysqld_t getty_t:process getattr;<br>allow mysqld_t init_t:process getattr;<br><br>#!!!! This avc can be allowed using the boolean 'nis_enabled'<br>allow mysqld_t kerberos_port_t:tcp_socket name_bind;<br>allow mysqld_t kernel_t:process getattr;<br><br>#!!!! This avc can be allowed using the boolean 'domain_kernel_load_modules'<br>allow mysqld_t kernel_t:system module_request;<br>allow mysqld_t lvm_t:process getattr;<br>allow mysqld_t mysqld_safe_t:process getattr;<br>allow mysqld_t policykit_t:process getattr;<br>allow mysqld_t postfix_master_t:process getattr;<br>allow mysqld_t postfix_pickup_t:process getattr;<br>allow mysqld_t postfix_qmgr_t:process getattr;<br>allow mysqld_t sysctl_net_t:file { getattr open read };<br>allow mysqld_t syslogd_t:process getattr;<br>allow mysqld_t system_dbusd_t:process getattr;<br>allow mysqld_t systemd_logind_t:process getattr;<br>allow mysqld_t tuned_t:process getattr;<br>allow mysqld_t udev_t:process getattr;<br>allow mysqld_t unconfined_service_t:process getattr;<br>allow mysqld_t unconfined_t:process getattr;<br>allow mysqld_t tuned_t:process getattr;<br>allow mysqld_t udev_t:process getattr;<br>allow mysqld_t sshd_t:process getattr;<br>allow mysqld_t self:netlink_tcpdiag_socket { bind create getattr nlmsg_read setopt };<br><br>allow mysqld_t self:process { getattr setpgid };<br><br>#!!!! The file '/var/lib/mysql/mysql.sock' is mislabeled on your system.<br>#!!!! Fix with $ restorecon -R -v /var/lib/mysql/mysql.sock<br>#!!!! This avc can be allowed using the boolean 'daemons_enable_cluster_mode'<br>allow mysqld_t self:unix_stream_socket connectto;<br>allow mysqld_t sshd_t:process getattr;<br>allow mysqld_t sysctl_net_t:dir search;<br>allow mysqld_t sysctl_net_t:file { getattr open read };<br>allow mysqld_t syslogd_t:process getattr;<br>allow mysqld_t system_dbusd_t:process getattr;<br>allow mysqld_t systemd_logind_t:process getattr;<br><br>#!!!! WARNING 'mysqld_t' is not allowed to write or create to tmp_t. Change the label to mysqld_tmp_t.<br>allow mysqld_t tmp_t:file write;<br>allow mysqld_t tuned_t:process getattr;<br>allow mysqld_t udev_t:process getattr;<br>allow mysqld_t unconfined_service_t:process getattr;<br>allow mysqld_t unconfined_t:process getattr;<br><br>#!!!! This avc can be allowed using one of the these booleans:<br># nis_enabled, mysql_connect_any<br>allow mysqld_t unreserved_port_t:tcp_socket { name_bind name_connect };<br> |
I can understand some of these rules. For example, one of the TCP ports used by Kerberos is 4444 and it is also used by PXC for the SST transfer. Similarly, MySQL needs to write to /tmp. But what about all the other rules?
We could load the PXC.pp module we got in the previous section and consider our job done. It will likely allow the PXC node to start and operate normally but what exactly is happening? Why did MySQL or one of its subprocesses asked for the process attributes getattr of all the running processes like sshd, syslogd and cron. Looking directly in the audit.log file, I found many entries like these:
|
1 |
type=AVC msg=audit(1527792830.989:136): avc: denied { getattr } for pid=3683 comm="ss"<br> scontext=system_u:system_r:mysqld_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process<br>type=AVC msg=audit(1527792830.990:137): avc: denied { getattr } for pid=3683 comm="ss"<br> scontext=system_u:system_r:mysqld_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclass=process<br>type=AVC msg=audit(1527792830.991:138): avc: denied { getattr } for pid=3683 comm="ss"<br> scontext=system_u:system_r:mysqld_t:s0 tcontext=system_u:system_r:syslogd_t:s0 tclass=process<br> |
So, ss, a network utility tool, scans all the processes. That rang a bell… I knew where to look for, the sst script. Here’s the source of the problem in the wsrep_sst_xtrabackup-v2 file:
|
1 |
wait_for_listen()<br>{<br> local HOST=$1<br> local PORT=$2<br> local MODULE=$3<br><br> for i in {1..300}<br> do<br> ss -p state listening "( sport = :$PORT )" | grep -qE 'socat|nc' && break<br> sleep 0.2<br> done<br><br> echo "ready ${HOST}:${PORT}/${MODULE}//$sst_ver"<br>}<br> |
This bash function is used when the node is a joiner and it checks using ss if the TCP port used by socat or nc is opened. The check is needed in order to avoid replying too early with the “ready” message. The code is functionally correct but wrong, security wise. Instead of looking if there is a socat or nc command running in the list of processes owned by the mysql user, it checks if any of the processes has opened the SST port and only then does it checks if the name of the command is socat or nc. Since we don’t know which processes will be running on the server, we can’t write a good security profile. For example, in the future, one could add the ntpd daemon, causing PXC to fail to start yet again. To avoid that, the function needs to be modified like this:
|
1 |
wait_for_listen()<br>{<br> local HOST=$1<br> local PORT=$2<br> local MODULE=$3<br><br> for i in {1..300}<br> do<br> sleep 0.2<br> # List only our (mysql user) processes to avoid triggering SELinux<br> for cmd in $(ps -u $(id -u) -o pid,comm | sed 's/^s*//g' | tr ' ' '|' | grep -E 'socat|nc')<br> do<br> pid=$(echo $cmd | cut -d'|' -f1)<br> # List the sockets of the pid<br> sockets=$(ls -l /proc/$pid/fd | grep socket | cut -d'[' -f2 | cut -d ']' -f1 | tr 'n' '|')<br> if [[ -n $sockets ]]; then<br> # Is one of these sockets listening on the SST port? <br> # If so, we need to break from 2 loops<br> grep -E "${sockets:0:-1}" /proc/$pid/net/tcp | <br> grep "00000000:$(printf '%X' $PORT)" > /dev/null <br> && break 2<br> fi<br> done<br> done<br><br> echo "ready ${HOST}:${PORT}/${MODULE}//$sst_ver"<br>}<br> |
The modified function removes many of the denied messages in the audit log file and simplifies a lot the content of PXC.te. I tested the above modification and made a pull request to PXC. Among the remaining items, we have:
|
1 |
allow mysqld_t self:process { getattr setpgid };<br> |
setpgid is called often used after a fork to set the process group, usually through the setsid call. MySQL uses fork when it starts with the daemonize option but our installation of Percona XtraDB cluster uses mysqld_safe and does not directly run as a daemon. Another fork call is part of the wsrep source files and is used to launch processes like the SST script and is done when mysqld is already running with reduced privileges. This later invocation is certainly our culprit.
What about TPC ports? PXC uses quite a few. Of course there is the 3306/tcp port used to access MySQL. Galera also uses the ports 4567/tcp for replication, 4568/tcp for IST and 4444/tcp for SST. Let’s have a look which ports SELinux allows PXC to use:
|
1 |
[root@BlogSELinux1 audit]# semanage port -l | grep mysql<br>mysqld_port_t tcp 1186, 3306, 63132-63164<br> |
No surprise, port 3306/tcp is authorized but if you are new to MySQL, you may wonder what uses the 1186/tcp. It is the port used by NDB cluster for inter-node communication (NDB API). Now, if we try to add the missing ports:
|
1 |
[root@BlogSELinux1 audit]# semanage port -a -t mysqld_port_t -p tcp 4567<br>ValueError: Port tcp/4567 already defined<br>[root@BlogSELinux1 audit]# semanage port -a -t mysqld_port_t -p tcp 4568<br>[root@BlogSELinux1 audit]# semanage port -a -t mysqld_port_t -p tcp 4444<br>ValueError: Port tcp/4444 already defined<br> |
4568/tcp was successfully added but, 4444/tcp and 4567/tcp failed because they are already assigned to another security context. For example, 4444/tcp belongs to the kerberos security context:
|
1 |
[root@BlogSELinux1 audit]# semanage port -l | grep kerberos_port<br>kerberos_port_t tcp 88, 750, 4444<br>kerberos_port_t udp 88, 750, 4444<br> |
A TCP port is not allowed by SELinux to belong to more than one security context. We have no other choice than to move the two missing ports to the mysqld_t security context:
|
1 |
[root@BlogSELinux1 audit]# semanage port -m -t mysqld_port_t -p tcp 4444<br>[root@BlogSELinux1 audit]# semanage port -m -t mysqld_port_t -p tcp 4567<br>[root@BlogSELinux1 audit]# semanage port -l | grep mysqld<br>mysqld_port_t tcp 4567, 4444, 4568, 1186, 3306, 63132-63164<br> |
If you happen to be planning to deploy a Kerberos server on the same servers you may have to run PXC using a different port for Galera replication. In that case, and in the case where you want to run MySQL on a port other than 3306/tcp, you’ll need to add the port to the mysqld_port_t context like we just did above. Do not worry too much for the port 4567/tcp, it is reserved for tram which, from what I found, is a remote access protocol for routers.
It is very frequent to run MySQL with non-standard paths/directories. With SELinux, you don’t list the authorized path in the security context, you add the security context labels to the paths. Adding a context label is a two steps process, basically change and apply. For example, if you are using /data as the MySQL datadir, you need to do:
|
1 |
semanage fcontext -a -t mysqld_db_t "/data(/.*)?"<br>restorecon -R -v /data<br> |
On a RedHat/Centos 7 server, the MySQL file contexts and their associated paths are:
|
1 |
[root@BlogSELinux1 ~]# bzcat /etc/selinux/targeted/active/modules/100/mysql/cil | grep filecon<br>(filecon "HOME_DIR/.my.cnf" file (system_u object_r mysqld_home_t ((s0) (s0))))<br>(filecon "/root/.my.cnf" file (system_u object_r mysqld_home_t ((s0) (s0))))<br>(filecon "/usr/lib/systemd/system/mysqld.*" file (system_u object_r mysqld_unit_file_t ((s0) (s0))))<br>(filecon "/usr/lib/systemd/system/mariadb.*" file (system_u object_r mysqld_unit_file_t ((s0) (s0))))<br>(filecon "/etc/my.cnf" file (system_u object_r mysqld_etc_t ((s0) (s0))))<br>(filecon "/etc/mysql(/.*)?" any (system_u object_r mysqld_etc_t ((s0) (s0))))<br>(filecon "/etc/my.cnf.d(/.*)?" any (system_u object_r mysqld_etc_t ((s0) (s0))))<br>(filecon "/etc/rc.d/init.d/mysqld" file (system_u object_r mysqld_initrc_exec_t ((s0) (s0))))<br>(filecon "/etc/rc.d/init.d/mysqlmanager" file (system_u object_r mysqlmanagerd_initrc_exec_t ((s0) (s0))))<br>(filecon "/usr/bin/mysqld_safe" file (system_u object_r mysqld_safe_exec_t ((s0) (s0))))<br>(filecon "/usr/bin/mysql_upgrade" file (system_u object_r mysqld_exec_t ((s0) (s0))))<br>(filecon "/usr/libexec/mysqld" file (system_u object_r mysqld_exec_t ((s0) (s0))))<br>(filecon "/usr/libexec/mysqld_safe-scl-helper" file (system_u object_r mysqld_safe_exec_t ((s0) (s0))))<br>(filecon "/usr/sbin/mysqld(-max)?" file (system_u object_r mysqld_exec_t ((s0) (s0))))<br>(filecon "/usr/sbin/mysqlmanager" file (system_u object_r mysqlmanagerd_exec_t ((s0) (s0))))<br>(filecon "/usr/sbin/ndbd" file (system_u object_r mysqld_exec_t ((s0) (s0))))<br>(filecon "/var/lib/mysql(-files|-keyring)?(/.*)?" any (system_u object_r mysqld_db_t ((s0) (s0))))<br>(filecon "/var/lib/mysql/mysql.sock" socket (system_u object_r mysqld_var_run_t ((s0) (s0))))<br>(filecon "/var/log/mariadb(/.*)?" any (system_u object_r mysqld_log_t ((s0) (s0))))<br>(filecon "/var/log/mysql.*" file (system_u object_r mysqld_log_t ((s0) (s0))))<br>(filecon "/var/run/mariadb(/.*)?" any (system_u object_r mysqld_var_run_t ((s0) (s0))))<br>(filecon "/var/run/mysqld(/.*)?" any (system_u object_r mysqld_var_run_t ((s0) (s0))))<br>(filecon "/var/run/mysqld/mysqlmanager.*" file (system_u object_r mysqlmanagerd_var_run_t ((s0) (s0))))<br> |
If you want to avoid security issues with SELinux, you should stay within those paths. A good example of an offending path is the PXC configuration file and directory which are now located in their own directory. These are not labeled correctly for SELinux:
|
1 |
[root@BlogSELinux1 ~]# ls -Z /etc/per*<br>-rw-r--r--. root root system_u:object_r:etc_t:s0 /etc/percona-xtradb-cluster.cnf<br><br>/etc/percona-xtradb-cluster.conf.d:<br>-rw-r--r--. root root system_u:object_r:etc_t:s0 mysqld.cnf<br>-rw-r--r--. root root system_u:object_r:etc_t:s0 mysqld_safe.cnf<br>-rw-r--r--. root root system_u:object_r:etc_t:s0 wsrep.cnf<br> |
I must admit that even if the security context labels on those files were not set, I got no audit messages and everything worked normally. Nevetheless, adding the labels is straightforward:
|
1 |
[root@BlogSELinux1 ~]# semanage fcontext -a -t mysqld_etc_t "/etc/percona-xtradb-cluster.cnf"<br>[root@BlogSELinux1 ~]# semanage fcontext -a -t mysqld_etc_t "/etc/percona-xtradb-cluster.conf.d(/.*)?"<br>[root@BlogSELinux1 ~]# restorecon -v /etc/percona-xtradb-cluster.cnf<br>restorecon reset /etc/percona-xtradb-cluster.cnf context system_u:object_r:etc_t:s0->system_u:object_r:mysqld_etc_t:s0<br>[root@BlogSELinux1 ~]# restorecon -R -v /etc/percona-xtradb-cluster.conf.d/<br>restorecon reset /etc/percona-xtradb-cluster.conf.d context system_u:object_r:etc_t:s0->system_u:object_r:mysqld_etc_t:s0<br>restorecon reset /etc/percona-xtradb-cluster.conf.d/wsrep.cnf context system_u:object_r:etc_t:s0->system_u:object_r:mysqld_etc_t:s0<br>restorecon reset /etc/percona-xtradb-cluster.conf.d/mysqld.cnf context system_u:object_r:etc_t:s0->system_u:object_r:mysqld_etc_t:s0<br>restorecon reset /etc/percona-xtradb-cluster.conf.d/mysqld_safe.cnf context system_u:object_r:etc_t:s0->system_u:object_r:mysqld_etc_t:s0<br> |
Here is a list of all the variables you should check for paths used by MySQL
That’s quite a long list and I may have missed some. If for any of these variables you use a non-standard path, you’ll need to adjust the context labels as we just did above.
I would understand if you feel a bit lost, I am not a SELinux guru and it took me some time to understand decently how it works. Let’s recap how we can enable SELinux for PXC from what we learned in the previous sections.
|
1 |
yum install policycoreutils-python.x86_64<br> |
|
1 |
semanage port -a -t mysqld_port_t -p tcp 4568<br>semanage port -m -t mysqld_port_t -p tcp 4444<br>semanage port -m -t mysqld_port_t -p tcp 4567<br> |
Replace the wait_for_listen function in the /usr/bin/wsrep_sst_xtrabackup-v2 file by the version above. Hopefully, the next PXC release will include a SELinux friendly wait_for_listen function.
These steps seems optional but for completeness:
|
1 |
semanage fcontext -a -t mysqld_etc_t "/etc/percona-xtradb-cluster.cnf"<br>semanage fcontext -a -t mysqld_etc_t "/etc/percona-xtradb-cluster.conf.d(/.*)?"<br>restorecon -v /etc/percona-xtradb-cluster.cnf<br>restorecon -R -v /etc/percona-xtradb-cluster.conf.d/<br> |
Create the file PXC.te with this content:
|
1 |
module PXC 1.0;<br><br>require {<br> type unconfined_t;<br> type mysqld_t;<br> type unconfined_service_t;<br> type tmp_t;<br> type sysctl_net_t;<br> type kernel_t;<br> type mysqld_safe_t;<br> class process { getattr setpgid };<br> class unix_stream_socket connectto;<br> class system module_request;<br> class file { getattr open read write };<br> class dir search;<br>}<br><br>#============= mysqld_t ==============<br><br>allow mysqld_t kernel_t:system module_request;<br>allow mysqld_t self:process { getattr setpgid };<br>allow mysqld_t self:unix_stream_socket connectto;<br>allow mysqld_t sysctl_net_t:dir search;<br>allow mysqld_t sysctl_net_t:file { getattr open read };<br>allow mysqld_t tmp_t:file write;<br> |
|
1 |
checkmodule -M -m -o PXC.mod PXC.te<br>semodule_package -o PXC.pp -m PXC.mod<br>semodule -i PXC.pp<br> |
Set SELinux into permissive mode in /etc/sysconfig/selinux and reboot. Validate everything works fine in Permissive mode, check the audit.log for any denied messages. If there are denied messages, address them.
Last step, enforce SELinux:
|
1 |
setenforce 1<br>perl -pi -e 's/SELINUX=permissive/SELINUX=enforcing/g' /etc/sysconfig/selinux<br> |
As we can see, enabling SELinux with PXC is not straightforward but, once the process is understood, it is not that hard either. In an IT world where security is more than ever a major concern, enabling SELinux with PXC is a nice step forward. In an upcoming post, we’ll look at the other security framework, Apparmor.
Resources
RELATED POSTS