Security Enhanced Linux (SELinux) provides an additional layer of system security, mostly adopted in Red Hat based Linux systems. SELinux implements Mandatory Access Control (MAC) to enable system administrators to create comprehensive and fine-grained security policies.

In SELinux, every system resource and process has a special security label called an SELinux context, also referred to as SELinux label. SELinux context is an identifier which abstracts away the system-level details and focuses on the security properties of the entity. An SELinux policy use the contexts defined in a series of rules that defines how a process interacts with other processes and various system resources.

SELinux

In this article we walk you through how you can configure your WordPress website to work with SELinux in enforcing Mode. We will do a complete setup of wordpress website and how all the configurations are performed.

Step 1: Install PHP & Web Server

We will setup a wordpress website which uses Nginx as web server and PHP-FPM to serve PHP pages.

I’m working on CentOS 8 server and below commands will be used to install PHP and required extensions.

sudo yum -y install @php
sudo yum -y install php php-cli php-fpm php-mysqlnd php-zip php-devel php-gd php-mbstring php-curl php-xml php-pear php-bcmath php-json

Install Nginx or Apache Web server

## Using Nginx Web Server
sudo yum -y install @nginx

## Using Apache httpd server
sudo yum -y install @httpd

Step 2: Setup MariaDB Database Server

Install MariaDB database server.

sudo yum -y install @mariadb

Start and enable mariadb service:

sudo systemctl enable --now mariadb

Confirm that mariadb service is in running state:

$ systemctl status mariadb
● mariadb.service - MariaDB 10.3 database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2021-01-28 09:14:58 UTC; 27s ago
     Docs: man:mysqld(8)
           https://mariadb.com/kb/en/library/systemd/
  Process: 17237 ExecStartPost=/usr/libexec/mysql-check-upgrade (code=exited, status=0/SUCCESS)
  Process: 17102 ExecStartPre=/usr/libexec/mysql-prepare-db-dir mariadb.service (code=exited, status=0/SUCCESS)
  Process: 17078 ExecStartPre=/usr/libexec/mysql-check-socket (code=exited, status=0/SUCCESS)
 Main PID: 17205 (mysqld)
   Status: "Taking your SQL requests now..."
    Tasks: 30 (limit: 4763)
   Memory: 79.5M
   CGroup: /system.slice/mariadb.service
           └─17205 /usr/libexec/mysqld --basedir=/usr

Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: See the MariaDB Knowledgebase at http://mariadb.com/kb or the
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: MySQL manual for more instructions.
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: Please report any problems at http://mariadb.org/jira
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: The latest information about MariaDB is available at http://mariadb.org/.
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: You can find additional information about the MySQL part at:
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: http://dev.mysql.com
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: Consider joining MariaDB's strong and vibrant community:
Jan 28 09:14:58 centos mysql-prepare-db-dir[17102]: https://mariadb.org/get-involved/
Jan 28 09:14:58 centos mysqld[17205]: 2021-01-28  9:14:58 0 [Note] /usr/libexec/mysqld (mysqld 10.3.27-MariaDB) starting as process 17205 ...
Jan 28 09:14:58 centos systemd[1]: Started MariaDB 10.3 database server.

Secure your database server instalallation:

$ sudo mysql_secure_installation

Login to MariaDB shell and create wordpress website database:

$ mysql -u root -p
CREATE DATABASE wordpress;
GRANT ALL ON wordpress.* TO [email protected] IDENTIFIED BY "StrongDBPassw0rd";
FLUSH PRIVILEGES;
QUIT

Test connection to the database with the user we created.

$ mysql -u wordpress_user -p'StrongDBPassw0rd'
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 17
Server version: 10.3.27-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| wordpress          |
+--------------------+
2 rows in set (0.001 sec)

MariaDB [(none)]> \q
Bye

Step 3: Setup WordPress Website

Download latest wordpress for installation:

sudo yum -y install wget vim bash-completion
wget wordpress.org/latest.tar.gz

Extract downloaded wordpress archive:

tar xvf latest.tar.gz

