diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ce7d25..f9145ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - load-balancing on unix socket for php-fpm - stub_status vhost for metrics - opcache optimization for php-fpm +- EasyEngine configuration backup before migration +- EasyEngine configuration cleanup after migration +- WordOps configuration backup before upgrade +- Previous acme.sh certs migration +- "wo maintenance" command to perform server package update & cleanup #### Changed @@ -27,6 +32,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - "--letsencrypt=subdomain" option - hardened nginx ssl_ecdh_curve - Update phpredisadmin +- Increase MySQL root password size to 16 characters +- Increase MySQL users password size to 16 characters #### Fixed @@ -37,6 +44,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Nginx upgrade from previous WordOps release - Force new Nginx templates during update - Error message about missing my.cnf file during upgrade +- PHP 7.2 & PHP 7.3 pool configuration during upgrade +- WordOps backup directory creation before upgrade +- EasyEngine database sync during migration ### v3.9.4 - 2019-03-15 diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index f08fced..8ff11e5 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -11,7 +11,7 @@ _wo_complete() # SETUP THE BASE LEVEL (everything after "wo") if [ $COMP_CWORD -eq 1 ]; then COMPREPLY=( $(compgen \ - -W "stack site debug clean secure import-slow-log log update sync info --version --help --quiet" \ + -W "stack site debug clean secure import-slow-log log update sync info maintenance --version --help --quiet" \ -- $cur) ) diff --git a/config/plugins.d/maintenance.conf b/config/plugins.d/maintenance.conf new file mode 100644 index 0000000..07ac10a --- /dev/null +++ b/config/plugins.d/maintenance.conf @@ -0,0 +1,8 @@ +### Example Plugin Configuration for WordOps + +[maintenance] + +### If enabled, load a plugin named `example` either from the Python module +### `wo.cli.plugins.example` or from the file path +### `/var/lib/wo/plugins/example.py` +enable_plugin = true diff --git a/install b/install index 3cad1be..cb6315b 100644 --- a/install +++ b/install @@ -83,10 +83,16 @@ fi ### wo_branch="$1" readonly wo_log_dir=/var/log/wo/ +readonly wo_backup_dir=/var/lib/wo-backup/ readonly wo_install_log=/var/log/wo/install.log readonly wo_linux_distro=$(lsb_release -is) readonly wo_distro_version=$(lsb_release -sc) readonly wo_distro_id=$(lsb_release -rs) +TIME_FORMAT='%d-%b-%Y-%H%M%S' +TIME=$(date +"$TIME_FORMAT") +NGINX_BACKUP_FILE="/var/lib/wo-backup/nginx-backup.$TIME.tar.gz" +EE_BACKUP_FILE="/var/lib/wo-backup/ee-backup.$TIME.tar.gz" +WO_BACKUP_FILE="/var/lib/wo-backup/wo-backup.$TIME.tar.gz" if [ -x /usr/local/bin/ee ]; then migration=1 @@ -118,11 +124,20 @@ if [ ! -d "$wo_log_dir" ]; then wo_lib_echo "Creating WordOps log directory, just a second..." mkdir -p "$wo_log_dir" || wo_lib_error "Whoops - seems we are unable to create the log directory $wo_log_dir, exit status " $? + # create wordops log files touch /var/log/wo/{wordops.log,install.log} chmod -R 700 /var/log/wo || wo_lib_error "Whoops, there was an error setting the permissions on the WordOps log folder, exit status " $? fi +if [ ! -d "$wo_backup_dir" ]; then + + wo_lib_echo "Creating WordOps backup directory, just a second..." + mkdir -p "$wo_backup_dir" || wo_lib_error "Whoops - seems we are unable to create the backup directory $wo_backup_dir, exit status " $? + chmod -R 600 "$wo_backup_dir" + +fi + ### # 2 - Setup the dependencies for installation #### @@ -137,7 +152,7 @@ wo_install_dep() { fi locale-gen en - } >> /var/log/wo/install.log 2>&1 + } >> "$wo_install_log" 2>&1 # Support PFS if [ -f /etc/nginx/nginx.conf ]; then # Replace previous ciphers @@ -151,6 +166,7 @@ wo_install_dep() { if [ ! -d /var/www/html/.well-known/acme-challenge ]; then mkdir -p /var/www/html/.well-known/acme-challenge chown -R www-data:www-data /var/www/html /var/www/html/.well-known + chmod 750 /var/www/html /var/www/html/.well-known fi } @@ -177,7 +193,6 @@ wo_sync_db() { ### cp /var/lib/ee/ee.db /var/lib/wo/dbase.db - rm -rf /var/lib/ee else # Create an empty database for WordOps @@ -306,7 +321,7 @@ wo_update_wp_cli() { [ ! -f /etc/bash_completion.d/wp-completion.bash ] && { wget -qO /etc/bash_completion.d/wp-completion.bash https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash } - } >> /var/log/wo/install.log 2>&1 + } >> "$wo_install_log" 2>&1 } wo_install_acme_sh() { @@ -338,9 +353,25 @@ wo_install_acme_sh() { if [ ! -d /var/www/html/.well-known/acme-challenge ]; then mkdir -p /var/www/html/.well-known/acme-challenge chown -R www-data:www-data /var/www/html /var/www/html/.well-known + chmod 750 /var/www/html /var/www/html/.well-known + else + chmod 750 /var/www/html /var/www/html/.well-known fi - } >> /var/log/wo/install.log 2>&1 + } >> "$wo_install_log" 2>&1 + fi + if [ -d "$HOME/.acme/.sh" ]; then + { + rsync -az --exclude="account.conf" \ + --exclude="acme.sh" \ + --exclude="acme.sh.env" \ + --exclude="deploy" \ + --exclude="dnsapi" \ + --exclude="http.header" \ + --exclude="ca" \ + "$HOME/.acme.sh/" \ + /etc/letsencrypt/renewal/ + } >> "$wo_install_log" 2>&1 fi } @@ -357,7 +388,7 @@ wo_install() { git clone -b "$wo_branch" https://github.com/WordOps/WordOps.git /tmp/wordops --quiet cd /tmp/wordops || exit 1 - } >> /var/log/wo/install.log 2>&1 + } >> "$wo_install_log" 2>&1 python3 setup.py install } @@ -366,18 +397,12 @@ wo_upgrade_nginx() { { - if [ -d /var/lib/wo/backup/nginx ]; then - TIME_FORMAT='%d-%b-%Y-%H%M%S' - TIME=$(date +"$TIME_FORMAT") - BACKUP_FILE="/var/lib/wo/backup/nginx-backup.$TIME.tar.gz" - - tar -I pigz "$BACKUP_FILE" /var/lib/wo/backup/nginx - rm -rf /var/lib/wo/backup/nginx + if [ -d /var/lib/wo-backup/nginx ]; then + tar -I pigz "$NGINX_BACKUP_FILE" /var/lib/wo-backup/nginx + rm -rf /var/lib/wo-backup/nginx fi - # backup nginx conf - mkdir -p /var/lib/wo/backup - rsync -az /etc/nginx/ /var/lib/wo/backup/nginx/ - + # backup nginx conf + /usr/bin/rsync -az /etc/nginx/ /var/lib/wo-backup/nginx/ # chec if the package nginx-ee is installed CHECK_NGINX_EE=$(dpkg --list | grep nginx-ee) @@ -400,79 +425,85 @@ wo_upgrade_nginx() { rm -f /tmp/nginx-wo.key sudo apt-get update - # stop nginx - service nginx stop + CHECK_NGINX_UPSTREAM_VERSION=$(grep "v3.9.5" /etc/nginx/conf.d/upstream.conf) + if [ -z "$CHECK_NGINX_UPSTREAM_VERSION" ]; then - # prevent apt preference to block install - [ -f /etc/apt/preferences.d/nginx-block ] && { - mv /etc/apt/preferences.d/nginx-block "$HOME/nginx-block" - } + # stop nginx + service nginx stop - if [ -n "$CHECK_NGINX_EE" ]; then - # remove previous package - apt-mark unhold nginx-ee nginx-common nginx-custom - apt-get -y -qq autoremove nginx-ee nginx-common nginx-custom --purge - elif [ -n "$CHECK_NGINX_WO" ]; then - apt-mark unhold nginx-wo nginx-common nginx-custom - apt-get -y -qq autoremove nginx-wo nginx-common nginx-custom --purge - fi + # prevent apt preference to block install + [ -f /etc/apt/preferences.d/nginx-block ] && { + mv /etc/apt/preferences.d/nginx-block "$HOME/nginx-block" + } - # install new nginx package - if [ -x /usr/local/bin/wo ]; then - # remove previous php-fpm pool configuration - if [ -n "$CHECK_PHP72" ]; then - apt-get remove php7.2-fpm -y -qq --purge - rm -f /etc/php/7.2/fpm/pool.d/* + # install new nginx package + if [ -x /usr/local/bin/wo ]; then + + if [ -n "$CHECK_NGINX_EE" ]; then + # remove previous package + apt-mark unhold nginx-ee nginx-common nginx-custom + apt-get -y -qq autoremove nginx-ee nginx-common nginx-custom --purge + rm -rf /etc/nginx + elif [ -n "$CHECK_NGINX_WO" ]; then + apt-mark unhold nginx-wo nginx-common nginx-custom + apt-get -y -qq autoremove nginx-wo nginx-common nginx-custom --purge + rm -rf /etc/nginx + fi + + # remove previous php-fpm pool configuration + if [ -n "$CHECK_PHP72" ]; then + apt-get remove php7.2-fpm -y -qq --purge + rm -f /etc/php/7.2/fpm/pool.d/* + fi + /usr/local/bin/wo stack install --nginx --php + if [ -n "$CHECK_PHP73" ]; then + apt-get remove php7.3-fpm -y -qq --purge + rm -f /etc/php/7.3/fpm/pool.d/* + /usr/local/bin/wo stack install --php73 + fi fi - /usr/local/bin/wo stack install --nginx --php - if [ -n "$CHECK_PHP73" ]; then - apt-get remove php7.3-fpm -y -qq --purge - rm -f /etc/php/7.3/fpm/pool.d/* - /usr/local/bin/wo stack install --php73 + + # restore sites and configuration + /usr/bin/rsync -auz /var/lib/wo-backup/nginx/ /etc/nginx/ + + # update redis.conf headers + if [ -f /etc/nginx/common/redis.conf ]; then + sed -i "s/X-Cache /X-SRCache-Fetch-Status /g" /etc/nginx/common/redis.conf + sed -i "s/X-Cache-2 /X-SRCache-Store-Status /g" /etc/nginx/common/redis.conf + fi - fi - # restore sites and configuration - /usr/bin/rsync -auz /var/lib/wo/backup/nginx/ /etc/nginx/ - - # update redis.conf headers - if [ -f /etc/nginx/common/redis.conf ]; then - sed -i "s/X-Cache /X-SRCache-Fetch-Status /g" /etc/nginx/common/redis.conf - sed -i "s/X-Cache-2 /X-SRCache-Store-Status /g" /etc/nginx/common/redis.conf - - fi - - VERIFY_NGINX_CONFIG=$(nginx -t 2>&1 | grep failed) - # check if nginx -t do not return errors - if [ -z "$VERIFY_NGINX_CONFIG" ]; then - systemctl stop nginx - systemctl start nginx - else - VERIFY_NGINX_BUCKET=$(nginx -t 2>&1 | grep "server_names_hash_bucket_size") - if [ -n "$VERIFY_NGINX_BUCKET" ]; then - sed -i "s/# server_names_hash_bucket_size 64;/server_names_hash_bucket_size 64;/g" /etc/nginx/nginx.conf + VERIFY_NGINX_CONFIG=$(nginx -t 2>&1 | grep failed) + # check if nginx -t do not return errors + if [ -z "$VERIFY_NGINX_CONFIG" ]; then + systemctl stop nginx + systemctl start nginx + else + VERIFY_NGINX_BUCKET=$(nginx -t 2>&1 | grep "server_names_hash_bucket_size") + if [ -n "$VERIFY_NGINX_BUCKET" ]; then + sed -i "s/# server_names_hash_bucket_size 64;/server_names_hash_bucket_size 64;/g" /etc/nginx/nginx.conf + fi + systemctl stop nginx + systemctl start nginx fi - systemctl stop nginx - systemctl start nginx + + # set back apt preference + [ -f "$HOME/nginx-block" ] && { + mv "$HOME/nginx-block" /etc/apt/preferences.d/nginx-block + } fi - - # set back apt preference - [ -f "$HOME/nginx-block" ] && { - mv "$HOME/nginx-block" /etc/apt/preferences.d/nginx-block - } - - } >> /var/log/wo/install.log 2>&1 + } >> "$wo_install_log" 2>&1 } wo_update_latest() { if [ -f /etc/nginx/fastcgi_params ]; then - grep -q 'HTTP_PROXY' /etc/nginx/fastcgi_params - if [[ $? -ne 0 ]]; then + CHECK_HTTP_PROXY=$(grep 'HTTP_PROXY' /etc/nginx/fastcgi_params) + if [ -z "$CHECK_HTTP_PROXY" ]; then echo 'fastcgi_param HTTP_PROXY "";' >> /etc/nginx/fastcgi_params echo 'fastcgi_param HTTP_PROXY "";' >> /etc/nginx/fastcgi.conf - service nginx restart + service nginx restart | tee -ai $wo_install_log fi fi @@ -506,8 +537,8 @@ wo_update_latest() { fi # Fix WordPress example.html issue # Ref: http://wptavern.com/xss-vulnerability-in-jetpack-and-the-twenty-fifteen-default-theme-affects-millions-of-wordpress-users - dpkg --get-selections | grep -v deinstall | grep nginx - if [ $? -eq 0 ]; then + CHECK_DEINSTALL_NGINX=$(dpkg --get-selections | grep -v deinstall | grep nginx) + if [ -z "$CHECK_DEINSTALL_NGINX" ]; then cp /usr/lib/wo/templates/locations.mustache /etc/nginx/common/locations-php72.conf fi @@ -515,12 +546,12 @@ wo_update_latest() { # Fix Redis-server security issue # http://redis.io/topics/security if [ -f /etc/redis/redis.conf ]; then - grep -0 -v "#" /etc/redis/redis.conf | grep 'bind' + grep -0 -v "#" /etc/redis/redis.conf | grep 'bind' >> /dev/null 2>&1 - if [ $? -ne 0 ]; then + if [ "$?" -ne 0 ]; then sed -i '$ a bind 127.0.0.1' /etc/redis/redis.conf & - service redis-server restart + service redis-server restart > /dev/null 2>&1 fi fi @@ -558,6 +589,18 @@ wo_git_init() { } >> /var/log/wo/install.log 2>&1 } +wo_backup_ee() { + tar -I pigz -cf "$EE_BACKUP_FILE" /etc/nginx /usr/local/bin/ee /usr/local/lib/python3.6/dist-packages/ee-*.egg /etc/ee /var/lib/ee >> /var/log/wo/install.log 2>&1 +} + +wo_backup_wo() { + tar -I pigz -cf "$WO_BACKUP_FILE" /etc/nginx/ /usr/local/lib/python3.6/dist-packages/wo-*.egg /etc/wo >> /var/log/wo/install.log 2>&1 +} + +wo_clean_ee() { + rm -f /usr/local/bin/ee /etc/bash_completion.d/ee_auto.rc /usr/local/lib/python3.6/dist-packages/ee-*.egg /etc/ee /var/lib/ee >> /var/log/wo/install.log 2>&1 +} + ### # 4 - WO MAIN SETUP ### @@ -570,13 +613,16 @@ if [ -x /usr/local/bin/wo ]; then if [ "$wo_ans" = "y" ] || [ "$wo_ans" = "Y" ]; then wo_lib_echo "Installing wo dependencies " | tee -ai $wo_install_log wo_install_dep | tee -ai $wo_install_log + wo_lib_echo "Backing-up WO install" | tee -ai $wo_install_log + wo_backup_wo | tee -ai $wo_install_log wo_lib_echo "Syncing WO database" | tee -ai $wo_install_log - wo_sync_db >> $wo_install_log 2>&1 secure_wo_db | tee -ai $wo_install_log wo_lib_echo "Installing WordOps " | tee -ai $wo_install_log - wo_install | tee -ai $wo_install_log + wo_install >> wo_install_log 2>&1 + if [ -x "$(command -v nginx)" ]; then wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log wo_upgrade_nginx | tee -ai $wo_install_log + fi wo_update_latest | tee -ai $wo_install_log wo_lib_echo "Installing acme.sh" | tee -ai $wo_install_log wo_install_acme_sh | tee -ai $wo_install_log @@ -596,19 +642,25 @@ else if [ "$wo_ans" = "y" ] || [ "$wo_ans" = "Y" ]; then wo_lib_echo "Installing wo dependencies " | tee -ai $wo_install_log wo_install_dep | tee -ai $wo_install_log + wo_lib_echo "Backing-up EE install" | tee -ai $wo_install_log + wo_backup_ee | tee -ai $wo_install_log wo_lib_echo "Syncing WO database" | tee -ai $wo_install_log - wo_sync_db >> $wo_install_log 2>&1 + wo_sync_db | tee -ai $wo_install_log secure_wo_db | tee -ai $wo_install_log wo_lib_echo "Installing WordOps " | tee -ai $wo_install_log - wo_install | tee -ai $wo_install_log + wo_install >> wo_install_log 2>&1 + if [ -x "$(command -v nginx)" ]; then wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log wo_upgrade_nginx | tee -ai $wo_install_log + fi wo_update_latest | tee -ai $wo_install_log wo_lib_echo "Installing acme.sh" | tee -ai $wo_install_log wo_install_acme_sh | tee -ai $wo_install_log wo_lib_echo "Running post-install steps " | tee -ai $wo_install_log wo_git_init | tee -ai $wo_install_log wo_update_wp_cli | tee -ai $wo_install_log + wo_lib_echo "Cleaning-up EE previous install" | tee -ai $wo_install_log + wo_clean_ee | tee -ai $wo_install_log else wo_lib_error "Not installing WordOps, exit status = " 1 fi @@ -632,12 +684,12 @@ wo sync | tee -ai $wo_install_log if [ "$migration" -eq "1" ]; then echo wo_lib_echo "The migration from EasyEngine to WordOps was succesfull!" - wo_lib_echo "The EasyEngine backup files can be found in /var/lib/wo/ee-backup.tgz" + wo_lib_echo "The EasyEngine backup files can be found in /var/lib/wo-backup/ee-backup.tgz" echo wo_lib_echo_info "For autocompletion, run the following command:" wo_lib_echo_info "source /etc/bash_completion.d/wo_auto.rc" echo - wo_lib_echo "WordOps (wo) help: https://wordops.io/docs" + wo_lib_echo "WordOps (wo) help: https://docs.wordops.io" else echo wo_lib_echo "For WordOps (wo) auto completion, run the following command" @@ -645,5 +697,5 @@ else wo_lib_echo_info "source /etc/bash_completion.d/wo_auto.rc" echo wo_lib_echo "Yay! WordOps (wo) installed/updated successfully" - wo_lib_echo "WordOps (wo) help: https://wordops.io/docs" + wo_lib_echo "WordOps (wo) help: https://docs.wordops.io" fi diff --git a/wo/cli/plugins/maintenance.py b/wo/cli/plugins/maintenance.py new file mode 100644 index 0000000..666668d --- /dev/null +++ b/wo/cli/plugins/maintenance.py @@ -0,0 +1,55 @@ +"""Maintenance Plugin for WordOps""" + +from cement.core.controller import CementBaseController, expose +from cement.core import handler, hook +from wo.core.logging import Log +from wo.core.variables import WOVariables +from wo.core.aptget import WOAptGet +from wo.core.apt_repo import WORepo +from wo.core.services import WOService +from wo.core.fileutils import WOFileUtils +from wo.core.shellexec import WOShellExec +from wo.core.git import WOGit +from wo.core.download import WODownload + + +def wo_maintenance_hook(app): + pass + + +class WOMaintenanceController(CementBaseController): + class Meta: + label = 'maintenance' + stacked_on = 'base' + stacked_type = 'nested' + description = ('update server packages to latest version') + usage = "wo maintenance" + + @expose(hide=True) + def default(self): + + try: + Log.info(self, "updating apt-cache, please wait...") + WOShellExec.cmd_exec(self, "apt-get update") + Log.info(self, "updating packages, please wait...") + WOShellExec.cmd_exec(self, "DEBIAN_FRONTEND=noninteractive " + "apt-get -o " + "Dpkg::Options::='--force-confmiss' " + "-o Dpkg::Options::='--force-confold' " + "-y dist-upgrade") + Log.info(self, "cleaning-up packages, please wait...") + WOShellExec.cmd_exec(self, "apt-get -y --purge autoremove") + WOShellExec.cmd_exec(self, "apt-get -y autoclean") + except OSError as e: + Log.debug(self, str(e)) + Log.error(self, "Package updates failed !") + except Exception as e: + Log.debug(self, str(e)) + Log.error(self, "Packages updates failed !") + + +def load(app): + # register the plugin class.. this only happens if the plugin is enabled + handler.register(WOMaintenanceController) + # register a hook (function) to run after arguments are parsed. + hook.register('post_argument_parsing', wo_maintenance_hook) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index ae2b9eb..cb6305a 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -50,7 +50,7 @@ class WOSecureController(CementBaseController): """This function secures authentication""" passwd = ''.join([random.choice (string.ascii_letters + string.digits) - for n in range(6)]) + for n in range(16)]) if not self.app.pargs.user_input: username = input("Provide HTTP authentication user " "name [{0}] :".format(WOVariables.wo_user)) diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index e84226c..28a3faa 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -306,9 +306,9 @@ def setupwordpress(self, data): "\n\ndefine(\'WP_DEBUG\', false);")) try: if WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root" - .format(WOVariables.wo_wpcli_path) - + " core config " - + "--dbname=\'{0}\' --dbprefix=\'{1}\' " + .format(WOVariables.wo_wpcli_path) + + " core config " + + "--dbname=\'{0}\' --dbprefix=\'{1}\' " "--dbuser=\'{2}\' --dbhost=\'{3}\' " .format(data['wo_db_name'], wo_wp_prefix, data['wo_db_user'], data['wo_db_host'] @@ -330,11 +330,11 @@ def setupwordpress(self, data): else: Log.debug(self, "Generating wp-config for WordPress multisite") Log.debug(self, "bash -c \"php {0} --allow-root " - .format(WOVariables.wo_wpcli_path) - + "core config " - + "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbhost=\'{2}\' " - .format(data['wo_db_name'], wo_wp_prefix, data['wo_db_host']) - + "--dbuser=\'{0}\' --dbpass=\'{1}\' " + .format(WOVariables.wo_wpcli_path) + + "core config " + + "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbhost=\'{2}\' " + .format(data['wo_db_name'], wo_wp_prefix, data['wo_db_host']) + + "--dbuser=\'{0}\' --dbpass=\'{1}\' " "--extra-php<