Saturday, September 7, 2019

Web host set-up with fresh Ubuntu 18.04 Install - apache2 as webserver with nginx as proxy

Install Apache2


First install Apache2, PHP-FPM, and FastCGI Apache module
$ sudo apt update && sudo apt upgrade
$ sudo apt-get install apache2 php-fpm
$ wget https://mirrors.edge.kernel.org/ubuntu/pool/multiverse/liba/libapache-mod-fastcgi/libapache2-mod-fastcgi_2.4.7~0910052141-1.2_amd64.deb
$ sudo dpkg -i libapache2-mod-fastcgi_2.4.7~0910052141-1.2_amd64.deb
Configure Apache2 to use PHP-FPM.  First change Apache port number to 8080.
$ sudo cp /etc/apache2/ports.conf /etc/apache2/ports.conf.default
$ sudo nano /etc/apache2/ports.conf
Change Listen 80 in the ports.conf file you just opened to Listen 8080. Save file.
Configure hostname.
$ sudo nano /etc/apache2/apache2.conf
Add the following:
ServerName localhost
Open hosts file
$ sudo nano /etc/hosts
Edit as needed:
127.0.0.1 localhost
127.0.1.1 myhostname
Create directory for apache2 logs and change ownership and permissions.
$ sudo mkdir /var/log/apache2
$ sudo chmod 750 /var/log/apache2
$ sudo chown root:adm /var/log/apache2
Apache comes with a default virtual host file called 000-default.conf.  Copy this file for your domain.
$ sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/your-domain.com.conf
Open your-domain.conf file just created.
$ sudo nano /etc/apache2/sites-available/your-domain.com.conf
Change the listening port to 8080
The file contents should look something like this:
<VirtualHost *:8080>

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>
Disable 000-default.conf and enable the new conf file for your domain as follows:
$ sudo a2dissite 000-default.conf
$ sudo a2ensite your-domain.com.conf
Reload Apache.
$ sudo systemctl reload apache2
Install net-tools if necessary for the netstat command.
$ sudo apt install net-tools
Verify Apache is listening to port 8080.
$ sudo netstat -tlpn
Output:
Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      2204/systemd-resolv
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      21618/sshd
tcp6       0      0 :::8080                 :::*                    LISTEN      7454/apache2
tcp6       0      0 :::22                   :::*                    LISTEN      21618/sshd


Create directory structure:

$ sudo mkdir -p /var/www/your-domain.com/public_html
The above creates directories owned by root.  Change ownership so other users can access.
$ sudo chown -R $USER:$USER /var/www/your-domain.com/public_html
Modify permissions to ensure that read access is permitted to the general web directory and all of the files and folders it contains.
$ sudo chmod -R 755 /var/www
Set up a temporary web page for testing
$ nano /var/www/your-domain.com/public_html/index.html
Add the following contents<
<html>
  <head>
    <title>Welcome!</title>
  </head>
  <body>
    <h1>Welcome to our test page!</h1>
  </body>
</html>

Create New Virtual Host File


Edit the your-domain.conf file created earlier.
$ sudo nano /etc/apache2/sites-available/your-domain.com.conf
Edit the file with the following changes on red.
<VirtualHost *:8080>

        ServerAdmin [email protected]
        ServerName your-domain.com
        ServerAlias *.your-domain.com
        DocumentRoot /var/www/your-domain.com/public_html    
        <Directory /var/www/your-domain.com/public_html>
            AllowOverride All
        </Directory>    
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>
The entry AllowOverride All  enables .htaccess support. Save the file.

Enable the New Virtual Host File


Activate each site as follows (should already be activated):
$ sudo a2ensite your-domain.com.conf
You should get a message that confirms the site was enabled.
Output
Enabling site your-domain.com.
To activate the new configuration, you need to run:
  service apache2 reload
