Table of Contents

Install CentOS 8 LAMP stack

This is install a LAMP stack on CentOS 8 with virtual host support, PHP, fail2ban, SSH security, letsencrypt, auto updates.

Install CentOS 8 minimal with 2 CPU, 512MB+ RAM, 20GB+ storage, set FQDN, set static IP, enable NTP.

3.) After install if finished reboot → login → perform a “dnf update”.

Create limited user account and add to wheel group for sudo
useradd example_user && passwd example_user
usermod -aG wheel example_user
Install dependencies and vim
dnf install unzip wget vim tar

Logout of root and login using sudo user

Disallow root login over SSH
sudo vim /etc/ssh/sshd_config

then set

PermitRootLogin no
Generate SSH key for sudo user on client computer (not the webserver)

To help keep things organized we'll create a keypair that is specific to the user and the remote sudo user+host.
https://www.ssh.com/ssh/keygen/

ssh-keygen -C "your_email@example.com" -f ~/.ssh/your_email@example.com-remote_sudo_username_@remote_hostname -t ed25519

Record the private and public keys in a secure document for the webserver.
Copy the public key to the remote webserver.

ssh-copy-id -i ~/.ssh/your_email@example.com-remote_sudo_username_@remote_hostname.pub sudo_username@remote_hostname
sudo vim /etc/ssh/sshd_config

then set

PasswordAuthentication no

Restart sshd

sudo systemctl restart sshd

Login using SSH key

ssh -i deployment_key.txt demo@192.237.248.66

https://www.tecmint.com/install-lamp-on-centos-8/ https://linuxize.com/post/how-to-install-php-on-centos-8/ https://www.linode.com/docs/web-servers/lamp/how-to-install-a-lamp-stack-on-centos-8/ https://www.linode.com/docs/web-servers/apache/how-to-install-and-configure-fastcgi-and-php-fpm-on-centos-8/ https://www.digitalocean.com/community/tutorials/how-to-run-multiple-php-versions-on-one-server-using-apache-and-php-fpm-on-ubuntu-18-04 https://certbot.eff.org/lets-encrypt/centosrhel8-apache.html

Install Apache

Configure hosts
sudo vim /etc/hosts

Add a line for your FQDN

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.10.30  websrv01.domainname.com websrv01
sudo dnf install httpd httpd-tools
sudo systemctl enable httpd
sudo systemctl start httpd
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

Install MariaDB

sudo dnf install mariadb-server mariadb
sudo systemctl enable mariadb
sudo systemctl start mariadb
sudo mysql_secure_installation

Install PHP

sudo dnf install dnf-utils http://rpms.remirepo.net/enterprise/remi-release-8.rpm
sudo dnf module reset php
sudo dnf module enable php:remi-8.2
sudo dnf install php php-opcache php-gd php-curl php-mysqlnd mod_fcgid
sudo systemctl enable php-fpm
sudo systemctl start php-fpm

Increase upload and post size limit for PHP

sudo vim /etc/php.ini

Modify the following lines to meet your requirements

upload_max_filesize = 50M ;
post_max_size = 50M ;
Create php PHP pools for each site

Note: isn't taking full advantage of the feature, feel free to research more if needed.

sudo cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/example.com.conf

Edit the file to change the socket name, user and group, and socket listen address. Ensure that the listen address is different from the listen address that you set in the main PHP pool configuration file. You can append the name of your site as part of the file name, for example, listen = /var/run/php-fpm/example.com.sock. Also, ensure that you comment out or replace any existing user and group and add your own user and group settings as shown in the example.

sudo vim /etc/php-fpm.d/example.com.conf
; 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]

...

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = apache
group = apache

...
listen = /var/run/php-fpm/www.sock

Note: you can replace each instance of www with example.domain.com by using the following in vim:

:g/www/s//example\.domain\.com/g

The $s searches through the whole file, the \. is needed because . is a special regex pattern and the \ escapes it… though I'm still not entirely sure what escapes is; regex to me is like cooking or playing music, magic.

Create Virtual Hosts

Create directories for sites
sudo mkdir -p /var/www/html/site1.your_domain/public_html
sudo mkdir -p /var/www/html/site2.your_domain/public_html
Set Permissions
sudo chown -R apache:apache /var/www/html/site1.your_domain/public_html
sudo chown -R apache:apache /var/www/html/site2.your_domain/public_html
sudo chmod -R 755 /var/www/html/site1.your_domain/public_html
sudo chmod -R 755 /var/www/html/site2.your_domain/public_html

Create a file in each site to test webserver and show php version

sudo vim /var/www/html/site1.your_domain/public_html/info.php

Add

<?php phpinfo(); ?>
Setup virtual host config files
sudo vim /etc/httpd/conf.d/site1.your_domain.conf

Add the following content. Make sure the website directory path, server name, and PHP version match your setup:

<Directory /var/www/html/example.com/public_html>
        Require all granted
        Options -Indexes -MultiViews -Includes -FollowSymLinks
</Directory>
<VirtualHost *:80>
     ServerAdmin webmaster@example.com
     ServerName example.com
     ServerAlias www.example.com
     DocumentRoot /var/www/html/example.com/public_html
     ErrorLog /var/log/httpd/example.com_error.log
     CustomLog /var/log/httpd/example.com_access.log combined
     DirectoryIndex index.php
     <IfModule mod_fcgid.c>
         Options +ExecCGI
         FcgidConnectTimeout 20
         AddType  application/x-httpd-php         .php
         AddHandler application/x-httpd-php .php
         Alias /php7-fcgi /usr/lib/cgi-bin/php7-fcgi
         ProxyPassMatch " ^/(.*\.php(/.*)?)$" "unix:/run/php-fpm/example.com.sock|fcgi://localhost/var/www/html/example.com/public_html/"
     </IfModule>
</VirtualHost>

Save and close the file when you are finished. Then check the Apache configuration file for any syntax errors:

sudo httpd -t

You’ll see the following output:

Output
Syntax OK
Disable Exposing Apache Server Info

https://www.if-not-true-then-false.com/2009/howto-hide-and-modify-apache-server-information-serversignature-and-servertokens-and-hide-php-version-x-powered-by/
This will help to prevent being the target of a an attack if you're running an out of date or vunerable release

sudo vim /etc/httpd/conf/httpd.conf

Add the following:

ServerSignature Off
ServerTokens Prod
Disable Exposing PHP Version Info

And add your timezone…

sudo vim /etc/php.ini

Change the following:

expose_php = Off
date.timezone = America/Los_Angeles

Finally, restart the Apache service and PHP-FPM to implement your changes:

sudo systemctl restart httpd
sudo systemctl restart php-fpm

Now that you have configured Apache to serve each site, you will test them to make sure the proper PHP versions are running.

test.example.com/info.php

delete the info.php after your'e done.

delete the welconf.conf to disable the default apache welcome page.

sudo rm /etc/httpd/conf.d/welcome.conf
Remove Default Access from httpd.conf

This is important as it would allow access to the /var/www/html path via HTTP.

sudo vim /etc/httpd/conf/httpd.conf

Modify the following statements to mirror the examples below:

DocumentRoot "/var/www/html"

<Directory "/var/www">
    AllowOverride None
    Require all denied
    Options None
</Directory>

<Directory "/var/www/html">
    AllowOverride None
    Require all denied
    Options None
</Directory>

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Require all denied
    Options None
</Directory>

Letsencrypt

Install certbot
wget https://dl.eff.org/certbot-auto
sudo mv certbot-auto /usr/local/bin/certbot-auto
sudo chown root /usr/local/bin/certbot-auto
sudo chmod 0755 /usr/local/bin/certbot-auto

Generate local certificates

sudo /usr/libexec/httpd-ssl-gencerts

Request and install certificates for virtual hosts

sudo /usr/local/bin/certbot-auto --apache

When prompted enabled Redirect so all requests go over https

Enable autorenew via crontab

echo "0 0,12 * * * root python3 -c 'import random; import time; time.sleep(random.random() * 3600)' && /usr/local/bin/certbot-auto renew -q" | sudo tee -a /etc/crontab > /dev/null

OR... Certificate manually via CA

Enable SSL Apache

https://www.thesslstore.com/knowledgebase/ssl-generate/generate-csr-apache-web-server-using-openssl-v-2/ https://forums.centos.org/viewtopic.php?t=43230 https://www.getpagespeed.com/server-setup/ssl-directory

Install Mod SSL

sudo dnf install mod_ssl
sudo /usr/libexec/httpd-ssl-gencerts
Create Private Key and CSR
sudo openssl req -new -newkey rsa:4096 -nodes -keyout site1.domain.com.key -out site1.domain.com.csr

Move the files to your PKI folder

sudo mv site1.domain.com.key /etc/pki/tls/private/

Set permissions on private key

sudo chown root:root /etc/pki/tls/private/site1.domain.com.key
sudo chmod 600 /etc/pki/tls/private/site1.domain.com.key
sudo restorecon -RvF /etc/pki/tls/private/

Submit the CSR to your CA (public or private)

Copy the BASE64 certificate from your CA to your cert folder

sudo vim /etc/pki/tls/certs/site1.domain.com.crt

Create the CA bundle associated with your certificate

sudo vim /etc/pki/tls/certs/site1.domain.com.ca-bundle

Paste the subordinate followed by the root BASE64 certificates

Edit the conf file for you website and change

<VirtualHost *:443>
    DocumentRoot /var/www/html/site1.domain.com/public_html
    ServerName site1.domain.com
        SSLEngine on
        SSLCertificateFile /etc/pki/tls/certs/site1.domain.com.crt
        SSLCertificateKeyFile /etc/pki/tls/private/site1.domain.com.key
        SSLCertificateChainFile /etc/pki/tls/certs/site1.domain.com.ca-bundle
        ....
        ....
        ....
    </VirtualHost>
