Install Apache 2.4.x with MPM Event and configure to use PHP-FPM on FreeBSD 11.2

In this short guide you will learn how to install and configure Apache 2.4.x with MPM Event and suEXEC to work together with PHP 7.2.x PHP-FPM on FreeBSD 11.2 x64.

Goal of this guide

Install the latest Apache 2.4 on FreeBSD 11.2 from ports with suEXEC, mpm event support. Use PHP 7.2 FPM with Apache 2.4 to serve PHP web applications like Drupal, WordPress, Joomla etc. Run as secure as possible.

The binary apache24 package isn’t compiled with suEXEC support. I need this option because I like to run all virtualhost with  dedicated user to improve security and flexibility.

Install Apache 2.4.x from ports

If you have not installed ports before please do this with the following commands.

portsnap fetch
portsnap extract
portsnap fetch update

Lets’s install apache24 with suexec and mpm event support.

cd /usr/ports/www/apache24
make config-recursive

Select the right modules and options. Please select SUEXEC and MPM_EVENT.

make install clean

Wait until apache24 and dependent ports compile. It may several minutes. After successfully installation configure apache24 service to start at boot time and start apache24.

sysrc apache24_enable="yes"
service apache24 start

Check log files under /var/log and processes.

less /var/log/httpd-error.log

[Wed Aug 08 12:02:23.194511 2018] [mpm_event:notice] [pid 94737:tid 34397577216] AH00489: Apache/2.4.34 (FreeBSD) configured -- resuming normal operations
[Wed Aug 08 12:02:23.194977 2018] [core:notice] [pid 94737:tid 34397577216] AH00094: Command line: '/usr/local/sbin/httpd -D NOHTTPACCEPT'

At this time Apache 2.4 is up and running and use MPM Event worker but we need to do some more configuration. First find

#Include etc/apache24/extra/httpd-mpm.conf

in /usr/local/etc/apache24/httpd.conf and delete #. After that you can set necessary MPM Pool parameters in /usr/local/etc/apache24/extra/httpd-mpm.conf. After every config file modification you should use apachectl configtest command to make syntax check on Apaches config files.

Please modify the following parameters in httpd.conf:

  • ServerAdmin
  • ServerName
  • Listen
  • EnableSendfile on (if your DocumentRoot is NOT on NFS!)

Set the anti-clickjacking X-Frame-Options, X-XSS-Protection, X-Content-Type-Options header

Find <IfModule headers_module> section in httpd.conf and add the following in it:

Header always append X-Frame-Options SAMEORIGIN
Header set X-XSS-Protection "1; mode=block"
Header set X-Content-Type-Options nosniff

Enable suEXEC module

Before you enable suEXEC please read the official documentation.

Find the

LoadModule suexec_module libexec/apache24/mod_suexec.so

in httpd.conf and delete # from the begining of the row to enable suexec module. Use apachectl configtest and restart apache24 (apachectl restart or service apache24 restart). Then check suEXEC in /var/log/httpd-error.log.

grep suexec /var/log/httpd-error.log 
[Wed Aug 08 13:32:49.515184 2018] [suexec:notice] [pid 13729:tid 34397577216] AH01232: suEXEC mechanism enabled (wrapper: /usr/local/sbin/suexec)

So Apache 2.4 is up and running with suEXEC support.

To enable various default settings in httpd.conf file please find

Include etc/apache24/extra/httpd-default.conf

row and delete the # character from the begining. After that you should fine tune some settings in extra/httpd-default.conf. I suggest to set ServerTokens and ServerSignature parameters to:

ServerTokens Prod
ServerSignature Off

After that please run apachectl configtest and apachectl restart commands.

Create dedicated local user to run PHP/CGI applications

I use FQDN as username. In this example I use one of my own domain, laszlolaszlo.com. You have to use your own! Let’s create the new user with proper settings.

set SUEXECDIR=/usr/local/www
set FQDN=laszlolaszlo.com
set FQDNHOME="$SUEXECDIR/$FQDN"
pwgen -1 -B -s -y -n -c 24 (use strong password!)
pw useradd $FQDN -s csh -d $FQDNHOME -G sftp-only
mkdir $FQDNHOME
mkdir $FQDNHOME/public_html
mkdir $FQDNHOME/temp
mkdir $FQDNHOME/private_files
mkdir $FQDNHOME/drush-backups
chown "$FQDN":"$FQDN" $FQDNHOME/public_html
chown "$FQDN":"$FQDN" $FQDNHOME/temp
chown "$FQDN":"$FQDN" $FQDNHOME/private_files
chown "$FQDN":"$FQDN" $FQDNHOME/drush-backups

I leave the user home directory ($FQDNHOME) at owner of root user because I want to grant chrooted SFTP only access to the user and in this case it is mandatory.

Install and configure PHP 7.2.x and additional modules

There are a lots of PHP 7.2 module. I installed the followings only. Feel free to install any additional modules what you need.

pkg install \
drush-php72 php72 php72-bz2 php72-composer php72-ctype php72-curl php72-dom php72-fileinfo php72-filter \
php72-gd php72-hash php72-iconv php72-imap php72-intl php72-json php72-mbstring php72-mysqli php72-opcache \
php72-openssl php72-pdo php72-pdo_mysql php72-pdo_pgsql php72-pear php72-pear-Auth php72-pear-Auth_SASL \
php72-pear-HTTP_Upload php72-pecl-crypto php72-pecl-geoip php72-pecl-scrypt php72-pgsql php72-phar php72-session \
php72-simplexml php72-tidy php72-tokenizer php72-xml php72-xmlwriter php72-zip php72-zlib

Enable php-fpm service at boot.

sysrc php_fpm_enable="YES"

