This is an old revision of the document!
apachesurvival
This tutorial is for users of Debian GNU/Linux using the LAMP stack, wanting TLS encryption, multiple self-hosted websites, feature rich content management, etc., without sacrificing ownership or security. This tutorial will cover:
There is probably a bit more … but this will get us started. With the exception of Cacti, these are skills I learned on BSD / macOS and then migrated to Debian GNU/Linux which runs them better, has better implementations of the LAMP stack, more feature control, and most importantly is #freesoftware.
Installing apache, setting up two ore more websites. If you are interested in setting up a website outside of /var/www/, say for example, /home/server, then please see the addenda at the bottom covering non-standard web root directories. There is nothing wrong with this approach, but there are some simple precautions. For now, proceed with the examples below which guide you through creating two virtual hosts, but this can be scaled to as many as you like:
sudo apt install apache2 
sudo mkdir -p /var/www/site1.com/public_html
sudo mkdir -p /var/www/site2.com/public_html
sudo chown -R $USER:$USER /var/www/site1.com/public_html 
sudo chown -R $USER:$USER /var/www/site2.com/public_html
sudo chmod -R 755 /var/www
nano /var/www/site1.com/public_html/index.html
<html>
  <head>
    <title>site1</title>
  </head>
  <body>
    <h1>site1</h1>
  </body>
</html>
nano /var/www/site2.com/public_html/index.html
<html>
  <head>
    <title>site2</title>
  </head>
  <body>
    <h1>site2</h1>
  </body>
</html>
sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/site1.com.conf
sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/site2.com.conf
sudo nano /etc/apache2/sites-available/site1.com.conf
<VirtualHost *:80>
      ServerAdmin name@site1.com
      ServerName site1.com
      ServerAlias www.site1.com
      DocumentRoot /var/www/site1.com/public_html
      ErrorLog ${APACHE_LOG_DIR}/error.log
      CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
sudo nano /etc/apache2/sites-available/site2.com.conf
<VirtualHost *:80>
      ServerAdmin name@site2.com
      ServerName site2.com
      ServerAlias www.site2.com
      DocumentRoot /var/www/site2.com/public_html
      ErrorLog ${APACHE_LOG_DIR}/error.log
      CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
