How To Install Linux, Apache, MySQL (MariaDB), PHP-FPM (LAMP) Stack on CentOS Stream 9

main menu
PHP Version 8.0.20

This article describes how to install a Web server with application back-end PHP and database back-end MySQL using MariaDB. In continuing the same topic, but with different software from the previous article – How To Install Linux, Nginx, MySQL (MariaDB), PHP-FPM (LEMP) Stack on CentOS Stream 9, where the Web server is Nginx with application back-end PHP-FPM, which is a sort of CGI (FastCGI). In this article, the Web server is Apache and the application is again PHP-FPM, because since the CentOS 8 the Apache mod_php is deprecated.
All the software installed throughout this article is from the CentOS Stream 9 official repositories including the EPEL repository. The machine is installed with a minimal installation of CentOS Stream 9 and there is a how-to here – Network installation of CentOS Stream 9 (20220606.0) – minimal server installation.
Here are the steps to perform:

  1. Install, configure and start the database MariaDB.
  2. Install, configure and start the PHP-FPM and PHP cli.
  3. Install, configure and start the Web server Apache 2.x.
  4. Configure the system – firewall and SELinux.
  5. Test the installation with a phpMyAdmin installation.
  6. Bonus – Apache HTTPS with SSL certificate – self-signed and letsencrypt.

STEP 1) Install, configure and start the database MariaDB.

First, install the MariaDB server by:

dnf install -y mariadb-server

To configure the MariaDB server, the main file is /etc/my.cnf, which just includes all files under the folder /etc/my.cnf.d/

[root@srv ~]# cat /etc/my.cnf
#
# This group is read both both by the client and the server
# use it for options that affect everything
#
[client-server]

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d

[root@srv ~]# ls -altr /etc/my.cnf.d/
total 32
-rw-r--r--.  1 root root  295 Mar 25  2022 client.cnf
-rw-r--r--.  1 root root  120 May 18 07:55 spider.cnf
-rw-r--r--.  1 root root  232 May 18 07:55 mysql-clients.cnf
-rw-r--r--.  1 root root  763 May 18 07:55 enable_encryption.preset
-rw-r--r--.  1 root root 1458 Jun 13 13:24 mariadb-server.cnf
-rw-r--r--.  1 root root   42 Jun 13 13:29 auth_gssapi.cnf
drwxr-xr-x.  2 root root 4096 Oct  6 06:34 .
drwxr-xr-x. 81 root root 4096 Oct  6 06:34 ..

The most important file for the MariaDB server is /etc/my.cnf.d/mariadb-server.cnf, where all the server options are included. Under section “[mysqld]” add options to tune the MariaDB server. Supported options could be found here: https://mariadb.com/kb/en/mysqld-options/
Add the following options under “[mysqld]” in /etc/my.cnf.d/mariadb-server.cnf

# Generic
skip-external-locking
skip-character-set-client-handshake
skip-name-resolve

key_buffer_size                 = 1024M
sort_buffer_size                = 200K
net_buffer_length               = 64K
read_buffer_size                = 256K
read_rnd_buffer_size            = 512K
myisam_sort_buffer_size         = 256M
thread_cache_size               = 100
max_connections                 = 1000
max_heap_table_size             = 512M
open_files_limit                = 30000
max_allowed_packet              = 16M
myisam-recover-options  = BACKUP

sync_binlog=0
sql_mode="NO_ENGINE_SUBSTITUTION"

# InnoDB
innodb_buffer_pool_size = 4G
innodb_log_buffer_size = 24M
innodb_log_file_size = 128M
innodb_flush_method = O_DIRECT
innodb_file_per_table
innodb_flush_log_at_trx_commit = 0
innodb_lock_wait_timeout = 150
innodb_thread_concurrency = 0