Update GeoIP database

/usr/local/bin/geoipupdate.sh

Create a production php.ini file.

Then edit this php.ini file for your needs. Don’t forget to set at least the date.timezone parameter. I use my own php.ini.

cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

Configure PHP-FPM

I use my own php-fpm.conf file. Edit this file under /usr/local/etc/php-fpm.conf for your needs. I modified some important parameters:

  • emergency_restart_threshold = 2
  • emergency_restart_interval = 30
  • process_control_timeout = 60
  • process.max = 128

Create dedicated PHP-FPM pool for (every) this laszlolaszlo.com domain. I show you an example template file. You have to fine tune for your needs. It isn’t a complete example config. There’re a several configurable parameters.

cat php-pool.template

[FQDN]

user = $pool
group = $pool
listen = /var/run/$pool-php-fpm.sock
listen.owner = $pool
listen.group = www
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500
request_terminate_timeout = 300
security.limit_extensions = 
env[HOSTNAME] = $pool
env[TMP] = /usr/local/www/$pool/tmp
env[TMPDIR] = /usr/local/www/$pool/tmp
env[TEMP] = /usr/local/www/$pool/tmp
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.$pool.log
php_admin_flag[log_errors] = on

You have to replace only FQDN to your domain with a simple awk or sed command or with vi. Don’t replace $pool!

cd /usr/local/etc/php-fpm.d
sed ‘s/FQDN/’$FQDN’/g’ php-pool.template > $FQDN.conf

Do you remember? Before we set the $FQDN environment variable to our domain (in this example laszlolaszlo.com)! Please check the newly create pool conf file. If everything looks fine you can restart/start PHP-FPM service.

service php-fpm restart

Check the new pools socket under /var/run folder.

ls -la /var/run/ | grep $FQDN
srw-rw----   1 laszlolaszlo.com   www         0 Aug  9 09:26 laszlolaszlo.com-php-fpm.sock

Looks good. Permissions are correct. The www user (which runs Apache) has read-write permission on the socket which is mandatory.

Create Apache named based virtualhost configuration for your domain. I will create all virtualhosts in /usr/local/etc/apache24/vhosts.d . By default this vhosts.d directory isn’t exist. Please create it before or use any other location under etc/apache24 folder. I will use Let’s Encrypt service to generate SSL certificate to my domains. I use a default, minimal vhost configuration.

cat /usr/local/etc/apache24/vhosts.d/000_default.conf 

<VirtualHost _default_:80> 
    ServerName 18.197.192.141
    DocumentRoot /usr/local/www/apache24/data
    <Directory "/usr/local/www/apache24/data">
        Require all granted
    </Directory>
</VirtualHost>

Install necessary certbot packages with pkg command.

pkg install py27-certbot py27-certbot-apache
rehash

After that you can request certificate from Let’s Encrypt for your domain with the following command.

certbot certonly -d $FQDN --webroot
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for laszlolaszlo.com
Input the webroot for laszlolaszlo.com: (Enter 'c' to cancel): /usr/local/www/apache24/data
Waiting for verification...
Cleaning up challenges

Your can find your new cert(s) under /usr/local/etc/letsencrypt/live/$FQDN folder. So we can create the new virtualhost configuration with TLS support. I use my own vhost.template file. Geel free to use this at your own risk. With a simple sed I will create the new configuration file for laszlolaszlo.com domain. Change FQDN string with the contetn of $FQDN environment variable.

<VirtualHost *:80> 
    ServerName FQDN
    Redirect permanent / https://FQDN/ 
</VirtualHost>

<VirtualHost *:443> 
  ServerName FQDN
  SuexecUserGroup FQDN FQDN
  ErrorLog /var/log/FQDN-error.log
  CustomLog "/var/log/FQDN-access.log" common  
  DocumentRoot /usr/local/www/FQDN/public_html
  SSLEngine On 
  <Directory "/usr/local/www/FQDN/public_html">
    Options Indexes MultiViews SymLinksIfOwnerMatch
    AllowOverride All
    Require all granted
  </Directory>

  SSLCertificateFile "/usr/local/etc/letsencrypt/live/FQDN/cert.pem"
  SSLCertificateKeyFile "/usr/local/etc/letsencrypt/live/FQDN/privkey.pem"
  SSLCertificateChainFile "/usr/local/etc/letsencrypt/live/FQDN/fullchain.pem"

  SSLUseStapling on
  SSLStaplingResponderTimeout 5
  SSLStaplingReturnResponderErrors off

  Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
  Header always set X-Frame-Options SAMEORIGIN

  <FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
  </FilesMatch>

  <FilesMatch "\.php$">
    <If "-f %{REQUEST_FILENAME}">
      SetHandler "proxy:unix:/var/run/FQDN-php-fpm.sock|fcgi://localhost/"
    </If>
  </FilesMatch>
</VirtualHost>

cd /usr/local/etc/apache24/vhosts.d
echo $FQDN
laszlolaszlo.com
sed 's/FQDN/'$FQDN'/g' vhost.template > $FQDN.conf

apachectl configtest
Performing sanity check on apache24 configuration:
Syntax OK
apachectl restart 
Performing sanity check on apache24 configuration:
Syntax OK
Stopping apache24.
Waiting for PIDS: 67995.
Performing sanity check on apache24 configuration:
Syntax OK
Starting apache24.

To test PHP-FPM create a temporary info.php file.

cat <<EOF > /usr/local/www/$FQDN/public_html/info.php
<?php
phpinfo();
?>
EOF
chown "$FQDN":"$FQDN" $FQDNHOME/public_html/info.php

After that test with a browser, open https://$FQDN/info.php URL. If you found everything just fine please delete the info.php file.

vhost.template

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.