sudo a2ensite site1.com.conf
sudo a2ensite site2.com.conf
sudo cp -r /var/www/html /root/
sudo rm -r /var/www/html
sudo a2dissite 000-default.conf
sudo nano /etc/hosts
127.0.0.1       localhost
127.0.1.1       host.domain.com  hostname
# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
#Virtual Hosts - NOT Optional - replace xxx etc., with external IP
xxx.xxx.xxx.xxx site1.com
xxx.xxx.xxx.xxx www.site1.com
xxx.xxx.xxx.xxx site2.com
xxx.xxx.xxx.xxx www.site2.com
sudo apache2ctl configtest
sudo systemctl restart apache2.service
Visit site1.com and site2.com - debug, set up TLS - repeat this for additional sites, set up firewall w/ common exceptions.
sudo ufw install sudo ufw allow 22 sudo ufw allow 80 sudo ufw allow 443
sudo ufw enable
Create self-signed TLS certificates, set up ssl.conf for each virtual host.
sudo openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout /etc/ssl/private/site1.key -out /etc/ssl/certs/site1.crt Country Name (2 letter code) [AU]: <Country Initials> State or Province Name (full name) [Some-State]: <State or Commonwealth, etc., Name> Locality Name (eg, city) []: <City or Township, etc., Name> Organization Name (eg, company) [Internet Widgits Pty Ltd]: <Group or Entity, etc., Name> Organizational Unit Name (eg, section) []: <Department or Branch, etc., Name> Common Name (e.g. server FQDN or YOUR name) []: <site1 ip address> Email Address []: person@site1.com sudo openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout /etc/ssl/private/site2.key -out /etc/ssl/certs/site2.crt Country Name (2 letter code) [AU]: <Country Initials> State or Province Name (full name) [Some-State]: <State or Commonwealth, etc., Name> Locality Name (eg, city) []: <City or Township, etc., Name> Organization Name (eg, company) [Internet Widgits Pty Ltd]: <Group or Entity, etc., Name> Organizational Unit Name (eg, section) []: <Department or Branch, etc., Name> Common Name (e.g. server FQDN or YOUR name) []: <site1 ip address> Email Address []: person@site1.com
Configure diffie-hellman key for all TLS enabled virtual hosts, configure ssl-params.conf for all TLS enabled virtual hosts.
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096 sudo cp /etc/apache2/conf-available/ssl-params.conf /root/ sudo nano /etc/apache2/conf-available/ssl-params.conf # from https://cipherli.st/ # and https://raymii.org/s/tutorials/Strong_SSL_Security_On_Apache2.html SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLProtocol All -SSLv2 -SSLv3 SSLHonorCipherOrder On # Disable preloading HSTS for now. You can use the commented out header line that includes # the "preload" directive if you understand the implications. #Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains" #Nextcloud likes this: #Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains" Header always set X-Frame-Options DENY Header always set X-Content-Type-Options nosniff # Requires Apache >= 2.4 SSLCompression off SSLSessionTickets Off SSLUseStapling on SSLStaplingCache "shmcb:logs/stapling-cache(150000)" SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"
Configure virtual hosts for TLS for each domain previously configured above.
sudo cp /etc/apache2/sites-available/default-ssl.conf /root/default-ssl.conf.bak sudo cp /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-available/site1.com-ssl.conf.bak sudo cp /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-available/site2.com-ssl.conf.bak
Create TLS sites in sites-enabled:
sudo nano /etc/apache2/sites-available/site1.com-ssl.conf
<IfModule mod_ssl.c>
      <VirtualHost _default_:443>
              ServerAdmin name@site1.com
              ServerName site1.com
              DocumentRoot /var/www/site1.com/public_html
              ErrorLog ${APACHE_LOG_DIR}/error.log
              CustomLog ${APACHE_LOG_DIR}/access.log combined
              SSLEngine on
              SSLCertificateFile      /etc/ssl/certs/site1.crt
              SSLCertificateKeyFile /etc/ssl/private/site1.key
              <FilesMatch "\.(cgi|shtml|phtml|php)$">
                              SSLOptions +StdEnvVars
              </FilesMatch>
              <Directory /usr/lib/cgi-bin>
                              SSLOptions +StdEnvVars
              </Directory>
              BrowserMatch "MSIE [2-6]" \
                             nokeepalive ssl-unclean-shutdown \
                             downgrade-1.0 force-response-1.0
      </VirtualHost>
</IfModule>
sudo nano /etc/apache2/sites-available/site2.com-ssl.conf
  <IfModule mod_ssl.c>
      <VirtualHost _default_:443>
              ServerAdmin name@site2.com
              ServerName site2.com
              ServerAlias www.site2.com
              DocumentRoot /var/www/site2.com/public_html
              ErrorLog ${APACHE_LOG_DIR}/error.log
              CustomLog ${APACHE_LOG_DIR}/access.log combined
              SSLEngine on
              SSLCertificateFile      /etc/ssl/certs/site2.crt
              SSLCertificateKeyFile /etc/ssl/private/site2.key
              <FilesMatch "\.(cgi|shtml|phtml|php)$">
                              SSLOptions +StdEnvVars
              </FilesMatch>
              <Directory /usr/lib/cgi-bin>
                              SSLOptions +StdEnvVars
              </Directory>
              BrowserMatch "MSIE [2-6]" \
                             nokeepalive ssl-unclean-shutdown \
                             downgrade-1.0 force-response-1.0
      </VirtualHost>
</IfModule>
Redirect the original sites-enabled to default to TLS.
sudo nano /etc/apache2/sites-available/site1.com.conf
      Redirect permanent "/" "https://site1.com/