The most important options is innodb_buffer_pool_size, which should be 70% of available RAM (for machines with low amount of RAM, those above the 32G the percentage should be higher) or the size of the database.
It’s worth noting that no additional configuration is required to start the MariaDB server, but it’s good to make some optimization even for a test/staging/dev environment.
Second, start the services and enable it to start on boot. Check if the services has started successfully:

[root@srv ~]# systemctl enable mariadb
Created symlink /etc/systemd/system/mysql.service → /usr/lib/systemd/system/mariadb.service.
Created symlink /etc/systemd/system/mysqld.service → /usr/lib/systemd/system/mariadb.service.
Created symlink /etc/systemd/system/multi-user.target.wants/mariadb.service → /usr/lib/systemd/system/mariadb.service.
[root@srv ~]# systemctl start mariadb
[root@srv ~]# systemctl status mariadb
● mariadb.service - MariaDB 10.5 database server
     Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)
     Active: active (running) since Fri 2022-10-07 10:23:35 UTC; 5s ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
    Process: 4322 ExecStartPre=/usr/libexec/mariadb-check-socket (code=exited, status=0/SUCCESS)
    Process: 4344 ExecStartPre=/usr/libexec/mariadb-prepare-db-dir mariadb.service (code=exited, sta>
    Process: 4446 ExecStartPost=/usr/libexec/mariadb-check-upgrade (code=exited, status=0/SUCCESS)
   Main PID: 4431 (mariadbd)
     Status: "Taking your SQL requests now..."
      Tasks: 14 (limit: 23072)
     Memory: 369.0M
        CPU: 800ms
     CGroup: /system.slice/mariadb.service
             └─4431 /usr/libexec/mariadbd --basedir=/usr

Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: you need to be the system 'mysql' user to connect.
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: After connecting you can set the password, if you >
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: able to connect as any of these users with a passw>
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: See the MariaDB Knowledgebase at https://mariadb.c>
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: Please report any problems at https://mariadb.org/>
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: The latest information about MariaDB is available >
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: Consider joining MariaDB's strong and vibrant comm>
Oct 07 10:23:35 srv mariadb-prepare-db-dir[4383]: https://mariadb.org/get-involved/
Oct 07 10:23:35 srv mariadbd[4431]: 2022-10-07 10:23:35 0 [Note] /usr/libexec/mariadbd (mysqld 10.5.>
[root@srv ~]# pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
        ├─agetty
        ├─auditd───{auditd}
        ├─chronyd
        ├─crond
        ├─dbus-broker-lau───dbus-broker
        ├─firewalld───{firewalld}
        ├─irqbalance───{irqbalance}
        ├─mariadbd───13*[{mariadbd}]
        ├─mdadm
        ├─polkitd───5*[{polkitd}]
        ├─rsyslogd───2*[{rsyslogd}]
        ├─sshd───sshd───sshd───bash───pstree
        ├─systemd───(sd-pam)
        ├─systemd-journal
        ├─systemd-logind
        └─systemd-udevd

MariaDB server will accept connections through the Unix socket file: /var/lib/mysql/mysql.sock. If the user wants to accept network connections to the MariaDB server, the following options should be enabled (uncomment it or add it) in /etc/my.cnf.d/mariadb-server.cnf:

bind-address=0.0.0.0

It will accept connections on all network interfaces, if only on a specific one is desired, just add the IP instead of 0.0.0.0.

Secure the installation with the MariaDB tool: mysql_secure_installation. This build-in tool presents several questions, which are considered to make the current installation of MariaDB more secure. By default, the MariaDB root password (the user with all privileges) has no password, so this tool sets root password and more.

[root@srv ~]# mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user. If you've just installed MariaDB, and
haven't set the root password yet, you should just press enter here.

Enter current password for root (enter for none): 
OK, successfully used password, moving on...

Setting the root password or using the unix_socket ensures that nobody
can log into the MariaDB root user without the proper authorisation.

You already have your root account protected, so you can safely answer 'n'.

Switch to unix_socket authentication [Y/n] Y
Enabled successfully!
Reloading privilege tables..
 ... Success!


You already have your root account protected, so you can safely answer 'n'.

Change the root password? [Y/n] Y
New password: 
Re-enter new password: 
Password updated successfully!
Reloading privilege tables..
 ... Success!


By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] Y
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] Y
 ... Success!

