open-contracting / deploy

Deployment configuration and scripts
https://ocdsdeploy.readthedocs.io/en/latest/
Apache License 2.0
2 stars 3 forks source link

Add WordPress auto-update script #428

Closed jpmckinney closed 6 months ago

jpmckinney commented 1 year ago

WP can auto-update itself (/wp-admin/update-core.php) and plugins (/wp-admin/plugins.php).

That said, we can guarantee that these are updated by running a WP CLI script outside WordPress (e.g. to protect against an attacker gaining access to the WP admin and disabling updates – with the script, the attacker would need to be able to modify a readable, executable, but non-writable file owned by root and/or edit crontab).

Dogsbody shared a sample script in Slack.

jpmckinney commented 1 year ago

We should have an email sent when upgrading (even better if email is only sent for minor/major releases), as it has happened that a minor/major release broke the site.

Edit: Alternatively, we don't auto-upgrade for major, in which case Wordfence will send an email. We can use logic like at https://github.com/dependabot/fetch-metadata/blob/924483a3d758e3539d35e512c5ca92c75c7a748d/src/dependabot/update_metadata.ts#L77C17-L94 to find major upgrades.

jpmckinney commented 6 months ago

Here's the bash script mentioned.

Instead, we use a must-use plugin, which would be equally difficult for an attacker to remove: https://ocdsdeploy.readthedocs.io/en/latest/develop/update/wordpress.html#wordpress

Regarding the issue description, logging in as admin requires: basic authentication to access wp-login.php; the admin user login; and multi-factor authentication. So, that's not a likely scenario.

#!/usr/bin/env bash
#
# Description: Updates wordpress site core and plugins
#
# Usage: $0 [siteid|all]
# Or in cron: @daily root $0
#
# Configuration:  The following variables can be set in the site.config file(s)
#     AUTOPATCH=yes    # Enables this autopatching script for the site
#
# Notes:  If you start getting 'Error: No plugins updated' from this script then it is probaly due to a premium plugin being used on the site
#           Take a look at the code below to exclude the premium plugin from being automatically updated.

set -e
set -u

# Check we are root
if [ ${LOGNAME} != "root" ]; then
  echo "ERROR: Execution of $0 stopped as not run by user root!"
  exit 1
fi

# Default to running on all sites with the autopatch variable set to yes

USERINPUT=${1:-all}

# If user input is "all" we want to update all the sites we can.
if [[ ${USERINPUT} == "all" ]]; then
  # find siteconfigs that need autopatching. 
  SITETODO=$(find /home/ -mindepth 2 -maxdepth 2 -path "/home/ez[0-9]*" -name "site.config" -type f -exec grep -il 'AUTOPATCH=yes' {} \; | grep -oP 'ez[0-9]+')

#Else we were passed a single site ID so check $1 is a vaild site
elif [ -f "/home/${USERINPUT}/site.config" ]; then
  SITETODO=${USERINPUT}
else
  echo "ERROR: File /home/${USERINPUT}/site.config does not exist"
  echo "ERROR: Site does NOT exist. Patching not possible."
  exit 3
fi

# Check and create the wp-cli cache
WP_CLI_CACHE=/home/wp-cli_cache
if ! [ -d "$WP_CLI_CACHE" ]; then
  mkdir $WP_CLI_CACHE
  chown admin:ezusers $WP_CLI_CACHE
  chmod 2775 $WP_CLI_CACHE
fi

# Loop through the sites to do, and do them!
for SITE in ${SITETODO}; do
  # Turning this into a big ol' subshell so the site.config import is cleared down each loop.
  (
  #echo "Updating ${SITE}..."
  source /home/${SITE}/site.config
  # Double check the site is what we think it is
  if [ "$SITE" != "$SITENAME" ]; then
    echo "ERROR: $SITE is not an valid sitename"
    exit 4
  fi
  #Used so it we don't have to flag every command
  WP_CLI="sudo -u ${SITENAME} -i WP_CLI_CACHE_DIR=$WP_CLI_CACHE -- wp-cli --quiet --path=/home/${SITENAME}/public_html --skip-plugins"
  # Check everything is up to date
  #echo "    ...updating core..."
  $WP_CLI core update
  #echo "    ...updating themes..."
  $WP_CLI theme update --all
  #echo "    ...updating plugins..."
  $WP_CLI plugin update --all --exclude=updraftplus,elementor-pro,form-vibes,jetpack,child-theme-configurator # premium plugins that causes an error https://github.com/wp-cli/wp-cli/issues/5320
  #echo "    ...updating language..."
  $WP_CLI core language update # This should be the last to be updated as plugins may have translation updates
  )
done