This readme is for using FreePBX 16 on a RHEL 9 server and its various clones. FreePBX 16 supports only PHP 7.4, while EL9 and clones ship with PHP 8.x. In general, all the FreePBX 16 development targets EL8 systems, not EL9. This makes using FreePBX on EL9 clones an even more adventurous journey, but it works. I have to say that there is a FreeePBX 17 development branch in beta state, but it seems to be stale for about an year now. In general all FreePBX development slowed down recently: rumors say that Sangoma is wondering what to do after Red Hat decision to close it's sources. Wait and see.

Anyway anyhow, if you want to use FPBX on your EL9 box, you have to find a way to have PHP 7.4 on your EL9 machine. A popular and effective solution is to use the PHP versions provided by Remi. It also has a convenient Wizard page: https://rpms.remirepo.net/wizard/ . that page also explains a way to make different PHP versions coexist on the same machine, in case you want to
 have both 7.4 for FreePBX and 8.x for other more up-to-date software.

This document shows how to run FreePBX with Apache and php-fpm, on dedicated ports (81 and 444/https) to be able to run only the FreePBX virtual host under the asterisk user. There can be so many other ways. For example, mod_php can be used instead of php-fpm (see APACHE_MOD_PHP for this). There are other options too: In the past, I liked creating an instance of Lighttpd listening on non-standard ports. It can be done even today and indeed, it would probably lead to a saving of resources. I also used Apache with mod_ruid2. It's only a matter of personal taste.

I use the mpm-itk Apache MPM to be able to run my FreePBX Virtual Host under the Asterisk user, without modifying the whole Apache configuration. And php-fpm, which is the default php andler in C8.
Running FreePBX as the asterisk user involves that the session files will be owned by the asterisk user too, and that they have to be kept in a directory writable from that user. So I will change it in config below. This also has a minor consequence on the FreePBX tools which are run run from the CLI, if they have to deal with the session directory. I explain this a note in the dedicated NOTES readme file.

To enable mpm-itk:
dnf install httpd-itk


edit /etc/httpd/conf.modules.d/00-mpm.conf . Apply the following patch to enable mpm-prefork and not mpm-event.
--- 00-mpm.conf.orig    2021-11-12 05:54:30.000000000 +0100
+++ 00-mpm.conf 2022-01-09 09:36:05.508006572 +0100
@@ -8,7 +8,8 @@
 # NOTE: If enabling prefork, the httpd_graceful_shutdown SELinux
 # boolean should be enabled, to allow graceful stop/shutdown.
 #
-#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+# Activate mpm_prefork since it is a requisite for mpm_itk
+LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
 
 # worker MPM: Multi-Processing Module implementing a hybrid
 # multi-threaded multi-process web server
@@ -20,4 +21,4 @@
 # threads only for connections with active processing
 # See: http://httpd.apache.org/docs/2.4/mod/event.html
 #
-LoadModule mpm_event_module modules/mod_mpm_event.so
+#LoadModule mpm_event_module modules/mod_mpm_event.so


Then edit /etc/httpd/conf.modules.d/00-mpm-itk.conf to load the module. Do it via the following patch:
--- 00-mpm-itk.conf.orig        2021-11-30 08:16:52.000000000 +0100
+++ 00-mpm-itk.conf     2022-01-09 09:37:48.870921167 +0100
@@ -1,4 +1,4 @@
 # ITK MPM (Multi-Processing Module). Mpm-itk allows you to run each of your
 # vhost under a separate uid and gid - in short, the scripts and configuration
 # files for one vhost no longer have to be readable for all the other vhosts.
-#LoadModule mpm_itk_module modules/mod_mpm_itk.so
+LoadModule mpm_itk_module modules/mod_mpm_itk.so


If you're using SELinux, the following command is required by mpm-itk:
setsebool -P httpd_graceful_shutdown 1


Then let's configure php-fpm:
cd /etc/php-fpm.d/
cp -p www.conf freepbx.conf 

Apply the following patch to the just-created freepbx.conf :
--- www.conf    2021-06-02 20:53:34.000000000 +0200
+++ freepbx.conf        2022-01-09 10:46:50.628495177 +0100
@@ -1,7 +1,7 @@
 ; Start a new pool named 'www'.
 ; the variable $pool can be used in any directive and will be replaced by the
 ; pool name ('www' here)
-[www]
+[freepbx]
 
 ; Per pool prefix
 ; It only applies on the following directives:
@@ -21,9 +21,9 @@
 ; Note: The user is mandatory. If the group is not set, the default user's group
 ;       will be used.
 ; RPM: apache user chosen to provide access to the same directories as httpd
-user = apache
+user = asterisk
 ; RPM: Keep a group allowed to write in log dir.
-group = apache
+group = asterisk
 
 ; The address on which to accept FastCGI requests.
 ; Valid syntaxes are:
@@ -35,7 +35,7 @@
 ;                            (IPv6 and IPv4-mapped) on a specific port;
 ;   '/path/to/unix/socket' - to listen on a unix socket.
 ; Note: This value is mandatory.
-listen = /run/php-fpm/www.sock
+listen = /run/php-fpm/$pool.sock
 
 ; Set listen(2) backlog.
 ; Default Value: 511
@@ -45,14 +45,14 @@
 ; permissions must be set in order to allow connections from a web server.
 ; Default Values: user and group are set as the running user
 ;                 mode is set to 0660
-;listen.owner = nobody
-;listen.group = nobody
+listen.owner = asterisk
+listen.group = asterisk
 ;listen.mode = 0660
 
 ; When POSIX Access Control Lists are supported you can set them using
 ; these options, value is a comma separated list of user/group names.
 ; When set, listen.owner and listen.group are ignored
-listen.acl_users = apache,nginx
+;listen.acl_users = apache,nginx
 ;listen.acl_groups =
 
 ; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
@@ -433,6 +433,6 @@
 ; See warning about choosing the location of these directories on your system
 ; at http://php.net/session.save-path
 php_value[session.save_handler] = files
-php_value[session.save_path]    = /var/lib/php/session
+php_value[session.save_path]    = /var/lib/php/asterisksession
 php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache
 ;php_value[opcache.file_cache]  = /var/lib/php/opcache


Now crete the sessions directory for the * user referenced previously:
cp -rp /var/lib/php/session /var/lib/php/asterisksession
chgrp asterisk /var/lib/php/asterisksession


systemctl restart php-fpm



Then create the file /etc/httpd/conf.d/freepbx.conf with the following content:
Listen 81
Listen 444

<VirtualHost *:81>
        ServerName pbx.domain.com
        ServerAlias freepbx

        ServerAdmin webmaster@domain.com

        DocumentRoot /usr/share/freepbx

        <Directory "/usr/share/freepbx">
                Options Indexes FollowSymLinks

                ExpiresActive on
                ExpiresByType image/png "access plus 1 year"
                ExpiresByType image/gif "access plus 1 year"
                ExpiresByType image/jpg "access plus 1 year"
                ExpiresByType css/text "access plus 1 year"

                AllowOverride All

                <IfModule mod_authz_core.c>
                        Require all granted
                </IfModule>
        </Directory>

        <IfModule mpm_itk_module>
                AssignUserId asterisk asterisk
        </IfModule>

        <IfModule !mod_php7.c>
                <FilesMatch \.(php|phar)$>
                        SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://php-fpm"
                </FilesMatch>
        </IfModule>

        <IfModule mod_php7.c>
                php_value session.save_handler "files"
                php_value session.save_path    "/var/lib/php/asterisksession"
        </IfModule>

        ErrorLog logs/freepbx-error_log
        CustomLog logs/freepbx-access_log common
</VirtualHost>

<VirtualHost *:444>
        ServerName pbx.domain.com
        ServerAlias freepbx

        ServerAdmin webmaster@domain.com

        DocumentRoot /usr/share/freepbx

        <Directory "/usr/share/freepbx">
                Options Indexes FollowSymLinks

                ExpiresActive on
                ExpiresByType image/png "access plus 1 year"
                ExpiresByType image/gif "access plus 1 year"
                ExpiresByType image/jpg "access plus 1 year"
                ExpiresByType css/text "access plus 1 year"

                AllowOverride All

                <IfModule mod_authz_core.c>
                        Require all granted
                </IfModule>
        </Directory>

        <IfModule mpm_itk_module>
                AssignUserId asterisk asterisk
        </IfModule>

        <IfModule !mod_php7.c>
                <FilesMatch \.(php|phar)$>
                        SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://php-fpm"
                </FilesMatch>
        </IfModule>

        <IfModule mod_php7.c>
                php_value session.save_handler "files"
                php_value session.save_path    "/var/lib/php/asterisksession"
        </IfModule>

        ErrorLog logs/freepbx-ssl-error_log
        CustomLog logs/freepbx-ssl-access_log common

        SSLEngine on
        SSLCertificateFile /etc/pki/tls/certs/localhost.crt
        SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

        <Files ~ "\.(cgi|shtml)$">
                SSLOptions +StdEnvVars
        </Files>
        <Directory "/var/www/cgi-bin">
                SSLOptions +StdEnvVars
        </Directory>
        SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
</VirtualHost>


And if you dare using SELinux (port 81 is already there on C8, don't know for what):
semanage port -a -t http_port_t -p tcp 444
setsebool -P domain_can_mmap_files 1
setsebool -P daemons_enable_cluster_mode 1
setsebool -P httpd_mod_auth_pam 1
setsebool -P httpd_execmem 1
setsebool -P httpd_run_stickshift 1

Finally
systemctl restart httpd

And if using firewalld:
firewall-cmd --add-port=81/tcp
firewall-cmd --add-port=444/tcp
firewall-cmd --runtime-to-permanent