By default, MariaDB comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] Y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] Y
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

For user convenience, add the root password in /root/.my.cnf as follow and the system root user will enter the MariaDB console without entering a password.

root@srv ~]# cat /root/.my.cnf 
[client]
password="iep9ooQueik0em#ohPa2"
[root@srv ~]# chmod 400 /root/.my.cnf
[root@srv ~]# mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 13
Server version: 10.5.16-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)]> ^DBye
root@srv ~]# 

STEP 2) Install, configure and start the application part – PHP-FPM and PHP cli.

For around 20 years Apache used to run PHP processor with a module in its process, i.e. MOD_PHP, but it got deprecated and the mod_php module hasn’t been included in CentOS since CentOS 8/Stream 8. It’s not up to an CentOS Stream 8 developers decision, the problem is Apache starting from 2.4.27 stopped supporting the HTTP2 protocol with the Apache Prefork (the Apache module, which handles the HTTP requests), which module works with MOD_PHP.
FPM is FastCGI Process Manager, it starts a daemon, which runs a predefined number of PHP-FPM processes and it waits for Unix or TCP/IP connections to process them with PHP processor.
First, install PHP-FPM and PHP cli.

dnf install -y php-fpm php-cli php-pdo php-mbstring php-mysqlnd php-gd php-intl

There are two parts of PHP configuration:

  • PHP configuration – the application, which processes the PHP files. The configuration is in /etc/php.ini and the custom modules configurations are in the directory /etc/php.d/
  • PHP FPM configuration – the process, which controls the execution of the PHP daemon, which process the PHP files. The configuration is in /etc/php-fpm.conf and custom daemons’ configurations (there may be multiple instances of the daemon, each of which will start its own tree of processes) are under /etc/php-fpm.d/. By default, there is only one pool of PHP-FPM processes with configuration file /etc/php-fpm.d/www.conf

PHP configuration

The configuration is in file /etc/php.ini and in general, there are 3 important options:

.....
date.timezone = UTC
.....
memory_limit = 256M
.....
max_execution_time = 60
.....

The file is around 1660 lines with options and comments, but in most cases, the first limits to hit are the above ones. By default, date.timezone is not set and is commented, so comment it out and set a value, the time zone, which used by the date functions – http://php.net/date.timezone. The second memory_limit is how much memory is allowed for each process and the third max_execution_time is how much time is allowed a process to take before being killed by the PHP.

PHP FPM Configuration

/etc/php-fpm.conf – defines global configuration for all pools and includes the files for each pool in /etc/php-fpm.d/. So the first pool, which will start one PHP process tree is in /etc/php-fpm.d/www.conf
Here are the important options, which should be uncommented or modified as follows:

.....
user = apache
group = apache
.....
listen.backlog = 1024
.....
pm.max_requests = 20000
.....

Enure the user to be apache, because this Web server will be used. The backlog means how many connections to queue when there are connections to the process, which cannot be processed immediately.
Interesting options to manage the process counts of the PHP processes are: pm.max_children, pm.start_servers, pm.min_spare_servers, pm.max_spare_servers, pm.process_idle_timeout, pm.max_requests (always set a limit to how many requests a process can accept, memory leaks do exists, so restarting a PHP process is a good thing! Be careful, with low values on a busy server.)
The default configuration will accept connections only through the Unix socket file: /run/php-fpm/www.sock. To accept network connections modify the listen and listen.allowed_clients options:

.....
listen="10.10.10.10:9000"
.....
;listen.allowed_clients=
.....