sudo nano /etc/apache2/sites-available/site2.com.conf
      Redirect permanent "/" "https://site2.com/
Enable both TLS sites, check configuration:
sudo a2enmod ssl sudo a2enmod headers sudo a2enconf ssl-params sudo a2ensite site1.com-ssl.conf sudo a2ensite site2.com-ssl.conf sudo apache2ctl configtest
Ignore error below, or set global ServerName (not advised) to avoid:
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message Syntax OK sudo systemctl restart apache2
Set up Let's Encrypt for free certificate authority on the SSL certs you just made.
sudo apt install certbot letsencrypt python-certbot-apache certbot --authenticator standalone --installer apache -d site1.com --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2" certbot --authenticator standalone --installer apache -d site2.com --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2" sudo systemctl restart apache2
Test them both, first clear cache and restart browser, set up cron job to update every week:
https://www.ssllabs.com/ssltest/analyze.html?d=site1.com&latest https://www.ssllabs.com/ssltest/analyze.html?d=site2.com&latest sudo crontab -e 30 2 * * 1 /usr/bin/certbot renew >> /var/log/le-renew.log sudo systemctl restart cron.service sudo systemctl restart apache2
Manually check certificates by:
sudo certbot renew
Okay, now you need to make sure that your server stays running and that uptime is maximized. Let's create two simple shell scripts that check the status, and restart the service if needed. First create apache-notify.sh:
sudo touch /usr/local/bin/apache-notify.sh sudo chmod 750 /usr/local/bin/apache-notify.sh sudo chown $USER:$USER /usr/local/bin/apache-notify.sh sudo nano /usr/local/bin/apache-notify.sh
Ok, now that we created the script file and made it executable, paste in the contents below but adjust them to your needs.
#!/bin/sh
#functions
RESTART="/bin/systemctl restart apache2.service"
SERVICE="apache2.service"
LOGFILE="/home/username/Desktop/apache-restart.log"
#check for the word dead in the service output from systemctl
if
    systemctl status apache2.service | grep dead
then
    echo "Sir, apache2 failed at $(date), so I restarted it for you." >> $LOGFILE
else
    echo "Ms., apache2 was running as of $(date)" >> $LOGFILE
fi
Ok, now we also want to restart it at exactly the same time that we found out it wasn't running. So, you can also create a script called apache-restart.sh that restarts the service when it finds it dead:
#!/bin/sh
#functions
RESTART="/bin/systemctl restart apache2.service"
SERVICE="apache2.service"
LOGFILE="/home/username/Desktop/apache-restart.log"
#check for the word dead in the service output from systemctl
if
    systemctl status apache2.service | grep dead
then
    $RESTART >> $LOGFILE  
fi
Thanks to @varange on a Digital Ocean forum who inspired these scripts, but I had to change them for systemd, and I also changed them into two scripts because the way he had originally composed them together with an “else” comment, there was always a minute discrepancy between the notification it was down, and the time it restarted. Lastly, these will not run automatically, so create a cron job:
sudo crontab -e * * * * * /bin/bash /usr/local/bin/apache-notify.sh * * * * * /bin/bash /usr/local/bin/apache-restart.sh sudo systemctl stop apache2 sudo systemctl restart cron sudo systemctl status apache2 [Wait 1 minute, and then run same command again to confirm working]
Make sure to run the cron job as root, or it will not work. You can also run the scripts manually if you want since we added them to the default user PATH above. Also, make sure your log files do not explode by adding some entries to /etc/logrotate.d/
/home/user/Desktop/mysql-restart.log {
      daily
      rotate 10
      delaycompress
      compress
      notifempty
      missingok
      size 100000k
}
Ok, now that you have a few self-hosted sites that are protected, running, and monitored, you can start creating your Self-Hosted Word Press!
This tutorial is a designated “Invariant Section” of the “Technotronic” section of Haack's Wiki as described on the Start Page.
— oemb1905 2019/01/02 20:36