Data is the most important asset in a company, so you should take all the security considerations into account in your database to keep it as safe as possible. One important security feature on RedHat-based Operating Systems is SELinux. In this blog, we will see what this feature is and how to configure it for PostgreSQL and TimescaleDB databases.
What is SELinux?
Security-Enhanced Linux (SELinux) is a security architecture for Linux systems that allows administrators to have more control over who can access the system. It defines access controls for the applications, processes, and files on a system using security policies, which are a set of rules that tell SELinux what can be accessed.
When an application or process, known as a subject, makes a request to access an object, like a file, SELinux checks with an access vector cache (AVC), where permissions are cached for subjects and objects. If SELinux is unable to make a decision about access based on the cached permissions, it sends the request to the security server. The security server checks for the security context of the app or process and the file. The security context is applied from the SELinux policy database and the permission is granted or denied.
There are different ways to configure it. You can take a look at the main SELinux configuration file in /etc/selinux/config to see how it is currently configured.
$ cat /etc/selinux/config
# 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.
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
There are two directives in this file. The SELINUX directive specifies the SELinux mode and it can have three possible values: Enforcing, Permissive, or Disabled.
Enforcing (Default): It will enable and enforce the SELinux security policy on the system, denying unauthorized access attempts by users and processes.
Permissive: Using it, SELinux is enabled but will not enforce the security policy. All policy violations are logged in the audit logs. It is a good way to test SELinux before enforcing it.
Disabled: SELinux is turned off.
The SELINUXTYPE directive specifies the policy to be used. The default value is targeted and with this policy, SELinux allows you to customize and fine-tune access control permissions.
Even when the Enforcing mode is the default one, disabling SELinux has become a common practice as it is easier than cope with it. Of course, this is not recommended and you should have it configured at least in Permissive mode and check the logs periodically looking for unusual behavior.
How to Configure SELinux
If SELinux is disabled in your environment, you can enable it by editing the /etc/selinux/config configuration file and setting SELINUX=permissive or SELINUX=enforcing.
If you have never used SELinux, the best way to configure it is by using the Permissive mode first, check the logs looking for denied messages in the message log file, and fix it if needed:
$ grep "SELinux" /var/log/messages
After everything has been reviewed and it is safe to proceed, you can configure SELinux to enforcing using the previous method or without restarting by using the following command:
$ setenforce 1
If you need to rollback, run:
$ setenforce 0
This command can be used to switch between Enforcing and Permissive modes on the fly but these changes don’t persist if you reboot the system. You will need to configure the value in the configuration file to make it persistent.
You can check the current SELinux status using the getenforce command:
Or for more detailed information, you can use sestatus instead:
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: 33
How to Configure SELinux for PostgreSQL and TimescaleDB
If you are installing PostgreSQL/TimescaleDB using the default configuration and default data directory, and SELinux is also configured by default, most probably you won’t have any issue, but the problem is if you want to use, for example, a specific location where to store your database, so let’s see how to configure SELinux to make it works. For this example, we will install PostgreSQL 13 on CentOS 8, and will use /pgsql/data/ as the data directory.
First, enable the postgresql module:
$ dnf module enable postgresql:13
Install the corresponding PostgreSQL 13 packages:
$ dnf -y install postgresql-server postgresql-contrib postgresql-libs
Enable the service:
$ systemctl enable postgresql.service
Initialize your PostgreSQL database:
$ postgresql-setup --initdb
Now, if you just start the service without changing anything, like the data directory, it will start fine, otherwise, you will see an error like:
Jun 15 19:41:40 ip-172-31-24-90.us-east-2.compute.internal postmaster: postmaster: could not access the server configuration file "/pgsql/data/postgresql.conf": Permission denied
Jun 15 19:41:40 ip-172-31-24-90.us-east-2.compute.internal systemd: postgresql.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
Jun 15 19:41:40 ip-172-31-24-90.us-east-2.compute.internal systemd: postgresql.service: Failed with result 'exit-code'.
So, let’s see how to fix it. First, change your data directory in your PostgreSQL configuration file and service. For this, edit the PostgreSQL service file and change the PGDATA location:
$ vi /etc/systemd/system/multi-user.target.wants/postgresql.service
Edit the PostgreSQL bash profile file and change the PGDATA location:
$ vi /var/lib/pgsql/.bash_profile
Read the changes:
$ systemctl daemon-reload
Initialize the new PostgreSQL database:
$ postgresql-setup --initdb
* Initializing database in '/pgsql/data'
* Initialized, logs are in /var/lib/pgsql/initdb_postgresql.log
It is not necessary to specify the new data directory location as you did that previously in the configuration files. Now, before starting it, you need to change the SELinux label:
$ chcon -Rt postgresql_db_t /pgsql/data
This command will change the SELinux security context, and the flags are:
-R, –recursive: Operate on files and directories recursively
-t, –type=TYPE: Set type TYPE in the target security context
Then, start the PostgreSQL service:
$ systemctl start postgresql.service
And your database is running now:
$ ps aux |grep postgres |head -1
postgres 28566 0.0 3.0 497152 25312 ? Ss 21:16 0:00 /usr/bin/postmaster -D /pgsql/data
This is just a basic example of how to configure SELinux for PostgreSQL/TimescaleDB. There are different ways to do this with different restrictions or tools. The best SELinux implementation or configuration depends on the business requirements.
How to use PostgreSQL and TimescaleDB with ClusterControl and SELinux
ClusterControl doesn’t manage any Linux kernel security modules like SELinux. When deploying a PostgreSQL or TimescaleDB cluster by using ClusterControl, you can specify if you want ClusterControl to disable SELinux for you during the deployment process to reduce the risk of errors:
If you don’t want to disable it, you can use the Permissive mode and monitor the log in your servers to make sure that you have the correct SELinux configuration. After that, you can change it to Enforcing following the instructions mentioned above.