The PHP-FPM will accept network connections from IP 10.10.10.10 on port 9000.
In the configuration file there are many comments, which the user should read, because they may help him to find interesting options like access logging, slow logging, error logging, status and ping pages and more.
Start and enable on boot the PHP-FPM service:

[root@srv ~]# systemctl enable php-fpm
Created symlink /etc/systemd/system/multi-user.target.wants/php-fpm.service → /usr/lib/systemd/system/php-fpm.service.
[root@srv ~]# systemctl start php-fpm
[root@srv ~]# systemctl status php-fpm
● 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 2022-10-06 08:06:42 UTC; 3s ago
   Main PID: 4817 (php-fpm)
     Status: "Ready to handle connections"
      Tasks: 6 (limit: 23072)
     Memory: 10.4M
        CPU: 83ms
     CGroup: /system.slice/php-fpm.service
             ├─4817 "php-fpm: master process (/etc/php-fpm.conf)"
             ├─4818 "php-fpm: pool www"
             ├─4819 "php-fpm: pool www"
             ├─4820 "php-fpm: pool www"
             ├─4821 "php-fpm: pool www"
             └─4822 "php-fpm: pool www"

Oct 06 08:06:42 srv systemd[1]: Starting The PHP FastCGI Process Manager...
Oct 06 08:06:42 srv systemd[1]: Started The PHP FastCGI Process Manager.

There is a master PHP-FPM process, which ran 5 process to accept connections.
Fix the PHP session write permissions with:

[root@srv ~]# ls -altrZ /var/lib/php/
total 24
drwxrwx---.  2 root apache system_u:object_r:httpd_var_run_t:s0 4096 Aug  1 09:42 wsdlcache
drwxrwx---.  2 root apache system_u:object_r:httpd_var_run_t:s0 4096 Aug  1 09:42 session
drwxr-xr-x.  2 root root   system_u:object_r:httpd_var_lib_t:s0 4096 Aug  1 09:42 peclxml
drwxrwx---.  2 root apache system_u:object_r:httpd_var_lib_t:s0 4096 Aug  1 09:42 opcache
drwxr-xr-x. 26 root root   system_u:object_r:var_lib_t:s0       4096 Oct  9 17:25 ..
drwxr-xr-x.  6 root root   system_u:object_r:httpd_var_lib_t:s0 4096 Oct  9 17:25 ..

By default, the directories’ groups are apache and it should stay apache. A blank page may be displayed if the PHP-FPM could not write to these directories.
The SELinux permission are fine.

STEP 3) Install, configure and start the Web server Apache.

For more than 15 years, Apache server has been from branch 2.x and many reference it as an Apache2 despite its name is The Apache HTTP Server (“httpd“) and in CentOS world it is referred as “httpd“. The package name is httpd and in many cases, for naming is used httpd. Bear in mind, there many packages with httpd in their names, which means they are related to the Apache web server. Still “apache” is also used, for example, the user and group names are “apache:apache“.
First, install the Apache server.

dnf install -y httpd

The important folders are:

[root@srv ~]# ls -al /etc/httpd/
total 20
drwxr-xr-x.  5 root root 4096 Oct  9 17:33 .
drwxr-xr-x. 87 root root 4096 Oct  9 17:33 ..
drwxr-xr-x.  2 root root 4096 Oct  9 17:33 conf
drwxr-xr-x.  2 root root 4096 Oct  9 17:33 conf.d
drwxr-xr-x.  2 root root 4096 Oct  9 17:33 conf.modules.d
lrwxrwxrwx.  1 root root   19 Jul 22 11:42 logs -> ../../var/log/httpd
lrwxrwxrwx.  1 root root   29 Jul 22 11:42 modules -> ../../usr/lib64/httpd/modules
lrwxrwxrwx.  1 root root   10 Jul 22 11:42 run -> /run/httpd
lrwxrwxrwx.  1 root root   19 Jul 22 11:42 state -> ../../var/lib/httpd
[root@srv ~]# ls -al /etc/httpd/conf/
total 36
drwxr-xr-x. 2 root root  4096 Oct  9 17:33 .
drwxr-xr-x. 5 root root  4096 Oct  9 17:33 ..
-rw-r--r--. 1 root root 12005 Jul 22 11:41 httpd.conf
-rw-r--r--. 1 root root 13430 Jul 22 11:41 magic
[root@srv ~]# ls -al /etc/httpd/conf.d/
total 28
drwxr-xr-x. 2 root root 4096 Oct  9 17:33 .
drwxr-xr-x. 5 root root 4096 Oct  9 17:33 ..
-rw-r--r--. 1 root root 2916 Jul 22 11:42 autoindex.conf
-rw-r--r--. 1 root root 1577 Aug  1 09:42 php.conf
-rw-r--r--. 1 root root  400 Jul 22 11:43 README
-rw-r--r--. 1 root root 1252 Jul 22 11:41 userdir.conf
-rw-r--r--. 1 root root  653 Jul 22 11:41 welcome.conf
[root@srv ~]# ls -al /etc/httpd/conf.modules.d/
total 56
drwxr-xr-x. 2 root root 4096 Oct  9 17:33 .
drwxr-xr-x. 5 root root 4096 Oct  9 17:33 ..
-rw-r--r--. 1 root root 3325 Jul 22 11:41 00-base.conf
-rw-r--r--. 1 root root   47 Jul 22 11:41 00-brotli.conf
-rw-r--r--. 1 root root  139 Jul 22 11:41 00-dav.conf
-rw-r--r--. 1 root root   41 Jul 22 11:41 00-lua.conf
-rw-r--r--. 1 root root  948 Jul 22 11:41 00-mpm.conf
-rw-r--r--. 1 root root  787 Jul 22 11:41 00-optional.conf
-rw-r--r--. 1 root root 1073 Jul 22 11:41 00-proxy.conf
-rw-r--r--. 1 root root   88 Jul 22 11:41 00-systemd.conf
-rw-r--r--. 1 root root  367 Jul 22 11:41 01-cgi.conf
-rw-r--r--. 1 root root   45 Aug 10  2021 10-h2.conf
-rw-r--r--. 1 root root   57 Aug 10  2021 10-proxy_h2.conf
-rw-r--r--. 1 root root  496 Jul 22 11:42 README

See the bonus sections for HTTPS for mode details.
To include the SSL certificate support the mod_ssl Apache module must be installed:

dnf install -y httpd mod_ssl

The main configuration file is /etc/httpd/conf/httpd.conf. The directory /etc/httpd/conf/ hosts global configuration files, which are rarely modified by the user.
The /etc/httpd/conf.d contains additional configuration files with the extension .conf, which are used to fine tune the modules loaded from /etc/httpd/conf.modules.d/. The configuration files in /etc/httpd/conf.modules.d/ are used only to describe how to modules.
So /etc/httpd/conf.d is the place where configuration for different sites should be included in different .conf files.
The configuration for virtual host with domain example.mydomain.com (following the same logic as the article for the NGINX, mentioned above) will be in /etc/httpd/conf.d/example.mydomain.com.conf. The skeleton looks as follows:

<virtualhost *:80>

ServerName example.mydomain.com
ServerAlias www.example.mydomain.com
DocumentRoot /var/www/example.mydomain.com

<Directory "/var/www/example.mydomain.com">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

#logs
ErrorLog /var/log/httpd/example.mydomain.com-error.log
CustomLog /var/log/httpd/example.mydomain.com-access.log combined

</virtualhost>

Note the Directory directive, which
Create the directory, change the owner to the preferred one or apache:apache, test the configuration and start the service.