Move wordpress folder created to /var/www directory:

sudo mv wordpress /var/www/mywebsite

Copy configuration template:

sudo cp /var/www/mywebsite/wp-config-sample.php /var/www/mywebsite/wp-config.php

Edit the file and populate database connection values:

$ sudo vim /var/www/mywebsite/wp-config.php

/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );

/** MySQL database username */
define( 'DB_USER', 'wordpress_user' );

/** MySQL database password */
define( 'DB_PASSWORD', 'StrongDBPassw0rd' );

Save and close the file.

Step 4: Configure Nginx and PHP-FPM

Edit PHP FPM configuration file and configure like below.

$ sudo vim /etc/php-fpm.d/www.conf

listen = /run/php-fpm/www.sock
user = nginx  # For httpd keep it as apache
group = nginx # For httpd keep it as apache

# Set permissions for unix socket
listen.owner = nginx # For httpd keep it as apache
listen.group = nginx # For httpd keep it as apache
listen.mode = 0660

# Choose how the process manager will control the number of child processes
pm = ondemand

Create Nginx configuration for the website:

sudo vim /etc/nginx/conf.d/mywebsite.conf

Modify and use below configurations:

##################################
# WORDPRESS NGINX CONFIGURATIONS
##################################
server {
        listen 80;
        root /var/www/mywebsite;
        server_name mywebsite.com www.mywebsite.com;      # Set valid & correct domain
	access_log /var/log/nginx/mywebsite_access.log;   # Configure access log file
	error_log /var/log/nginx/mywebsite_error.log;     # Configure error log file

	location / {
    		index                               index.php index.html;
    		try_files                           $uri $uri/ /index.php?$args;
	}

#############
# Specify a charset
############
        charset                         utf-8;

############
# GZIP
###########

        gzip                            on;
	gzip_vary on;
	gzip_proxied any;
	gzip_comp_level 6;
	gzip_min_length 1100;
	gzip_buffers 16 8k;
	gzip_http_version 1.1;
	gzip_types image/svg+xml text/plain  text/xml text/css text/javascript application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript application/x-font-ttf application/vnd.ms-fontobject font/opentype font/ttf font/eot font/otf;

############
# this prevents hidden files (beginning with a period) from being served
############

location ~ /\. {
        access_log                      off;
        log_not_found                   off;
        deny                            all;
}

###########
# SEND EXPIRES HEADERS AND TURN OFF 404 LOGGING
###########

        location ~* ^.+.(xml|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        access_log                      off;
        log_not_found                   off;
        expires                         max;
}

############
# Pass uploaded files to wp-includes/ms-files.php.
############

#       rewrite                         /files/$ /index.php last;

if ($uri !~ wp-content/plugins) {
        rewrite /files/(.+)$ /wp-includes/ms-files.php?file=$1 last;
}


############
# Pass all .php files onto a php-fpm or php-cgi server
############

location ~ \.php$ {

        # Try the files specified in order. In our case, try the requested URI and if
        # that fails, try (successfully) to pass a 404 error.
        # zero day exploit defense

        try_files                       $uri =404;

        # Include the fastcgi_params defaults provided by nginx

        include                         /etc/nginx/fastcgi_params;

        # The amount of time for upstream to wait for a fastcgi process to send data.
        # We keep this *extremely* high so that one can be lazy when remote debugging.

        fastcgi_read_timeout            3600s;

         # Buffer size for reading the header of the backend FastCGI process.
        # This defaults to the value of a single fastcgi_buffers, so does not
        # need to be specified in our case, but it's good to be explicit.

        fastcgi_buffer_size             128k;
	fastcgi_connect_timeout 3s;
	fastcgi_send_timeout 120s;
	fastcgi_temp_file_write_size 256k;



        # The number and size of the buffers into which the reply from the FastCGI
        # process in the backend is read.
        #
        # 4 buffers at 128k means that any reply by FastCGI greater than 512k goes
        # to disk and replies under 512k are handled directly in memory.

        #fastcgi_buffers                 4 128k;
        fastcgi_buffers                 256 16k;

        # SCRIPT_FILENAME is a required parameter for things to work properly,
        # but was missing in the default fastcgi_params on upgrade to nginx 1.4.
        # We define it here to be sure that it exists.

        fastcgi_param                   SCRIPT_FILENAME $document_root$fastcgi_script_name;


 # Use the upstream for php7.0-fpm that we defined in nginx.conf

        fastcgi_pass                    unix:/run/php-fpm/www.sock;
        #fastcgi_pass                    127.0.0.1:9000;

        # And get to serving the file!

        fastcgi_index                   index.php;
}


############
# ROBOTS
###########

         location = /robots.txt {
               allow all;
               log_not_found off;
               access_log off;
        }


############
# RESTRICTIONS
############

# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
 deny all;
 }
}

