====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 ==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: Require all granted Options -Indexes -MultiViews -Includes -FollowSymLinks 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 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/" 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" AllowOverride None Require all denied Options None AllowOverride None Require all denied Options None AllowOverride None Require all denied Options None ====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 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 .... .... .... ==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): ServerName site1.domain.com RewriteEngine on RewriteCond %{SERVER_NAME} =site1.domain.com RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] 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