Disable HTTP in Firewall

Do this only if you aren't using Letsencrypt for SSL/TLS

sudo firewall-cmd --remove-service=http --permanent
sudo firewall-cmd --reload

Or…. if you want to leave http open and have the client automatically redirected to https add this to your website.conf file, below the existing Virtual Host block (add a new one):

<VirtualHost *:80>
    ServerName site1.domain.com
RewriteEngine on
RewriteCond %{SERVER_NAME} =site1.domain.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Restart Apache

sudo systemctl restart httpd

Note: if you are using your own internal CA you'll need to import the root and any intermediate certificates into your browsers/etc.

Install Postfix

This is a send only SMTP server, create your public DNS records first. Then setup a virtual host for the domain of the mail server as this will allow us to use Letsencrypt certificates on the mail server. So if your smtp server is going to be mail.subdomain.domain.com then setup a virtual host to match that first then use certbot to get a letsencrypt certificate for that virtual host. We don't open any firewall rules to allow smtp traffic in as this will only send out messages from the host it runs on or the private subnets it's attached to.

Install Postfix + mailx
sudo dnf install postfix mailx
sudo systemctl enable postfix
sudo systemctl start postfix
sudo systemctl status postfix
Configure Postfix

This will configure it as a send only SMTP server

http://www.postfix.org/STANDARD_CONFIGURATION_README.html#local_network

sudo vim /etc/postfix/main.cf

Modify the following lines to match your domain/subnet

mydomain = subdomain.domain.com
myorigin = $mydomain
mynetworks = 127.0.0.0/8, 10.0.0.0/24, localhost

Restart Postfix

sudo systemctl restart postfix

Test sending an E-Mail

echo "Test Message" | mailx -s "Test Subject" recipient@somewhere.com
Letsencrypt certificates for Postfix

https://www.geekyramblings.net/2019/04/12/lets-encrypt-and-postfix/

sudo vim /etc/postfix/main.cf

Set the following variables

smtp_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtp_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
#smtp_tls_CApath = /etc/ssl/certs
#smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
#smtpd_tls_CApath = /etc/ssl/certs
#smtpd_tls_CAfile = /etc/ssl/certs/ca-bundle.crt

Restart postfix

sudo systemctl restart postfix

Test sending an E-Mail again

echo "Test Message 2" | mailx -s "Test Subject 2" recipient@somewhere.com

I haven't validated the TLS certificates but everything appears to be working. For a full test you'll likely need to have a working reply to email to have the results sent back.

Create Script to Reload Postfix
sudo vim /etc/letsencrypt/renewal-hooks/reload_postfix

Add the following

!/bin/sh
 for domain in $RENEWED_DOMAINS; do
   case $domain in
     subdomain.domain.com)
       cd /etc/letsencrypt/live/$domain
       chgrp letsencrypt *.pem
       chmod 640 *.pem
       /sbin/service postfix reload > /dev/null
       ;;
   esac
 done

Set permissions

sudo chmod 755 /etc/letsencrypt/renewal-hooks/reload_postfix

Setup a dedicated user for sending E-mail, use no_reply or similar so people get the picture. Use this info for SMTP authentication for your sending applications.

useradd no_reply && passwd no_reply

Automatic Updates for CentOS

https://www.tecmint.com/dnf-automatic-install-security-updates-automatically-in-centos-8/

sudo dnf install dnf-automatic
sudo vim /etc/dnf/automatic.conf

Set:

upgrade_type = security
download_updates = yes
apply_updates = yes
system_name = (your system name)
emit_via = motd

Enable the auto-update timer

sudo systemctl enable --now dnf-automatic.timer

fail2ban

https://idroot.us/install-fail2ban-centos-8/ https://www.digitalocean.com/community/tutorials/how-to-protect-an-apache-server-with-fail2ban-on-ubuntu-14-04

sudo dnf install fail2ban
Create a Jail for SSHd
sudo vim /etc/fail2ban/jail.d/sshd.local

Add the following:

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s

[selinux-ssh]
enabled  = true
port     = ssh
logpath  = %(auditd_log)s
Create a Jail for Apache
sudo vim /etc/fail2ban/jail.d/apache.local

Add the following:

[apache-auth]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s

[apache-badbots]
enabled  = true
port     = http,https
logpath  = %(apache_access_log)s
bantime  = 48h
maxretry = 1

[apache-noscript]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s

[apache-overflows]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s
maxretry = 2

[apache-nohome]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s
maxretry = 2

[apache-botsearch]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s
maxretry = 2

[apache-modsecurity]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s
maxretry = 2

[apache-shellshock]
enabled = true
port    = http,https
logpath = %(apache_error_log)s
maxretry = 1
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
sudo fail2ban-client status sshd