Validate nginx configurations:

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Step 5: Configure SELinux and Start Services

Confirm SELinux is set in enforcing mode.

$ 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:      32

If not in Enforcing mode enable it by running the commands below:

sudo setenforce 1
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/g' /etc/selinux/config

Allow web server network connection:

sudo setsebool -P httpd_can_network_connect 1

Set SELinux contexts on the wordpress data directory:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/mywebsite(/.*)?"
sudo restorecon -Rv /var/www/mywebsite

Start nginx and php-fpm services:

sudo systemctl restart nginx php-fpm
sudo systemctl enable nginx php-fpm

Confirm state of the services:

$ systemctl status nginx php-fpm
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/nginx.service.d
           └─php-fpm.conf
   Active: active (running) since Thu 2021-01-28 09:51:25 UTC; 31s ago
 Main PID: 18334 (nginx)
    Tasks: 2 (limit: 4763)
   Memory: 4.0M
   CGroup: /system.slice/nginx.service
           ├─18334 nginx: master process /usr/sbin/nginx
           └─18335 nginx: worker process

Jan 28 09:51:25 centos systemd[1]: Starting The nginx HTTP and reverse proxy server...
Jan 28 09:51:25 centos nginx[18326]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Jan 28 09:51:25 centos nginx[18326]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Jan 28 09:51:25 centos systemd[1]: nginx.service: Failed to parse PID from file /run/nginx.pid: Invalid argument
Jan 28 09:51:25 centos systemd[1]: Started The nginx HTTP and reverse proxy server.

● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2021-01-28 09:51:25 UTC; 30s ago
 Main PID: 18330 (php-fpm)
   Status: "Processes active: 0, idle: 0, Requests: 0, slow: 0, Traffic: 0req/sec"
    Tasks: 1 (limit: 4763)
   Memory: 15.9M
   CGroup: /system.slice/php-fpm.service
           └─18330 php-fpm: master process (/etc/php-fpm.conf)

Jan 28 09:51:25 centos systemd[1]: php-fpm.service: Succeeded.
Jan 28 09:51:25 centos systemd[1]: Stopped The PHP FastCGI Process Manager.
Jan 28 09:51:25 centos systemd[1]: Starting The PHP FastCGI Process Manager...
Jan 28 09:51:25 centos php-fpm[18330]: [28-Jan-2021 09:51:25] WARNING: [pool www] ACL set, listen.owner = 'nginx' is ignored
Jan 28 09:51:25 centos php-fpm[18330]: [28-Jan-2021 09:51:25] WARNING: [pool www] ACL set, listen.group = 'nginx' is ignored
Jan 28 09:51:25 centos systemd[1]: Started The PHP FastCGI Process Manager.

Step 6: Finalize WordPress website configuration

With DNS configured correctly for the domain used in our wordpress website we can visit the domain and complete initial wordpress site configurations.

http://mywebsite.com

Create admin user with a password for authenticating.

Login to WordPress Dashboard:

Your WordPress website is setup and working.

More guides:

How To Manage Docker Containers & Images in Linux

How To Run GNS3 VM on VirtualBox

Monitor Nginx Web Server using NGINX Amplify Agent

LEAVE A REPLY

Please enter your comment!
Please enter your name here