launch

CLI::Wordpress Launch ImplementationDetails

wordpress-launch.yaml

On zef install https://github.com/p6steve/raku-CLI-Wordpress.git, the file wordpress-launch.yaml is populated. It contains site-specific configuration and should be edited to match your domain name, etc before setup is run.

#~/.rawp-config/wordpress-launch.yaml
instance:
  domain-name: furnival.net
  admin-email: '[email protected]'
  db-image: mysql:8.0
  wordpress-image: wordpress:php8.0-fpm-alpine
  webserver-image: nginx:1.15.12-alpine
  certbot-image: certbot/certbot
  wpcli-image: wordpress:cli-php8.0

Care should be taken to preserve any changes to this file in the event of a zef reinstall

rawp setup, launch & renewal

This sequence largely follows the guidance at viz. https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-docker-compose

The rawp setup command positions the various configuration files in the ~/wordpress directory and populates them with info from wordpress-launch.yaml.

The rawp launch command will then start a new Wordpress instance. It performs a 'staging' phase to check your kit for a no ssl website and, if OK, then fetches and installs a letsencrypt ssl certificate and restarts the website.

The rawp renewal command will then configure the crontab for periodic renewal of the letsencrypt certificate.

Launch Details

###0. Prerequisites

  • server system instance

    • a server running Ubuntu 20.04, along with a non-root user with sudo privileges and an active firewall

    • docker installed on your server

    • docker-compose installed on your server

  • registered domain name, DNS records:

    • a record with your_domain -> IP

    • a record with www.your_domain -> IP

###1. Environment Variables The rawp setup command automatically setups config files following these steps.

These secrets are autogenerated during setup to avoid storing them in GitHub.

#sudo vi .env [~/.env]
MYSQL_ROOT_PASSWORD='xxx'
MYSQL_USER=yyy
MYSQL_PASSWORD='zzz'

Because your .env file contains sensitive information, you want to ensure that it is included in your project’s .gitignore and .dockerignore files.

#sudo vi .dockerignore [~/wordpress/.dockerignore]
.env
.git
.dockerignore

###2. Webserver Configuration The rawp setup command automatically setups config files following these steps.

Initially a no ssl config is used for staging...

#~/wordpress/nginx-conf/nginx.conf
server {
    listen 80;
    listen [::]:80;

    server_name your_domain www.your_domain;

    index index.php index.html index.htm;

    root /var/www/html;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
}

If OK, this is swapped to the ssl version & restarted...

#~/wordpress/nginx-conf/nginx.conf
server {
        listen 80;
        listen [::]:80;

        server_name your_domain www.your_domain;

        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }

        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        server_name your_domain www.your_domain;

        index index.php index.html index.htm;

        root /var/www/html;

        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/furnival.net/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/furnival.net/privkey.pem;

        include /etc/nginx/conf.d/options-ssl-nginx.conf;

        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        # enable strict transport security only if you understand the implications

        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location ~ /\.ht {
                deny all;
        }

        location = /favicon.ico {
                log_not_found off; access_log off;
        }
        location = /robots.txt {
                log_not_found off; access_log off; allow all;
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }
}

###3. Docker Compose Services The rawp setup command automatically setups config files following these steps.

The set of services is predefined by the docker-compose.yaml. The specific docker images used are set in the wordpress-launch.yaml.

#`/wordpress/docker-compose.yaml
version: '3'

services:
  db:
    image: mysql:8.0
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes:
      - dbdata:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - app-network

  wordpress:
    depends_on:
      - db
    image: wordpress:5.1.1-fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - wordpress:/var/www/html
    networks:
      - app-network

  webserver:
    depends_on:
      - wordpress
    image: nginx:1.15.12-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - '80:80'
    volumes:
      - wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - app-network

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email $admin-email --agree-tos --no-eff-email --staging -d $domain-name -d www.$domain-name

  wpcli:
    container_name: wpcli
    depends_on:
      - wordpress
    image: wordpress:cli
    user: 1000:1000
    command: tail -f /dev/null
    volumes:
      - wordpress:/var/www/html
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    profiles:
      - dev

volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge

###4. SSL Certificates and Credentials The rawp launch command automatically setups SSL following these steps.

Start No SSL config for staging

sudo docker-compose up -d sudo docker-compose ps

Output
  Name                 Command               State           Ports
-------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0
db          docker-entrypoint.sh --def ...   Up       3306/tcp, 33060/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:80->80/tcp
wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp

Run certbot staging command

sudo docker-compose run certbot certonly --webroot --webroot-path=/var/www/html --email admin-email --agree-tos --no-eff-email -d your_domain -d www.your_domain --staging

Check dummy certificate is OK

sudo docker-compose exec webserver ls -la /etc/letsencrypt/live

Output
total 16
drwx------    3 root     root          4096 May 10 15:45 .
drwxr-xr-x    9 root     root          4096 May 10 15:45 ..
-rw-r--r--    1 root     root           740 May 10 15:45 README
drwxr-xr-x    2 root     root          4096 May 10 15:45 your_domain

Run cerbot production command

Now that you know your request will be successful, you can edit the certbot service definition to remove the --staging flag.

sudo docker-compose run certbot certonly --webroot --webroot-path=/var/www/html --email admin-email --agree-tos --no-eff-email  -d your_domain -d www.your_domain --force-renewal

Note that letsencrypt restricts this to 5 calls per week.

The certbot image is then restarted: sudo docker-compose up --force-recreate --no-deps certbot

###5. Modifying the Web Server Configuration and Service Definition

The rawp launch command automatically setups SSL following these steps.

Enabling SSL in your Nginx configuration will involve adding an HTTP redirect to HTTPS, specifying your SSL certificate and key locations, and adding security parameters and headers.

Since you are going to recreate the webserver service to include these additions, you can stop it now: sudo docker-compose stop webserver

Before modifying the configuration file, get the recommended Nginx security parameter from Certbot using curl: sudo curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf

This command will save these parameters in a file called options-ssl-nginx.conf, located in the nginx-conf directory.

Next, the no SSL nginx configuration file is replaced by the SSL nginx configuration file (see section 2).

Recreate the webserver service: sudo docker-compose up -d --force-recreate --no-deps webserver

###6. Front End Setup

Now you can visit your new server forntend and perform the standard Wordpress configuration steps to select language and setup an admin user.

###7. Certificate Renewals The rawp renewal command automatically sets up letsencrypt SSL certificate renewal following these steps.

Letsencrypt certificates are valid for 90 days. This cron job will renew your certificates and reload your nginx configuration.

The renewal script is predefined at ~/wordpress/ssl_renew.sh:

#!/bin/bash

DOCKER="/usr/bin/docker"
COMPOSE="/usr/bin/docker-compose"

cd /home/ubuntu/wordpress
$DOCKER container prune -f
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver

This script is loaded to the server instance crontab with the following lines:

0 12 * * * /home/ubuntu/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
0 */4 * * * date >> /var/log/cron.log 2>&1    #4 hourly timestamp

It is then run every 4 hours. You can monitor the status with tail -f /var/log/cron.log

CLI::Wordpress v0.0.1

Simple wpcli command

Authors

  • p6steve

License

Artistic-2.0

Dependencies

YAMLishJSON::Fast

Test Dependencies

Provides

  • CLI::Wordpress

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.