User Tools

Site Tools


computing:apachesurvival

This is an old revision of the document!



  • apachesurvival
  • Jonathan Haack
  • Haack's Networking
  • netcmnd@jonathanhaack.com

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:

  • Establish LAMP stack
  • Virtual hosts for more than one website
  • Creation of self-signed SSL
  • Let's Encrypt with Certbot
  • MySQL survival commands
  • Installation of Joomla, Wordpress, Dokuwiki, Cacti
  • Installation and configuration of local sftp server
  • Directory permissions
  • firewall rules with ufw
  • symbolic links for External Drive outside of root of webserver (risky)

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.

sudo apache2ctl configtest
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 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 ssh
sudo ufw allow 22
sudo ufw allow 222
sudo ufw allow http
sudo ufw allow 80
sudo ufw allow https
sudo ufw allow 443
sudo ufw allow 'WWW Secure'
sudo ufw allow 'WWW Full'
sudo ufw allow 'WWW'
sudo ufw allow 1194/udp
sudo ufw allow 1194
sudo ufw allow git
sudo ufw allow openvpn
sudo ufw allow samba
sudo ufw allow nfs
sudo ufw allow vnc
sudo ufw allow 21
sudo ufw allow ftp
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"
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>

Leave both http and https, or only https; for https-only, add a line to the .conf below ('permanent' is optional).

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 and re-directs, check configuration:

sudo a2enmod ssl
sudo a2enmod headers
sudo a2ensite site1.com.conf
sudo a2ensite site2.com.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  

Set up ftp server and then set up content mgt & mySQL

sudo apt install proftpd ftp ftp-ssl ftpd-ssl
cd /etc/proftpd
openssl req -new -x509 -days 7300 -nodes -out ftpd-rsa.pem -keyout ftpd-rsa-key.pem
sudo nano /etc/proftpd/proftpd.conf

<IfModule mod_tls.c>
   TLSEngine on
   TLSLog /var/log/proftpd-tls.log
   TLSProtocol TLSv1
   # Are clients required to use FTP over TLS when talking to this server?
   TLSRequired off
   TLSRSACertificateFile    /etc/proftpd/ftpd-rsa.pem
   TLSRSACertificateKeyFile /etc/proftpd/ftpd-rsa-key.pem
   # Authenticate clients that want to use FTP over TLS?
   TLSVerifyClient off
</IfModule>

sudo systemctl restart proftpd.service

Might change the TLS version/requirement - now, to MySQL set up and making index.php default.

sudo apt install mysql-server php7.0 phpmyadmin apache2-utils php libapache2-mod-php php-mcrypt php-mysql
sudo mysql_secure_installation
sudo nano /etc/apache2/mods-enabled/dir.conf

<IfModule mod_dir.c>
    DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm
</IfModule>

sudo systemctl restart mysqld.service

Secure phpmyadmin with user phpmyadmin and .htaccess file .phpmyadmin for security.

sudo htpasswd -c /etc/apache2/.phpmyadmin phpmyadmin  
sudo nano /usr/share/phpmyadmin/.htaccess

AuthType Basic
AuthName "Restricted Files"
AuthUserFile /etc/apache2/.phpmyadmin
Require valid-user

sudo systemctl restart apache2.service

Now, the MySQL - more here than neeeded in case of trouble:

sudo mysql -u root -p
mysql> CREATE DATABASE database1name DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
mysql> GRANT ALL ON database1name.* TO 'databaseuser'@'localhost' IDENTIFIED BY 'passwdhere';
mysql> CREATE DATABASE database2name DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
mysql> GRANT ALL ON database2name.* TO 'databaseuser'@'localhost' IDENTIFIED BY 'passwdhere';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

Install PHP, configure .htaccess to allow overrides, enable apache modules:

sudo apt update
sudo apt-get install php-curl php-gd php-mbstring php-mcrypt php-xml php-xmlrpc
sudo nano /etc/apache2/apache2.conf

<Directory /var/www/html/>
    AllowOverride All
</Directory>

sudo systemctl restart apache2
sudo a2enmod rewrite
sudo apache2ctl configtest

If you have not set the fully qualified domain name, you may get an error - that can safely be ignored unless you desire it.

cd ~/Downloads
mkdir wpdownload
cd wpdownload
curl -O https://wordpress.org/latest.tar.gz
tar xzvf latest.tar.gz
touch ~/Downloads/wordpress/.htaccess
sudo chmod 660 ~/Downloads/wordpress/.htaccess
cp ~/Downloads/wpdownload/wordpress/wp-sample-config.php ~/Downloads/wpdownload/wordpress/wp-config.php
mkdir ~/Downloads/wpdownload/wordpress/wp-content/upgrade
sudo cp -ar ~/Downloads/wpdownload/wordpress/. /var/www/site1.com/public_html/
sudo cp -ar ~/Downloads/wpdownload/wordpress/. /var/www/site2.com/public_html/
sudo chown -R username:www-data /var/www/site1.com/public_html
sudo chown -R username:www-data /var/www/site2.com/public_html
sudo find /var/www/site1.com/public_html -type d -exec chmod g+s {} \;
sudo find /var/www/site2.com/public_html -type d -exec chmod g+s {} \;
sudo chmod g+w /var/www/site1.com/public_html/wp-content
sudo chmod g+w /var/www/site2.com/public_html/wp-content
sudo chmod -R g+w /var/www/site1.com/public_html/wp-content/themes
sudo chmod -R g+w /var/www/site2.com/public_html/wp-content/themes
sudo chmod -R g+w /var/www/site1.com/public_html/wp-content/plugins
sudo chmod -R g+w /var/www/site2.com/public_html/wp-content/plugins

curl -s https://api.wordpress.org/secret-key/1.1/salt/

sudo nano /var/www/site1.com/public_html/
<swap the defined values>
sudo nano /var/www/site2.com/public_html/
<swap the defined values>

sudo nano /var/www/site1.com/public_html/wp-config.php

. . .
define('DB_NAME', 'database1name');
/** MySQL database username */
define('DB_USER', 'databaseuser');
/** MySQL database password */
define('DB_PASSWORD', 'passwdhere');
. . .
define('FS_METHOD', 'direct');

sudo nano /var/www/site2.com/public_html/wp-config.php

. . .
define('DB_NAME', 'database2name');
/** MySQL database username */
define('DB_USER', 'databaseuser');
/** MySQL database password */
define('DB_PASSWORD', 'passwdhere');
. . .
define('FS_METHOD', 'direct');

sudo systemctl restart apache2

You are now done …

computing/apachesurvival.1526008980.txt.gz · Last modified: 2018/11/25 01:33 (external edit)