[root@srv ~]# mkdir /var/www/example.mydomain.com
[root@srv ~]# chown -R apache:apache /var/www/example.mydomain.com
[root@srv ~]# httpd -t
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::a00:27ff:fe5d:c605%enp0s3. Set the 'ServerName' directive globally to suppress this message
Syntax OK
[root@srv ~]# systemctl start httpd
[root@srv ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
     Active: active (running) since Fri 2022-10-07 11:16:27 UTC; 5s ago
       Docs: man:httpd.service(8)
   Main PID: 5562 (httpd)
     Status: "Started, listening on: port 80"
      Tasks: 213 (limit: 23072)
     Memory: 29.0M
        CPU: 77ms
     CGroup: /system.slice/httpd.service
             ├─5562 /usr/sbin/httpd -DFOREGROUND
             ├─5563 /usr/sbin/httpd -DFOREGROUND
             ├─5564 /usr/sbin/httpd -DFOREGROUND
             ├─5565 /usr/sbin/httpd -DFOREGROUND
             └─5566 /usr/sbin/httpd -DFOREGROUND

Oct 07 11:16:27 srv systemd[1]: Starting The Apache HTTP Server...
Oct 07 11:16:27 srv httpd[5562]: AH00558: httpd: Could not reliably determine the server's fully qua>
Oct 07 11:16:27 srv httpd[5562]: Server configured, listening on: port 80
Oct 07 11:16:27 srv systemd[1]: Started The Apache HTTP Server.
[root@srv ~]# systemctl enable httpd
Created symlink /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.

The syntax is OK, despite a warning for one of the IPv6, just skip this warning!

The PHP Configuration file in Apache is loaded from /etc/httpd/conf.d/php.conf. This files instruct the Apache web server to handle all files with extenstion .php to the PHP-FPM using the Unix socket /run/php-fpm/www.sock or the default FastCGI port, i.e. 9000.
Note, this file could be removed and the content added to a file in the /etc/httpd/conf.d/example.mydomain.com.conf, so only example.mydomain.com would process PHP files.

STEP 4) Configure the system – firewall and SELinux

By default, CentOS Stream 9 uses firewalld service and firewall-cmd cli to control it. Allow HTTP trafffic to the server:

[root@srv ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  forward: yes
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
[root@srv ~]# firewall-cmd --permanent --add-service=http
success
[root@srv ~]# firewall-cmd --reload
success
[root@srv ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources: 
  services: cockpit dhcpv6-client http ssh
  ports: 
  protocols: 
  forward: yes
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

If the web root directories is under /var/www there is no need to tune the SELinux. If the user want to change to another location not under /var/www, the SELinux label httpd_sys_content_t must be added to the parent directory.
For example, if the web root directories are under /mnt/storage/www, the following commands should be issued:

[root@srv ~]# semanage fcontext -a -t httpd_sys_rw_content_t '/mnt/storage/www(/.*)?'
[root@srv ~]# restorecon -Rv /mnt/storage/www
Relabeled /mnt/storage/www from unconfined_u:object_r:mnt_t:s0 to unconfined_u:object_r:httpd_sys_rw_content_t:s0

Otherwise, the SELinux will block any web request to the /mnt/storage/www.

STEP 5) Test all the three components – Apache, PHP-FPM and MariaDB.

First test is Apache+PHP-FPM and the purpose is to show phpinfo() page. Create a file /var/www/example.mydomain.com/test.php with the content as shown below and then set the owner to “apache:apache”.

[root@srv ~]# cat /var/www/example.mydomain.com/test.php
<?php
phpinfo();
[root@srv ~]# chown -R apache:apache /var/www/example.mydomain.com

main menu
phpinfo page

Apache environment with PHP-FPM:
main menu
phpinfo apache environment

Second, test the Apache+PHP-FPM + MariaDB, i.e. the connectivity to the database from the application layer PHP-FPM.
Just download the phpMyAdmin package from https://files.phpmyadmin.net/phpMyAdmin/5.2.0/phpMyAdmin-5.2.0-all-languages.zip or the latest version. Unpack, add default config and load it in the browser:

[root@srv ~]# cd /var/www/example.mydomain.com
[root@srv example.mydomain.com]# wget https://files.phpmyadmin.net/phpMyAdmin/5.2.0/phpMyAdmin-5.2.0-all-languages.zip
--2022-10-09 19:07:03--  https://files.phpmyadmin.net/phpMyAdmin/5.2.0/phpMyAdmin-5.2.0-all-languages.zip
Resolving files.phpmyadmin.net (files.phpmyadmin.net)... 185.76.8.3, 2a02:6ea0:d900::3
Connecting to files.phpmyadmin.net (files.phpmyadmin.net)|185.76.8.3|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 14112668 (13M) [application/zip]
Saving to: ‘phpMyAdmin-5.2.0-all-languages.zip’

phpMyAdmin-5.2.0-all-lang 100%[==================================>]  13.46M  4.04MB/s    in 3.3s    

2022-10-09 19:07:07 (4.04 MB/s) - ‘phpMyAdmin-5.2.0-all-languages.zip’ saved [14112668/14112668]

[root@srv example.mydomain.com]# unzip phpMyAdmin-5.2.0-all-languages.zip 
Archive:  phpMyAdmin-5.2.0-all-languages.zip
   creating: phpMyAdmin-5.2.0-all-languages/
.....
.....
  inflating: phpMyAdmin-5.2.0-all-languages/yarn.lock  
[root@srv example.mydomain.com]# mv phpMyAdmin-5.2.0-all-languages phpmyadmin
[root@srv example.mydomain.com]# cd phpmyadmin
[root@srv example.mydomain.com]# cp config.sample.inc.php config.inc.php

Then open the browser and login with MariaDB root account and the password set in the (STEP 1). The phpMyAdmin should be located in http://example.mydomain.com/phpmyadmin/.

main menu
phpMyAdmin

Bonus) Apache with SSL certificate – self-signed and letsencrypt.

Two types of certificate generations for the HTTPS server block will be presented in this section:

  1. Self-signed certificate. The browsers will report it is unsafe and the user should accept the certificate before viewing the site. It is convinient for development stage.
  2. Letsencrypt certificate – the software needed and how to generate. A free and valid certificate. More info – https://letsencrypt.org/

First, the Apache mod_ssl module must be installed to support HTTPS connections:

dnf install -y mod_ssl

An additional configuration files appears /etc/httpd/conf.d/ssl.conf, which tunes the mod_ssl module and includes the Listen directive for all IPs on 443 port. There is a default virtual host, which SSL certificate and the key files are missing, so generate one SSL certificate more for localhost or link or copy the one available.

Generate self-signed certificate

Generate the self-signed certificate and configure the firewall to accept HTTPS connections.

[root@srv ~]# mkdir /etc/ssl/apache
[root@srv ~]# openssl genrsa -out /etc/ssl/apache/example.mydomain.com.key 4096
[root@srv ~]# openssl req -new -key /etc/ssl/apache/example.mydomain.com.key -out /etc/ssl/apache/example.mydomain.com.csr -subj '/C=us/ST=newyork/L=newyork/O=mygroup/OU=servicing/CN=example.mydomain.com/emailAddress=admin@mydomain.com'
[root@srv ~]# openssl x509 -req -days 365 -in /etc/ssl/apache/example.mydomain.com.csr -signkey /etc/ssl/apache/example.mydomain.com.key -out /etc/ssl/apache/example.mydomain.com.crt
[root@srv ~]# chmod 400 /etc/ssl/apache/*
[root@srv ~]# ls -altr /etc/ssl/apache/
total 20
drwxr-xr-x. 3 root root 4096 Oct 10 00:02 ..
-r--------. 1 root root 3272 Oct 10 00:03 example.mydomain.com.key
-r--------. 1 root root 1765 Oct 10 00:03 example.mydomain.com.csr
drwxr-xr-x. 2 root root 4096 Oct 10 00:04 .
-r--------. 1 root root 2049 Oct 10 00:04 example.mydomain.com.crt
[root@srv ~]# ln -s /etc/ssl/apache/example.mydomain.com.key /etc/pki/tls/private/localhost.key
[root@srv ~]# ln -s /etc/ssl/apache/example.mydomain.com.crt /etc/pki/tls/certs/localhost.crt