Allow port 8080 in the firewall if needed
$ sudo ufw allow 8080
Restart Apache.
$ sudo systemctl restart apache2
The sites are configured.  Verify that you can reach the site (http://your-domain.com:8080.
If you can't reach the site, try running the following.
$ sudo apache2ctl -S
Verify the information is correct.

Next configure support for PHP and FastCGI.


The module mod_fastcgi depends on mod_action.  Enable mod_action:
$ sudo a2enmod actions
Backup the existing fastcgi conf file:
$ sudo cp /etc/apache2/mods-enabled/fastcgi.conf /etc/apache2/mods-enabled/fastcgi.conf.default
Edit the file:
$ sudo nano /etc/apache2/mods-enabled/fastcgi.conf
Replace the contents of the file with the following:
<IfModule mod_fastcgi.c>
  AddHandler fastcgi-script .fcgi
  FastCgiIpcDir /var/lib/apache2/fastcgi
  AddType application/x-httpd-fastphp .php
  Action application/x-httpd-fastphp /php-fcgi
  Alias /php-fcgi /usr/lib/cgi-bin/php-fcgi
  FastCgiExternalServer /usr/lib/cgi-bin/php-fcgi -socket /run/php/php7.2-fpm.sock -pass-header Authorization
  <Directory /usr/lib/cgi-bin>
    Require all granted
  </Directory>
</IfModule>
Save the file and check the configuration
$ sudo apachectl -t
If the syntac is OK, reload apache.
$ sudo systemctl reload apache2
Verify php is working.  Create a info.php file.
$ echo "<?php phpinfo(); ?>" | sudo tee /var/www/your-domain.com/public_html/info.php
Go to http://your_ip:8080\info.php.  Near the top, Server API should say FPM/FastCGI. Find SERVER_SOFTWARE.  It should say is Apache on Ubuntu.

Install nginx


$ sudo apt install nginx
Delete default symlink no longer needed.
$ sudo rm /etc/nginx/sites-enabled/default
Create a virtual host file.
$ sudo nano /etc/nginx/sites-available/your-domain.com
Populate as follows:
server {
    listen 80;

    root /var/www/your-domain.com/public_html;
    index index.php index.html index.htm;

    server_name your-domain.com *.your-domain.com;
    location / {
        try_files $uri $uri/ /index.php;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
        include snippets/fastcgi-php.conf;
    }
}
Enable the site by creating a symbolic link to the sites-enabled directory.
$ sudo ln -s /etc/nginx/sites-available/your-domain.com /etc/nginx/sites-enabled/your-domain.com
Test the Nginx configuration.
$ sudo nginx -t
If the test is OK, restart nginx.
$ sudo systemctl reload nginx
Open the browser and try to access the info.php file.
http://your-domain.com/info.php
Once the info.php file is opened find SERVER_SOFTWARE on the page.  It should identify nginx as the server. Also find DOCUMENT_ROOT. Verify the document root is correct.

Proxy nginx domain names to apache


Create nginx virtual host for forwarding requests to apache.
$ sudo nano /etc/nginx/sites-available/apache
Populate file with the following.
server {
    listen 80;
    server_name your-domain.com *.your-domain.com;

    location / {
        proxy_pass http://your_ip:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
Save the file, then enable it by creating a symbolic link.
$ sudo ln -s /etc/nginx/sites-available/apache /etc/nginx/sites-enabled/apache
If you get a file already exists error try this.
$ cd /etc/nginx/sites-enabled/apache
$ sudo ln -s /etc/nginx/sites-available/apache
Test the configuration
$ sudo nginx -t
If no errors reload nginx
$ sudo systemctl reload nginx
Go to http://you-domain.com/info.php. 
Review the php info.  Verify DOCUMENT_ROOT is correct for the apache root.  Verify SERVER_SOFTWARE is apache. Variables HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR were added by nginx and contain your ip address.

Configure Apache to set the rewrite values for proxy


The apache module mod\_rpaf rewrites values REMOTE_ADDR, HTTPS and HTTP_PORT to the values provided by a reverse proxy. Install the following.
$ sudo apt install unzip build-essential apache2-dev
Download the latest stable rpaf release, unzip, and install the module.
$ cd /tmp
$ wget https://github.com/gnif/mod_rpaf/archive/stable.zip
$ unzip stable.zip
$ cd mod_rpaf-stable
$ make
$ sudo make install
$ sudo apt install libtool.bin
$ libtool --finish /usr/lib/apache2/modules
Create file in the mods-available directory which will load mod_rpaf
$ sudo nano /etc/apache2/mods-available/rpaf.load
Populate the file with the following
LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so
Save the file.  Create a configuration file in the same directory
$ sudo nano /etc/apache2/mods-available/rpaf.conf
Populate as follows:
    <IfModule mod_rpaf.c>
        RPAF_Enable             On
        RPAF_Header             X-Real-Ip
        RPAF_ProxyIPs           your_ip 
        RPAF_SetHostName        On
        RPAF_SetHTTPS           On
        RPAF_SetPort            On
    </IfModule>
Save the file. Enable the module
$ sudo a2enmod rpaf
Test the configuration
$ sudo apachectl -t
If no errors reload apache2
$ sudo systemctl reload apache2; 
Return to the browser and go to http://your-domain.com/info.php. The REMOTE_ADDR variable should now be your local computer’s public ip address.

Secure Website


You can secure your website with a free Let's Encrypt Certificate.

Install certbot


Add the PPA and install certbot (install software-properties-common if necessary)
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt update
Install certbot packages:
$ sudo apt install python-certbot-nginx 

Configure letsencrypt.conf


When installing certificate Let's Encrypt places a temporary file in the path htttp://your-doman.com/.well-known/acme-challenge. If it doesn't receive a proper response the certificate is rejected.  Here are steps needed to pass the challenge.

Copy the your-domain.com file created earlier in the /etc/nginx/sites-available/ directory to a new your-domain.com.conf file for ssl.
$ sudo cp /etc/nginx/sites-available/your-domain.com /etc/nginx/conf.d/your-domain.com.conf
Edit the new file.
$ sudo nano /etc/nginx/conf.d/your-domain.com.conf
Make changes as shown:
server {
    listen 80;

    root /var/www/your-domain.com/public_html;
    index index.php index.html index.htm;

    server_name your-domain.com *.your-domain.com;
    location / {
        try_files $uri $uri/ /index.php;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
        include snippets/fastcgi-php.conf;
    }
    location ~ /.well-known/acme-challenge {
        allow all;
    }
}
When let's encrypt issues the certificate it will add 443 ssl data to the existing service block of the /etc/nginx/conf.d/your-domain.com.conf file.


SSL installation 


Check syntax:
$ sudo nginx -t
If OK, reload nginx
$ sudo systemctl reload nginx
Make sure the https port 443 is included in your firewall configuration. See Install Firewall (ufw).

Install certificate:


If the dry-run passes you can issue the certificate
$ sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Answer any prompts and wait for the confirmation that the certificate has been issued.  If you have problems getting the certificate issue, try visiting the website https://letsdebug.net/.
Since we are using the webroot plugin we need to reload the nginx server at renewal.  To do this, append --renew-hook "systemctl reload nginx" to the /etc/cron.d/certbot file:
$ sudo nano /etc/cron.d/certbot
There is a one-line string of text.  Append so it looks like this
$ 0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(3600))' && certbot -q renew --renew-hook "systemctl reload nginx"
The Let's Encrypt Certificate is only valid for 90 days.  However the certbot installation includes a cron script that auto renews the certificate 30 days before expiration.  The script is located at /etc/cron.d.

You can test the renewal process by running the following command again:
$ sudo certbot renew --dry-run