Categories
devops

WordPress on Linode

After using Heroku for a WordPress installation (here and here), Metatooth’s next WordPress installation is running on Linode. Metatooth had been using AWS and Heroku for cloud hosting, the economic conditions of the pandemic made for a budgetary decision. In addition, experiencing the impact of an AWS failure on a business dependent on compute availability, and a desire to not further enrich Jeff Bezos, led to this change.

It always pays to start at the beginning! The desired outcome was  Setting Up Multiple WordPress Sites with LXD Containers. The very helpful “Before You Begin” section on each Guide led all the way back to Getting Started with Linode. For this author, the key understanding came when reading A Beginner’s Guide to LXD. After a couple false starts, voila!

tgl@electra:~$ lxc list -c ns4t
+--------+---------+----------------------+-----------+
|  NAME  |  STATE  |         IPV4         |   TYPE    |
+--------+---------+----------------------+-----------+
| db     | RUNNING | 10.88.102.185 (eth0) | CONTAINER |
+--------+---------+----------------------+-----------+
| nginx1 | RUNNING | 10.88.102.96 (eth0)  | CONTAINER |
+--------+---------+----------------------+-----------+
| nginx2 | RUNNING | 10.88.102.112 (eth0) | CONTAINER |
+--------+---------+----------------------+-----------+
| proxy  | RUNNING | 10.88.102.42 (eth0)  | CONTAINER |
+--------+---------+----------------------+-----------+
tgl@electra:~$ 

The nginx1 container provides https://electra.growherbert.com and nginx2 provides https://growherbert.com.  One stumbling point was that the listen directive for each proxied nginx container must specify both “ssl” and “proxy_protocol”. For example:

ubuntu@proxy:~$ cat /etc/nginx/sites-available/electra.growherbert.com 
server {
	listen 80 proxy_protocol;
        listen [::]:80 proxy_protocol;

        server_name electra.growherbert.com;

        return 301 https://$host$request_uri;
}

server {
        listen 443 ssl proxy_protocol;
 
        server_name electra.growherbert.com;

        ssl_certificate /root/.acme.sh/growherbert.com/fullchain.cer;
        ssl_certificate_key /root/.acme.sh/growherbert.com/growherbert.com.key;

        location / {
                include /etc/nginx/proxy_params;

                proxy_pass http://nginx1.lxd;
        }

        real_ip_header proxy_protocol;
        set_real_ip_from 127.0.0.1;
}
ubuntu@proxy:~$ 

Lastly was the need to switch DNS providers in order to make use of acme.sh in the proxy container. There was even a helpful Guide for using this script —Secure a Website or Domain with Let’s Encrypt and acme.sh. Switching was easy using Linode’s Domain Manager.

What are you doing with LXD Containers? Tell us about it in the comments!

Categories
devops

NGINX Heroku WordPress

NGINX, Heroku, and WordPress combined. This website is powered by WordPress on Heroku. Details on configuration are here.

My next #DevOps task is to add NGINX in front of the WordPress application. In the future, this could be used as a load balancer. Until then, NGINX will reverse proxy the requests to the Heroku application instance.

To start, I spun up a virtual machine running Ubuntu on Amazon’s EC2. Check out how to install and configure NGINX here. Make sure to open your firewall to HTTP & HTTPS traffic!

I encountered two major issues:

  1. The request passed by NGINX through the reverse proxy was not being understood by Heroku’s application stack.
  2. Once the application stack properly handled the request, WordPress’s response triggered a redirect loop.

Reverse Proxy

Heroku’s stack uses the host of the request to route to the appropriate application instance. The originating Host value is (in my example) ‘wwww.metatooth.com’. Heroku shouldn’t respond to this. Turn off the app’s Custom Domain feature if it does. Update the reverse proxy to set a new Host header that Heroku will respond to.

server {
    server_name www.metatooth.com;

    location / {
        proxy_pass https://calm-waters-18762.herokuapp.com;
        proxy_set_header Host calm-waters-18762.herokuapp.com;
    }

    # configure SSL here
}

Redirect Loop

Setting the Host request header got Heroku’s attention, now WordPress responds to that request with a redirect loop. I encountered this problem while trying to configure an HTTPS-only WordPress installation. The way out of the redirect loop was to update two more request headers and then to use those headers in the WordPress configuration. Here’s the new section of wp-config.php

 
if ( $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )
{
        $_SERVER['HTTPS']       = 'on';
        $_SERVER['SERVER_PORT'] = '443';
        define('FORCE_SSL_ADMIN', true);
}

if ( isset($_SERVER['HTTP_X_FORWARDED_HOST']) ) {
        $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
}

That gets WordPress to not redirect to https on these proxied requests, but only if the request headers of X-Forwarded-Proto and X-Forwarded-Host are specified. The NGINX configuration now is:

server {
    server_name www.metatooth.com;

    location / {
        proxy_pass https://calm-waters-18762.herokuapp.com;
        proxy_set_header Host calm-waters-18762.herokuapp.com;
        proxy_set_header X-Forwarded-Host $host;    
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # configure SSL here
}

That’s how I got NGINX, Heroku, and WordPress to play well together. Next task is to use an upstream for the proxy_pass directives. What’s your infrastructure layout and what did you learn along the way? Tell me about it in the comments!

Categories
coding

LAMP Ubuntu 19.04

LAMP on Ubuntu 19.04 should be a snap, and it is. If you install all the packages! I was missing libapache2-mod-php and php-mysql. Here’s the blog post that helped me out.

How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 16.04

LAMP starts with Linux

Ubuntu 19.04 also provides a meta-package for this.
sudo apt-get install lamp-server^ will install all dependencies, and give instructions for enabling PHP under Apache2. Mind the trailing caret (^)! This meta-package selects MySQL 5.7 over MariaDB 10.3. A brief search indicates this is the best choice.

Why am I doing this? In order to have a development environment for this WordPress 5 website. WordPress is widely used, but is not this developer’s cup of tea. However, it does have a strong community and it’s development is ongoing, so time to get on the bandwagon. Or at least use a staging methodology to push changes to www.metatooth.com.

My first website was of the LAMP variety. I guess 20 years is not that long for a platform’s life-cycle. What platform’s longevity surprises you? Tell me about it in the comments!

Categories
Uncategorized

WordPress 5 on Heroku

I started off using mhoofman/wordpress-heroku. One gotcha from there is that the PostgreSQL plugin does not work.  Here’s what worked for me on my Ubuntu 18.10 desktop.

Dependencies

wget, git, and heroku-cli

$ sudo apt install git wget git
$ sudo snap install --classic heroku
Get WordPress

Download from https://wordpress.org/download/, extract, move wp-config-sample.php to wp-config.php.

$ wget https://wordpress.org/latest.tar.gz
$ tar xzvf latest.tar.gz
$ cd wordpress
$ mv wp-config-sample.php wp-config.php

I then made the following edits to wp-config.php  This allows all parameters to be set via ‘heroku config:set’.  The first block is used to configure the WP Offload Media Lite plugin.

define( 'AS3CF_SETTINGS', serialize( array(
    'provider' => 'aws',
    'access-key-id' => getenv('AWS_ACCESS_KEY_ID'),
    'secret-access-key' => getenv('AWS_SECRET_ACCESS_KEY'),
) ) );

Now get and parse the MySQL connection.

$url = parse_url(getenv('CLEARDB_DATABASE_URL'));

define('DB_NAME', trim($url['path'], '/'));
define('DB_USER', $url['user']);
define('DB_PASSWORD', $url['pass']);
define('DB_HOST', $url['host']);

Finally, define authentication keys and salts.

define('AUTH_KEY',         getenv('AUTH_KEY'));
define('SECURE_AUTH_KEY',  getenv('SECURE_AUTH_KEY'));
define('LOGGED_IN_KEY',    getenv('LOGGED_IN_KEY'));
define('NONCE_KEY',        getenv('NONCE_KEY'));
define('AUTH_SALT',        getenv('AUTH_SALT'));
define('SECURE_AUTH_SALT', getenv('SECURE_AUTH_SALT'));
define('LOGGED_IN_SALT',   getenv('LOGGED_IN_SALT'));
define('NONCE_SALT',       getenv('NONCE_SALT'));
Download and install plugins and themes.

I added the following plugins. Unpack each into the ‘/wp-content/plugins’ directory.

Do the same with desired theme, unpacking to ‘/wp-content/themes’.

Initialize git repository and create Heroku app

The base code, plugins, and themes are in place. wp-config.php created and modified.  Now initialize a git repository and create an App on Heroku.com.

$ git init .
$ heroku create
Add MySQL and Sendgrid
$ heroku addons:create cleardb
$ heroku addons:create sendgrid
Now set those configuration variables

You’ll need your AWS Access Keys.

$ heroku config:set AWS_ACCESS_KEY_ID
$ heroku config:set AWS_SECRET_ACCESS_KEY

Authentication keys and salts must also be configured.  Do it one-by-one, or script it.

$ heroku config:set AUTH_KEY:a-strong-password
$ heroku config:set SECURE_AUTH_KEY:another-strong-password

Continue for all the keys and salts.

That’s it! Deploy and then configure your site and the plugins from WordPress itself.
$ git checkout -b production
$ git push heroku production:master

Point your web browser to Heroku’s App URL.  You should then get WordPress’s famous five-minute install. You can see my resulting code at terryg/wordpress-heroku.