The default virtual host and the certificate and key paths are declared in /etc/httpd/conf.d/ssl.conf.

Add the HTTPS service to the firewall with firewall-cmd command-line utility.

[root@srv ~]# firewall-cmd --permanent --add-service=https
success
[root@srv ~]# firewall-cmd --reload
success

Here is the Apache server block (in /etc/httpd/conf.d/example.mydomain.com.conf below the above HTTP server block) for the HTTPS connections:

<VirtualHost *:443>
ServerName example.mydomain.com
ServerAlias www.example.mydomain.com
DocumentRoot /var/www/example.mydomain.com

<Directory "/var/www/example.mydomain.com">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

#ssl
SSLEngine on
SSLCertificateFile /etc/ssl/apache/example.mydomain.com.crt
SSLCertificateKeyFile /etc/ssl/apache/example.mydomain.com.key

#logs
ErrorLog /var/log/httpd/example.mydomain.com-ssl_error.log
#some SSL logging
#CustomLog /var/log/httpd/logs/example.mydomain.com-ssl "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
CustomLog /var/log/httpd/example.mydomain.com-ssl_access.log combined
</VirtualHost>

Test and restart the Apache web server.

[root@srv ~]# httpd -t
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::a00:27ff:fe5d:c605%enp0s3. Set the 'ServerName' directive globally to suppress this message
Syntax OK
[root@srv ~]# systemctl restart httpd

The phpMyAdmin should be located in https://example.mydomain.com/phpmyadmin/.

main menu
https phpMyAdmin

Letsencrypt certificate

Install the needed software. certbot is the command-line tool to generate, renewn and manage Let’s Encrypt SSL Certificates.

dnf install -y epel-release
dnf install -y certbot

The domain example.mydomain.com must have an accessible Internet IP, which the servers behind the letsencrypt organization would access using HTTP/HTTPS.

[root@srv ~]# certbot certonly --webroot -w /var/www/example.mydomain.com -d example.mydomain.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for example.mydomain.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.mydomain.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.mydomain.com/privkey.pem
This certificate expires on 2023-01-09.
These files will be updated when the certificate renews.

NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

The Apache configuration in /etc/httpd/conf.d/example.mydomain.com.conf looks like:

<VirtualHost *:443>
ServerName example.mydomain.com
ServerAlias www.example.mydomain.com
DocumentRoot /var/www/example.mydomain.com

<Directory "/var/www/example.mydomain.com">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

#ssl
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.mydomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.mydomain.com/privkey.pem

#logs
ErrorLog /var/log/httpd/example.mydomain.com-ssl_error.log
#some SSL logging
#CustomLog /var/log/httpd/logs/example.mydomain.com-ssl "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
CustomLog /var/log/httpd/example.mydomain.com-ssl_access.log combined
</VirtualHost>

Essensially the same with the self-singed one, but with modified the path of SSLCertificateFile and SSLCertificateKeyFile.
Test and restart the Apache web server.

[root@srv ~]# httpd -t
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::a00:27ff:fe5d:c605%enp0s3. Set the 'ServerName' directive globally to suppress this message
Syntax OK
[root@srv ~]# systemctl restart httpd

The phpMyAdmin should be located in https://example.mydomain.com/phpmyadmin/, but this time the SSL certificate will be valid for example.mydomain.com and no errors or crititcal warnings will receive the users of the site.

Leave a Reply

Your email address will not be published. Required fields are marked *