From 819557fa5315cac96509efab202f57009ea357e7 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 13 Jul 2019 19:56:01 +0200 Subject: [PATCH 01/44] implementing wildcard SSL certs --- wo/cli/plugins/site.py | 46 +++++------------ wo/cli/plugins/site_functions.py | 86 ++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 33 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 7f1597c..54818c6 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -333,6 +333,11 @@ class WOSiteCreateController(CementBaseController): action='store' or 'store_const', choices=('on', 'subdomain', 'wildcard'), const='on', nargs='?')), + (['--dns'], + dict(help="choose dns provider api for letsencrypt", + action='store' or 'store_const', + choices=('cf', 'do'), + const='cf', nargs='?')), (['--hsts'], dict(help="enable HSTS for site secured with letsencrypt", action='store_true')), @@ -726,43 +731,18 @@ class WOSiteCreateController(CementBaseController): Log.error(self, "Check the log for details: " "`tail /var/log/wo/wordops.log` and please try again") - if self.app.pargs.letsencrypt == "on": + if self.app.pargs.letsencrypt: data['letsencrypt'] = True letsencrypt = True - if data['letsencrypt'] is True: - setupLetsEncrypt(self, wo_domain) + if self.app.pargs.letsencrypt == "on": + setupLetsEncrypt(self, wo_domain) + elif self.app.pargs.letsencrypt == "subodmain": + setupLetsEncryptSubdomain(self, wo_domain) + elif self.app.pargs.letsencrypt == "wildcard": + setupLetsEncryptWildcard(self, wo_domain) + httpsRedirect(self, wo_domain) - - if self.app.pargs.hsts: - setupHsts(self, wo_domain) - - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - - Log.info(self, "Congratulations! Successfully Configured " - "SSl for Site " - " https://{0}".format(wo_domain)) - - # Add nginx conf folder into GIT - WOGit.add(self, ["{0}/conf/nginx".format(wo_site_webroot)], - msg="Adding letsencrypts config of site: {0}" - .format(wo_domain)) - updateSiteInfo(self, wo_domain, ssl=letsencrypt) - - elif data['letsencrypt'] is False: - Log.info(self, "Not using Let\'s encrypt for Site " - " http://{0}".format(wo_domain)) - - if self.app.pargs.letsencrypt == "subdomain": - data['letsencrypt'] = True - letsencrypt = True - - if data['letsencrypt'] is True: - setupLetsEncryptSubdomain(self, wo_domain) - httpsRedirect(self, wo_domain) - if self.app.pargs.hsts: setupHsts(self, wo_domain) diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 9d35048..73f8a8a 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1433,6 +1433,92 @@ def setupLetsEncryptSubdomain(self, wo_domain_name): "you are running Let\'s Encrypt Client " "\n to allow it to verify the site automatically.") +# setup letsencrypt for domain + www.domain + + +def setupLetsEncryptWildcard(self, wo_domain_name): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns dns_cf " + "-k ec-384 -f" + .format(wo_domain_name)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns dns_cf " + "-k ec-384 -f" + .format(wo_domain_name)) + + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Cert deployment for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && " + "service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to install certificate", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + + # letsencrypt cert renewal From 2657af9abbbc258a7d19fa59964b5235191ae1ab Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 13 Jul 2019 20:42:11 +0200 Subject: [PATCH 02/44] Add wildcard to update command --- wo/cli/plugins/site.py | 16 ++++++++++++++-- wo/cli/plugins/site_functions.py | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 54818c6..5c7d20d 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -807,6 +807,11 @@ class WOSiteUpdateController(CementBaseController): action='store' or 'store_const', choices=('on', 'off', 'renew', 'subdomain', 'wildcard'), const='on', nargs='?')), + (['--dns'], + dict(help="choose dns provider api for letsencrypt", + action='store' or 'store_const', + choices=('cf', 'do'), + const='cf', nargs='?')), (['--hsts'], dict(help="configure hsts for the site", action='store' or 'store_const', @@ -1199,6 +1204,11 @@ class WOSiteUpdateController(CementBaseController): data['basic'] = False cache = 'wpredis' + if pargs.dns == "do": + dns_cf = False + else: + dns_cf = True + if (php73 is old_php73) and (stype == oldsitetype and cache == oldcachetype): return 1 @@ -1257,10 +1267,12 @@ class WOSiteUpdateController(CementBaseController): if data['letsencrypt'] is True: if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot)): - if not pargs.letsencrypt == "subdomain": + if self.app.pargs.letsencrypt == "on": setupLetsEncrypt(self, wo_domain) - else: + elif self.app.pargs.letsencrypt == "subodmain": setupLetsEncryptSubdomain(self, wo_domain) + elif self.app.pargs.letsencrypt == "wildcard": + setupLetsEncryptWildcard(self, wo_domain, dns_cf) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot), diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 73f8a8a..9a491f5 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1436,7 +1436,7 @@ def setupLetsEncryptSubdomain(self, wo_domain_name): # setup letsencrypt for domain + www.domain -def setupLetsEncryptWildcard(self, wo_domain_name): +def setupLetsEncryptWildcard(self, wo_domain_name, dns_cf=True): if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" .format(wo_domain_name)): From b7e7da0d2f06b481946cffefc86ef97eae49e6b4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 14 Jul 2019 01:07:34 +0200 Subject: [PATCH 03/44] Update bash-completion and add wildcard redirection --- config/bash_completion.d/wo_auto.rc | 32 +- wo/cli/plugins/site.py | 9 +- wo/cli/plugins/site_functions.py | 3537 ++++++++++++++------------- 3 files changed, 1802 insertions(+), 1776 deletions(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index ead7ffc..ede18c6 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -22,7 +22,7 @@ _wo_complete() # HANDLE EVERYTHING AFTER THE SECOND LEVEL NAMESPACE "clean") COMPREPLY=( $(compgen \ - -W "--memcache --opcache --fastcgi --redis --all" \ + -W "--opcache --fastcgi --redis --all" \ -- $cur) ) ;; @@ -79,12 +79,12 @@ _wo_complete() ;; "upgrade" ) COMPREPLY=( $(compgen \ - -W "--web --nginx --php --php73 --mysql --all --php56 --no-prompt --wpcli" \ + -W "--web --nginx --php --php73 --mysql --all --netdata --no-prompt --wpcli" \ -- $cur) ) ;; "start" | "stop" | "reload" | "restart" | "status") COMPREPLY=( $(compgen \ - -W "--nginx --php --php73 --mysql --memcache --redis --fail2ban --netdata" \ + -W "--nginx --php --php73 --mysql --redis --fail2ban --netdata" \ -- $cur) ) ;; "migrate") @@ -159,13 +159,13 @@ _wo_complete() "create") COMPREPLY=( $(compgen \ - -W "--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --proxy= --wpredis --letsencrypt --letsencrypt=subdomain -le" \ + -W "--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --proxy= --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" \ -- $cur) ) ;; "update") COMPREPLY=( $(compgen \ - -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew" \ + -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" \ -- $cur) ) ;; "delete") @@ -213,7 +213,7 @@ _wo_complete() if [ ${COMP_WORDS[2]} == "create" ]; then retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --letsencrypt --php73" elif [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=subdomain --le=off " + retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=subdomain --le=off --le=wildcard --dns --dns=cf --dns=do" else retlist="" fi @@ -230,9 +230,9 @@ _wo_complete() "--wpsubdir" | "--wpsubdomain") if [ ${COMP_WORDS[1]} != "debug" ]; then if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--wpsc --wpfc --user --email --pass --wpredis --letsencrypt --letsencrypt=subdomain --php73" + retlist="--wpsc --wpfc --user --email --pass --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --le --le=subdomain --le=wildcard --php73 --dns --dns=cf --dns=do" elif [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew" + retlist="--wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=off --le=subdomain --le=wildcard --dns --dns=cf --dns=do" else retlist="" fi @@ -248,7 +248,7 @@ _wo_complete() "--wpredis" | "--wpfc" | "--wpsc" | "--wpsubdir" | "--wpsubdomain" | "--user" | "--pass" | "--email" | "--wp") if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --php73 --letsencrypt --letsencrypt=subdomain" + retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --php73 --letsencrypt --letsencrypt=subdomain --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" else retlist="" fi @@ -261,7 +261,7 @@ _wo_complete() "--wpredis" | "--wpfc") if [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew" + retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=off --le=subdomain --le=wildcard --dns --dns=cf --dns=do" else retlist="" fi @@ -272,11 +272,11 @@ _wo_complete() -- $cur) ) ;; - "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--memcached" | "--redis | --phpredisadmin") + "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--redis | --phpredisadmin | --netdata") if [[ ${COMP_WORDS[2]} == "install" || ${COMP_WORDS[2]} == "purge" || ${COMP_WORDS[2]} == "remove" ]]; then - retlist="--web --admin --nginx --php --php73 --mysql--wpcli --phpmyadmin --adminer --utils --memcache --redis --phpredisadmin" + retlist="--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --netdata" elif [[ ${COMP_WORDS[2]} == "start" || ${COMP_WORDS[2]} == "reload" || ${COMP_WORDS[2]} == "restart" || ${COMP_WORDS[2]} == "stop" ]]; then - retlist="--nginx --php --php73 --mysql --memcache --redis" + retlist="--nginx --php --php73 --mysql --redis --netdata" elif [[ ${COMP_WORDS[1]} == "debug" ]]; then retlist="--start --nginx --php --php73 --fpm --fpm7 --mysql -i --interactive -stop --import-slow-log --import-slow-log-interval= -" if [[ $prev == '--mysql' ]]; then @@ -324,8 +324,8 @@ _wo_complete() -- $cur) ) ;; - "--memcached" | "--opcache" | "--fastcgi" | "--all" | "--redis") - retlist="--memcached --opcache --fastcgi --redis --all" + "--opcache" | "--fastcgi" | "--all" | "--redis") + retlist="--opcache --fastcgi --redis --all" ret="${retlist[@]/$prev}" COMPREPLY=( $(compgen \ -W "$(echo $ret)" \ @@ -363,7 +363,7 @@ _wo_complete() case "$mprev" in "--user" | "--email" | "--pass") if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain" + retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" fi ret="${retlist[@]/$prev}" COMPREPLY=( $(compgen \ diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 5c7d20d..afbf507 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -737,12 +737,14 @@ class WOSiteCreateController(CementBaseController): if data['letsencrypt'] is True: if self.app.pargs.letsencrypt == "on": setupLetsEncrypt(self, wo_domain) + httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "subodmain": setupLetsEncryptSubdomain(self, wo_domain) + httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": setupLetsEncryptWildcard(self, wo_domain) + httpsRedirect(self, wo_domain, True, True) - httpsRedirect(self, wo_domain) if self.app.pargs.hsts: setupHsts(self, wo_domain) @@ -1269,18 +1271,19 @@ class WOSiteUpdateController(CementBaseController): .format(wo_site_webroot)): if self.app.pargs.letsencrypt == "on": setupLetsEncrypt(self, wo_domain) + httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "subodmain": setupLetsEncryptSubdomain(self, wo_domain) + httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": setupLetsEncryptWildcard(self, wo_domain, dns_cf) + httpsRedirect(self, wo_domain, True, True) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot), '{0}/conf/nginx/ssl.conf' .format(wo_site_webroot)) - httpsRedirect(self, wo_domain) - if not WOService.reload_service(self, 'nginx'): Log.error(self, "service nginx reload failed. " "check issues with `nginx -t` command") diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 9a491f5..ba6e3ea 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1,1757 +1,1780 @@ -from wo.cli.plugins.stack import WOStackController -from wo.core.fileutils import WOFileUtils -from wo.core.mysql import * -from wo.core.shellexec import * -from wo.core.sslutils import SSL -from wo.core.variables import WOVariables -from wo.cli.plugins.sitedb import * -from wo.core.aptget import WOAptGet -from wo.core.git import WOGit -from wo.core.logging import Log -from wo.core.sendmail import WOSendMail -from wo.core.services import WOService -import subprocess -from subprocess import CalledProcessError -import os -import random -import string -import sys -import getpass -import glob -import re -import platform - - -class SiteError(Exception): - """Custom Exception Occured when setting up site""" - - def __init__(self, message): - self.message = message - - def __str__(self): - return repr(self.message) - - -def pre_run_checks(self): - - # Check nginx configuration - Log.info(self, "Running pre-update checks, please wait...") - try: - Log.debug(self, "checking NGINX configuration ...") - FNULL = open('/dev/null', 'w') - ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, - stderr=subprocess.STDOUT) - except CalledProcessError as e: - Log.debug(self, "{0}".format(str(e))) - raise SiteError("nginx configuration check failed.") - - -def check_domain_exists(self, domain): - if getSiteInfo(self, domain): - return True - return False - - -def setupdomain(self, data): - - # for debug purpose - # for key, value in data.items() : - # print (key, value) - - wo_domain_name = data['site_name'] - wo_site_webroot = data['webroot'] - - # Check if nginx configuration already exists - # if os.path.isfile('/etc/nginx/sites-available/{0}' - # .format(wo_domain_name)): - # raise SiteError("nginx configuration already exists for site") - - Log.info(self, "Setting up NGINX configuration \t", end='') - # write nginx config for file - try: - wo_site_nginx_conf = open('/etc/nginx/sites-available/{0}' - .format(wo_domain_name), encoding='utf-8', - mode='w') - if not data['php73']: - self.app.render((data), 'virtualconf.mustache', - out=wo_site_nginx_conf) - else: - self.app.render((data), 'virtualconf-php7.mustache', - out=wo_site_nginx_conf) - wo_site_nginx_conf.close() - except IOError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("create nginx configuration failed for site") - except Exception as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("create nginx configuration failed for site") - finally: - # Check nginx -t and return status over it - try: - Log.debug(self, "Checking generated nginx conf, please wait...") - FNULL = open('/dev/null', 'w') - ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, - stderr=subprocess.STDOUT) - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - except CalledProcessError as e: - Log.debug(self, "{0}".format(str(e))) - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + - Log.OKBLUE + "]") - raise SiteError("created nginx configuration failed for site." - " check with `nginx -t`") - - # create symbolic link for - WOFileUtils.create_symlink(self, ['/etc/nginx/sites-available/{0}' - .format(wo_domain_name), - '/etc/nginx/sites-enabled/{0}' - .format(wo_domain_name)]) - - # Creating htdocs & logs directory - Log.info(self, "Setting up webroot \t\t", end='') - try: - if not os.path.exists('{0}/htdocs'.format(wo_site_webroot)): - os.makedirs('{0}/htdocs'.format(wo_site_webroot)) - if not os.path.exists('{0}/logs'.format(wo_site_webroot)): - os.makedirs('{0}/logs'.format(wo_site_webroot)) - if not os.path.exists('{0}/conf/nginx'.format(wo_site_webroot)): - os.makedirs('{0}/conf/nginx'.format(wo_site_webroot)) - - WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.access.log' - .format(wo_domain_name), - '{0}/logs/access.log' - .format(wo_site_webroot)]) - WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.error.log' - .format(wo_domain_name), - '{0}/logs/error.log' - .format(wo_site_webroot)]) - except Exception as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("setup webroot failed for site") - finally: - # TODO Check if directories are setup - if (os.path.exists('{0}/htdocs'.format(wo_site_webroot)) and - os.path.exists('{0}/logs'.format(wo_site_webroot))): - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - else: - Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") - raise SiteError("setup webroot failed for site") - - -def setupdatabase(self, data): - wo_domain_name = data['site_name'] - wo_random = (''.join(random.sample(string.ascii_uppercase + - string.ascii_lowercase + - string.digits, 24))) - wo_replace_dot = wo_domain_name.replace('.', '_') - prompt_dbname = self.app.config.get('mysql', 'db-name') - prompt_dbuser = self.app.config.get('mysql', 'db-user') - wo_mysql_grant_host = self.app.config.get('mysql', 'grant-host') - wo_db_name = '' - wo_db_username = '' - wo_db_password = '' - - if prompt_dbname == 'True' or prompt_dbname == 'true': - try: - wo_db_name = input('Enter the MySQL database name [{0}]: ' - .format(wo_replace_dot)) - except EOFError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("Unable to input database name") - - if not wo_db_name: - wo_db_name = wo_replace_dot - - if prompt_dbuser == 'True' or prompt_dbuser == 'true': - try: - wo_db_username = input('Enter the MySQL database user name [{0}]: ' - .format(wo_replace_dot)) - wo_db_password = getpass.getpass(prompt='Enter the MySQL database' - ' password [{0}]: ' - .format(wo_random)) - except EOFError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("Unable to input database credentials") - - if not wo_db_username: - wo_db_username = wo_replace_dot - if not wo_db_password: - wo_db_password = wo_random - - if len(wo_db_username) > 16: - Log.debug(self, 'Autofix MySQL username (ERROR 1470 (HY000)),' - ' please wait') - wo_db_username = (wo_db_name[0:6] + generate_random()) - - # create MySQL database - Log.info(self, "Setting up database\t\t", end='') - Log.debug(self, "Creating database {0}".format(wo_db_name)) - try: - if WOMysql.check_db_exists(self, wo_db_name): - Log.debug(self, "Database already exists, Updating DB_NAME .. ") - wo_db_name = (wo_db_name[0:6] + generate_random()) - wo_db_username = (wo_db_name[0:6] + generate_random()) - except MySQLConnectionError as e: - raise SiteError("MySQL Connectivity problem occured") - - try: - WOMysql.execute(self, "create database `{0}`" - .format(wo_db_name)) - except StatementExcecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") - raise SiteError("create database execution failed") - # Create MySQL User - Log.debug(self, "Creating user {0}".format(wo_db_username)) - Log.debug(self, "create user `{0}`@`{1}` identified by ''" - .format(wo_db_username, wo_mysql_grant_host)) - try: - WOMysql.execute(self, - "create user `{0}`@`{1}` identified by '{2}'" - .format(wo_db_username, wo_mysql_grant_host, - wo_db_password), log=False) - except StatementExcecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") - raise SiteError("creating user failed for database") - - # Grant permission - Log.debug(self, "Setting up user privileges") - try: - WOMysql.execute(self, - "grant all privileges on `{0}`.* to `{1}`@`{2}`" - .format(wo_db_name, - wo_db_username, wo_mysql_grant_host)) - except StatementExcecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") - SiteError("grant privileges to user failed for database ") - - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - - data['wo_db_name'] = wo_db_name - data['wo_db_user'] = wo_db_username - data['wo_db_pass'] = wo_db_password - data['wo_db_host'] = WOVariables.wo_mysql_host - data['wo_mysql_grant_host'] = wo_mysql_grant_host - return(data) - - -def setupwordpress(self, data): - wo_domain_name = data['site_name'] - wo_site_webroot = data['webroot'] - prompt_wpprefix = self.app.config.get('wordpress', 'prefix') - wo_wp_user = self.app.config.get('wordpress', 'user') - wo_wp_pass = self.app.config.get('wordpress', 'password') - wo_wp_email = self.app.config.get('wordpress', 'email') - # Random characters - wo_random = (''.join(random.sample(string.ascii_uppercase + - string.ascii_lowercase + - string.digits, 15))) - wo_wp_prefix = '' - # wo_wp_user = '' - # wo_wp_pass = '' - - if 'wp-user' in data.keys() and data['wp-user']: - wo_wp_user = data['wp-user'] - if 'wp-email' in data.keys() and data['wp-email']: - wo_wp_email = data['wp-email'] - if 'wp-pass' in data.keys() and data['wp-pass']: - wo_wp_pass = data['wp-pass'] - - Log.info(self, "Downloading WordPress \t\t", end='') - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - try: - if WOShellExec.cmd_exec(self, "wp --allow-root core" - " download"): - pass - else: - Log.info(self, "[" + Log.ENDC + Log.FAIL + - "Fail" + Log.OKBLUE + "]") - raise SiteError("download WordPress core failed") - except CommandExecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") - raise SiteError(self, "download WordPress core failed") - - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - - if not (data['wo_db_name'] and data['wo_db_user'] and data['wo_db_pass']): - data = setupdatabase(self, data) - if prompt_wpprefix == 'True' or prompt_wpprefix == 'true': - try: - wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ') - while not re.match('^[A-Za-z0-9_]*$', wo_wp_prefix): - Log.warn(self, "table prefix can only " - "contain numbers, letters, and underscores") - wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ' - ) - except EOFError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("input table prefix failed") - - if not wo_wp_prefix: - wo_wp_prefix = 'wp_' - - # Modify wp-config.php & move outside the webroot - - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - Log.debug(self, "Setting up wp-config file") - if not data['multisite']: - Log.debug(self, "Generating wp-config for WordPress Single site") - Log.debug(self, "bash -c \"php {0} --allow-root " - .format(WOVariables.wo_wpcli_path) + - "config create " + - "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbuser=\'{2}\' " - "--dbhost=\'{3}\' " - .format(data['wo_db_name'], wo_wp_prefix, - data['wo_db_user'], data['wo_db_host']) + - "--dbpass=\'{0}\' " - "--extra-php< {1}/{0}.gz" - .format(data['wo_db_name'], - backup_path)): - Log.info(self, - "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") - raise SiteError("mysqldump failed to backup database") - except CommandExecutionError as e: - Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") - raise SiteError("mysqldump failed to backup database") - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - # move wp-config.php/wo-config.php to backup - if data['currsitetype'] in ['mysql', 'proxy']: - if data['php73'] is True and not data['wp']: - WOFileUtils.copyfile(self, configfiles[0], backup_path) - else: - WOFileUtils.mvfile(self, configfiles[0], backup_path) - else: - WOFileUtils.copyfile(self, configfiles[0], backup_path) - - -def site_package_check(self, stype): - apt_packages = [] - packages = [] - stack = WOStackController() - stack.app = self.app - if stype in ['html', 'proxy', 'php', 'mysql', 'wp', 'wpsubdir', - 'wpsubdomain', 'php73']: - Log.debug(self, "Setting apt_packages variable for Nginx") - - # Check if server has nginx-custom package - if not (WOAptGet.is_installed(self, 'nginx-custom') or - WOAptGet.is_installed(self, 'nginx-mainline')): - # check if Server has nginx-plus installed - if WOAptGet.is_installed(self, 'nginx-plus'): - # do something - # do post nginx installation configuration - Log.info(self, "NGINX PLUS Detected ...") - apt = ["nginx-plus"] + WOVariables.wo_nginx - # apt_packages = apt_packages + WOVariables.wo_nginx - stack.post_pref(apt, packages) - elif WOAptGet.is_installed(self, 'nginx'): - Log.info(self, "WordOps detected a previously" - "installed Nginx package. " - "It may or may not have required modules. " - "\nIf you need help, please create an issue at " - "https://github.com/WordOps/WordOps/issues/ \n") - apt = ["nginx"] + WOVariables.wo_nginx - # apt_packages = apt_packages + WOVariables.wo_nginx - stack.post_pref(apt, packages) - else: - apt_packages = apt_packages + WOVariables.wo_nginx - else: - # Fix for Nginx white screen death - if not WOFileUtils.grep(self, '/etc/nginx/fastcgi_params', - 'SCRIPT_FILENAME'): - with open('/etc/nginx/fastcgi_params', encoding='utf-8', - mode='a') as wo_nginx: - wo_nginx.write('fastcgi_param \tSCRIPT_FILENAME ' - '\t$request_filename;\n') - - if self.app.pargs.php and self.app.pargs.php73: - Log.error( - self, "Error: two different PHP versions cannot be " - "combined within the same WordOps site") - - if not self.app.pargs.php73 and stype in ['php', 'mysql', 'wp', 'wpsubdir', - 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for PHP 7.2") - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - apt_packages = apt_packages + WOVariables.wo_php + \ - WOVariables.wo_php_extra - else: - apt_packages = apt_packages + WOVariables.wo_php - - if self.app.pargs.php73 and stype in ['mysql', 'wp', - 'wpsubdir', 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for PHP 7.3") - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVariables.wo_php + \ - WOVariables.wo_php73 + WOVariables.wo_php_extra - else: - apt_packages = apt_packages + WOVariables.wo_php73 - - if stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for MySQL") - if not WOShellExec.cmd_exec(self, "mysqladmin ping"): - apt_packages = apt_packages + WOVariables.wo_mysql - packages = packages + [["https://raw.githubusercontent.com/" - "major/MySQLTuner-perl/master/" - "mysqltuner.pl", "/usr/bin/mysqltuner", - "MySQLTuner"]] - - if stype in ['wp', 'wpsubdir', 'wpsubdomain']: - Log.debug(self, "Setting packages variable for WP-CLI") - if not WOShellExec.cmd_exec(self, "command -v wp"): - packages = packages + [["https://github.com/wp-cli/wp-cli/" - "releases/download/v{0}/" - "wp-cli-{0}.phar" - .format(WOVariables.wo_wp_cli), - "/usr/local/bin/wp", "WP-CLI"]] - if self.app.pargs.wpredis: - Log.debug(self, "Setting apt_packages variable for redis") - if not WOAptGet.is_installed(self, 'redis-server'): - apt_packages = apt_packages + WOVariables.wo_redis - - if (os.path.isfile("/etc/nginx/nginx.conf") and - not os.path.isfile("/etc/nginx/common/redis-php72.conf")): - - data = dict() - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/redis-php72.conf') - wo_nginx = open('/etc/nginx/common/redis-php72.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'redis.mustache', - out=wo_nginx) - wo_nginx.close() - - if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): - if not WOFileUtils.grep(self, "/etc/nginx/conf.d/" - "upstream.conf", - "redis"): - with open("/etc/nginx/conf.d/upstream.conf", - "a") as redis_file: - redis_file.write("upstream redis {\n" - " server 127.0.0.1:6379;\n" - " keepalive 10;\n}") - - if (os.path.isfile("/etc/nginx/nginx.conf") and - not os.path.isfile("/etc/nginx/conf.d/redis.conf")): - with open("/etc/nginx/conf.d/redis.conf", "a") as redis_file: - redis_file.write("# Log format Settings\n" - "log_format rt_cache_redis '$remote_addr" - " $upstream_response_time " - "$srcache_fetch_status [$time_local] '\n" - "'$http_host \"$request\" $status" - " $body_bytes_sent '\n" - "'\"$http_referer\" \"$http_user_agent\"';\n") - - if self.app.pargs.php73: - Log.debug(self, "Setting apt_packages variable for PHP 7.3") - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVariables.wo_php + \ - WOVariables.wo_php73 + WOVariables.wo_php_extra - else: - apt_packages = apt_packages + WOVariables.wo_php73 - - if (os.path.isdir("/etc/nginx/common") and - not os.path.isfile("/etc/nginx/common/php73.conf")): - data = dict() - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/locations-wo.conf') - wo_nginx = open('/etc/nginx/common/locations-wo.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'locations-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/php73.conf') - wo_nginx = open('/etc/nginx/common/php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/wpcommon-php73.conf') - wo_nginx = open('/etc/nginx/common/wpcommon-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'wpcommon-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/wpfc-php73.conf') - wo_nginx = open('/etc/nginx/common/wpfc-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'wpfc-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/wpsc-php73.conf') - wo_nginx = open('/etc/nginx/common/wpsc-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'wpsc-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - if (os.path.isfile("/etc/nginx/nginx.conf") and - not os.path.isfile("/etc/nginx/common/redis-php73.conf")): - data = dict() - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/redis-php73.conf') - wo_nginx = open('/etc/nginx/common/redis-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'redis-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): - if not WOFileUtils.grep(self, "/etc/nginx/conf.d/upstream.conf", - "php73"): - with open("/etc/nginx/conf.d/upstream.conf", "a") as php_file: - php_file.write("upstream php73 {\nserver" - "unix:/var/run/php/php73-fpm.sock;\n}\n" - "upstream debug73" - " {\nserver 127.0.0.1:9173;\n}\n") - - return(stack.install(apt_packages=apt_packages, packages=packages, - disp_msg=False)) - - -def updatewpuserpassword(self, wo_domain, wo_site_webroot): - - wo_wp_user = '' - wo_wp_pass = '' - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - - # Check if wo_domain is wordpress install - try: - is_wp = WOShellExec.cmd_exec(self, "wp --allow-root core" - " version") - except CommandExecutionError as e: - raise SiteError("is WordPress site? check command failed ") - - # Exit if wo_domain is not wordpress install - if not is_wp: - Log.error(self, "{0} does not seem to be a WordPress site" - .format(wo_domain)) - - try: - wo_wp_user = input("Provide WordPress user name [admin]: ") - except Exception as e: - Log.debug(self, "{0}".format(e)) - Log.error(self, "\nCould not update password") - - if wo_wp_user == "?": - Log.info(self, "Fetching WordPress user list") - try: - WOShellExec.cmd_exec(self, "wp --allow-root user list " - "--fields=user_login | grep -v user_login") - except CommandExecutionError as e: - raise SiteError("fetch wp userlist command failed") - - if not wo_wp_user: - wo_wp_user = 'admin' - - try: - is_user_exist = WOShellExec.cmd_exec(self, "wp --allow-root user list " - "--fields=user_login | grep {0}$ " - .format(wo_wp_user)) - except CommandExecutionError as e: - raise SiteError("if wp user exists check command failed") - - if is_user_exist: - try: - wo_wp_pass = getpass.getpass(prompt="Provide password for " - "{0} user: " - .format(wo_wp_user)) - - while not wo_wp_pass: - wo_wp_pass = getpass.getpass(prompt="Provide password for " - "{0} user: " - .format(wo_wp_user)) - except Exception as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("failed to read password input ") - - try: - WOShellExec.cmd_exec(self, "wp --allow-root user update {0}" - " --user_pass={1}" - .format(wo_wp_user, wo_wp_pass)) - except CommandExecutionError as e: - raise SiteError("wp user password update command failed") - Log.info(self, "Password updated successfully") - - else: - Log.error(self, "Invalid WordPress user {0} for {1}." - .format(wo_wp_user, wo_domain)) - - -def display_cache_settings(self, data): - if data['wpsc']: - if data['multisite']: - Log.info(self, "Configure WPSC:" - "\t\thttp://{0}/wp-admin/network/settings.php?" - "page=wpsupercache" - .format(data['site_name'])) - else: - Log.info(self, "Configure WPSC:" - "\t\thttp://{0}/wp-admin/options-general.php?" - "page=wpsupercache" - .format(data['site_name'])) - - if data['wpredis']: - if data['multisite']: - Log.info(self, "Configure redis-cache:" - "\thttp://{0}/wp-admin/network/settings.php?" - "page=redis-cache".format(data['site_name'])) - else: - Log.info(self, "Configure redis-cache:" - "\thttp://{0}/wp-admin/options-general.php?" - "page=redis-cache".format(data['site_name'])) - Log.info(self, "Object Cache:\t\tEnable") - - -def logwatch(self, logfiles): - import zlib - import base64 - import time - from wo.core import logwatch - - def callback(filename, lines): - for line in lines: - if line.find(':::') == -1: - print(line) - else: - data = line.split(':::') - try: - print(data[0], data[1], - zlib.decompress(base64.decodestring(data[2]))) - except Exception as e: - Log.info(time.time(), - 'caught exception rendering a new log line in %s' - % filename) - - l = logwatch.LogWatcher(logfiles, callback) - l.loop() - - -def detSitePar(opts): - """ - Takes dictionary of parsed arguments - 1.returns sitetype and cachetype - 2. raises RuntimeError when wrong combination is used like - "--wp --wpsubdir" or "--html --wp" - """ - sitetype, cachetype = '', '' - typelist = list() - cachelist = list() - for key, val in opts.items(): - if val and key in ['html', 'php', 'mysql', 'wp', - 'wpsubdir', 'wpsubdomain', 'php73']: - typelist.append(key) - elif val and key in ['wpfc', 'wpsc', 'wpredis']: - cachelist.append(key) - - if len(typelist) > 1 or len(cachelist) > 1: - if len(cachelist) > 1: - raise RuntimeError( - "Could not determine cache type." - "Multiple cache parameter entered") - elif False not in [x in ('php', 'mysql', 'html') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php73', 'mysql', 'html') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php', 'mysql') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php73', 'mysql') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('html', 'mysql') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php', 'html') for x in typelist]: - sitetype = 'php' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php73', 'html') for x in typelist]: - sitetype = 'php73' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wp', 'wpsubdir') for x in typelist]: - sitetype = 'wpsubdir' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wp', 'wpsubdomain') for x in typelist]: - sitetype = 'wpsubdomain' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wp', 'php73') for x in typelist]: - sitetype = 'wp' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wpsubdir', 'php73') for x in typelist]: - sitetype = 'wpsubdir' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wpsubdomain', 'php73') for x in typelist]: - sitetype = 'wpsubdomain' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - else: - raise RuntimeError("could not determine site and cache type") - else: - if not typelist and not cachelist: - sitetype = None - cachetype = None - elif (not typelist or "php73" in typelist) and cachelist: - sitetype = 'wp' - cachetype = cachelist[0] - elif typelist and (not cachelist): - sitetype = typelist[0] - cachetype = 'basic' - else: - sitetype = typelist[0] - cachetype = cachelist[0] - - return (sitetype, cachetype) - - -def generate_random(): - wo_random10 = (''.join(random.sample(string.ascii_uppercase + - string.ascii_lowercase + - string.digits, 24))) - return wo_random10 - - -def deleteDB(self, dbname, dbuser, dbhost, exit=True): - try: - # Check if Database exists - try: - if WOMysql.check_db_exists(self, dbname): - # Drop database if exists - Log.debug(self, "dropping database `{0}`".format(dbname)) - WOMysql.execute(self, - "drop database `{0}`".format(dbname), - errormsg='Unable to drop database {0}' - .format(dbname)) - except StatementExcecutionError as e: - Log.debug(self, "drop database failed") - Log.info(self, "Database {0} not dropped".format(dbname)) - - except MySQLConnectionError as e: - Log.debug(self, "Mysql Connection problem occured") - - if dbuser != 'root': - Log.debug(self, "dropping user `{0}`".format(dbuser)) - try: - WOMysql.execute(self, - "drop user `{0}`@`{1}`" - .format(dbuser, dbhost)) - except StatementExcecutionError as e: - Log.debug(self, "drop database user failed") - Log.info(self, "Database {0} not dropped".format(dbuser)) - try: - WOMysql.execute(self, "flush privileges") - except StatementExcecutionError as e: - Log.debug(self, "drop database failed") - Log.info(self, "Database {0} not dropped".format(dbname)) - except Exception as e: - Log.error(self, "Error occured while deleting database", exit) - - -def deleteWebRoot(self, webroot): - # do some preprocessing before proceeding - webroot = webroot.strip() - if (webroot == "/var/www/" or webroot == "/var/www" or - webroot == "/var/www/.." or webroot == "/var/www/."): - Log.debug(self, "Tried to remove {0}, but didn't remove it" - .format(webroot)) - return False - - if os.path.isdir(webroot): - Log.debug(self, "Removing {0}".format(webroot)) - WOFileUtils.rm(self, webroot) - return True - Log.debug(self, "{0} does not exist".format(webroot)) - return False - - -def removeNginxConf(self, domain): - if os.path.isfile('/etc/nginx/sites-available/{0}' - .format(domain)): - Log.debug(self, "Removing Nginx configuration") - WOFileUtils.rm(self, '/etc/nginx/sites-enabled/{0}' - .format(domain)) - WOFileUtils.rm(self, '/etc/nginx/sites-available/{0}' - .format(domain)) - WOService.reload_service(self, 'nginx') - WOGit.add(self, ["/etc/nginx"], - msg="Deleted {0} " - .format(domain)) - - -def removeAcmeConf(self, domain): - if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' - .format(domain)): - Log.debug(self, "Removing Acme configuration") - WOFileUtils.rm(self, '/etc/letsencrypt/renewal/{0}_ecc' - .format(domain)) - WOFileUtils.rm(self, '/etc/letsencrypt/live/{0}' - .format(domain)) - WOGit.add(self, ["/etc/letsencrypt"], - msg="Deleted {0} " - .format(domain)) - - -def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', - dbhost=''): - """ - Removes the nginx configuration and database for the domain provided. - doCleanupAction(self, domain='sitename', webroot='', - dbname='', dbuser='', dbhost='') - """ - if domain: - if os.path.isfile('/etc/nginx/sites-available/{0}' - .format(domain)): - removeNginxConf(self, domain) - if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' - .format(domain)): - removeAcmeConf(self, domain) - - if webroot: - deleteWebRoot(self, webroot) - - if dbname: - if not dbuser: - raise SiteError("dbuser not provided") - if not dbhost: - raise SiteError("dbhost not provided") - deleteDB(self, dbname, dbuser, dbhost) - - -# setup letsencrypt for domain + www.domain -def setupLetsEncrypt(self, wo_domain_name): - - if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" - .format(wo_domain_name)): - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d www.{0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d www.{0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - - if ssl: - - try: - Log.info(self, "Deploying SSL cert with acme.sh") - Log.debug(self, "Cert deployment for domain: {0}" - .format(wo_domain_name)) - sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && " - "service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - updateSiteInfo(self, wo_domain_name, ssl=True) - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to install certificate", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - -# setup letsencrypt for a subdomain - - -def setupLetsEncryptSubdomain(self, wo_domain_name): - - if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" - .format(wo_domain_name)): - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - if ssl: - - try: - Log.info(self, "Deploying SSL cert with acme.sh") - Log.debug(self, "Deploying cert for domain: {0}" - .format(wo_domain_name)) - sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - updateSiteInfo(self, wo_domain_name, ssl=True) - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to create ssl.conf", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - -# setup letsencrypt for domain + www.domain - - -def setupLetsEncryptWildcard(self, wo_domain_name, dns_cf=True): - - if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" - .format(wo_domain_name)): - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d *.{0} --dns dns_cf " - "-k ec-384 -f" - .format(wo_domain_name)) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d *.{0} --dns dns_cf " - "-k ec-384 -f" - .format(wo_domain_name)) - - if ssl: - - try: - Log.info(self, "Deploying SSL cert with acme.sh") - Log.debug(self, "Cert deployment for domain: {0}" - .format(wo_domain_name)) - sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && " - "service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - updateSiteInfo(self, wo_domain_name, ssl=True) - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to install certificate", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - - -# letsencrypt cert renewal - - -def renewLetsEncrypt(self, wo_domain_name): - - ssl = WOShellExec.cmd_exec( - self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--renew -d {0} --ecc --force" - .format(wo_domain_name)) - - mail_list = '' - if not ssl: - Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!", - False) - if (SSL.getExpirationDays(self, wo_domain_name) > 0): - Log.error(self, "Your current certificate will expire within " + - str(SSL.getExpirationDays(self, wo_domain_name)) + - " days.", False) - else: - Log.error(self, "Your current certificate already expired!", False) - - # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, - # "[FAIL] HTTPS cert renewal {0}".format(wo_domain_name), - # "Hi,\n\nHTTPS certificate renewal for https://{0} - # was unsuccessful.".format(wo_domain_name) + - # "\nPlease check the WordOps log for reason - # The current expiry date is : " + - # str(SSL.getExpirationDate(self, wo_domain_name)) + - # "\n\nFor support visit https://wordops.net/support . - # \n\nBest regards,\nYour WordOps Worker", files=mail_list, - # port=25, isTls=False) - Log.error(self, "Check the WO log for more details " - "`tail /var/log/wo/wordops.log`") - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, - # "[SUCCESS] Let's Encrypt certificate renewal {0}".format(wo_domain_name), - # "Hi,\n\nYour Let's Encrypt certificate has been renewed for - # https://{0} .".format(wo_domain_name) + - # "\nYour new certificate will expire on : " + - # str(SSL.getExpirationDate(self, wo_domain_name)) + - # "\n\nBest regards,\nYour WordOps Worker", files=mail_list, - # port=25, isTls=False) - -# redirect= False to disable https redirection - - -def setupHsts(self, wo_domain_name): - Log.info( - self, "Adding /var/www/{0}/conf/nginx/hsts.conf" - .format(wo_domain_name)) - - hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - hstsconf.write("more_set_headers " - "\"Strict-Transport-Security: " - "max-age=31536000; " - "'includeSubDomains; " - "preload\";") - hstsconf.close() - return 0 - - -def httpsRedirect(self, wo_domain_name, redirect=True): - if redirect: - if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name)): - WOFileUtils.mvfile(self, - "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name), - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - else: - try: - Log.info( - self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - - sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("server {\n" - "\tlisten 80;\n" + - "\tlisten [::]:80;\n" + - "\tserver_name www.{0} {0};\n" - .format(wo_domain_name) + - "\treturn 301 https://{0}" - .format(wo_domain_name)+"$request_uri;\n}") - sslconf.close() - # Nginx Configation into GIT - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - - Log.info(self, "Added HTTPS Force Redirection for Site " - " http://{0}".format(wo_domain_name)) - WOGit.add(self, - ["/etc/nginx"], msg="Adding /etc/nginx/conf.d/" - "force-ssl-{0}.conf".format(wo_domain_name)) - else: - if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)): - WOFileUtils.mvfile(self, "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name)) - Log.info(self, "Disabled HTTPS Force Redirection for Site " - " http://{0}".format(wo_domain_name)) - - -def archivedCertificateHandle(self, domain): - Log.warn(self, "You already have an existing certificate " - "for the domain requested.\n" - "(ref: {0}/" - "{1}_ecc/{1}.conf)".format(WOVariables.wo_ssl_archive, domain) + - "\nPlease select an option from below?" - "\n\t1: Reinstall existing certificate" - "\n\t2: Keep the existing certificate for now" - "\n\t3: Renew & replace the certificate (limit ~5 per 7 days)" - "") - check_prompt = input( - "\nType the appropriate number [1-3] or any other key to cancel: ") - if not os.path.isfile("{0}/{1}/fullchain.pem" - .format(WOVariables.wo_ssl_live, domain)): - Log.error( - self, "{0}/{1}/fullchain.pem file is missing." - .format(WOVariables.wo_ssl_live, domain)) - - if check_prompt == "1": - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"service nginx restart\" " - .format(WOVariables.wo_ssl_live, - domain)) - if ssl: - - try: - - if not os.path.isfile("/var/www/{0}/conf/nginx/ssl.conf" - .format(domain)): - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(domain)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(domain), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate " - "{0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, domain)) - sslconf.close() - - updateSiteInfo(self, domain, ssl=True) - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - - elif (check_prompt == "2"): - Log.info(self, "Using Existing Certificate files") - if not os.path.isfile("{0}/{1}/fullchain.pem" - .format(WOVariables.wo_ssl_live, domain)): - Log.error(self, "Certificate files not found. Skipping.\n" - "Please check if following file exist" - "\n\t/etc/letsencrypt/live/{0}/fullchain.pem\n\t" - "/etc/letsencrypt/live/{0}/key.pem".format(domain)) - - updateSiteInfo(self, domain, ssl=True) - - elif (check_prompt == "3"): - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--renew -d {0} --ecc " - "--force" - .format(domain)) - - if ssl: - - try: - - WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "ssl_trusted_certificate " - "{0}/{1}/ca.pem;\n" - "--reloadcmd " - "\"service nginx restart\" " - .format(WOVariables.wo_ssl_live, domain)) - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while installing " - "the certificate") - - else: - Log.error(self, "Operation cancelled by user.") - - if os.path.isfile("{0}/conf/nginx/ssl.conf" - .format(domain)): - Log.info(self, "Existing ssl.conf . Backing it up ..") - WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf" - .format(domain), - '/var/www/{0}/conf/nginx/ssl.conf.bak' - .format(domain)) - - return ssl +from wo.cli.plugins.stack import WOStackController +from wo.core.fileutils import WOFileUtils +from wo.core.mysql import * +from wo.core.shellexec import * +from wo.core.sslutils import SSL +from wo.core.variables import WOVariables +from wo.cli.plugins.sitedb import * +from wo.core.aptget import WOAptGet +from wo.core.git import WOGit +from wo.core.logging import Log +from wo.core.sendmail import WOSendMail +from wo.core.services import WOService +import subprocess +from subprocess import CalledProcessError +import os +import random +import string +import sys +import getpass +import glob +import re +import platform + + +class SiteError(Exception): + """Custom Exception Occured when setting up site""" + + def __init__(self, message): + self.message = message + + def __str__(self): + return repr(self.message) + + +def pre_run_checks(self): + + # Check nginx configuration + Log.info(self, "Running pre-update checks, please wait...") + try: + Log.debug(self, "checking NGINX configuration ...") + FNULL = open('/dev/null', 'w') + ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, + stderr=subprocess.STDOUT) + except CalledProcessError as e: + Log.debug(self, "{0}".format(str(e))) + raise SiteError("nginx configuration check failed.") + + +def check_domain_exists(self, domain): + if getSiteInfo(self, domain): + return True + return False + + +def setupdomain(self, data): + + # for debug purpose + # for key, value in data.items() : + # print (key, value) + + wo_domain_name = data['site_name'] + wo_site_webroot = data['webroot'] + + # Check if nginx configuration already exists + # if os.path.isfile('/etc/nginx/sites-available/{0}' + # .format(wo_domain_name)): + # raise SiteError("nginx configuration already exists for site") + + Log.info(self, "Setting up NGINX configuration \t", end='') + # write nginx config for file + try: + wo_site_nginx_conf = open('/etc/nginx/sites-available/{0}' + .format(wo_domain_name), encoding='utf-8', + mode='w') + if not data['php73']: + self.app.render((data), 'virtualconf.mustache', + out=wo_site_nginx_conf) + else: + self.app.render((data), 'virtualconf-php7.mustache', + out=wo_site_nginx_conf) + wo_site_nginx_conf.close() + except IOError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("create nginx configuration failed for site") + except Exception as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("create nginx configuration failed for site") + finally: + # Check nginx -t and return status over it + try: + Log.debug(self, "Checking generated nginx conf, please wait...") + FNULL = open('/dev/null', 'w') + ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, + stderr=subprocess.STDOUT) + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + except CalledProcessError as e: + Log.debug(self, "{0}".format(str(e))) + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + + Log.OKBLUE + "]") + raise SiteError("created nginx configuration failed for site." + " check with `nginx -t`") + + # create symbolic link for + WOFileUtils.create_symlink(self, ['/etc/nginx/sites-available/{0}' + .format(wo_domain_name), + '/etc/nginx/sites-enabled/{0}' + .format(wo_domain_name)]) + + # Creating htdocs & logs directory + Log.info(self, "Setting up webroot \t\t", end='') + try: + if not os.path.exists('{0}/htdocs'.format(wo_site_webroot)): + os.makedirs('{0}/htdocs'.format(wo_site_webroot)) + if not os.path.exists('{0}/logs'.format(wo_site_webroot)): + os.makedirs('{0}/logs'.format(wo_site_webroot)) + if not os.path.exists('{0}/conf/nginx'.format(wo_site_webroot)): + os.makedirs('{0}/conf/nginx'.format(wo_site_webroot)) + + WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.access.log' + .format(wo_domain_name), + '{0}/logs/access.log' + .format(wo_site_webroot)]) + WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.error.log' + .format(wo_domain_name), + '{0}/logs/error.log' + .format(wo_site_webroot)]) + except Exception as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("setup webroot failed for site") + finally: + # TODO Check if directories are setup + if (os.path.exists('{0}/htdocs'.format(wo_site_webroot)) and + os.path.exists('{0}/logs'.format(wo_site_webroot))): + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + else: + Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") + raise SiteError("setup webroot failed for site") + + +def setupdatabase(self, data): + wo_domain_name = data['site_name'] + wo_random = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 24))) + wo_replace_dot = wo_domain_name.replace('.', '_') + prompt_dbname = self.app.config.get('mysql', 'db-name') + prompt_dbuser = self.app.config.get('mysql', 'db-user') + wo_mysql_grant_host = self.app.config.get('mysql', 'grant-host') + wo_db_name = '' + wo_db_username = '' + wo_db_password = '' + + if prompt_dbname == 'True' or prompt_dbname == 'true': + try: + wo_db_name = input('Enter the MySQL database name [{0}]: ' + .format(wo_replace_dot)) + except EOFError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("Unable to input database name") + + if not wo_db_name: + wo_db_name = wo_replace_dot + + if prompt_dbuser == 'True' or prompt_dbuser == 'true': + try: + wo_db_username = input('Enter the MySQL database user name [{0}]: ' + .format(wo_replace_dot)) + wo_db_password = getpass.getpass(prompt='Enter the MySQL database' + ' password [{0}]: ' + .format(wo_random)) + except EOFError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("Unable to input database credentials") + + if not wo_db_username: + wo_db_username = wo_replace_dot + if not wo_db_password: + wo_db_password = wo_random + + if len(wo_db_username) > 16: + Log.debug(self, 'Autofix MySQL username (ERROR 1470 (HY000)),' + ' please wait') + wo_db_username = (wo_db_name[0:6] + generate_random()) + + # create MySQL database + Log.info(self, "Setting up database\t\t", end='') + Log.debug(self, "Creating database {0}".format(wo_db_name)) + try: + if WOMysql.check_db_exists(self, wo_db_name): + Log.debug(self, "Database already exists, Updating DB_NAME .. ") + wo_db_name = (wo_db_name[0:6] + generate_random()) + wo_db_username = (wo_db_name[0:6] + generate_random()) + except MySQLConnectionError as e: + raise SiteError("MySQL Connectivity problem occured") + + try: + WOMysql.execute(self, "create database `{0}`" + .format(wo_db_name)) + except StatementExcecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") + raise SiteError("create database execution failed") + # Create MySQL User + Log.debug(self, "Creating user {0}".format(wo_db_username)) + Log.debug(self, "create user `{0}`@`{1}` identified by ''" + .format(wo_db_username, wo_mysql_grant_host)) + try: + WOMysql.execute(self, + "create user `{0}`@`{1}` identified by '{2}'" + .format(wo_db_username, wo_mysql_grant_host, + wo_db_password), log=False) + except StatementExcecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") + raise SiteError("creating user failed for database") + + # Grant permission + Log.debug(self, "Setting up user privileges") + try: + WOMysql.execute(self, + "grant all privileges on `{0}`.* to `{1}`@`{2}`" + .format(wo_db_name, + wo_db_username, wo_mysql_grant_host)) + except StatementExcecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") + SiteError("grant privileges to user failed for database ") + + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + + data['wo_db_name'] = wo_db_name + data['wo_db_user'] = wo_db_username + data['wo_db_pass'] = wo_db_password + data['wo_db_host'] = WOVariables.wo_mysql_host + data['wo_mysql_grant_host'] = wo_mysql_grant_host + return(data) + + +def setupwordpress(self, data): + wo_domain_name = data['site_name'] + wo_site_webroot = data['webroot'] + prompt_wpprefix = self.app.config.get('wordpress', 'prefix') + wo_wp_user = self.app.config.get('wordpress', 'user') + wo_wp_pass = self.app.config.get('wordpress', 'password') + wo_wp_email = self.app.config.get('wordpress', 'email') + # Random characters + wo_random = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 15))) + wo_wp_prefix = '' + # wo_wp_user = '' + # wo_wp_pass = '' + + if 'wp-user' in data.keys() and data['wp-user']: + wo_wp_user = data['wp-user'] + if 'wp-email' in data.keys() and data['wp-email']: + wo_wp_email = data['wp-email'] + if 'wp-pass' in data.keys() and data['wp-pass']: + wo_wp_pass = data['wp-pass'] + + Log.info(self, "Downloading WordPress \t\t", end='') + WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) + try: + if WOShellExec.cmd_exec(self, "wp --allow-root core" + " download"): + pass + else: + Log.info(self, "[" + Log.ENDC + Log.FAIL + + "Fail" + Log.OKBLUE + "]") + raise SiteError("download WordPress core failed") + except CommandExecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") + raise SiteError(self, "download WordPress core failed") + + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + + if not (data['wo_db_name'] and data['wo_db_user'] and data['wo_db_pass']): + data = setupdatabase(self, data) + if prompt_wpprefix == 'True' or prompt_wpprefix == 'true': + try: + wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ') + while not re.match('^[A-Za-z0-9_]*$', wo_wp_prefix): + Log.warn(self, "table prefix can only " + "contain numbers, letters, and underscores") + wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ' + ) + except EOFError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("input table prefix failed") + + if not wo_wp_prefix: + wo_wp_prefix = 'wp_' + + # Modify wp-config.php & move outside the webroot + + WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) + Log.debug(self, "Setting up wp-config file") + if not data['multisite']: + Log.debug(self, "Generating wp-config for WordPress Single site") + Log.debug(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config create " + + "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbuser=\'{2}\' " + "--dbhost=\'{3}\' " + .format(data['wo_db_name'], wo_wp_prefix, + data['wo_db_user'], data['wo_db_host']) + + "--dbpass=\'{0}\' " + "--extra-php< {1}/{0}.gz" + .format(data['wo_db_name'], + backup_path)): + Log.info(self, + "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") + raise SiteError("mysqldump failed to backup database") + except CommandExecutionError as e: + Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") + raise SiteError("mysqldump failed to backup database") + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + # move wp-config.php/wo-config.php to backup + if data['currsitetype'] in ['mysql', 'proxy']: + if data['php73'] is True and not data['wp']: + WOFileUtils.copyfile(self, configfiles[0], backup_path) + else: + WOFileUtils.mvfile(self, configfiles[0], backup_path) + else: + WOFileUtils.copyfile(self, configfiles[0], backup_path) + + +def site_package_check(self, stype): + apt_packages = [] + packages = [] + stack = WOStackController() + stack.app = self.app + if stype in ['html', 'proxy', 'php', 'mysql', 'wp', 'wpsubdir', + 'wpsubdomain', 'php73']: + Log.debug(self, "Setting apt_packages variable for Nginx") + + # Check if server has nginx-custom package + if not (WOAptGet.is_installed(self, 'nginx-custom') or + WOAptGet.is_installed(self, 'nginx-mainline')): + # check if Server has nginx-plus installed + if WOAptGet.is_installed(self, 'nginx-plus'): + # do something + # do post nginx installation configuration + Log.info(self, "NGINX PLUS Detected ...") + apt = ["nginx-plus"] + WOVariables.wo_nginx + # apt_packages = apt_packages + WOVariables.wo_nginx + stack.post_pref(apt, packages) + elif WOAptGet.is_installed(self, 'nginx'): + Log.info(self, "WordOps detected a previously" + "installed Nginx package. " + "It may or may not have required modules. " + "\nIf you need help, please create an issue at " + "https://github.com/WordOps/WordOps/issues/ \n") + apt = ["nginx"] + WOVariables.wo_nginx + # apt_packages = apt_packages + WOVariables.wo_nginx + stack.post_pref(apt, packages) + else: + apt_packages = apt_packages + WOVariables.wo_nginx + else: + # Fix for Nginx white screen death + if not WOFileUtils.grep(self, '/etc/nginx/fastcgi_params', + 'SCRIPT_FILENAME'): + with open('/etc/nginx/fastcgi_params', encoding='utf-8', + mode='a') as wo_nginx: + wo_nginx.write('fastcgi_param \tSCRIPT_FILENAME ' + '\t$request_filename;\n') + + if self.app.pargs.php and self.app.pargs.php73: + Log.error( + self, "Error: two different PHP versions cannot be " + "combined within the same WordOps site") + + if not self.app.pargs.php73 and stype in ['php', 'mysql', 'wp', 'wpsubdir', + 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for PHP 7.2") + if not WOAptGet.is_installed(self, 'php7.2-fpm'): + if not WOAptGet.is_installed(self, 'php7.3-fpm'): + apt_packages = apt_packages + WOVariables.wo_php + \ + WOVariables.wo_php_extra + else: + apt_packages = apt_packages + WOVariables.wo_php + + if self.app.pargs.php73 and stype in ['mysql', 'wp', + 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for PHP 7.3") + if not WOAptGet.is_installed(self, 'php7.3-fpm'): + if not WOAptGet.is_installed(self, 'php7.2-fpm'): + apt_packages = apt_packages + WOVariables.wo_php + \ + WOVariables.wo_php73 + WOVariables.wo_php_extra + else: + apt_packages = apt_packages + WOVariables.wo_php73 + + if stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for MySQL") + if not WOShellExec.cmd_exec(self, "mysqladmin ping"): + apt_packages = apt_packages + WOVariables.wo_mysql + packages = packages + [["https://raw.githubusercontent.com/" + "major/MySQLTuner-perl/master/" + "mysqltuner.pl", "/usr/bin/mysqltuner", + "MySQLTuner"]] + + if stype in ['wp', 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting packages variable for WP-CLI") + if not WOShellExec.cmd_exec(self, "command -v wp"): + packages = packages + [["https://github.com/wp-cli/wp-cli/" + "releases/download/v{0}/" + "wp-cli-{0}.phar" + .format(WOVariables.wo_wp_cli), + "/usr/local/bin/wp", "WP-CLI"]] + if self.app.pargs.wpredis: + Log.debug(self, "Setting apt_packages variable for redis") + if not WOAptGet.is_installed(self, 'redis-server'): + apt_packages = apt_packages + WOVariables.wo_redis + + if (os.path.isfile("/etc/nginx/nginx.conf") and + not os.path.isfile("/etc/nginx/common/redis-php72.conf")): + + data = dict() + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/redis-php72.conf') + wo_nginx = open('/etc/nginx/common/redis-php72.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'redis.mustache', + out=wo_nginx) + wo_nginx.close() + + if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): + if not WOFileUtils.grep(self, "/etc/nginx/conf.d/" + "upstream.conf", + "redis"): + with open("/etc/nginx/conf.d/upstream.conf", + "a") as redis_file: + redis_file.write("upstream redis {\n" + " server 127.0.0.1:6379;\n" + " keepalive 10;\n}") + + if (os.path.isfile("/etc/nginx/nginx.conf") and + not os.path.isfile("/etc/nginx/conf.d/redis.conf")): + with open("/etc/nginx/conf.d/redis.conf", "a") as redis_file: + redis_file.write("# Log format Settings\n" + "log_format rt_cache_redis '$remote_addr" + " $upstream_response_time " + "$srcache_fetch_status [$time_local] '\n" + "'$http_host \"$request\" $status" + " $body_bytes_sent '\n" + "'\"$http_referer\" \"$http_user_agent\"';\n") + + if self.app.pargs.php73: + Log.debug(self, "Setting apt_packages variable for PHP 7.3") + if not WOAptGet.is_installed(self, 'php7.3-fpm'): + if not WOAptGet.is_installed(self, 'php7.2-fpm'): + apt_packages = apt_packages + WOVariables.wo_php + \ + WOVariables.wo_php73 + WOVariables.wo_php_extra + else: + apt_packages = apt_packages + WOVariables.wo_php73 + + if (os.path.isdir("/etc/nginx/common") and + not os.path.isfile("/etc/nginx/common/php73.conf")): + data = dict() + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/locations-wo.conf') + wo_nginx = open('/etc/nginx/common/locations-wo.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'locations-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/php73.conf') + wo_nginx = open('/etc/nginx/common/php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/wpcommon-php73.conf') + wo_nginx = open('/etc/nginx/common/wpcommon-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'wpcommon-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/wpfc-php73.conf') + wo_nginx = open('/etc/nginx/common/wpfc-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'wpfc-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/wpsc-php73.conf') + wo_nginx = open('/etc/nginx/common/wpsc-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'wpsc-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + if (os.path.isfile("/etc/nginx/nginx.conf") and + not os.path.isfile("/etc/nginx/common/redis-php73.conf")): + data = dict() + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/redis-php73.conf') + wo_nginx = open('/etc/nginx/common/redis-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'redis-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): + if not WOFileUtils.grep(self, "/etc/nginx/conf.d/upstream.conf", + "php73"): + with open("/etc/nginx/conf.d/upstream.conf", "a") as php_file: + php_file.write("upstream php73 {\nserver" + "unix:/var/run/php/php73-fpm.sock;\n}\n" + "upstream debug73" + " {\nserver 127.0.0.1:9173;\n}\n") + + return(stack.install(apt_packages=apt_packages, packages=packages, + disp_msg=False)) + + +def updatewpuserpassword(self, wo_domain, wo_site_webroot): + + wo_wp_user = '' + wo_wp_pass = '' + WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) + + # Check if wo_domain is wordpress install + try: + is_wp = WOShellExec.cmd_exec(self, "wp --allow-root core" + " version") + except CommandExecutionError as e: + raise SiteError("is WordPress site? check command failed ") + + # Exit if wo_domain is not wordpress install + if not is_wp: + Log.error(self, "{0} does not seem to be a WordPress site" + .format(wo_domain)) + + try: + wo_wp_user = input("Provide WordPress user name [admin]: ") + except Exception as e: + Log.debug(self, "{0}".format(e)) + Log.error(self, "\nCould not update password") + + if wo_wp_user == "?": + Log.info(self, "Fetching WordPress user list") + try: + WOShellExec.cmd_exec(self, "wp --allow-root user list " + "--fields=user_login | grep -v user_login") + except CommandExecutionError as e: + raise SiteError("fetch wp userlist command failed") + + if not wo_wp_user: + wo_wp_user = 'admin' + + try: + is_user_exist = WOShellExec.cmd_exec(self, "wp --allow-root user list " + "--fields=user_login | grep {0}$ " + .format(wo_wp_user)) + except CommandExecutionError as e: + raise SiteError("if wp user exists check command failed") + + if is_user_exist: + try: + wo_wp_pass = getpass.getpass(prompt="Provide password for " + "{0} user: " + .format(wo_wp_user)) + + while not wo_wp_pass: + wo_wp_pass = getpass.getpass(prompt="Provide password for " + "{0} user: " + .format(wo_wp_user)) + except Exception as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("failed to read password input ") + + try: + WOShellExec.cmd_exec(self, "wp --allow-root user update {0}" + " --user_pass={1}" + .format(wo_wp_user, wo_wp_pass)) + except CommandExecutionError as e: + raise SiteError("wp user password update command failed") + Log.info(self, "Password updated successfully") + + else: + Log.error(self, "Invalid WordPress user {0} for {1}." + .format(wo_wp_user, wo_domain)) + + +def display_cache_settings(self, data): + if data['wpsc']: + if data['multisite']: + Log.info(self, "Configure WPSC:" + "\t\thttp://{0}/wp-admin/network/settings.php?" + "page=wpsupercache" + .format(data['site_name'])) + else: + Log.info(self, "Configure WPSC:" + "\t\thttp://{0}/wp-admin/options-general.php?" + "page=wpsupercache" + .format(data['site_name'])) + + if data['wpredis']: + if data['multisite']: + Log.info(self, "Configure redis-cache:" + "\thttp://{0}/wp-admin/network/settings.php?" + "page=redis-cache".format(data['site_name'])) + else: + Log.info(self, "Configure redis-cache:" + "\thttp://{0}/wp-admin/options-general.php?" + "page=redis-cache".format(data['site_name'])) + Log.info(self, "Object Cache:\t\tEnable") + + +def logwatch(self, logfiles): + import zlib + import base64 + import time + from wo.core import logwatch + + def callback(filename, lines): + for line in lines: + if line.find(':::') == -1: + print(line) + else: + data = line.split(':::') + try: + print(data[0], data[1], + zlib.decompress(base64.decodestring(data[2]))) + except Exception as e: + Log.info(time.time(), + 'caught exception rendering a new log line in %s' + % filename) + + l = logwatch.LogWatcher(logfiles, callback) + l.loop() + + +def detSitePar(opts): + """ + Takes dictionary of parsed arguments + 1.returns sitetype and cachetype + 2. raises RuntimeError when wrong combination is used like + "--wp --wpsubdir" or "--html --wp" + """ + sitetype, cachetype = '', '' + typelist = list() + cachelist = list() + for key, val in opts.items(): + if val and key in ['html', 'php', 'mysql', 'wp', + 'wpsubdir', 'wpsubdomain', 'php73']: + typelist.append(key) + elif val and key in ['wpfc', 'wpsc', 'wpredis']: + cachelist.append(key) + + if len(typelist) > 1 or len(cachelist) > 1: + if len(cachelist) > 1: + raise RuntimeError( + "Could not determine cache type." + "Multiple cache parameter entered") + elif False not in [x in ('php', 'mysql', 'html') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php73', 'mysql', 'html') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php73', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('html', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php', 'html') for x in typelist]: + sitetype = 'php' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php73', 'html') for x in typelist]: + sitetype = 'php73' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wp', 'wpsubdir') for x in typelist]: + sitetype = 'wpsubdir' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wp', 'wpsubdomain') for x in typelist]: + sitetype = 'wpsubdomain' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wp', 'php73') for x in typelist]: + sitetype = 'wp' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wpsubdir', 'php73') for x in typelist]: + sitetype = 'wpsubdir' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wpsubdomain', 'php73') for x in typelist]: + sitetype = 'wpsubdomain' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + else: + raise RuntimeError("could not determine site and cache type") + else: + if not typelist and not cachelist: + sitetype = None + cachetype = None + elif (not typelist or "php73" in typelist) and cachelist: + sitetype = 'wp' + cachetype = cachelist[0] + elif typelist and (not cachelist): + sitetype = typelist[0] + cachetype = 'basic' + else: + sitetype = typelist[0] + cachetype = cachelist[0] + + return (sitetype, cachetype) + + +def generate_random(): + wo_random10 = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 24))) + return wo_random10 + + +def deleteDB(self, dbname, dbuser, dbhost, exit=True): + try: + # Check if Database exists + try: + if WOMysql.check_db_exists(self, dbname): + # Drop database if exists + Log.debug(self, "dropping database `{0}`".format(dbname)) + WOMysql.execute(self, + "drop database `{0}`".format(dbname), + errormsg='Unable to drop database {0}' + .format(dbname)) + except StatementExcecutionError as e: + Log.debug(self, "drop database failed") + Log.info(self, "Database {0} not dropped".format(dbname)) + + except MySQLConnectionError as e: + Log.debug(self, "Mysql Connection problem occured") + + if dbuser != 'root': + Log.debug(self, "dropping user `{0}`".format(dbuser)) + try: + WOMysql.execute(self, + "drop user `{0}`@`{1}`" + .format(dbuser, dbhost)) + except StatementExcecutionError as e: + Log.debug(self, "drop database user failed") + Log.info(self, "Database {0} not dropped".format(dbuser)) + try: + WOMysql.execute(self, "flush privileges") + except StatementExcecutionError as e: + Log.debug(self, "drop database failed") + Log.info(self, "Database {0} not dropped".format(dbname)) + except Exception as e: + Log.error(self, "Error occured while deleting database", exit) + + +def deleteWebRoot(self, webroot): + # do some preprocessing before proceeding + webroot = webroot.strip() + if (webroot == "/var/www/" or webroot == "/var/www" or + webroot == "/var/www/.." or webroot == "/var/www/."): + Log.debug(self, "Tried to remove {0}, but didn't remove it" + .format(webroot)) + return False + + if os.path.isdir(webroot): + Log.debug(self, "Removing {0}".format(webroot)) + WOFileUtils.rm(self, webroot) + return True + Log.debug(self, "{0} does not exist".format(webroot)) + return False + + +def removeNginxConf(self, domain): + if os.path.isfile('/etc/nginx/sites-available/{0}' + .format(domain)): + Log.debug(self, "Removing Nginx configuration") + WOFileUtils.rm(self, '/etc/nginx/sites-enabled/{0}' + .format(domain)) + WOFileUtils.rm(self, '/etc/nginx/sites-available/{0}' + .format(domain)) + WOService.reload_service(self, 'nginx') + WOGit.add(self, ["/etc/nginx"], + msg="Deleted {0} " + .format(domain)) + + +def removeAcmeConf(self, domain): + if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' + .format(domain)): + Log.debug(self, "Removing Acme configuration") + WOFileUtils.rm(self, '/etc/letsencrypt/renewal/{0}_ecc' + .format(domain)) + WOFileUtils.rm(self, '/etc/letsencrypt/live/{0}' + .format(domain)) + WOGit.add(self, ["/etc/letsencrypt"], + msg="Deleted {0} " + .format(domain)) + + +def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', + dbhost=''): + """ + Removes the nginx configuration and database for the domain provided. + doCleanupAction(self, domain='sitename', webroot='', + dbname='', dbuser='', dbhost='') + """ + if domain: + if os.path.isfile('/etc/nginx/sites-available/{0}' + .format(domain)): + removeNginxConf(self, domain) + if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' + .format(domain)): + removeAcmeConf(self, domain) + + if webroot: + deleteWebRoot(self, webroot) + + if dbname: + if not dbuser: + raise SiteError("dbuser not provided") + if not dbhost: + raise SiteError("dbhost not provided") + deleteDB(self, dbname, dbuser, dbhost) + + +# setup letsencrypt for domain + www.domain +def setupLetsEncrypt(self, wo_domain_name): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d www.{0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d www.{0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Cert deployment for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && " + "service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to install certificate", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + +# setup letsencrypt for a subdomain + + +def setupLetsEncryptSubdomain(self, wo_domain_name): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Deploying cert for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to create ssl.conf", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + +# setup letsencrypt for domain + www.domain + + +def setupLetsEncryptWildcard(self, wo_domain_name, dns_cf=True): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns dns_cf " + "-k ec-384 -f" + .format(wo_domain_name)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns dns_cf " + "-k ec-384 -f" + .format(wo_domain_name)) + + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Cert deployment for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && " + "service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to install certificate", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + + +# letsencrypt cert renewal + + +def renewLetsEncrypt(self, wo_domain_name): + + ssl = WOShellExec.cmd_exec( + self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--renew -d {0} --ecc --force" + .format(wo_domain_name)) + + mail_list = '' + if not ssl: + Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!", + False) + if (SSL.getExpirationDays(self, wo_domain_name) > 0): + Log.error(self, "Your current certificate will expire within " + + str(SSL.getExpirationDays(self, wo_domain_name)) + + " days.", False) + else: + Log.error(self, "Your current certificate already expired!", False) + + # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, + # "[FAIL] HTTPS cert renewal {0}".format(wo_domain_name), + # "Hi,\n\nHTTPS certificate renewal for https://{0} + # was unsuccessful.".format(wo_domain_name) + + # "\nPlease check the WordOps log for reason + # The current expiry date is : " + + # str(SSL.getExpirationDate(self, wo_domain_name)) + + # "\n\nFor support visit https://wordops.net/support . + # \n\nBest regards,\nYour WordOps Worker", files=mail_list, + # port=25, isTls=False) + Log.error(self, "Check the WO log for more details " + "`tail /var/log/wo/wordops.log`") + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, + # "[SUCCESS] Let's Encrypt certificate renewal {0}".format(wo_domain_name), + # "Hi,\n\nYour Let's Encrypt certificate has been renewed for + # https://{0} .".format(wo_domain_name) + + # "\nYour new certificate will expire on : " + + # str(SSL.getExpirationDate(self, wo_domain_name)) + + # "\n\nBest regards,\nYour WordOps Worker", files=mail_list, + # port=25, isTls=False) + +# redirect= False to disable https redirection + + +def setupHsts(self, wo_domain_name): + Log.info( + self, "Adding /var/www/{0}/conf/nginx/hsts.conf" + .format(wo_domain_name)) + + hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + hstsconf.write("more_set_headers " + "\"Strict-Transport-Security: " + "max-age=31536000; " + "'includeSubDomains; " + "preload\";") + hstsconf.close() + return 0 + + +def httpsRedirect(self, wo_domain_name, redirect=True, wildcard=False): + if redirect: + if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" + .format(wo_domain_name)): + WOFileUtils.mvfile(self, + "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" + .format(wo_domain_name), + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + else: + if wildcard: + try: + Log.info( + self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("server {\n" + "\tlisten 80;\n" + + "\tlisten [::]:80;\n" + + "\tserver_name *.{0} {0};\n" + .format(wo_domain_name) + + "\treturn 301 https://$host" + "$request_uri;\n}") + sslconf.close() + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + else: + try: + Log.info( + self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + + sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("server {\n" + "\tlisten 80;\n" + + "\tlisten [::]:80;\n" + + "\tserver_name www.{0} {0};\n" + .format(wo_domain_name) + + "\treturn 301 https://{0}" + .format(wo_domain_name)+"$request_uri;\n}") + sslconf.close() + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + + Log.info(self, "Added HTTPS Force Redirection for Site " + " http://{0}".format(wo_domain_name)) + # Nginx Configation into GIT + WOGit.add(self, + ["/etc/nginx"], msg="Adding /etc/nginx/conf.d/" + "force-ssl-{0}.conf".format(wo_domain_name)) + else: + if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)): + WOFileUtils.mvfile(self, "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" + .format(wo_domain_name)) + Log.info(self, "Disabled HTTPS Force Redirection for Site " + " http://{0}".format(wo_domain_name)) + + +def archivedCertificateHandle(self, domain): + Log.warn(self, "You already have an existing certificate " + "for the domain requested.\n" + "(ref: {0}/" + "{1}_ecc/{1}.conf)".format(WOVariables.wo_ssl_archive, domain) + + "\nPlease select an option from below?" + "\n\t1: Reinstall existing certificate" + "\n\t2: Keep the existing certificate for now" + "\n\t3: Renew & replace the certificate (limit ~5 per 7 days)" + "") + check_prompt = input( + "\nType the appropriate number [1-3] or any other key to cancel: ") + if not os.path.isfile("{0}/{1}/fullchain.pem" + .format(WOVariables.wo_ssl_live, domain)): + Log.error( + self, "{0}/{1}/fullchain.pem file is missing." + .format(WOVariables.wo_ssl_live, domain)) + + if check_prompt == "1": + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"service nginx restart\" " + .format(WOVariables.wo_ssl_live, + domain)) + if ssl: + + try: + + if not os.path.isfile("/var/www/{0}/conf/nginx/ssl.conf" + .format(domain)): + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(domain)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(domain), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate " + "{0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, domain)) + sslconf.close() + + updateSiteInfo(self, domain, ssl=True) + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + + elif (check_prompt == "2"): + Log.info(self, "Using Existing Certificate files") + if not os.path.isfile("{0}/{1}/fullchain.pem" + .format(WOVariables.wo_ssl_live, domain)): + Log.error(self, "Certificate files not found. Skipping.\n" + "Please check if following file exist" + "\n\t/etc/letsencrypt/live/{0}/fullchain.pem\n\t" + "/etc/letsencrypt/live/{0}/key.pem".format(domain)) + + updateSiteInfo(self, domain, ssl=True) + + elif (check_prompt == "3"): + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--renew -d {0} --ecc " + "--force" + .format(domain)) + + if ssl: + + try: + + WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "ssl_trusted_certificate " + "{0}/{1}/ca.pem;\n" + "--reloadcmd " + "\"service nginx restart\" " + .format(WOVariables.wo_ssl_live, domain)) + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while installing " + "the certificate") + + else: + Log.error(self, "Operation cancelled by user.") + + if os.path.isfile("{0}/conf/nginx/ssl.conf" + .format(domain)): + Log.info(self, "Existing ssl.conf . Backing it up ..") + WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf" + .format(domain), + '/var/www/{0}/conf/nginx/ssl.conf.bak' + .format(domain)) + + return ssl From d73adafc228318b62a1c9cbc96874bb1b260dcb4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 14 Jul 2019 11:49:21 +0200 Subject: [PATCH 04/44] Implement DNS API with Letsencrypt --- wo/cli/plugins/maintenance.py | 105 +++++++++++++++---------------- wo/cli/plugins/site.py | 9 ++- wo/cli/plugins/site_functions.py | 10 +-- 3 files changed, 64 insertions(+), 60 deletions(-) diff --git a/wo/cli/plugins/maintenance.py b/wo/cli/plugins/maintenance.py index adf945c..cdbbd14 100644 --- a/wo/cli/plugins/maintenance.py +++ b/wo/cli/plugins/maintenance.py @@ -1,53 +1,52 @@ -"""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.shellexec import WOShellExec - - - -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) +"""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.shellexec import WOShellExec + + +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/site.py b/wo/cli/plugins/site.py index afbf507..492a646 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -812,7 +812,6 @@ class WOSiteUpdateController(CementBaseController): (['--dns'], dict(help="choose dns provider api for letsencrypt", action='store' or 'store_const', - choices=('cf', 'do'), const='cf', nargs='?')), (['--hsts'], dict(help="configure hsts for the site", @@ -941,6 +940,11 @@ class WOSiteUpdateController(CementBaseController): except SiteError as e: Log.debug(self, str(e)) Log.info(self, "\nFail to enable HSTS") + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + Log.info(self, "HSTS is enabled for " + "https://{0}".format(wo_domain)) return 0 if ((stype == 'php' and @@ -1276,7 +1280,8 @@ class WOSiteUpdateController(CementBaseController): setupLetsEncryptSubdomain(self, wo_domain) httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": - setupLetsEncryptWildcard(self, wo_domain, dns_cf) + wo_acme_dns = pargs.dns + setupLetsEncryptWildcard(self, wo_domain, wo_acme_dns) httpsRedirect(self, wo_domain, True, True) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index ba6e3ea..95f656d 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1436,7 +1436,7 @@ def setupLetsEncryptSubdomain(self, wo_domain_name): # setup letsencrypt for domain + www.domain -def setupLetsEncryptWildcard(self, wo_domain_name, dns_cf=True): +def setupLetsEncryptWildcard(self, wo_domain_name, wo_acme_dns=dns_cf): if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" .format(wo_domain_name)): @@ -1453,18 +1453,18 @@ def setupLetsEncryptWildcard(self, wo_domain_name, dns_cf=True): "--config-home " "'/etc/letsencrypt/config' " "--issue " - "-d {0} -d *.{0} --dns dns_cf " + "-d {0} -d *.{0} --dns {1} " "-k ec-384 -f" - .format(wo_domain_name)) + .format(wo_domain_name, wo_acme_dns)) else: Log.info(self, "Issuing SSL cert with acme.sh") ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " "--config-home " "'/etc/letsencrypt/config' " "--issue " - "-d {0} -d *.{0} --dns dns_cf " + "-d {0} -d *.{0} --dns {1} " "-k ec-384 -f" - .format(wo_domain_name)) + .format(wo_domain_name, wo_acme_dns)) if ssl: From e216126900dac372fc1f5f506420ad7b09ceea37 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 14 Jul 2019 13:37:46 +0200 Subject: [PATCH 05/44] Fix dns variable --- wo/cli/plugins/site.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 492a646..8d1f69f 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1210,11 +1210,6 @@ class WOSiteUpdateController(CementBaseController): data['basic'] = False cache = 'wpredis' - if pargs.dns == "do": - dns_cf = False - else: - dns_cf = True - if (php73 is old_php73) and (stype == oldsitetype and cache == oldcachetype): return 1 From 2eb05e7b829071e30eb3e4989f0ae3df65573a90 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 14 Jul 2019 13:53:47 +0200 Subject: [PATCH 06/44] Fix dns_cf variable --- wo/cli/plugins/site_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 95f656d..f89b5ac 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1436,7 +1436,7 @@ def setupLetsEncryptSubdomain(self, wo_domain_name): # setup letsencrypt for domain + www.domain -def setupLetsEncryptWildcard(self, wo_domain_name, wo_acme_dns=dns_cf): +def setupLetsEncryptWildcard(self, wo_domain_name, wo_acme_dns='dns_cf'): if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" .format(wo_domain_name)): From 37bd353fa4e4bc76b13d2682ed99585938eaed31 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 14 Jul 2019 22:50:34 +0200 Subject: [PATCH 07/44] Add upgrade script for phpmyadmin --- config/bash_completion.d/wo_auto.rc | 4 +- docs/wo.8 | 628 ++--- wo/cli/plugins/info.py | 586 ++--- wo/cli/plugins/maintenance.py | 104 +- wo/cli/plugins/site_functions.py | 3560 +++++++++++++-------------- wo/cli/plugins/stack.py | 3 +- wo/cli/plugins/stack_upgrade.py | 36 +- wo/core/variables.py | 373 +-- 8 files changed, 2665 insertions(+), 2629 deletions(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index ede18c6..c8f4d45 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -79,7 +79,7 @@ _wo_complete() ;; "upgrade" ) COMPREPLY=( $(compgen \ - -W "--web --nginx --php --php73 --mysql --all --netdata --no-prompt --wpcli" \ + -W "--web --nginx --php --php73 --mysql --all --netdata --composer --phpmyadmin --no-prompt --wpcli" \ -- $cur) ) ;; "start" | "stop" | "reload" | "restart" | "status") @@ -310,7 +310,7 @@ _wo_complete() "--all") if [ ${COMP_WORDS[1]} == "clean" ]; then - retlist="--memcache --opcache --fastcgi --redis" + retlist="--opcache --fastcgi --redis" elif [ ${COMP_WORDS[2]} == "delete" ]; then retlist="--db --files --force" elif [ ${COMP_WORDS[2]} == "update" ]; then diff --git a/docs/wo.8 b/docs/wo.8 index f0d2891..6c168b2 100644 --- a/docs/wo.8 +++ b/docs/wo.8 @@ -1,314 +1,314 @@ -.TH wo 8 "WordOps (wo) version: 3.3.8" "Sep 10,2015" "WordOps" -.SH NAME -.B WordOps (wo) -\- Manage Nginx Based Websites. -.SH SYNOPSIS -wo [ --version | --help | info | stack | site | debug | update | clean | import_slow_log | log | secure | sync | maintenance] -.TP -wo stack [ install | remove | purge | migrate | upgrade] [ --web | --all | --nginx | --php | --php73 | --mysql | --admin | --adminer | --redis | --phpmyadmin | --phpredisadmin | --wpcli | --utils | --dashboard | --netdata ] -.TP -wo stack [ status | start | stop | reload | restart ] [--all | --nginx | --php | --php73 |--mysql | --web | --memcached | --redis] -.TP -wo site [ list | info | show | enable | disable | edit | cd | show ] [ example.com ] -.TP -wo site create example.com [ --html | --php | --php73 | --mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis | --letsencrypt/-le/--letsencrypt=subdomain]] -.TP -wo site update example.com [ --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ] [--password] [--letsencrypt=on/off/subdomain/renew]] -.TP -wo site delete example.com [--db | --files | --all | --no-prompt | --force/-f ] -.TP -wo debug [ -i | --all=on/off |--nginx=on/off | --rewrite=on/off | --php=on/off | --fpm=on/off | --mysql=on/off ] -.TP -wo debug example.com [ -i | --all=on/off | --nginx=on/off | --rewrite=on/off | --wp=on/off ] -.TP -wo secure [ --auth | --port | --ip ] -.SH DESCRIPTION -WordOps aka wo is the opensource project developed with the purpose to automate web-server configuration. -.br -WordOps is the collection of python script that provides automation for the web-server -.br -installation, site creation, services debugging & monitoring. -.SH OPTIONS -.TP -.B --version -.br -Display WordOps (wo) version information. -.TP -.B info -.br -wo info - Display Nginx, PHP, MySQL and wo common location information -.br -wo site info - Diplay given website details like enable, disable. weboot and log files. -.TP -.B --help -.br -Display WordOps (wo) help. -.TP -.B stack -.TP -.B install [ --all | --web | --nginx | --php | --php73 |--mysql | --redis | --adminer | --phpmyadmin | --phpredismyadmin | --wpcli | --utils ] -.br -Install Nginx PHP5 MySQL Postfix stack Packages if not used with -.br -any options.Installs specific package if used with option. -.TP -.B remove [ --all | --web | --nginx | --php | --php73 |--mysql | --redis | --adminer | --phpmyadmin | --phpredismyadmin | --wpcli | --utils ] -.br -Remove Nginx PHP5 MySQL Postfix stack Packages if not used with -.br -any options. Remove specific package if used with option. -.TP -.B purge [ --all | --web | --nginx | --php | --php73 |--mysql | --redis | --adminer | --phpmyadmin | --phpredismyadmin | --wpcli | --utils ] -.br -Purge Nginx PHP5 MySQL Postfix stack Packages if not used with any -.br -options.Purge specific package if used with option. -.TP -.B status -.br -Display status of NGINX, PHP7.2-FPM, MySQL, Redis-Server services. -.TP -.B start -.br -Start services NGINX, PHP7.2-FPM, MySQL, Redis-Server. -.TP -.B stop -.br -Stop services NGINX, PHP7.2-FPM, MySQL, Redis-Server. -.TP -.B reload -.br -Reload services NGINX, PHP7.2-FPM, MySQL, Redis-Server. -.TP -.B restart -.br -Restart services NGINX, PHP7.2-FPM, MySQL, Redis-Server. -.TP -.B site -.br -.TP -.B cd [ example.com ] -.br -Change directory to webroot of specified site in subshell. -.TP -.B log [ example.com ] -.br -monitor access and error logs for site specified. -.TP -.B list [ --enabled | --disabled ] -.br -Lists all available sites from /etc/nginx/sites-enabled/ -.br -by default & enable argument. Display sites list from -.br -/etc/nginx/sites-available/ if used with available option. -.TP -.B info [ example.com ] -.br -prints information about site such as access log, error log -.br -location and type of site. -.TP -.B show [ example.com ] -.br -Display NGINX configuration of site. -.TP -.B enable [ example.com ] -.br -Enable site by creating softlink with site file in -.br -/etc/nginx/sites-available to /etc/nginx/sites-enabled/. -.TP -.B disable [ example.com ] -.br -Disable site by Destroying softlink with site file in -.br -/etc/nginx/sites-available to /etc/nginx/sites-enabled/. -.TP -.B edit [ example.com ] -.br -Edit NGINX configuration of site. -.TP -.B create [ example.com ] [ --html | --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ]] -.br -Create new site according to given options. If no options provided -.br -create static site with html only. -.TP -.B update [ example.com ] [ --html | --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [ --wpsc | --wpfc | --wpredis ] [--password]] -.br -Update site configuration according to specified options. -.TP -.B delete [ example.com ] [--no-prompt ] [--force/-f] [ --db | --files | --all ] -.br -Delete site i.e webroot, database, ad configuration permanently. -.TP -.B debug [ -i | --nginx=on/off | --php=on/off | --php73=on/off | --mysql=on/off | --rewrite=on/off | --fpm=on/off | --fpm7=on/off ] -.br -Starts server level debugging. If this is used without arguments it will start debugging -.br -all services.Else it will debug only service provided with argument.This will Stop -.br -Debugging if used with --all=off argument. -.TP -.B debug example.com [ -i | --nginx=on/off | --rewrite=on/off | --wp=on/off | --all=on/off ] -.br -Starts site level debugging. If this is used without arguments it will start debugging all -.br -services.Else it will debug only service provided with argument.This will Stop Debugging -.br -if used with --all=off argument. -.TP -.B secure [ --auth | --port | --ip ] -.br -Update security settings. -.TP -.B clean [ --fastcgi | --opcache | --memcached | --redis | --all ] -.br -Clean NGINX fastCGI cache, Opcache, memcached, Redis cache. -.br -Clean NGINX fastCGI cache if no option specified. -.SH ARGUMENTS -.TP -.B -i -.br -setup intractive mode while used with debug. -.TP -.B --nginx=on/off -.br -used with wo debug command. used to start or stop nginx debugging. -.TP -.B --php=on/off -.br -used with wo debug command. used to start or stop php debugging. -.TP -.B --php73=on/off -.br -used with wo debug command. used to start or stop php72 debugging. -.TP -.B --mysql=on/off -.br -used with wo debug command. used to start or stop mysql debugging. -.TP -.B --rewrite=on/off -.br -used with wo debug command. used to start or stop nginx rewrite rules debugging. -.TP -.B --fpm=on/off -.br -used with wo debug command. used to start or stop fpm debugging. -.TP -.B --wp=on/off -.br -used with wo debug command. used to start or stop wordpress site debugging. -.TP -.B --all=on/off -.br -used with wo debug command. used to stop debugging. -.TP -.B --all=off -.br -used with wo debug command. used to stop debugging. -.TP -.B --html -.br -Create a HTML website. -.TP -.B --php -.br -Create a PHP website. -.TP -.B --mysql -.br -Create a PHP+MySQL website. -.TP -.B --wp -.br -Create a WordPress Website. -.TP -.B --wpsubdir -.br -Create a Wordpress Multisite with Sub Directories Setup. -.TP -.B --wpsubdomain -.br -Create a Wordpress Multisite with Sub Domains Setup. -.br -.TP -.B --db -.br -Delete website database. -.br -.TP -.B --files -.br -Delete website webroot. -.br -.TP -.B --no-prompt -.br -Does not prompt for confirmation when delete command used. -.br -.TP -.B --force/-f -.br -Delete website webroot and database forcefully.Remove nginx configuration for site. -.br -.TP -.B --auth -.br -used with wo secure command. Update credential of HTTP authentication -.TP -.B --port -.br -used with wo secure command. Change WordOps admin port 22222. -.TP -.B --ip -.br -used with wo secure command. Update whitelist IP address -.SH WORDPRESS CACHING OPTIONS -.TP -.B --wpsc -.br -Install and activate Nginx-helper and WP Super Cache plugin. -.TP -.B --wpfc -.br -Install and activate Nginx-helper plugin with -.br -Nginx FastCGI cache. -.TP -.B --wpredis -.br -Install, activate, configure Nginx-helper and Redis Object Cache Plugin, Configure NGINX for Redis Page Caching. -.SH FILES -.br -/etc/wo/wo.conf -.SH BUGS -Report bugs at -.SH AUTHOR -.br -.B rtCamp Team -.I \ -.br -.B Mitesh Shah -.I \ -.br -.B Manish -.I \ -.br -.B Gaurav -.I \ -.br -.B Harshad -.I \ -.br -.B Prabuddha -.I \ -.br -.B Shital -.I \ -.br -.B Rajdeep Sharma -.I \ -.br +.TH wo 8 "WordOps (wo) version: 3.3.8" "Sep 10,2015" "WordOps" +.SH NAME +.B WordOps (wo) +\- Manage Nginx Based Websites. +.SH SYNOPSIS +wo [ --version | --help | info | stack | site | debug | update | clean | import_slow_log | log | secure | sync | maintenance ] +.TP +wo stack [ install | remove | purge | migrate | upgrade] [ --web | --all | --nginx | --php | --php73 | --mysql | --admin | --adminer | --redis | --phpmyadmin | --phpredisadmin | --wpcli | --utils | --dashboard | --netdata ] +.TP +wo stack [ status | start | stop | reload | restart ] [--all | --nginx | --php | --php73 |--mysql | --web | --redis] +.TP +wo site [ list | info | show | enable | disable | edit | cd | show ] [ example.com ] +.TP +wo site create example.com [ --html | --php | --php73 | --mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis | --letsencrypt/--le/--letsencrypt=subdomain/wildcard]] +.TP +wo site update example.com [ --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ] [--password] [--le/--letsencrypt=on/off/subdomain/renew]] +.TP +wo site delete example.com [--db | --files | --all | --no-prompt | --force/-f ] +.TP +wo debug [ -i | --all=on/off |--nginx=on/off | --rewrite=on/off | --php=on/off | --fpm=on/off | --mysql=on/off ] +.TP +wo debug example.com [ -i | --all=on/off | --nginx=on/off | --rewrite=on/off | --wp=on/off ] +.TP +wo secure [ --auth | --port | --ip ] +.SH DESCRIPTION +WordOps aka wo is the opensource project developed with the purpose to automate web-server configuration. +.br +WordOps is the collection of python script that provides automation for the web-server +.br +installation, site creation, services debugging & monitoring. +.SH OPTIONS +.TP +.B --version +.br +Display WordOps (wo) version information. +.TP +.B info +.br +wo info - Display Nginx, PHP, MySQL and wo common location information +.br +wo site info - Diplay given website details like enable, disable. weboot and log files. +.TP +.B --help +.br +Display WordOps (wo) help. +.TP +.B stack +.TP +.B install [ --all | --web | --nginx | --php | --php73 |--mysql | --redis | --adminer | --phpmyadmin | --phpredismyadmin | --wpcli | --utils | --netdata | --dashboard ] +.br +Install Nginx PHP5 MySQL Postfix stack Packages if not used with +.br +any options.Installs specific package if used with option. +.TP +.B remove [ --all | --web | --nginx | --php | --php73 |--mysql | --redis | --adminer | --phpmyadmin | --phpredismyadmin | --wpcli | --utils | --netdata | --dashboard ] +.br +Remove Nginx PHP5 MySQL Postfix stack Packages if not used with +.br +any options. Remove specific package if used with option. +.TP +.B purge [ --all | --web | --nginx | --php | --php73 |--mysql | --redis | --adminer | --phpmyadmin | --phpredismyadmin | --wpcli | --utils | --netdata | --dashboard ] +.br +Purge Nginx PHP5 MySQL Postfix stack Packages if not used with any +.br +options.Purge specific package if used with option. +.TP +.B status +.br +Display status of NGINX, PHP7.2-FPM, MySQL, Redis-Server services. +.TP +.B start +.br +Start services NGINX, PHP7.2-FPM, MySQL, Redis-Server. +.TP +.B stop +.br +Stop services NGINX, PHP7.2-FPM, MySQL, Redis-Server. +.TP +.B reload +.br +Reload services NGINX, PHP7.2-FPM, MySQL, Redis-Server. +.TP +.B restart +.br +Restart services NGINX, PHP7.2-FPM, MySQL, Redis-Server. +.TP +.B site +.br +.TP +.B cd [ example.com ] +.br +Change directory to webroot of specified site in subshell. +.TP +.B log [ example.com ] +.br +monitor access and error logs for site specified. +.TP +.B list [ --enabled | --disabled ] +.br +Lists all available sites from /etc/nginx/sites-enabled/ +.br +by default & enable argument. Display sites list from +.br +/etc/nginx/sites-available/ if used with available option. +.TP +.B info [ example.com ] +.br +prints information about site such as access log, error log +.br +location and type of site. +.TP +.B show [ example.com ] +.br +Display NGINX configuration of site. +.TP +.B enable [ example.com ] +.br +Enable site by creating softlink with site file in +.br +/etc/nginx/sites-available to /etc/nginx/sites-enabled/. +.TP +.B disable [ example.com ] +.br +Disable site by Destroying softlink with site file in +.br +/etc/nginx/sites-available to /etc/nginx/sites-enabled/. +.TP +.B edit [ example.com ] +.br +Edit NGINX configuration of site. +.TP +.B create [ example.com ] [ --html | --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ]] +.br +Create new site according to given options. If no options provided +.br +create static site with html only. +.TP +.B update [ example.com ] [ --html | --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [ --wpsc | --wpfc | --wpredis ] [--password]] +.br +Update site configuration according to specified options. +.TP +.B delete [ example.com ] [--no-prompt ] [--force/-f] [ --db | --files | --all ] +.br +Delete site i.e webroot, database, ad configuration permanently. +.TP +.B debug [ -i | --nginx=on/off | --php=on/off | --php73=on/off | --mysql=on/off | --rewrite=on/off | --fpm=on/off | --fpm7=on/off ] +.br +Starts server level debugging. If this is used without arguments it will start debugging +.br +all services.Else it will debug only service provided with argument.This will Stop +.br +Debugging if used with --all=off argument. +.TP +.B debug example.com [ -i | --nginx=on/off | --rewrite=on/off | --wp=on/off | --all=on/off ] +.br +Starts site level debugging. If this is used without arguments it will start debugging all +.br +services.Else it will debug only service provided with argument.This will Stop Debugging +.br +if used with --all=off argument. +.TP +.B secure [ --auth | --port | --ip ] +.br +Update security settings. +.TP +.B clean [ --fastcgi | --opcache | --redis | --all ] +.br +Clean NGINX fastCGI cache, Opcache, memcached, Redis cache. +.br +Clean NGINX fastCGI cache if no option specified. +.SH ARGUMENTS +.TP +.B -i +.br +setup intractive mode while used with debug. +.TP +.B --nginx=on/off +.br +used with wo debug command. used to start or stop nginx debugging. +.TP +.B --php=on/off +.br +used with wo debug command. used to start or stop php debugging. +.TP +.B --php73=on/off +.br +used with wo debug command. used to start or stop php72 debugging. +.TP +.B --mysql=on/off +.br +used with wo debug command. used to start or stop mysql debugging. +.TP +.B --rewrite=on/off +.br +used with wo debug command. used to start or stop nginx rewrite rules debugging. +.TP +.B --fpm=on/off +.br +used with wo debug command. used to start or stop fpm debugging. +.TP +.B --wp=on/off +.br +used with wo debug command. used to start or stop wordpress site debugging. +.TP +.B --all=on/off +.br +used with wo debug command. used to stop debugging. +.TP +.B --all=off +.br +used with wo debug command. used to stop debugging. +.TP +.B --html +.br +Create a HTML website. +.TP +.B --php +.br +Create a PHP website. +.TP +.B --mysql +.br +Create a PHP+MySQL website. +.TP +.B --wp +.br +Create a WordPress Website. +.TP +.B --wpsubdir +.br +Create a Wordpress Multisite with Sub Directories Setup. +.TP +.B --wpsubdomain +.br +Create a Wordpress Multisite with Sub Domains Setup. +.br +.TP +.B --db +.br +Delete website database. +.br +.TP +.B --files +.br +Delete website webroot. +.br +.TP +.B --no-prompt +.br +Does not prompt for confirmation when delete command used. +.br +.TP +.B --force/-f +.br +Delete website webroot and database forcefully.Remove nginx configuration for site. +.br +.TP +.B --auth +.br +used with wo secure command. Update credential of HTTP authentication +.TP +.B --port +.br +used with wo secure command. Change WordOps admin port 22222. +.TP +.B --ip +.br +used with wo secure command. Update whitelist IP address +.SH WORDPRESS CACHING OPTIONS +.TP +.B --wpsc +.br +Install and activate Nginx-helper and WP Super Cache plugin. +.TP +.B --wpfc +.br +Install and activate Nginx-helper plugin with +.br +Nginx FastCGI cache. +.TP +.B --wpredis +.br +Install, activate, configure Nginx-helper and Redis Object Cache Plugin, Configure NGINX for Redis Page Caching. +.SH FILES +.br +/etc/wo/wo.conf +.SH BUGS +Report bugs at +.SH AUTHOR +.br +.B rtCamp Team +.I \ +.br +.B Mitesh Shah +.I \ +.br +.B Manish +.I \ +.br +.B Gaurav +.I \ +.br +.B Harshad +.I \ +.br +.B Prabuddha +.I \ +.br +.B Shital +.I \ +.br +.B Rajdeep Sharma +.I \ +.br diff --git a/wo/cli/plugins/info.py b/wo/cli/plugins/info.py index 9e071d7..f478ccb 100644 --- a/wo/cli/plugins/info.py +++ b/wo/cli/plugins/info.py @@ -1,293 +1,293 @@ -"""WOInfo Plugin for WordOps""" - -from cement.core.controller import CementBaseController, expose -from cement.core import handler, hook -from wo.core.variables import WOVariables -from pynginxconfig import NginxConfig -from wo.core.aptget import WOAptGet -from wo.core.shellexec import WOShellExec -from wo.core.logging import Log -import os -import configparser - - -def wo_info_hook(app): - pass - - -class WOInfoController(CementBaseController): - class Meta: - label = 'info' - stacked_on = 'base' - stacked_type = 'nested' - description = ('Display configuration information related to Nginx,' - ' PHP and MySQL') - arguments = [ - (['--mysql'], - dict(help='Get MySQL configuration information', - action='store_true')), - (['--php'], - dict(help='Get PHP 7.2 configuration information', - action='store_true')), - (['--php73'], - dict(help='Get PHP 7.3 configuration information', - action='store_true')), - (['--nginx'], - dict(help='Get Nginx configuration information', - action='store_true')), - ] - usage = "wo info [options]" - - @expose(hide=True) - def info_nginx(self): - """Display Nginx information""" - version = os.popen("/usr/sbin/nginx -v 2>&1 | " - "awk -F '/' '{print $2}' | " - "awk -F ' ' '{print $1}' | tr '\n' ' '").read() - allow = os.popen("grep ^allow /etc/nginx/common/acl.conf | " - "cut -d' ' -f2 | cut -d';' -f1 | tr '\n' ' '").read() - nc = NginxConfig() - nc.loadf('/etc/nginx/nginx.conf') - user = nc.get('user')[1] - worker_processes = nc.get('worker_processes')[1] - worker_connections = nc.get([('events',), 'worker_connections'])[1] - keepalive_timeout = nc.get([('http',), 'keepalive_timeout'])[1] - fastcgi_read_timeout = nc.get([('http',), - 'fastcgi_read_timeout'])[1] - client_max_body_size = nc.get([('http',), - 'client_max_body_size'])[1] - data = dict(version=version, allow=allow, user=user, - worker_processes=worker_processes, - keepalive_timeout=keepalive_timeout, - worker_connections=worker_connections, - fastcgi_read_timeout=fastcgi_read_timeout, - client_max_body_size=client_max_body_size) - self.app.render((data), 'info_nginx.mustache') - - @expose(hide=True) - def info_php(self): - """Display PHP information""" - version = os.popen("/usr/bin/php7.2 -v 2>/dev/null | " - "head -n1 | cut -d' ' -f2 |" - " cut -d'+' -f1 | tr -d '\n'").read - config = configparser.ConfigParser() - config.read('/etc/{0}/fpm/php.ini'.format("php/7.2")) - expose_php = config['PHP']['expose_php'] - memory_limit = config['PHP']['memory_limit'] - post_max_size = config['PHP']['post_max_size'] - upload_max_filesize = config['PHP']['upload_max_filesize'] - max_execution_time = config['PHP']['max_execution_time'] - - config.read('/etc/{0}/fpm/pool.d/www.conf'.format("php/7.2")) - www_listen = config['www']['listen'] - www_ping_path = config['www']['ping.path'] - www_pm_status_path = config['www']['pm.status_path'] - www_pm = config['www']['pm'] - www_pm_max_requests = config['www']['pm.max_requests'] - www_pm_max_children = config['www']['pm.max_children'] - www_pm_start_servers = config['www']['pm.start_servers'] - www_pm_min_spare_servers = config['www']['pm.min_spare_servers'] - www_pm_max_spare_servers = config['www']['pm.max_spare_servers'] - www_request_terminate_time = (config['www'] - ['request_terminate_timeout']) - try: - www_xdebug = (config['www']['php_admin_flag[xdebug.profiler_enable' - '_trigger]']) - except Exception as e: - www_xdebug = 'off' - - config.read('/etc/{0}/fpm/pool.d/debug.conf'.format("php/7.2")) - debug_listen = config['debug']['listen'] - debug_ping_path = config['debug']['ping.path'] - debug_pm_status_path = config['debug']['pm.status_path'] - debug_pm = config['debug']['pm'] - debug_pm_max_requests = config['debug']['pm.max_requests'] - debug_pm_max_children = config['debug']['pm.max_children'] - debug_pm_start_servers = config['debug']['pm.start_servers'] - debug_pm_min_spare_servers = config['debug']['pm.min_spare_servers'] - debug_pm_max_spare_servers = config['debug']['pm.max_spare_servers'] - debug_request_terminate = (config['debug'] - ['request_terminate_timeout']) - try: - debug_xdebug = (config['debug']['php_admin_flag[xdebug.profiler_' - 'enable_trigger]']) - except Exception as e: - debug_xdebug = 'off' - - data = dict(version=version, expose_php=expose_php, - memory_limit=memory_limit, post_max_size=post_max_size, - upload_max_filesize=upload_max_filesize, - max_execution_time=max_execution_time, - www_listen=www_listen, www_ping_path=www_ping_path, - www_pm_status_path=www_pm_status_path, www_pm=www_pm, - www_pm_max_requests=www_pm_max_requests, - www_pm_max_children=www_pm_max_children, - www_pm_start_servers=www_pm_start_servers, - www_pm_min_spare_servers=www_pm_min_spare_servers, - www_pm_max_spare_servers=www_pm_max_spare_servers, - www_request_terminate_timeout=www_request_terminate_time, - www_xdebug_profiler_enable_trigger=www_xdebug, - debug_listen=debug_listen, debug_ping_path=debug_ping_path, - debug_pm_status_path=debug_pm_status_path, - debug_pm=debug_pm, - debug_pm_max_requests=debug_pm_max_requests, - debug_pm_max_children=debug_pm_max_children, - debug_pm_start_servers=debug_pm_start_servers, - debug_pm_min_spare_servers=debug_pm_min_spare_servers, - debug_pm_max_spare_servers=debug_pm_max_spare_servers, - debug_request_terminate_timeout=debug_request_terminate, - debug_xdebug_profiler_enable_trigger=debug_xdebug) - self.app.render((data), 'info_php.mustache') - - @expose(hide=True) - def info_php73(self): - """Display PHP information""" - version = os.popen("/usr/bin/php7.3 -v 2>/dev/null | " - "head -n1 | cut -d' ' -f2 |" - " cut -d'+' -f1 | tr -d '\n'").read - config = configparser.ConfigParser() - config.read('/etc/php/7.3/fpm/php.ini') - expose_php = config['PHP']['expose_php'] - memory_limit = config['PHP']['memory_limit'] - post_max_size = config['PHP']['post_max_size'] - upload_max_filesize = config['PHP']['upload_max_filesize'] - max_execution_time = config['PHP']['max_execution_time'] - - config.read('/etc/php/7.3/fpm/pool.d/www.conf') - www_listen = config['www']['listen'] - www_ping_path = config['www']['ping.path'] - www_pm_status_path = config['www']['pm.status_path'] - www_pm = config['www']['pm'] - www_pm_max_requests = config['www']['pm.max_requests'] - www_pm_max_children = config['www']['pm.max_children'] - www_pm_start_servers = config['www']['pm.start_servers'] - www_pm_min_spare_servers = config['www']['pm.min_spare_servers'] - www_pm_max_spare_servers = config['www']['pm.max_spare_servers'] - www_request_terminate_time = (config['www'] - ['request_terminate_timeout']) - try: - www_xdebug = (config['www']['php_admin_flag[xdebug.profiler_enable' - '_trigger]']) - except Exception as e: - www_xdebug = 'off' - - config.read('/etc/php/7.3/fpm/pool.d/debug.conf') - debug_listen = config['debug']['listen'] - debug_ping_path = config['debug']['ping.path'] - debug_pm_status_path = config['debug']['pm.status_path'] - debug_pm = config['debug']['pm'] - debug_pm_max_requests = config['debug']['pm.max_requests'] - debug_pm_max_children = config['debug']['pm.max_children'] - debug_pm_start_servers = config['debug']['pm.start_servers'] - debug_pm_min_spare_servers = config['debug']['pm.min_spare_servers'] - debug_pm_max_spare_servers = config['debug']['pm.max_spare_servers'] - debug_request_terminate = (config['debug'] - ['request_terminate_timeout']) - try: - debug_xdebug = (config['debug']['php_admin_flag[xdebug.profiler_' - 'enable_trigger]']) - except Exception as e: - debug_xdebug = 'off' - - data = dict(version=version, expose_php=expose_php, - memory_limit=memory_limit, post_max_size=post_max_size, - upload_max_filesize=upload_max_filesize, - max_execution_time=max_execution_time, - www_listen=www_listen, www_ping_path=www_ping_path, - www_pm_status_path=www_pm_status_path, www_pm=www_pm, - www_pm_max_requests=www_pm_max_requests, - www_pm_max_children=www_pm_max_children, - www_pm_start_servers=www_pm_start_servers, - www_pm_min_spare_servers=www_pm_min_spare_servers, - www_pm_max_spare_servers=www_pm_max_spare_servers, - www_request_terminate_timeout=www_request_terminate_time, - www_xdebug_profiler_enable_trigger=www_xdebug, - debug_listen=debug_listen, debug_ping_path=debug_ping_path, - debug_pm_status_path=debug_pm_status_path, - debug_pm=debug_pm, - debug_pm_max_requests=debug_pm_max_requests, - debug_pm_max_children=debug_pm_max_children, - debug_pm_start_servers=debug_pm_start_servers, - debug_pm_min_spare_servers=debug_pm_min_spare_servers, - debug_pm_max_spare_servers=debug_pm_max_spare_servers, - debug_request_terminate_timeout=debug_request_terminate, - debug_xdebug_profiler_enable_trigger=debug_xdebug) - self.app.render((data), 'info_php.mustache') - - @expose(hide=True) - def info_mysql(self): - """Display MySQL information""" - version = os.popen("/usr/bin/mysql -V | awk '{print($5)}' | " - "cut -d ',' " - "-f1 | tr -d '\n'").read() - host = "localhost" - port = os.popen("/usr/bin/mysql -e \"show variables\" | " - "grep ^port | awk " - "'{print($2)}' | tr -d '\n'").read() - wait_timeout = os.popen("/usr/bin/mysql -e \"show variables\" | grep " - "^wait_timeout | awk '{print($2)}' | " - "tr -d '\n'").read() - interactive_timeout = os.popen("/usr/bin/mysql -e " - "\"show variables\" | grep " - "^interactive_timeout | awk " - "'{print($2)}' | tr -d '\n'").read() - max_used_connections = os.popen("/usr/bin/mysql -e " - "\"show global status\" | " - "grep Max_used_connections | awk " - "'{print($2)}' | tr -d '\n'").read() - datadir = os.popen("/usr/bin/mysql -e \"show variables\" | " - "grep datadir | awk" - " '{print($2)}' | tr -d '\n'").read() - socket = os.popen("/usr/bin/mysql -e \"show variables\" | " - "grep \"^socket\" | " - "awk '{print($2)}' | tr -d '\n'").read() - data = dict(version=version, host=host, port=port, - wait_timeout=wait_timeout, - interactive_timeout=interactive_timeout, - max_used_connections=max_used_connections, - datadir=datadir, socket=socket) - self.app.render((data), 'info_mysql.mustache') - - @expose(hide=True) - def default(self): - """default function for info""" - if (not self.app.pargs.nginx and not self.app.pargs.php and - not self.app.pargs.mysql and not self.app.pargs.php73): - self.app.pargs.nginx = True - self.app.pargs.php = True - self.app.pargs.mysql = True - if WOAptGet.is_installed(self, 'php7.3-fpm'): - self.app.pargs.php73 = True - - if self.app.pargs.nginx: - if (WOAptGet.is_installed(self, 'nginx-custom') or - WOAptGet.is_installed(self, 'nginx-wo')): - self.info_nginx() - else: - Log.error(self, "Nginx is not installed") - - if self.app.pargs.php: - if WOAptGet.is_installed(self, 'php7.2-fpm'): - self.info_php() - else: - Log.error(self, "PHP 7.2 is not installed") - - if self.app.pargs.php73: - if WOAptGet.is_installed(self, 'php7.3-fpm'): - self.info_php73() - else: - Log.error(self, "PHP 7.3 is not installed") - - if self.app.pargs.mysql: - if WOShellExec.cmd_exec(self, "mysqladmin ping"): - self.info_mysql() - else: - Log.error(self, "MySQL is not installed") - - -def load(app): - # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOInfoController) - - # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_info_hook) +"""WOInfo Plugin for WordOps""" + +from cement.core.controller import CementBaseController, expose +from cement.core import handler, hook +from wo.core.variables import WOVariables +from pynginxconfig import NginxConfig +from wo.core.aptget import WOAptGet +from wo.core.shellexec import WOShellExec +from wo.core.logging import Log +import os +import configparser + + +def wo_info_hook(app): + pass + + +class WOInfoController(CementBaseController): + class Meta: + label = 'info' + stacked_on = 'base' + stacked_type = 'nested' + description = ('Display configuration information related to Nginx,' + ' PHP and MySQL') + arguments = [ + (['--mysql'], + dict(help='Get MySQL configuration information', + action='store_true')), + (['--php'], + dict(help='Get PHP 7.2 configuration information', + action='store_true')), + (['--php73'], + dict(help='Get PHP 7.3 configuration information', + action='store_true')), + (['--nginx'], + dict(help='Get Nginx configuration information', + action='store_true')), + ] + usage = "wo info [options]" + + @expose(hide=True) + def info_nginx(self): + """Display Nginx information""" + version = os.popen("/usr/sbin/nginx -v 2>&1 | " + "awk -F '/' '{print $2}' | " + "awk -F ' ' '{print $1}' | tr '\n' ' '").read() + allow = os.popen("grep ^allow /etc/nginx/common/acl.conf | " + "cut -d' ' -f2 | cut -d';' -f1 | tr '\n' ' '").read() + nc = NginxConfig() + nc.loadf('/etc/nginx/nginx.conf') + user = nc.get('user')[1] + worker_processes = nc.get('worker_processes')[1] + worker_connections = nc.get([('events',), 'worker_connections'])[1] + keepalive_timeout = nc.get([('http',), 'keepalive_timeout'])[1] + fastcgi_read_timeout = nc.get([('http',), + 'fastcgi_read_timeout'])[1] + client_max_body_size = nc.get([('http',), + 'client_max_body_size'])[1] + data = dict(version=version, allow=allow, user=user, + worker_processes=worker_processes, + keepalive_timeout=keepalive_timeout, + worker_connections=worker_connections, + fastcgi_read_timeout=fastcgi_read_timeout, + client_max_body_size=client_max_body_size) + self.app.render((data), 'info_nginx.mustache') + + @expose(hide=True) + def info_php(self): + """Display PHP information""" + version = os.popen("/usr/bin/php7.2 -v 2>/dev/null | " + "head -n1 | cut -d' ' -f2 |" + " cut -d'+' -f1 | tr -d '\n'").read + config = configparser.ConfigParser() + config.read('/etc/{0}/fpm/php.ini'.format("php/7.2")) + expose_php = config['PHP']['expose_php'] + memory_limit = config['PHP']['memory_limit'] + post_max_size = config['PHP']['post_max_size'] + upload_max_filesize = config['PHP']['upload_max_filesize'] + max_execution_time = config['PHP']['max_execution_time'] + + config.read('/etc/{0}/fpm/pool.d/www.conf'.format("php/7.2")) + www_listen = config['www']['listen'] + www_ping_path = config['www']['ping.path'] + www_pm_status_path = config['www']['pm.status_path'] + www_pm = config['www']['pm'] + www_pm_max_requests = config['www']['pm.max_requests'] + www_pm_max_children = config['www']['pm.max_children'] + www_pm_start_servers = config['www']['pm.start_servers'] + www_pm_min_spare_servers = config['www']['pm.min_spare_servers'] + www_pm_max_spare_servers = config['www']['pm.max_spare_servers'] + www_request_terminate_time = (config['www'] + ['request_terminate_timeout']) + try: + www_xdebug = (config['www']['php_admin_flag[xdebug.profiler_enable' + '_trigger]']) + except Exception as e: + www_xdebug = 'off' + + config.read('/etc/{0}/fpm/pool.d/debug.conf'.format("php/7.2")) + debug_listen = config['debug']['listen'] + debug_ping_path = config['debug']['ping.path'] + debug_pm_status_path = config['debug']['pm.status_path'] + debug_pm = config['debug']['pm'] + debug_pm_max_requests = config['debug']['pm.max_requests'] + debug_pm_max_children = config['debug']['pm.max_children'] + debug_pm_start_servers = config['debug']['pm.start_servers'] + debug_pm_min_spare_servers = config['debug']['pm.min_spare_servers'] + debug_pm_max_spare_servers = config['debug']['pm.max_spare_servers'] + debug_request_terminate = (config['debug'] + ['request_terminate_timeout']) + try: + debug_xdebug = (config['debug']['php_admin_flag[xdebug.profiler_' + 'enable_trigger]']) + except Exception as e: + debug_xdebug = 'off' + + data = dict(version=version, expose_php=expose_php, + memory_limit=memory_limit, post_max_size=post_max_size, + upload_max_filesize=upload_max_filesize, + max_execution_time=max_execution_time, + www_listen=www_listen, www_ping_path=www_ping_path, + www_pm_status_path=www_pm_status_path, www_pm=www_pm, + www_pm_max_requests=www_pm_max_requests, + www_pm_max_children=www_pm_max_children, + www_pm_start_servers=www_pm_start_servers, + www_pm_min_spare_servers=www_pm_min_spare_servers, + www_pm_max_spare_servers=www_pm_max_spare_servers, + www_request_terminate_timeout=www_request_terminate_time, + www_xdebug_profiler_enable_trigger=www_xdebug, + debug_listen=debug_listen, debug_ping_path=debug_ping_path, + debug_pm_status_path=debug_pm_status_path, + debug_pm=debug_pm, + debug_pm_max_requests=debug_pm_max_requests, + debug_pm_max_children=debug_pm_max_children, + debug_pm_start_servers=debug_pm_start_servers, + debug_pm_min_spare_servers=debug_pm_min_spare_servers, + debug_pm_max_spare_servers=debug_pm_max_spare_servers, + debug_request_terminate_timeout=debug_request_terminate, + debug_xdebug_profiler_enable_trigger=debug_xdebug) + self.app.render((data), 'info_php.mustache') + + @expose(hide=True) + def info_php73(self): + """Display PHP information""" + version = os.popen("/usr/bin/php7.3 -v 2>/dev/null | " + "head -n1 | cut -d' ' -f2 |" + " cut -d'+' -f1 | tr -d '\n'").read + config = configparser.ConfigParser() + config.read('/etc/php/7.3/fpm/php.ini') + expose_php = config['PHP']['expose_php'] + memory_limit = config['PHP']['memory_limit'] + post_max_size = config['PHP']['post_max_size'] + upload_max_filesize = config['PHP']['upload_max_filesize'] + max_execution_time = config['PHP']['max_execution_time'] + + config.read('/etc/php/7.3/fpm/pool.d/www.conf') + www_listen = config['www']['listen'] + www_ping_path = config['www']['ping.path'] + www_pm_status_path = config['www']['pm.status_path'] + www_pm = config['www']['pm'] + www_pm_max_requests = config['www']['pm.max_requests'] + www_pm_max_children = config['www']['pm.max_children'] + www_pm_start_servers = config['www']['pm.start_servers'] + www_pm_min_spare_servers = config['www']['pm.min_spare_servers'] + www_pm_max_spare_servers = config['www']['pm.max_spare_servers'] + www_request_terminate_time = (config['www'] + ['request_terminate_timeout']) + try: + www_xdebug = (config['www']['php_admin_flag[xdebug.profiler_enable' + '_trigger]']) + except Exception as e: + www_xdebug = 'off' + + config.read('/etc/php/7.3/fpm/pool.d/debug.conf') + debug_listen = config['debug']['listen'] + debug_ping_path = config['debug']['ping.path'] + debug_pm_status_path = config['debug']['pm.status_path'] + debug_pm = config['debug']['pm'] + debug_pm_max_requests = config['debug']['pm.max_requests'] + debug_pm_max_children = config['debug']['pm.max_children'] + debug_pm_start_servers = config['debug']['pm.start_servers'] + debug_pm_min_spare_servers = config['debug']['pm.min_spare_servers'] + debug_pm_max_spare_servers = config['debug']['pm.max_spare_servers'] + debug_request_terminate = (config['debug'] + ['request_terminate_timeout']) + try: + debug_xdebug = (config['debug']['php_admin_flag[xdebug.profiler_' + 'enable_trigger]']) + except Exception as e: + debug_xdebug = 'off' + + data = dict(version=version, expose_php=expose_php, + memory_limit=memory_limit, post_max_size=post_max_size, + upload_max_filesize=upload_max_filesize, + max_execution_time=max_execution_time, + www_listen=www_listen, www_ping_path=www_ping_path, + www_pm_status_path=www_pm_status_path, www_pm=www_pm, + www_pm_max_requests=www_pm_max_requests, + www_pm_max_children=www_pm_max_children, + www_pm_start_servers=www_pm_start_servers, + www_pm_min_spare_servers=www_pm_min_spare_servers, + www_pm_max_spare_servers=www_pm_max_spare_servers, + www_request_terminate_timeout=www_request_terminate_time, + www_xdebug_profiler_enable_trigger=www_xdebug, + debug_listen=debug_listen, debug_ping_path=debug_ping_path, + debug_pm_status_path=debug_pm_status_path, + debug_pm=debug_pm, + debug_pm_max_requests=debug_pm_max_requests, + debug_pm_max_children=debug_pm_max_children, + debug_pm_start_servers=debug_pm_start_servers, + debug_pm_min_spare_servers=debug_pm_min_spare_servers, + debug_pm_max_spare_servers=debug_pm_max_spare_servers, + debug_request_terminate_timeout=debug_request_terminate, + debug_xdebug_profiler_enable_trigger=debug_xdebug) + self.app.render((data), 'info_php.mustache') + + @expose(hide=True) + def info_mysql(self): + """Display MySQL information""" + version = os.popen("/usr/bin/mysql -V | awk '{print($5)}' | " + "cut -d ',' " + "-f1 | tr -d '\n'").read() + host = "localhost" + port = os.popen("/usr/bin/mysql -e \"show variables\" | " + "grep ^port | awk " + "'{print($2)}' | tr -d '\n'").read() + wait_timeout = os.popen("/usr/bin/mysql -e \"show variables\" | grep " + "^wait_timeout | awk '{print($2)}' | " + "tr -d '\n'").read() + interactive_timeout = os.popen("/usr/bin/mysql -e " + "\"show variables\" | grep " + "^interactive_timeout | awk " + "'{print($2)}' | tr -d '\n'").read() + max_used_connections = os.popen("/usr/bin/mysql -e " + "\"show global status\" | " + "grep Max_used_connections | awk " + "'{print($2)}' | tr -d '\n'").read() + datadir = os.popen("/usr/bin/mysql -e \"show variables\" | " + "grep datadir | awk" + " '{print($2)}' | tr -d '\n'").read() + socket = os.popen("/usr/bin/mysql -e \"show variables\" | " + "grep \"^socket\" | " + "awk '{print($2)}' | tr -d '\n'").read() + data = dict(version=version, host=host, port=port, + wait_timeout=wait_timeout, + interactive_timeout=interactive_timeout, + max_used_connections=max_used_connections, + datadir=datadir, socket=socket) + self.app.render((data), 'info_mysql.mustache') + + @expose(hide=True) + def default(self): + """default function for info""" + if (not self.app.pargs.nginx and not self.app.pargs.php and + not self.app.pargs.mysql and not self.app.pargs.php73): + self.app.pargs.nginx = True + self.app.pargs.php = True + self.app.pargs.mysql = True + if WOAptGet.is_installed(self, 'php7.3-fpm'): + self.app.pargs.php73 = True + + if self.app.pargs.nginx: + if (WOAptGet.is_installed(self, 'nginx-custom') or + WOAptGet.is_installed(self, 'nginx-wo')): + self.info_nginx() + else: + Log.error(self, "Nginx is not installed") + + if self.app.pargs.php: + if WOAptGet.is_installed(self, 'php7.2-fpm'): + self.info_php() + else: + Log.error(self, "PHP 7.2 is not installed") + + if self.app.pargs.php73: + if WOAptGet.is_installed(self, 'php7.3-fpm'): + self.info_php73() + else: + Log.error(self, "PHP 7.3 is not installed") + + if self.app.pargs.mysql: + if WOShellExec.cmd_exec(self, "mysqladmin ping"): + self.info_mysql() + else: + Log.error(self, "MySQL is not installed") + + +def load(app): + # register the plugin class.. this only happens if the plugin is enabled + handler.register(WOInfoController) + + # register a hook (function) to run after arguments are parsed. + hook.register('post_argument_parsing', wo_info_hook) diff --git a/wo/cli/plugins/maintenance.py b/wo/cli/plugins/maintenance.py index cdbbd14..c787ef2 100644 --- a/wo/cli/plugins/maintenance.py +++ b/wo/cli/plugins/maintenance.py @@ -1,52 +1,52 @@ -"""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.shellexec import WOShellExec - - -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) +"""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.shellexec import WOShellExec + + +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/site_functions.py b/wo/cli/plugins/site_functions.py index f89b5ac..8697692 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1,1780 +1,1780 @@ -from wo.cli.plugins.stack import WOStackController -from wo.core.fileutils import WOFileUtils -from wo.core.mysql import * -from wo.core.shellexec import * -from wo.core.sslutils import SSL -from wo.core.variables import WOVariables -from wo.cli.plugins.sitedb import * -from wo.core.aptget import WOAptGet -from wo.core.git import WOGit -from wo.core.logging import Log -from wo.core.sendmail import WOSendMail -from wo.core.services import WOService -import subprocess -from subprocess import CalledProcessError -import os -import random -import string -import sys -import getpass -import glob -import re -import platform - - -class SiteError(Exception): - """Custom Exception Occured when setting up site""" - - def __init__(self, message): - self.message = message - - def __str__(self): - return repr(self.message) - - -def pre_run_checks(self): - - # Check nginx configuration - Log.info(self, "Running pre-update checks, please wait...") - try: - Log.debug(self, "checking NGINX configuration ...") - FNULL = open('/dev/null', 'w') - ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, - stderr=subprocess.STDOUT) - except CalledProcessError as e: - Log.debug(self, "{0}".format(str(e))) - raise SiteError("nginx configuration check failed.") - - -def check_domain_exists(self, domain): - if getSiteInfo(self, domain): - return True - return False - - -def setupdomain(self, data): - - # for debug purpose - # for key, value in data.items() : - # print (key, value) - - wo_domain_name = data['site_name'] - wo_site_webroot = data['webroot'] - - # Check if nginx configuration already exists - # if os.path.isfile('/etc/nginx/sites-available/{0}' - # .format(wo_domain_name)): - # raise SiteError("nginx configuration already exists for site") - - Log.info(self, "Setting up NGINX configuration \t", end='') - # write nginx config for file - try: - wo_site_nginx_conf = open('/etc/nginx/sites-available/{0}' - .format(wo_domain_name), encoding='utf-8', - mode='w') - if not data['php73']: - self.app.render((data), 'virtualconf.mustache', - out=wo_site_nginx_conf) - else: - self.app.render((data), 'virtualconf-php7.mustache', - out=wo_site_nginx_conf) - wo_site_nginx_conf.close() - except IOError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("create nginx configuration failed for site") - except Exception as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("create nginx configuration failed for site") - finally: - # Check nginx -t and return status over it - try: - Log.debug(self, "Checking generated nginx conf, please wait...") - FNULL = open('/dev/null', 'w') - ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, - stderr=subprocess.STDOUT) - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - except CalledProcessError as e: - Log.debug(self, "{0}".format(str(e))) - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + - Log.OKBLUE + "]") - raise SiteError("created nginx configuration failed for site." - " check with `nginx -t`") - - # create symbolic link for - WOFileUtils.create_symlink(self, ['/etc/nginx/sites-available/{0}' - .format(wo_domain_name), - '/etc/nginx/sites-enabled/{0}' - .format(wo_domain_name)]) - - # Creating htdocs & logs directory - Log.info(self, "Setting up webroot \t\t", end='') - try: - if not os.path.exists('{0}/htdocs'.format(wo_site_webroot)): - os.makedirs('{0}/htdocs'.format(wo_site_webroot)) - if not os.path.exists('{0}/logs'.format(wo_site_webroot)): - os.makedirs('{0}/logs'.format(wo_site_webroot)) - if not os.path.exists('{0}/conf/nginx'.format(wo_site_webroot)): - os.makedirs('{0}/conf/nginx'.format(wo_site_webroot)) - - WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.access.log' - .format(wo_domain_name), - '{0}/logs/access.log' - .format(wo_site_webroot)]) - WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.error.log' - .format(wo_domain_name), - '{0}/logs/error.log' - .format(wo_site_webroot)]) - except Exception as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("setup webroot failed for site") - finally: - # TODO Check if directories are setup - if (os.path.exists('{0}/htdocs'.format(wo_site_webroot)) and - os.path.exists('{0}/logs'.format(wo_site_webroot))): - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - else: - Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") - raise SiteError("setup webroot failed for site") - - -def setupdatabase(self, data): - wo_domain_name = data['site_name'] - wo_random = (''.join(random.sample(string.ascii_uppercase + - string.ascii_lowercase + - string.digits, 24))) - wo_replace_dot = wo_domain_name.replace('.', '_') - prompt_dbname = self.app.config.get('mysql', 'db-name') - prompt_dbuser = self.app.config.get('mysql', 'db-user') - wo_mysql_grant_host = self.app.config.get('mysql', 'grant-host') - wo_db_name = '' - wo_db_username = '' - wo_db_password = '' - - if prompt_dbname == 'True' or prompt_dbname == 'true': - try: - wo_db_name = input('Enter the MySQL database name [{0}]: ' - .format(wo_replace_dot)) - except EOFError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("Unable to input database name") - - if not wo_db_name: - wo_db_name = wo_replace_dot - - if prompt_dbuser == 'True' or prompt_dbuser == 'true': - try: - wo_db_username = input('Enter the MySQL database user name [{0}]: ' - .format(wo_replace_dot)) - wo_db_password = getpass.getpass(prompt='Enter the MySQL database' - ' password [{0}]: ' - .format(wo_random)) - except EOFError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("Unable to input database credentials") - - if not wo_db_username: - wo_db_username = wo_replace_dot - if not wo_db_password: - wo_db_password = wo_random - - if len(wo_db_username) > 16: - Log.debug(self, 'Autofix MySQL username (ERROR 1470 (HY000)),' - ' please wait') - wo_db_username = (wo_db_name[0:6] + generate_random()) - - # create MySQL database - Log.info(self, "Setting up database\t\t", end='') - Log.debug(self, "Creating database {0}".format(wo_db_name)) - try: - if WOMysql.check_db_exists(self, wo_db_name): - Log.debug(self, "Database already exists, Updating DB_NAME .. ") - wo_db_name = (wo_db_name[0:6] + generate_random()) - wo_db_username = (wo_db_name[0:6] + generate_random()) - except MySQLConnectionError as e: - raise SiteError("MySQL Connectivity problem occured") - - try: - WOMysql.execute(self, "create database `{0}`" - .format(wo_db_name)) - except StatementExcecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") - raise SiteError("create database execution failed") - # Create MySQL User - Log.debug(self, "Creating user {0}".format(wo_db_username)) - Log.debug(self, "create user `{0}`@`{1}` identified by ''" - .format(wo_db_username, wo_mysql_grant_host)) - try: - WOMysql.execute(self, - "create user `{0}`@`{1}` identified by '{2}'" - .format(wo_db_username, wo_mysql_grant_host, - wo_db_password), log=False) - except StatementExcecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") - raise SiteError("creating user failed for database") - - # Grant permission - Log.debug(self, "Setting up user privileges") - try: - WOMysql.execute(self, - "grant all privileges on `{0}`.* to `{1}`@`{2}`" - .format(wo_db_name, - wo_db_username, wo_mysql_grant_host)) - except StatementExcecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") - SiteError("grant privileges to user failed for database ") - - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - - data['wo_db_name'] = wo_db_name - data['wo_db_user'] = wo_db_username - data['wo_db_pass'] = wo_db_password - data['wo_db_host'] = WOVariables.wo_mysql_host - data['wo_mysql_grant_host'] = wo_mysql_grant_host - return(data) - - -def setupwordpress(self, data): - wo_domain_name = data['site_name'] - wo_site_webroot = data['webroot'] - prompt_wpprefix = self.app.config.get('wordpress', 'prefix') - wo_wp_user = self.app.config.get('wordpress', 'user') - wo_wp_pass = self.app.config.get('wordpress', 'password') - wo_wp_email = self.app.config.get('wordpress', 'email') - # Random characters - wo_random = (''.join(random.sample(string.ascii_uppercase + - string.ascii_lowercase + - string.digits, 15))) - wo_wp_prefix = '' - # wo_wp_user = '' - # wo_wp_pass = '' - - if 'wp-user' in data.keys() and data['wp-user']: - wo_wp_user = data['wp-user'] - if 'wp-email' in data.keys() and data['wp-email']: - wo_wp_email = data['wp-email'] - if 'wp-pass' in data.keys() and data['wp-pass']: - wo_wp_pass = data['wp-pass'] - - Log.info(self, "Downloading WordPress \t\t", end='') - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - try: - if WOShellExec.cmd_exec(self, "wp --allow-root core" - " download"): - pass - else: - Log.info(self, "[" + Log.ENDC + Log.FAIL + - "Fail" + Log.OKBLUE + "]") - raise SiteError("download WordPress core failed") - except CommandExecutionError as e: - Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") - raise SiteError(self, "download WordPress core failed") - - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - - if not (data['wo_db_name'] and data['wo_db_user'] and data['wo_db_pass']): - data = setupdatabase(self, data) - if prompt_wpprefix == 'True' or prompt_wpprefix == 'true': - try: - wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ') - while not re.match('^[A-Za-z0-9_]*$', wo_wp_prefix): - Log.warn(self, "table prefix can only " - "contain numbers, letters, and underscores") - wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ' - ) - except EOFError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("input table prefix failed") - - if not wo_wp_prefix: - wo_wp_prefix = 'wp_' - - # Modify wp-config.php & move outside the webroot - - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - Log.debug(self, "Setting up wp-config file") - if not data['multisite']: - Log.debug(self, "Generating wp-config for WordPress Single site") - Log.debug(self, "bash -c \"php {0} --allow-root " - .format(WOVariables.wo_wpcli_path) + - "config create " + - "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbuser=\'{2}\' " - "--dbhost=\'{3}\' " - .format(data['wo_db_name'], wo_wp_prefix, - data['wo_db_user'], data['wo_db_host']) + - "--dbpass=\'{0}\' " - "--extra-php< {1}/{0}.gz" - .format(data['wo_db_name'], - backup_path)): - Log.info(self, - "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") - raise SiteError("mysqldump failed to backup database") - except CommandExecutionError as e: - Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") - raise SiteError("mysqldump failed to backup database") - Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") - # move wp-config.php/wo-config.php to backup - if data['currsitetype'] in ['mysql', 'proxy']: - if data['php73'] is True and not data['wp']: - WOFileUtils.copyfile(self, configfiles[0], backup_path) - else: - WOFileUtils.mvfile(self, configfiles[0], backup_path) - else: - WOFileUtils.copyfile(self, configfiles[0], backup_path) - - -def site_package_check(self, stype): - apt_packages = [] - packages = [] - stack = WOStackController() - stack.app = self.app - if stype in ['html', 'proxy', 'php', 'mysql', 'wp', 'wpsubdir', - 'wpsubdomain', 'php73']: - Log.debug(self, "Setting apt_packages variable for Nginx") - - # Check if server has nginx-custom package - if not (WOAptGet.is_installed(self, 'nginx-custom') or - WOAptGet.is_installed(self, 'nginx-mainline')): - # check if Server has nginx-plus installed - if WOAptGet.is_installed(self, 'nginx-plus'): - # do something - # do post nginx installation configuration - Log.info(self, "NGINX PLUS Detected ...") - apt = ["nginx-plus"] + WOVariables.wo_nginx - # apt_packages = apt_packages + WOVariables.wo_nginx - stack.post_pref(apt, packages) - elif WOAptGet.is_installed(self, 'nginx'): - Log.info(self, "WordOps detected a previously" - "installed Nginx package. " - "It may or may not have required modules. " - "\nIf you need help, please create an issue at " - "https://github.com/WordOps/WordOps/issues/ \n") - apt = ["nginx"] + WOVariables.wo_nginx - # apt_packages = apt_packages + WOVariables.wo_nginx - stack.post_pref(apt, packages) - else: - apt_packages = apt_packages + WOVariables.wo_nginx - else: - # Fix for Nginx white screen death - if not WOFileUtils.grep(self, '/etc/nginx/fastcgi_params', - 'SCRIPT_FILENAME'): - with open('/etc/nginx/fastcgi_params', encoding='utf-8', - mode='a') as wo_nginx: - wo_nginx.write('fastcgi_param \tSCRIPT_FILENAME ' - '\t$request_filename;\n') - - if self.app.pargs.php and self.app.pargs.php73: - Log.error( - self, "Error: two different PHP versions cannot be " - "combined within the same WordOps site") - - if not self.app.pargs.php73 and stype in ['php', 'mysql', 'wp', 'wpsubdir', - 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for PHP 7.2") - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - apt_packages = apt_packages + WOVariables.wo_php + \ - WOVariables.wo_php_extra - else: - apt_packages = apt_packages + WOVariables.wo_php - - if self.app.pargs.php73 and stype in ['mysql', 'wp', - 'wpsubdir', 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for PHP 7.3") - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVariables.wo_php + \ - WOVariables.wo_php73 + WOVariables.wo_php_extra - else: - apt_packages = apt_packages + WOVariables.wo_php73 - - if stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for MySQL") - if not WOShellExec.cmd_exec(self, "mysqladmin ping"): - apt_packages = apt_packages + WOVariables.wo_mysql - packages = packages + [["https://raw.githubusercontent.com/" - "major/MySQLTuner-perl/master/" - "mysqltuner.pl", "/usr/bin/mysqltuner", - "MySQLTuner"]] - - if stype in ['wp', 'wpsubdir', 'wpsubdomain']: - Log.debug(self, "Setting packages variable for WP-CLI") - if not WOShellExec.cmd_exec(self, "command -v wp"): - packages = packages + [["https://github.com/wp-cli/wp-cli/" - "releases/download/v{0}/" - "wp-cli-{0}.phar" - .format(WOVariables.wo_wp_cli), - "/usr/local/bin/wp", "WP-CLI"]] - if self.app.pargs.wpredis: - Log.debug(self, "Setting apt_packages variable for redis") - if not WOAptGet.is_installed(self, 'redis-server'): - apt_packages = apt_packages + WOVariables.wo_redis - - if (os.path.isfile("/etc/nginx/nginx.conf") and - not os.path.isfile("/etc/nginx/common/redis-php72.conf")): - - data = dict() - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/redis-php72.conf') - wo_nginx = open('/etc/nginx/common/redis-php72.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'redis.mustache', - out=wo_nginx) - wo_nginx.close() - - if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): - if not WOFileUtils.grep(self, "/etc/nginx/conf.d/" - "upstream.conf", - "redis"): - with open("/etc/nginx/conf.d/upstream.conf", - "a") as redis_file: - redis_file.write("upstream redis {\n" - " server 127.0.0.1:6379;\n" - " keepalive 10;\n}") - - if (os.path.isfile("/etc/nginx/nginx.conf") and - not os.path.isfile("/etc/nginx/conf.d/redis.conf")): - with open("/etc/nginx/conf.d/redis.conf", "a") as redis_file: - redis_file.write("# Log format Settings\n" - "log_format rt_cache_redis '$remote_addr" - " $upstream_response_time " - "$srcache_fetch_status [$time_local] '\n" - "'$http_host \"$request\" $status" - " $body_bytes_sent '\n" - "'\"$http_referer\" \"$http_user_agent\"';\n") - - if self.app.pargs.php73: - Log.debug(self, "Setting apt_packages variable for PHP 7.3") - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVariables.wo_php + \ - WOVariables.wo_php73 + WOVariables.wo_php_extra - else: - apt_packages = apt_packages + WOVariables.wo_php73 - - if (os.path.isdir("/etc/nginx/common") and - not os.path.isfile("/etc/nginx/common/php73.conf")): - data = dict() - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/locations-wo.conf') - wo_nginx = open('/etc/nginx/common/locations-wo.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'locations-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/php73.conf') - wo_nginx = open('/etc/nginx/common/php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/wpcommon-php73.conf') - wo_nginx = open('/etc/nginx/common/wpcommon-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'wpcommon-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/wpfc-php73.conf') - wo_nginx = open('/etc/nginx/common/wpfc-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'wpfc-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/wpsc-php73.conf') - wo_nginx = open('/etc/nginx/common/wpsc-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'wpsc-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - if (os.path.isfile("/etc/nginx/nginx.conf") and - not os.path.isfile("/etc/nginx/common/redis-php73.conf")): - data = dict() - Log.debug(self, 'Writting the nginx configuration to ' - 'file /etc/nginx/common/redis-php73.conf') - wo_nginx = open('/etc/nginx/common/redis-php73.conf', - encoding='utf-8', mode='w') - self.app.render((data), 'redis-php7.mustache', - out=wo_nginx) - wo_nginx.close() - - if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): - if not WOFileUtils.grep(self, "/etc/nginx/conf.d/upstream.conf", - "php73"): - with open("/etc/nginx/conf.d/upstream.conf", "a") as php_file: - php_file.write("upstream php73 {\nserver" - "unix:/var/run/php/php73-fpm.sock;\n}\n" - "upstream debug73" - " {\nserver 127.0.0.1:9173;\n}\n") - - return(stack.install(apt_packages=apt_packages, packages=packages, - disp_msg=False)) - - -def updatewpuserpassword(self, wo_domain, wo_site_webroot): - - wo_wp_user = '' - wo_wp_pass = '' - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - - # Check if wo_domain is wordpress install - try: - is_wp = WOShellExec.cmd_exec(self, "wp --allow-root core" - " version") - except CommandExecutionError as e: - raise SiteError("is WordPress site? check command failed ") - - # Exit if wo_domain is not wordpress install - if not is_wp: - Log.error(self, "{0} does not seem to be a WordPress site" - .format(wo_domain)) - - try: - wo_wp_user = input("Provide WordPress user name [admin]: ") - except Exception as e: - Log.debug(self, "{0}".format(e)) - Log.error(self, "\nCould not update password") - - if wo_wp_user == "?": - Log.info(self, "Fetching WordPress user list") - try: - WOShellExec.cmd_exec(self, "wp --allow-root user list " - "--fields=user_login | grep -v user_login") - except CommandExecutionError as e: - raise SiteError("fetch wp userlist command failed") - - if not wo_wp_user: - wo_wp_user = 'admin' - - try: - is_user_exist = WOShellExec.cmd_exec(self, "wp --allow-root user list " - "--fields=user_login | grep {0}$ " - .format(wo_wp_user)) - except CommandExecutionError as e: - raise SiteError("if wp user exists check command failed") - - if is_user_exist: - try: - wo_wp_pass = getpass.getpass(prompt="Provide password for " - "{0} user: " - .format(wo_wp_user)) - - while not wo_wp_pass: - wo_wp_pass = getpass.getpass(prompt="Provide password for " - "{0} user: " - .format(wo_wp_user)) - except Exception as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("failed to read password input ") - - try: - WOShellExec.cmd_exec(self, "wp --allow-root user update {0}" - " --user_pass={1}" - .format(wo_wp_user, wo_wp_pass)) - except CommandExecutionError as e: - raise SiteError("wp user password update command failed") - Log.info(self, "Password updated successfully") - - else: - Log.error(self, "Invalid WordPress user {0} for {1}." - .format(wo_wp_user, wo_domain)) - - -def display_cache_settings(self, data): - if data['wpsc']: - if data['multisite']: - Log.info(self, "Configure WPSC:" - "\t\thttp://{0}/wp-admin/network/settings.php?" - "page=wpsupercache" - .format(data['site_name'])) - else: - Log.info(self, "Configure WPSC:" - "\t\thttp://{0}/wp-admin/options-general.php?" - "page=wpsupercache" - .format(data['site_name'])) - - if data['wpredis']: - if data['multisite']: - Log.info(self, "Configure redis-cache:" - "\thttp://{0}/wp-admin/network/settings.php?" - "page=redis-cache".format(data['site_name'])) - else: - Log.info(self, "Configure redis-cache:" - "\thttp://{0}/wp-admin/options-general.php?" - "page=redis-cache".format(data['site_name'])) - Log.info(self, "Object Cache:\t\tEnable") - - -def logwatch(self, logfiles): - import zlib - import base64 - import time - from wo.core import logwatch - - def callback(filename, lines): - for line in lines: - if line.find(':::') == -1: - print(line) - else: - data = line.split(':::') - try: - print(data[0], data[1], - zlib.decompress(base64.decodestring(data[2]))) - except Exception as e: - Log.info(time.time(), - 'caught exception rendering a new log line in %s' - % filename) - - l = logwatch.LogWatcher(logfiles, callback) - l.loop() - - -def detSitePar(opts): - """ - Takes dictionary of parsed arguments - 1.returns sitetype and cachetype - 2. raises RuntimeError when wrong combination is used like - "--wp --wpsubdir" or "--html --wp" - """ - sitetype, cachetype = '', '' - typelist = list() - cachelist = list() - for key, val in opts.items(): - if val and key in ['html', 'php', 'mysql', 'wp', - 'wpsubdir', 'wpsubdomain', 'php73']: - typelist.append(key) - elif val and key in ['wpfc', 'wpsc', 'wpredis']: - cachelist.append(key) - - if len(typelist) > 1 or len(cachelist) > 1: - if len(cachelist) > 1: - raise RuntimeError( - "Could not determine cache type." - "Multiple cache parameter entered") - elif False not in [x in ('php', 'mysql', 'html') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php73', 'mysql', 'html') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php', 'mysql') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php73', 'mysql') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('html', 'mysql') for x in typelist]: - sitetype = 'mysql' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php', 'html') for x in typelist]: - sitetype = 'php' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('php73', 'html') for x in typelist]: - sitetype = 'php73' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wp', 'wpsubdir') for x in typelist]: - sitetype = 'wpsubdir' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wp', 'wpsubdomain') for x in typelist]: - sitetype = 'wpsubdomain' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wp', 'php73') for x in typelist]: - sitetype = 'wp' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wpsubdir', 'php73') for x in typelist]: - sitetype = 'wpsubdir' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - elif False not in [x in ('wpsubdomain', 'php73') for x in typelist]: - sitetype = 'wpsubdomain' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] - else: - raise RuntimeError("could not determine site and cache type") - else: - if not typelist and not cachelist: - sitetype = None - cachetype = None - elif (not typelist or "php73" in typelist) and cachelist: - sitetype = 'wp' - cachetype = cachelist[0] - elif typelist and (not cachelist): - sitetype = typelist[0] - cachetype = 'basic' - else: - sitetype = typelist[0] - cachetype = cachelist[0] - - return (sitetype, cachetype) - - -def generate_random(): - wo_random10 = (''.join(random.sample(string.ascii_uppercase + - string.ascii_lowercase + - string.digits, 24))) - return wo_random10 - - -def deleteDB(self, dbname, dbuser, dbhost, exit=True): - try: - # Check if Database exists - try: - if WOMysql.check_db_exists(self, dbname): - # Drop database if exists - Log.debug(self, "dropping database `{0}`".format(dbname)) - WOMysql.execute(self, - "drop database `{0}`".format(dbname), - errormsg='Unable to drop database {0}' - .format(dbname)) - except StatementExcecutionError as e: - Log.debug(self, "drop database failed") - Log.info(self, "Database {0} not dropped".format(dbname)) - - except MySQLConnectionError as e: - Log.debug(self, "Mysql Connection problem occured") - - if dbuser != 'root': - Log.debug(self, "dropping user `{0}`".format(dbuser)) - try: - WOMysql.execute(self, - "drop user `{0}`@`{1}`" - .format(dbuser, dbhost)) - except StatementExcecutionError as e: - Log.debug(self, "drop database user failed") - Log.info(self, "Database {0} not dropped".format(dbuser)) - try: - WOMysql.execute(self, "flush privileges") - except StatementExcecutionError as e: - Log.debug(self, "drop database failed") - Log.info(self, "Database {0} not dropped".format(dbname)) - except Exception as e: - Log.error(self, "Error occured while deleting database", exit) - - -def deleteWebRoot(self, webroot): - # do some preprocessing before proceeding - webroot = webroot.strip() - if (webroot == "/var/www/" or webroot == "/var/www" or - webroot == "/var/www/.." or webroot == "/var/www/."): - Log.debug(self, "Tried to remove {0}, but didn't remove it" - .format(webroot)) - return False - - if os.path.isdir(webroot): - Log.debug(self, "Removing {0}".format(webroot)) - WOFileUtils.rm(self, webroot) - return True - Log.debug(self, "{0} does not exist".format(webroot)) - return False - - -def removeNginxConf(self, domain): - if os.path.isfile('/etc/nginx/sites-available/{0}' - .format(domain)): - Log.debug(self, "Removing Nginx configuration") - WOFileUtils.rm(self, '/etc/nginx/sites-enabled/{0}' - .format(domain)) - WOFileUtils.rm(self, '/etc/nginx/sites-available/{0}' - .format(domain)) - WOService.reload_service(self, 'nginx') - WOGit.add(self, ["/etc/nginx"], - msg="Deleted {0} " - .format(domain)) - - -def removeAcmeConf(self, domain): - if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' - .format(domain)): - Log.debug(self, "Removing Acme configuration") - WOFileUtils.rm(self, '/etc/letsencrypt/renewal/{0}_ecc' - .format(domain)) - WOFileUtils.rm(self, '/etc/letsencrypt/live/{0}' - .format(domain)) - WOGit.add(self, ["/etc/letsencrypt"], - msg="Deleted {0} " - .format(domain)) - - -def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', - dbhost=''): - """ - Removes the nginx configuration and database for the domain provided. - doCleanupAction(self, domain='sitename', webroot='', - dbname='', dbuser='', dbhost='') - """ - if domain: - if os.path.isfile('/etc/nginx/sites-available/{0}' - .format(domain)): - removeNginxConf(self, domain) - if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' - .format(domain)): - removeAcmeConf(self, domain) - - if webroot: - deleteWebRoot(self, webroot) - - if dbname: - if not dbuser: - raise SiteError("dbuser not provided") - if not dbhost: - raise SiteError("dbhost not provided") - deleteDB(self, dbname, dbuser, dbhost) - - -# setup letsencrypt for domain + www.domain -def setupLetsEncrypt(self, wo_domain_name): - - if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" - .format(wo_domain_name)): - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d www.{0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d www.{0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - - if ssl: - - try: - Log.info(self, "Deploying SSL cert with acme.sh") - Log.debug(self, "Cert deployment for domain: {0}" - .format(wo_domain_name)) - sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && " - "service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - updateSiteInfo(self, wo_domain_name, ssl=True) - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to install certificate", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - -# setup letsencrypt for a subdomain - - -def setupLetsEncryptSubdomain(self, wo_domain_name): - - if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" - .format(wo_domain_name)): - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) - if ssl: - - try: - Log.info(self, "Deploying SSL cert with acme.sh") - Log.debug(self, "Deploying cert for domain: {0}" - .format(wo_domain_name)) - sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - updateSiteInfo(self, wo_domain_name, ssl=True) - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to create ssl.conf", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - -# setup letsencrypt for domain + www.domain - - -def setupLetsEncryptWildcard(self, wo_domain_name, wo_acme_dns='dns_cf'): - - if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" - .format(wo_domain_name)): - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d *.{0} --dns {1} " - "-k ec-384 -f" - .format(wo_domain_name, wo_acme_dns)) - else: - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d *.{0} --dns {1} " - "-k ec-384 -f" - .format(wo_domain_name, wo_acme_dns)) - - if ssl: - - try: - Log.info(self, "Deploying SSL cert with acme.sh") - Log.debug(self, "Cert deployment for domain: {0}" - .format(wo_domain_name)) - sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && " - "service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - updateSiteInfo(self, wo_domain_name, ssl=True) - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to install certificate", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - - -# letsencrypt cert renewal - - -def renewLetsEncrypt(self, wo_domain_name): - - ssl = WOShellExec.cmd_exec( - self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--renew -d {0} --ecc --force" - .format(wo_domain_name)) - - mail_list = '' - if not ssl: - Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!", - False) - if (SSL.getExpirationDays(self, wo_domain_name) > 0): - Log.error(self, "Your current certificate will expire within " + - str(SSL.getExpirationDays(self, wo_domain_name)) + - " days.", False) - else: - Log.error(self, "Your current certificate already expired!", False) - - # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, - # "[FAIL] HTTPS cert renewal {0}".format(wo_domain_name), - # "Hi,\n\nHTTPS certificate renewal for https://{0} - # was unsuccessful.".format(wo_domain_name) + - # "\nPlease check the WordOps log for reason - # The current expiry date is : " + - # str(SSL.getExpirationDate(self, wo_domain_name)) + - # "\n\nFor support visit https://wordops.net/support . - # \n\nBest regards,\nYour WordOps Worker", files=mail_list, - # port=25, isTls=False) - Log.error(self, "Check the WO log for more details " - "`tail /var/log/wo/wordops.log`") - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, - # "[SUCCESS] Let's Encrypt certificate renewal {0}".format(wo_domain_name), - # "Hi,\n\nYour Let's Encrypt certificate has been renewed for - # https://{0} .".format(wo_domain_name) + - # "\nYour new certificate will expire on : " + - # str(SSL.getExpirationDate(self, wo_domain_name)) + - # "\n\nBest regards,\nYour WordOps Worker", files=mail_list, - # port=25, isTls=False) - -# redirect= False to disable https redirection - - -def setupHsts(self, wo_domain_name): - Log.info( - self, "Adding /var/www/{0}/conf/nginx/hsts.conf" - .format(wo_domain_name)) - - hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - hstsconf.write("more_set_headers " - "\"Strict-Transport-Security: " - "max-age=31536000; " - "'includeSubDomains; " - "preload\";") - hstsconf.close() - return 0 - - -def httpsRedirect(self, wo_domain_name, redirect=True, wildcard=False): - if redirect: - if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name)): - WOFileUtils.mvfile(self, - "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name), - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - else: - if wildcard: - try: - Log.info( - self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("server {\n" - "\tlisten 80;\n" + - "\tlisten [::]:80;\n" + - "\tserver_name *.{0} {0};\n" - .format(wo_domain_name) + - "\treturn 301 https://$host" - "$request_uri;\n}") - sslconf.close() - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - else: - try: - Log.info( - self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - - sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("server {\n" - "\tlisten 80;\n" + - "\tlisten [::]:80;\n" + - "\tserver_name www.{0} {0};\n" - .format(wo_domain_name) + - "\treturn 301 https://{0}" - .format(wo_domain_name)+"$request_uri;\n}") - sslconf.close() - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - - Log.info(self, "Added HTTPS Force Redirection for Site " - " http://{0}".format(wo_domain_name)) - # Nginx Configation into GIT - WOGit.add(self, - ["/etc/nginx"], msg="Adding /etc/nginx/conf.d/" - "force-ssl-{0}.conf".format(wo_domain_name)) - else: - if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)): - WOFileUtils.mvfile(self, "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name)) - Log.info(self, "Disabled HTTPS Force Redirection for Site " - " http://{0}".format(wo_domain_name)) - - -def archivedCertificateHandle(self, domain): - Log.warn(self, "You already have an existing certificate " - "for the domain requested.\n" - "(ref: {0}/" - "{1}_ecc/{1}.conf)".format(WOVariables.wo_ssl_archive, domain) + - "\nPlease select an option from below?" - "\n\t1: Reinstall existing certificate" - "\n\t2: Keep the existing certificate for now" - "\n\t3: Renew & replace the certificate (limit ~5 per 7 days)" - "") - check_prompt = input( - "\nType the appropriate number [1-3] or any other key to cancel: ") - if not os.path.isfile("{0}/{1}/fullchain.pem" - .format(WOVariables.wo_ssl_live, domain)): - Log.error( - self, "{0}/{1}/fullchain.pem file is missing." - .format(WOVariables.wo_ssl_live, domain)) - - if check_prompt == "1": - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"service nginx restart\" " - .format(WOVariables.wo_ssl_live, - domain)) - if ssl: - - try: - - if not os.path.isfile("/var/www/{0}/conf/nginx/ssl.conf" - .format(domain)): - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(domain)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(domain), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate " - "{0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, domain)) - sslconf.close() - - updateSiteInfo(self, domain, ssl=True) - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - - elif (check_prompt == "2"): - Log.info(self, "Using Existing Certificate files") - if not os.path.isfile("{0}/{1}/fullchain.pem" - .format(WOVariables.wo_ssl_live, domain)): - Log.error(self, "Certificate files not found. Skipping.\n" - "Please check if following file exist" - "\n\t/etc/letsencrypt/live/{0}/fullchain.pem\n\t" - "/etc/letsencrypt/live/{0}/key.pem".format(domain)) - - updateSiteInfo(self, domain, ssl=True) - - elif (check_prompt == "3"): - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--renew -d {0} --ecc " - "--force" - .format(domain)) - - if ssl: - - try: - - WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "ssl_trusted_certificate " - "{0}/{1}/ca.pem;\n" - "--reloadcmd " - "\"service nginx restart\" " - .format(WOVariables.wo_ssl_live, domain)) - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while installing " - "the certificate") - - else: - Log.error(self, "Operation cancelled by user.") - - if os.path.isfile("{0}/conf/nginx/ssl.conf" - .format(domain)): - Log.info(self, "Existing ssl.conf . Backing it up ..") - WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf" - .format(domain), - '/var/www/{0}/conf/nginx/ssl.conf.bak' - .format(domain)) - - return ssl +from wo.cli.plugins.stack import WOStackController +from wo.core.fileutils import WOFileUtils +from wo.core.mysql import * +from wo.core.shellexec import * +from wo.core.sslutils import SSL +from wo.core.variables import WOVariables +from wo.cli.plugins.sitedb import * +from wo.core.aptget import WOAptGet +from wo.core.git import WOGit +from wo.core.logging import Log +from wo.core.sendmail import WOSendMail +from wo.core.services import WOService +import subprocess +from subprocess import CalledProcessError +import os +import random +import string +import sys +import getpass +import glob +import re +import platform + + +class SiteError(Exception): + """Custom Exception Occured when setting up site""" + + def __init__(self, message): + self.message = message + + def __str__(self): + return repr(self.message) + + +def pre_run_checks(self): + + # Check nginx configuration + Log.info(self, "Running pre-update checks, please wait...") + try: + Log.debug(self, "checking NGINX configuration ...") + FNULL = open('/dev/null', 'w') + ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, + stderr=subprocess.STDOUT) + except CalledProcessError as e: + Log.debug(self, "{0}".format(str(e))) + raise SiteError("nginx configuration check failed.") + + +def check_domain_exists(self, domain): + if getSiteInfo(self, domain): + return True + return False + + +def setupdomain(self, data): + + # for debug purpose + # for key, value in data.items() : + # print (key, value) + + wo_domain_name = data['site_name'] + wo_site_webroot = data['webroot'] + + # Check if nginx configuration already exists + # if os.path.isfile('/etc/nginx/sites-available/{0}' + # .format(wo_domain_name)): + # raise SiteError("nginx configuration already exists for site") + + Log.info(self, "Setting up NGINX configuration \t", end='') + # write nginx config for file + try: + wo_site_nginx_conf = open('/etc/nginx/sites-available/{0}' + .format(wo_domain_name), encoding='utf-8', + mode='w') + if not data['php73']: + self.app.render((data), 'virtualconf.mustache', + out=wo_site_nginx_conf) + else: + self.app.render((data), 'virtualconf-php7.mustache', + out=wo_site_nginx_conf) + wo_site_nginx_conf.close() + except IOError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("create nginx configuration failed for site") + except Exception as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("create nginx configuration failed for site") + finally: + # Check nginx -t and return status over it + try: + Log.debug(self, "Checking generated nginx conf, please wait...") + FNULL = open('/dev/null', 'w') + ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, + stderr=subprocess.STDOUT) + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + except CalledProcessError as e: + Log.debug(self, "{0}".format(str(e))) + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + + Log.OKBLUE + "]") + raise SiteError("created nginx configuration failed for site." + " check with `nginx -t`") + + # create symbolic link for + WOFileUtils.create_symlink(self, ['/etc/nginx/sites-available/{0}' + .format(wo_domain_name), + '/etc/nginx/sites-enabled/{0}' + .format(wo_domain_name)]) + + # Creating htdocs & logs directory + Log.info(self, "Setting up webroot \t\t", end='') + try: + if not os.path.exists('{0}/htdocs'.format(wo_site_webroot)): + os.makedirs('{0}/htdocs'.format(wo_site_webroot)) + if not os.path.exists('{0}/logs'.format(wo_site_webroot)): + os.makedirs('{0}/logs'.format(wo_site_webroot)) + if not os.path.exists('{0}/conf/nginx'.format(wo_site_webroot)): + os.makedirs('{0}/conf/nginx'.format(wo_site_webroot)) + + WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.access.log' + .format(wo_domain_name), + '{0}/logs/access.log' + .format(wo_site_webroot)]) + WOFileUtils.create_symlink(self, ['/var/log/nginx/{0}.error.log' + .format(wo_domain_name), + '{0}/logs/error.log' + .format(wo_site_webroot)]) + except Exception as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("setup webroot failed for site") + finally: + # TODO Check if directories are setup + if (os.path.exists('{0}/htdocs'.format(wo_site_webroot)) and + os.path.exists('{0}/logs'.format(wo_site_webroot))): + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + else: + Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") + raise SiteError("setup webroot failed for site") + + +def setupdatabase(self, data): + wo_domain_name = data['site_name'] + wo_random = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 24))) + wo_replace_dot = wo_domain_name.replace('.', '_') + prompt_dbname = self.app.config.get('mysql', 'db-name') + prompt_dbuser = self.app.config.get('mysql', 'db-user') + wo_mysql_grant_host = self.app.config.get('mysql', 'grant-host') + wo_db_name = '' + wo_db_username = '' + wo_db_password = '' + + if prompt_dbname == 'True' or prompt_dbname == 'true': + try: + wo_db_name = input('Enter the MySQL database name [{0}]: ' + .format(wo_replace_dot)) + except EOFError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("Unable to input database name") + + if not wo_db_name: + wo_db_name = wo_replace_dot + + if prompt_dbuser == 'True' or prompt_dbuser == 'true': + try: + wo_db_username = input('Enter the MySQL database user name [{0}]: ' + .format(wo_replace_dot)) + wo_db_password = getpass.getpass(prompt='Enter the MySQL database' + ' password [{0}]: ' + .format(wo_random)) + except EOFError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("Unable to input database credentials") + + if not wo_db_username: + wo_db_username = wo_replace_dot + if not wo_db_password: + wo_db_password = wo_random + + if len(wo_db_username) > 16: + Log.debug(self, 'Autofix MySQL username (ERROR 1470 (HY000)),' + ' please wait') + wo_db_username = (wo_db_name[0:6] + generate_random()) + + # create MySQL database + Log.info(self, "Setting up database\t\t", end='') + Log.debug(self, "Creating database {0}".format(wo_db_name)) + try: + if WOMysql.check_db_exists(self, wo_db_name): + Log.debug(self, "Database already exists, Updating DB_NAME .. ") + wo_db_name = (wo_db_name[0:6] + generate_random()) + wo_db_username = (wo_db_name[0:6] + generate_random()) + except MySQLConnectionError as e: + raise SiteError("MySQL Connectivity problem occured") + + try: + WOMysql.execute(self, "create database `{0}`" + .format(wo_db_name)) + except StatementExcecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") + raise SiteError("create database execution failed") + # Create MySQL User + Log.debug(self, "Creating user {0}".format(wo_db_username)) + Log.debug(self, "create user `{0}`@`{1}` identified by ''" + .format(wo_db_username, wo_mysql_grant_host)) + try: + WOMysql.execute(self, + "create user `{0}`@`{1}` identified by '{2}'" + .format(wo_db_username, wo_mysql_grant_host, + wo_db_password), log=False) + except StatementExcecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") + raise SiteError("creating user failed for database") + + # Grant permission + Log.debug(self, "Setting up user privileges") + try: + WOMysql.execute(self, + "grant all privileges on `{0}`.* to `{1}`@`{2}`" + .format(wo_db_name, + wo_db_username, wo_mysql_grant_host)) + except StatementExcecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") + SiteError("grant privileges to user failed for database ") + + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + + data['wo_db_name'] = wo_db_name + data['wo_db_user'] = wo_db_username + data['wo_db_pass'] = wo_db_password + data['wo_db_host'] = WOVariables.wo_mysql_host + data['wo_mysql_grant_host'] = wo_mysql_grant_host + return(data) + + +def setupwordpress(self, data): + wo_domain_name = data['site_name'] + wo_site_webroot = data['webroot'] + prompt_wpprefix = self.app.config.get('wordpress', 'prefix') + wo_wp_user = self.app.config.get('wordpress', 'user') + wo_wp_pass = self.app.config.get('wordpress', 'password') + wo_wp_email = self.app.config.get('wordpress', 'email') + # Random characters + wo_random = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 15))) + wo_wp_prefix = '' + # wo_wp_user = '' + # wo_wp_pass = '' + + if 'wp-user' in data.keys() and data['wp-user']: + wo_wp_user = data['wp-user'] + if 'wp-email' in data.keys() and data['wp-email']: + wo_wp_email = data['wp-email'] + if 'wp-pass' in data.keys() and data['wp-pass']: + wo_wp_pass = data['wp-pass'] + + Log.info(self, "Downloading WordPress \t\t", end='') + WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) + try: + if WOShellExec.cmd_exec(self, "wp --allow-root core" + " download"): + pass + else: + Log.info(self, "[" + Log.ENDC + Log.FAIL + + "Fail" + Log.OKBLUE + "]") + raise SiteError("download WordPress core failed") + except CommandExecutionError as e: + Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") + raise SiteError(self, "download WordPress core failed") + + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + + if not (data['wo_db_name'] and data['wo_db_user'] and data['wo_db_pass']): + data = setupdatabase(self, data) + if prompt_wpprefix == 'True' or prompt_wpprefix == 'true': + try: + wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ') + while not re.match('^[A-Za-z0-9_]*$', wo_wp_prefix): + Log.warn(self, "table prefix can only " + "contain numbers, letters, and underscores") + wo_wp_prefix = input('Enter the WordPress table prefix [wp_]: ' + ) + except EOFError as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("input table prefix failed") + + if not wo_wp_prefix: + wo_wp_prefix = 'wp_' + + # Modify wp-config.php & move outside the webroot + + WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) + Log.debug(self, "Setting up wp-config file") + if not data['multisite']: + Log.debug(self, "Generating wp-config for WordPress Single site") + Log.debug(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config create " + + "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbuser=\'{2}\' " + "--dbhost=\'{3}\' " + .format(data['wo_db_name'], wo_wp_prefix, + data['wo_db_user'], data['wo_db_host']) + + "--dbpass=\'{0}\' " + "--extra-php< {1}/{0}.gz" + .format(data['wo_db_name'], + backup_path)): + Log.info(self, + "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") + raise SiteError("mysqldump failed to backup database") + except CommandExecutionError as e: + Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") + raise SiteError("mysqldump failed to backup database") + Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") + # move wp-config.php/wo-config.php to backup + if data['currsitetype'] in ['mysql', 'proxy']: + if data['php73'] is True and not data['wp']: + WOFileUtils.copyfile(self, configfiles[0], backup_path) + else: + WOFileUtils.mvfile(self, configfiles[0], backup_path) + else: + WOFileUtils.copyfile(self, configfiles[0], backup_path) + + +def site_package_check(self, stype): + apt_packages = [] + packages = [] + stack = WOStackController() + stack.app = self.app + if stype in ['html', 'proxy', 'php', 'mysql', 'wp', 'wpsubdir', + 'wpsubdomain', 'php73']: + Log.debug(self, "Setting apt_packages variable for Nginx") + + # Check if server has nginx-custom package + if not (WOAptGet.is_installed(self, 'nginx-custom') or + WOAptGet.is_installed(self, 'nginx-mainline')): + # check if Server has nginx-plus installed + if WOAptGet.is_installed(self, 'nginx-plus'): + # do something + # do post nginx installation configuration + Log.info(self, "NGINX PLUS Detected ...") + apt = ["nginx-plus"] + WOVariables.wo_nginx + # apt_packages = apt_packages + WOVariables.wo_nginx + stack.post_pref(apt, packages) + elif WOAptGet.is_installed(self, 'nginx'): + Log.info(self, "WordOps detected a previously" + "installed Nginx package. " + "It may or may not have required modules. " + "\nIf you need help, please create an issue at " + "https://github.com/WordOps/WordOps/issues/ \n") + apt = ["nginx"] + WOVariables.wo_nginx + # apt_packages = apt_packages + WOVariables.wo_nginx + stack.post_pref(apt, packages) + else: + apt_packages = apt_packages + WOVariables.wo_nginx + else: + # Fix for Nginx white screen death + if not WOFileUtils.grep(self, '/etc/nginx/fastcgi_params', + 'SCRIPT_FILENAME'): + with open('/etc/nginx/fastcgi_params', encoding='utf-8', + mode='a') as wo_nginx: + wo_nginx.write('fastcgi_param \tSCRIPT_FILENAME ' + '\t$request_filename;\n') + + if self.app.pargs.php and self.app.pargs.php73: + Log.error( + self, "Error: two different PHP versions cannot be " + "combined within the same WordOps site") + + if not self.app.pargs.php73 and stype in ['php', 'mysql', 'wp', 'wpsubdir', + 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for PHP 7.2") + if not WOAptGet.is_installed(self, 'php7.2-fpm'): + if not WOAptGet.is_installed(self, 'php7.3-fpm'): + apt_packages = apt_packages + WOVariables.wo_php + \ + WOVariables.wo_php_extra + else: + apt_packages = apt_packages + WOVariables.wo_php + + if self.app.pargs.php73 and stype in ['mysql', 'wp', + 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for PHP 7.3") + if not WOAptGet.is_installed(self, 'php7.3-fpm'): + if not WOAptGet.is_installed(self, 'php7.2-fpm'): + apt_packages = apt_packages + WOVariables.wo_php + \ + WOVariables.wo_php73 + WOVariables.wo_php_extra + else: + apt_packages = apt_packages + WOVariables.wo_php73 + + if stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for MySQL") + if not WOShellExec.cmd_exec(self, "mysqladmin ping"): + apt_packages = apt_packages + WOVariables.wo_mysql + packages = packages + [["https://raw.githubusercontent.com/" + "major/MySQLTuner-perl/master/" + "mysqltuner.pl", "/usr/bin/mysqltuner", + "MySQLTuner"]] + + if stype in ['wp', 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting packages variable for WP-CLI") + if not WOShellExec.cmd_exec(self, "command -v wp"): + packages = packages + [["https://github.com/wp-cli/wp-cli/" + "releases/download/v{0}/" + "wp-cli-{0}.phar" + .format(WOVariables.wo_wp_cli), + "/usr/local/bin/wp", "WP-CLI"]] + if self.app.pargs.wpredis: + Log.debug(self, "Setting apt_packages variable for redis") + if not WOAptGet.is_installed(self, 'redis-server'): + apt_packages = apt_packages + WOVariables.wo_redis + + if (os.path.isfile("/etc/nginx/nginx.conf") and + not os.path.isfile("/etc/nginx/common/redis-php72.conf")): + + data = dict() + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/redis-php72.conf') + wo_nginx = open('/etc/nginx/common/redis-php72.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'redis.mustache', + out=wo_nginx) + wo_nginx.close() + + if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): + if not WOFileUtils.grep(self, "/etc/nginx/conf.d/" + "upstream.conf", + "redis"): + with open("/etc/nginx/conf.d/upstream.conf", + "a") as redis_file: + redis_file.write("upstream redis {\n" + " server 127.0.0.1:6379;\n" + " keepalive 10;\n}") + + if (os.path.isfile("/etc/nginx/nginx.conf") and + not os.path.isfile("/etc/nginx/conf.d/redis.conf")): + with open("/etc/nginx/conf.d/redis.conf", "a") as redis_file: + redis_file.write("# Log format Settings\n" + "log_format rt_cache_redis '$remote_addr" + " $upstream_response_time " + "$srcache_fetch_status [$time_local] '\n" + "'$http_host \"$request\" $status" + " $body_bytes_sent '\n" + "'\"$http_referer\" \"$http_user_agent\"';\n") + + if self.app.pargs.php73: + Log.debug(self, "Setting apt_packages variable for PHP 7.3") + if not WOAptGet.is_installed(self, 'php7.3-fpm'): + if not WOAptGet.is_installed(self, 'php7.2-fpm'): + apt_packages = apt_packages + WOVariables.wo_php + \ + WOVariables.wo_php73 + WOVariables.wo_php_extra + else: + apt_packages = apt_packages + WOVariables.wo_php73 + + if (os.path.isdir("/etc/nginx/common") and + not os.path.isfile("/etc/nginx/common/php73.conf")): + data = dict() + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/locations-wo.conf') + wo_nginx = open('/etc/nginx/common/locations-wo.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'locations-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/php73.conf') + wo_nginx = open('/etc/nginx/common/php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/wpcommon-php73.conf') + wo_nginx = open('/etc/nginx/common/wpcommon-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'wpcommon-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/wpfc-php73.conf') + wo_nginx = open('/etc/nginx/common/wpfc-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'wpfc-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/wpsc-php73.conf') + wo_nginx = open('/etc/nginx/common/wpsc-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'wpsc-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + if (os.path.isfile("/etc/nginx/nginx.conf") and + not os.path.isfile("/etc/nginx/common/redis-php73.conf")): + data = dict() + Log.debug(self, 'Writting the nginx configuration to ' + 'file /etc/nginx/common/redis-php73.conf') + wo_nginx = open('/etc/nginx/common/redis-php73.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'redis-php7.mustache', + out=wo_nginx) + wo_nginx.close() + + if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): + if not WOFileUtils.grep(self, "/etc/nginx/conf.d/upstream.conf", + "php73"): + with open("/etc/nginx/conf.d/upstream.conf", "a") as php_file: + php_file.write("upstream php73 {\nserver" + "unix:/var/run/php/php73-fpm.sock;\n}\n" + "upstream debug73" + " {\nserver 127.0.0.1:9173;\n}\n") + + return(stack.install(apt_packages=apt_packages, packages=packages, + disp_msg=False)) + + +def updatewpuserpassword(self, wo_domain, wo_site_webroot): + + wo_wp_user = '' + wo_wp_pass = '' + WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) + + # Check if wo_domain is wordpress install + try: + is_wp = WOShellExec.cmd_exec(self, "wp --allow-root core" + " version") + except CommandExecutionError as e: + raise SiteError("is WordPress site? check command failed ") + + # Exit if wo_domain is not wordpress install + if not is_wp: + Log.error(self, "{0} does not seem to be a WordPress site" + .format(wo_domain)) + + try: + wo_wp_user = input("Provide WordPress user name [admin]: ") + except Exception as e: + Log.debug(self, "{0}".format(e)) + Log.error(self, "\nCould not update password") + + if wo_wp_user == "?": + Log.info(self, "Fetching WordPress user list") + try: + WOShellExec.cmd_exec(self, "wp --allow-root user list " + "--fields=user_login | grep -v user_login") + except CommandExecutionError as e: + raise SiteError("fetch wp userlist command failed") + + if not wo_wp_user: + wo_wp_user = 'admin' + + try: + is_user_exist = WOShellExec.cmd_exec(self, "wp --allow-root user list " + "--fields=user_login | grep {0}$ " + .format(wo_wp_user)) + except CommandExecutionError as e: + raise SiteError("if wp user exists check command failed") + + if is_user_exist: + try: + wo_wp_pass = getpass.getpass(prompt="Provide password for " + "{0} user: " + .format(wo_wp_user)) + + while not wo_wp_pass: + wo_wp_pass = getpass.getpass(prompt="Provide password for " + "{0} user: " + .format(wo_wp_user)) + except Exception as e: + Log.debug(self, "{0}".format(e)) + raise SiteError("failed to read password input ") + + try: + WOShellExec.cmd_exec(self, "wp --allow-root user update {0}" + " --user_pass={1}" + .format(wo_wp_user, wo_wp_pass)) + except CommandExecutionError as e: + raise SiteError("wp user password update command failed") + Log.info(self, "Password updated successfully") + + else: + Log.error(self, "Invalid WordPress user {0} for {1}." + .format(wo_wp_user, wo_domain)) + + +def display_cache_settings(self, data): + if data['wpsc']: + if data['multisite']: + Log.info(self, "Configure WPSC:" + "\t\thttp://{0}/wp-admin/network/settings.php?" + "page=wpsupercache" + .format(data['site_name'])) + else: + Log.info(self, "Configure WPSC:" + "\t\thttp://{0}/wp-admin/options-general.php?" + "page=wpsupercache" + .format(data['site_name'])) + + if data['wpredis']: + if data['multisite']: + Log.info(self, "Configure redis-cache:" + "\thttp://{0}/wp-admin/network/settings.php?" + "page=redis-cache".format(data['site_name'])) + else: + Log.info(self, "Configure redis-cache:" + "\thttp://{0}/wp-admin/options-general.php?" + "page=redis-cache".format(data['site_name'])) + Log.info(self, "Object Cache:\t\tEnable") + + +def logwatch(self, logfiles): + import zlib + import base64 + import time + from wo.core import logwatch + + def callback(filename, lines): + for line in lines: + if line.find(':::') == -1: + print(line) + else: + data = line.split(':::') + try: + print(data[0], data[1], + zlib.decompress(base64.decodestring(data[2]))) + except Exception as e: + Log.info(time.time(), + 'caught exception rendering a new log line in %s' + % filename) + + l = logwatch.LogWatcher(logfiles, callback) + l.loop() + + +def detSitePar(opts): + """ + Takes dictionary of parsed arguments + 1.returns sitetype and cachetype + 2. raises RuntimeError when wrong combination is used like + "--wp --wpsubdir" or "--html --wp" + """ + sitetype, cachetype = '', '' + typelist = list() + cachelist = list() + for key, val in opts.items(): + if val and key in ['html', 'php', 'mysql', 'wp', + 'wpsubdir', 'wpsubdomain', 'php73']: + typelist.append(key) + elif val and key in ['wpfc', 'wpsc', 'wpredis']: + cachelist.append(key) + + if len(typelist) > 1 or len(cachelist) > 1: + if len(cachelist) > 1: + raise RuntimeError( + "Could not determine cache type." + "Multiple cache parameter entered") + elif False not in [x in ('php', 'mysql', 'html') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php73', 'mysql', 'html') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php73', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('html', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php', 'html') for x in typelist]: + sitetype = 'php' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('php73', 'html') for x in typelist]: + sitetype = 'php73' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wp', 'wpsubdir') for x in typelist]: + sitetype = 'wpsubdir' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wp', 'wpsubdomain') for x in typelist]: + sitetype = 'wpsubdomain' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wp', 'php73') for x in typelist]: + sitetype = 'wp' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wpsubdir', 'php73') for x in typelist]: + sitetype = 'wpsubdir' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wpsubdomain', 'php73') for x in typelist]: + sitetype = 'wpsubdomain' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + else: + raise RuntimeError("could not determine site and cache type") + else: + if not typelist and not cachelist: + sitetype = None + cachetype = None + elif (not typelist or "php73" in typelist) and cachelist: + sitetype = 'wp' + cachetype = cachelist[0] + elif typelist and (not cachelist): + sitetype = typelist[0] + cachetype = 'basic' + else: + sitetype = typelist[0] + cachetype = cachelist[0] + + return (sitetype, cachetype) + + +def generate_random(): + wo_random10 = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 24))) + return wo_random10 + + +def deleteDB(self, dbname, dbuser, dbhost, exit=True): + try: + # Check if Database exists + try: + if WOMysql.check_db_exists(self, dbname): + # Drop database if exists + Log.debug(self, "dropping database `{0}`".format(dbname)) + WOMysql.execute(self, + "drop database `{0}`".format(dbname), + errormsg='Unable to drop database {0}' + .format(dbname)) + except StatementExcecutionError as e: + Log.debug(self, "drop database failed") + Log.info(self, "Database {0} not dropped".format(dbname)) + + except MySQLConnectionError as e: + Log.debug(self, "Mysql Connection problem occured") + + if dbuser != 'root': + Log.debug(self, "dropping user `{0}`".format(dbuser)) + try: + WOMysql.execute(self, + "drop user `{0}`@`{1}`" + .format(dbuser, dbhost)) + except StatementExcecutionError as e: + Log.debug(self, "drop database user failed") + Log.info(self, "Database {0} not dropped".format(dbuser)) + try: + WOMysql.execute(self, "flush privileges") + except StatementExcecutionError as e: + Log.debug(self, "drop database failed") + Log.info(self, "Database {0} not dropped".format(dbname)) + except Exception as e: + Log.error(self, "Error occured while deleting database", exit) + + +def deleteWebRoot(self, webroot): + # do some preprocessing before proceeding + webroot = webroot.strip() + if (webroot == "/var/www/" or webroot == "/var/www" or + webroot == "/var/www/.." or webroot == "/var/www/."): + Log.debug(self, "Tried to remove {0}, but didn't remove it" + .format(webroot)) + return False + + if os.path.isdir(webroot): + Log.debug(self, "Removing {0}".format(webroot)) + WOFileUtils.rm(self, webroot) + return True + Log.debug(self, "{0} does not exist".format(webroot)) + return False + + +def removeNginxConf(self, domain): + if os.path.isfile('/etc/nginx/sites-available/{0}' + .format(domain)): + Log.debug(self, "Removing Nginx configuration") + WOFileUtils.rm(self, '/etc/nginx/sites-enabled/{0}' + .format(domain)) + WOFileUtils.rm(self, '/etc/nginx/sites-available/{0}' + .format(domain)) + WOService.reload_service(self, 'nginx') + WOGit.add(self, ["/etc/nginx"], + msg="Deleted {0} " + .format(domain)) + + +def removeAcmeConf(self, domain): + if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' + .format(domain)): + Log.debug(self, "Removing Acme configuration") + WOFileUtils.rm(self, '/etc/letsencrypt/renewal/{0}_ecc' + .format(domain)) + WOFileUtils.rm(self, '/etc/letsencrypt/live/{0}' + .format(domain)) + WOGit.add(self, ["/etc/letsencrypt"], + msg="Deleted {0} " + .format(domain)) + + +def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', + dbhost=''): + """ + Removes the nginx configuration and database for the domain provided. + doCleanupAction(self, domain='sitename', webroot='', + dbname='', dbuser='', dbhost='') + """ + if domain: + if os.path.isfile('/etc/nginx/sites-available/{0}' + .format(domain)): + removeNginxConf(self, domain) + if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' + .format(domain)): + removeAcmeConf(self, domain) + + if webroot: + deleteWebRoot(self, webroot) + + if dbname: + if not dbuser: + raise SiteError("dbuser not provided") + if not dbhost: + raise SiteError("dbhost not provided") + deleteDB(self, dbname, dbuser, dbhost) + + +# setup letsencrypt for domain + www.domain +def setupLetsEncrypt(self, wo_domain_name): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d www.{0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d www.{0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Cert deployment for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && " + "service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to install certificate", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + +# setup letsencrypt for a subdomain + + +def setupLetsEncryptSubdomain(self, wo_domain_name): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -w /var/www/html " + "-k ec-384 -f" + .format(wo_domain_name)) + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Deploying cert for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to create ssl.conf", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + +# setup letsencrypt for domain + www.domain + + +def setupLetsEncryptWildcard(self, wo_domain_name, wo_acme_dns='dns_cf'): + + if os.path.isfile("/etc/letsencrypt/renewal/{0}_ecc/{0}.conf" + .format(wo_domain_name)): + if os.path.isfile("/etc/letsencrypt/" + "renewal/{0}_ecc/" + "fullchain.cer".format(wo_domain_name)): + Log.debug(self, "Let's Encrypt certificate " + "found for the domain: {0}" + .format(wo_domain_name)) + ssl = archivedCertificateHandle(self, wo_domain_name) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns {1} " + "-k ec-384 -f" + .format(wo_domain_name, wo_acme_dns)) + else: + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns {1} " + "-k ec-384 -f" + .format(wo_domain_name, wo_acme_dns)) + + if ssl: + + try: + Log.info(self, "Deploying SSL cert with acme.sh") + Log.debug(self, "Cert deployment for domain: {0}" + .format(wo_domain_name)) + sslsetup = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"nginx -t && " + "service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name)) + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + updateSiteInfo(self, wo_domain_name, ssl=True) + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + else: + Log.error(self, "Unable to install certificate", False) + Log.error(self, "Please make sure that your site is pointed to \n" + "same server on which " + "you are running Let\'s Encrypt Client " + "\n to allow it to verify the site automatically.") + + +# letsencrypt cert renewal + + +def renewLetsEncrypt(self, wo_domain_name): + + ssl = WOShellExec.cmd_exec( + self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--renew -d {0} --ecc --force" + .format(wo_domain_name)) + + mail_list = '' + if not ssl: + Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!", + False) + if (SSL.getExpirationDays(self, wo_domain_name) > 0): + Log.error(self, "Your current certificate will expire within " + + str(SSL.getExpirationDays(self, wo_domain_name)) + + " days.", False) + else: + Log.error(self, "Your current certificate already expired!", False) + + # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, + # "[FAIL] HTTPS cert renewal {0}".format(wo_domain_name), + # "Hi,\n\nHTTPS certificate renewal for https://{0} + # was unsuccessful.".format(wo_domain_name) + + # "\nPlease check the WordOps log for reason + # The current expiry date is : " + + # str(SSL.getExpirationDate(self, wo_domain_name)) + + # "\n\nFor support visit https://wordops.net/support . + # \n\nBest regards,\nYour WordOps Worker", files=mail_list, + # port=25, isTls=False) + Log.error(self, "Check the WO log for more details " + "`tail /var/log/wo/wordops.log`") + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + # WOSendMail("wordops@{0}".format(wo_domain_name), wo_wp_email, + # "[SUCCESS] Let's Encrypt certificate renewal {0}".format(wo_domain_name), + # "Hi,\n\nYour Let's Encrypt certificate has been renewed for + # https://{0} .".format(wo_domain_name) + + # "\nYour new certificate will expire on : " + + # str(SSL.getExpirationDate(self, wo_domain_name)) + + # "\n\nBest regards,\nYour WordOps Worker", files=mail_list, + # port=25, isTls=False) + +# redirect= False to disable https redirection + + +def setupHsts(self, wo_domain_name): + Log.info( + self, "Adding /var/www/{0}/conf/nginx/hsts.conf" + .format(wo_domain_name)) + + hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + hstsconf.write("more_set_headers " + "\"Strict-Transport-Security: " + "max-age=31536000; " + "'includeSubDomains; " + "preload\";") + hstsconf.close() + return 0 + + +def httpsRedirect(self, wo_domain_name, redirect=True, wildcard=False): + if redirect: + if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" + .format(wo_domain_name)): + WOFileUtils.mvfile(self, + "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" + .format(wo_domain_name), + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + else: + if wildcard: + try: + Log.info( + self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("server {\n" + "\tlisten 80;\n" + + "\tlisten [::]:80;\n" + + "\tserver_name *.{0} {0};\n" + .format(wo_domain_name) + + "\treturn 301 https://$host" + "$request_uri;\n}") + sslconf.close() + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + else: + try: + Log.info( + self, "Adding /etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + + sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write("server {\n" + "\tlisten 80;\n" + + "\tlisten [::]:80;\n" + + "\tserver_name www.{0} {0};\n" + .format(wo_domain_name) + + "\treturn 301 https://{0}" + .format(wo_domain_name)+"$request_uri;\n}") + sslconf.close() + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + + Log.info(self, "Added HTTPS Force Redirection for Site " + " http://{0}".format(wo_domain_name)) + # Nginx Configation into GIT + WOGit.add(self, + ["/etc/nginx"], msg="Adding /etc/nginx/conf.d/" + "force-ssl-{0}.conf".format(wo_domain_name)) + else: + if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)): + WOFileUtils.mvfile(self, "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" + .format(wo_domain_name)) + Log.info(self, "Disabled HTTPS Force Redirection for Site " + " http://{0}".format(wo_domain_name)) + + +def archivedCertificateHandle(self, domain): + Log.warn(self, "You already have an existing certificate " + "for the domain requested.\n" + "(ref: {0}/" + "{1}_ecc/{1}.conf)".format(WOVariables.wo_ssl_archive, domain) + + "\nPlease select an option from below?" + "\n\t1: Reinstall existing certificate" + "\n\t2: Keep the existing certificate for now" + "\n\t3: Renew & replace the certificate (limit ~5 per 7 days)" + "") + check_prompt = input( + "\nType the appropriate number [1-3] or any other key to cancel: ") + if not os.path.isfile("{0}/{1}/fullchain.pem" + .format(WOVariables.wo_ssl_live, domain)): + Log.error( + self, "{0}/{1}/fullchain.pem file is missing." + .format(WOVariables.wo_ssl_live, domain)) + + if check_prompt == "1": + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem " + "--reloadcmd " + "\"service nginx restart\" " + .format(WOVariables.wo_ssl_live, + domain)) + if ssl: + + try: + + if not os.path.isfile("/var/www/{0}/conf/nginx/ssl.conf" + .format(domain)): + Log.info( + self, "Adding /var/www/{0}/conf/nginx/ssl.conf" + .format(domain)) + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(domain), + encoding='utf-8', mode='w') + sslconf.write("listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate " + "{0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, domain)) + sslconf.close() + + updateSiteInfo(self, domain, ssl=True) + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") + + elif (check_prompt == "2"): + Log.info(self, "Using Existing Certificate files") + if not os.path.isfile("{0}/{1}/fullchain.pem" + .format(WOVariables.wo_ssl_live, domain)): + Log.error(self, "Certificate files not found. Skipping.\n" + "Please check if following file exist" + "\n\t/etc/letsencrypt/live/{0}/fullchain.pem\n\t" + "/etc/letsencrypt/live/{0}/key.pem".format(domain)) + + updateSiteInfo(self, domain, ssl=True) + + elif (check_prompt == "3"): + Log.info(self, "Issuing SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--renew -d {0} --ecc " + "--force" + .format(domain)) + + if ssl: + + try: + + WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " + "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem " + "--key-file {0}/{1}/key.pem " + "--fullchain-file " + "{0}/{1}/fullchain.pem " + "ssl_trusted_certificate " + "{0}/{1}/ca.pem;\n" + "--reloadcmd " + "\"service nginx restart\" " + .format(WOVariables.wo_ssl_live, domain)) + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while installing " + "the certificate") + + else: + Log.error(self, "Operation cancelled by user.") + + if os.path.isfile("{0}/conf/nginx/ssl.conf" + .format(domain)): + Log.info(self, "Existing ssl.conf . Backing it up ..") + WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf" + .format(domain), + '/var/www/{0}/conf/nginx/ssl.conf.bak' + .format(domain)) + + return ssl diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 4ea0cd4..c0c623e 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -370,7 +370,7 @@ class WOStackController(CementBaseController): # php73 conf if not os.path.isfile("/etc/nginx/common/php73.conf"): - # data = dict() + # data = dict() Log.debug(self, 'Writting the nginx configuration to ' 'file /etc/nginx/common/php73.conf') wo_nginx = open('/etc/nginx/common/php73.conf', @@ -1418,6 +1418,7 @@ class WOStackController(CementBaseController): self.app.pargs.web = True self.app.pargs.admin = True self.app.pargs.php73 = True + self.app.pargs.redis = True if self.app.pargs.web: self.app.pargs.nginx = True diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index c8f1446..8eb0343 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -38,6 +38,8 @@ class WOStackUpgradeController(CementBaseController): dict(help='Upgrade Redis', action='store_true')), (['--netdata'], dict(help='Upgrade Netdata', action='store_true')), + (['--phpmyadmin'], + dict(help='Upgrade phpMyAdmin', action='store_true')), (['--no-prompt'], dict(help="Upgrade Packages without any prompt", action='store_true')), @@ -83,7 +85,9 @@ class WOStackUpgradeController(CementBaseController): if ((not self.app.pargs.web) and (not self.app.pargs.nginx) and (not self.app.pargs.php) and (not self.app.pargs.mysql) and (not self.app.pargs.all) and (not self.app.pargs.wpcli) and - (not self.app.pargs.netdata) and (not self.app.pargs.redis)): + (not self.app.pargs.netdata) and + (not self.app.pargs.phpmyadmin) and + (not self.app.pargs.redis)): self.app.pargs.web = True if self.app.pargs.all: @@ -144,6 +148,18 @@ class WOStackUpgradeController(CementBaseController): 'kickstart-static64.sh', '/var/lib/wo/tmp/kickstart.sh', 'Netdata']] + if self.app.pargs.phpmyadmin: + if os.path.isdir('/var/www/22222/htdocs/db/pma'): + packages = packages + \ + [["https://files.phpmyadmin.net" + "/phpMyAdmin/{0}/" + "phpMyAdmin-{0}-" + "all-languages" + ".zip".format(WOVariables.wo_phpmyadmin), + "/var/lib/wo/tmp/pma.tar.gz", + "PHPMyAdmin"]] + else: + Log.error(self, "phpMyAdmin isn't installed") if len(packages) or len(apt_packages): @@ -190,6 +206,24 @@ class WOStackUpgradeController(CementBaseController): WOShellExec.cmd_exec(self, "/bin/bash /var/lib/wo/tmp/" "kickstart.sh " "--dont-wait") + if self.app.pargs.phpmyadmin: + Log.info(self, "Upgrading phpMyAdmin, please wait...") + WOExtract.extract( + self, '/var/lib/wo/tmp/pma.tar.gz', '/var/lib/wo/tmp/') + shutil.copyfile('{0}22222/htdocs/db/pma' + '/config.inc.php' + .format(WOVariables.wo_webroot), + '/var/lib/wo/tmp/phpMyAdmin-{0}' + '-all-languages/config.inc.php' + .format(WOVariables.wo_phpmyadmin) + ) + WOFileUtils.remove('{0}22222/htdocs/db/pma' + .format(WOVariables.wo_webroot)) + shutil.move('/var/lib/wo/tmp/phpMyAdmin-{0}' + '-all-languages/' + .format(WOVariables.wo_phpmyadmin), + '{0}22222/htdocs/db/pma/' + .format(WOVariables.wo_webroot)) Log.info(self, "Successfully updated packages") else: diff --git a/wo/core/variables.py b/wo/core/variables.py index 2e1b43f..6df473c 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -1,186 +1,187 @@ -"""WordOps core variable module""" -import platform -import socket -import configparser -import os -import psutil -import datetime - - -class WOVariables(): - """Intialization of core variables""" - - # WordOps version - wo_version = "3.9.5.4" - # WordOps packages versions - wo_wp_cli = "2.2.0" - wo_adminer = "4.7.1" - - # Get WPCLI path - wo_wpcli_path = os.popen('command -v wp | tr "\n" " "').read() - if wo_wpcli_path == '': - wo_wpcli_path = '/usr/local/bin/wp ' - - # get wan network interface name - wo_wan = os.popen("/sbin/ip -4 route get 8.8.8.8 | " - "grep -oP \"dev [^[:space:]]+ \" " - "| cut -d ' ' -f 2").read() - if wo_wan == '': - wo_wan = 'eth0' - - # Current date and time of System - wo_date = datetime.datetime.now().strftime('%d%b%Y%H%M%S') - - # WordOps core variables - wo_platform_distro = os.popen("lsb_release -si " - "| tr -d \'\\n\'").read().lower() - wo_platform_version = platform.linux_distribution()[1] - wo_platform_codename = os.popen("lsb_release -sc | tr -d \'\\n\'").read() - - # Get timezone of system - if os.path.isfile('/etc/timezone'): - with open("/etc/timezone", "r") as tzfile: - wo_timezone = tzfile.read().replace('\n', '') - if wo_timezone == "Etc/UTC": - wo_timezone = "UTC" - else: - wo_timezone = "Europe/Amsterdam" - - # Get FQDN of system - wo_fqdn = socket.getfqdn() - - # WordOps default webroot path - wo_webroot = '/var/www/' - - # WordOps default renewal SSL certificates path - wo_ssl_archive = '/etc/letsencrypt/renewal' - - # WordOps default live SSL certificates path - wo_ssl_live = '/etc/letsencrypt/live' - - # PHP user - wo_php_user = 'www-data' - - # Get git user name and EMail - config = configparser.ConfigParser() - config.read(os.path.expanduser("~")+'/.gitconfig') - try: - wo_user = config['user']['name'] - wo_email = config['user']['email'] - except Exception as e: - wo_user = input("Enter your name: ") - wo_email = input("Enter your email: ") - os.system("/usr/bin/git config --global user.name {0}".format(wo_user)) - os.system( - "/usr/bin/git config --global user.email {0}".format(wo_email)) - - # MySQL hostname - wo_mysql_host = "" - config = configparser.RawConfigParser() - if os.path.exists('/etc/mysql/conf.d/my.cnf'): - cnfpath = "/etc/mysql/conf.d/my.cnf" - else: - cnfpath = os.path.expanduser("~")+"/.my.cnf" - if [cnfpath] == config.read(cnfpath): - try: - wo_mysql_host = config.get('client', 'host') - except configparser.NoOptionError as e: - wo_mysql_host = "localhost" - else: - wo_mysql_host = "localhost" - - # WordOps stack installation variables - # Nginx repo and packages - if wo_platform_distro == 'ubuntu': - if wo_platform_codename == 'trusty': - wo_nginx_repo = ("deb http://download.opensuse.org" - "/repositories/home:" - "/virtubox:/WordOps/xUbuntu_14.04/ /") - else: - wo_nginx_repo = "ppa:wordops/nginx-wo" - elif wo_platform_distro == 'debian': - if wo_platform_codename == 'jessie': - wo_nginx_repo = ("deb http://download.opensuse.org" - "/repositories/home:" - "/virtubox:/WordOps/Debian_8.0/ /") - elif wo_platform_codename == 'stretch': - wo_nginx_repo = ("deb http://download.opensuse.org" - "/repositories/home:" - "/virtubox:/WordOps/Debian_9.0/ /") - else: - wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:" - "/virtubox:/WordOps/Raspbian_9.0/ /") - - wo_nginx = ["nginx-custom", "nginx-wo"] - wo_nginx_key = '188C9FB063F0247A' - - # PHP repo and packages - if wo_platform_distro == 'ubuntu': - wo_php_repo = "ppa:ondrej/php" - wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap", - "php7.2-readline", "php7.2-common", "php7.2-recode", - "php7.2-cli", "php7.2-mbstring", - "php7.2-bcmath", "php7.2-mysql", "php7.2-opcache", - "php7.2-zip", "php7.2-xml", "php7.2-soap"] - wo_php73 = ["php7.3-fpm", "php7.3-curl", "php7.3-gd", "php7.3-imap", - "php7.3-readline", "php7.3-common", "php7.3-recode", - "php7.3-cli", "php7.3-mbstring", - "php7.3-bcmath", "php7.3-mysql", "php7.3-opcache", - "php7.3-zip", "php7.3-xml", "php7.3-soap"] - wo_php_extra = ["php-memcached", "php-imagick", - "graphviz", "php-xdebug", "php-msgpack", "php-redis"] - wo_php_key = '' - else: - wo_php_repo = ( - "deb https://packages.sury.org/php/ {codename} main" - .format(codename=wo_platform_codename)) - wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap", - "php7.2-readline", "php7.2-common", "php7.2-recode", - "php7.2-cli", "php7.2-mbstring", - "php7.2-bcmath", "php7.2-mysql", "php7.2-opcache", - "php7.2-zip", "php7.2-xml", "php7.2-soap"] - wo_php73 = ["php7.3-fpm", "php7.3-curl", "php7.3-gd", "php7.3-imap", - "php7.3-readline", "php7.3-common", "php7.3-recode", - "php7.3-cli", "php7.3-mbstring", - "php7.3-bcmath", "php7.3-mysql", "php7.3-opcache", - "php7.3-zip", "php7.3-xml", "php7.3-soap"] - wo_php_extra = ["php-memcached", "php-imagick", - "graphviz", "php-xdebug", "php-msgpack", "php-redis"] - - wo_php_key = 'AC0E47584A7A714D' - - # MySQL repo and packages - if wo_platform_distro == 'ubuntu': - wo_mysql_repo = ("deb [arch=amd64,ppc64el] " - "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" - "10.3/ubuntu {codename} main" - .format(codename=wo_platform_codename)) - else: - wo_mysql_repo = ("deb [arch=amd64,ppc64el] " - "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" - "10.3/debian {codename} main" - .format(codename=wo_platform_codename)) - wo_mysql = ["mariadb-server", "percona-toolkit", "python3-mysqldb"] - - wo_fail2ban = "fail2ban" - - # Redis repo details - if wo_platform_distro == 'ubuntu': - wo_redis_repo = ("ppa:chris-lea/redis-server") - - else: - wo_redis_repo = ("deb https://packages.sury.org/php/ {codename} all" - .format(codename=wo_platform_codename)) - - wo_redis = ['redis-server', 'php-redis'] - - # Repo path - wo_repo_file = "wo-repo.list" - wo_repo_file_path = ("/etc/apt/sources.list.d/" + wo_repo_file) - - # Application dabase file path - basedir = os.path.abspath(os.path.dirname('/var/lib/wo/')) - wo_db_uri = 'sqlite:///' + os.path.join(basedir, 'dbase.db') - - def __init__(self): - pass +"""WordOps core variable module""" +import platform +import socket +import configparser +import os +import psutil +import datetime + + +class WOVariables(): + """Intialization of core variables""" + + # WordOps version + wo_version = "3.9.5.4" + # WordOps packages versions + wo_wp_cli = "2.2.0" + wo_adminer = "4.7.1" + wo_phpmyadmin = "4.9.0.1" + + # Get WPCLI path + wo_wpcli_path = os.popen('command -v wp | tr "\n" " "').read() + if wo_wpcli_path == '': + wo_wpcli_path = '/usr/local/bin/wp ' + + # get wan network interface name + wo_wan = os.popen("/sbin/ip -4 route get 8.8.8.8 | " + "grep -oP \"dev [^[:space:]]+ \" " + "| cut -d ' ' -f 2").read() + if wo_wan == '': + wo_wan = 'eth0' + + # Current date and time of System + wo_date = datetime.datetime.now().strftime('%d%b%Y%H%M%S') + + # WordOps core variables + wo_platform_distro = os.popen("lsb_release -si " + "| tr -d \'\\n\'").read().lower() + wo_platform_version = platform.linux_distribution()[1] + wo_platform_codename = os.popen("lsb_release -sc | tr -d \'\\n\'").read() + + # Get timezone of system + if os.path.isfile('/etc/timezone'): + with open("/etc/timezone", "r") as tzfile: + wo_timezone = tzfile.read().replace('\n', '') + if wo_timezone == "Etc/UTC": + wo_timezone = "UTC" + else: + wo_timezone = "Europe/Amsterdam" + + # Get FQDN of system + wo_fqdn = socket.getfqdn() + + # WordOps default webroot path + wo_webroot = '/var/www/' + + # WordOps default renewal SSL certificates path + wo_ssl_archive = '/etc/letsencrypt/renewal' + + # WordOps default live SSL certificates path + wo_ssl_live = '/etc/letsencrypt/live' + + # PHP user + wo_php_user = 'www-data' + + # Get git user name and EMail + config = configparser.ConfigParser() + config.read(os.path.expanduser("~")+'/.gitconfig') + try: + wo_user = config['user']['name'] + wo_email = config['user']['email'] + except Exception as e: + wo_user = input("Enter your name: ") + wo_email = input("Enter your email: ") + os.system("/usr/bin/git config --global user.name {0}".format(wo_user)) + os.system( + "/usr/bin/git config --global user.email {0}".format(wo_email)) + + # MySQL hostname + wo_mysql_host = "" + config = configparser.RawConfigParser() + if os.path.exists('/etc/mysql/conf.d/my.cnf'): + cnfpath = "/etc/mysql/conf.d/my.cnf" + else: + cnfpath = os.path.expanduser("~")+"/.my.cnf" + if [cnfpath] == config.read(cnfpath): + try: + wo_mysql_host = config.get('client', 'host') + except configparser.NoOptionError as e: + wo_mysql_host = "localhost" + else: + wo_mysql_host = "localhost" + + # WordOps stack installation variables + # Nginx repo and packages + if wo_platform_distro == 'ubuntu': + if wo_platform_codename == 'trusty': + wo_nginx_repo = ("deb http://download.opensuse.org" + "/repositories/home:" + "/virtubox:/WordOps/xUbuntu_14.04/ /") + else: + wo_nginx_repo = "ppa:wordops/nginx-wo" + elif wo_platform_distro == 'debian': + if wo_platform_codename == 'jessie': + wo_nginx_repo = ("deb http://download.opensuse.org" + "/repositories/home:" + "/virtubox:/WordOps/Debian_8.0/ /") + elif wo_platform_codename == 'stretch': + wo_nginx_repo = ("deb http://download.opensuse.org" + "/repositories/home:" + "/virtubox:/WordOps/Debian_9.0/ /") + else: + wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:" + "/virtubox:/WordOps/Raspbian_9.0/ /") + + wo_nginx = ["nginx-custom", "nginx-wo"] + wo_nginx_key = '188C9FB063F0247A' + + # PHP repo and packages + if wo_platform_distro == 'ubuntu': + wo_php_repo = "ppa:ondrej/php" + wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap", + "php7.2-readline", "php7.2-common", "php7.2-recode", + "php7.2-cli", "php7.2-mbstring", + "php7.2-bcmath", "php7.2-mysql", "php7.2-opcache", + "php7.2-zip", "php7.2-xml", "php7.2-soap"] + wo_php73 = ["php7.3-fpm", "php7.3-curl", "php7.3-gd", "php7.3-imap", + "php7.3-readline", "php7.3-common", "php7.3-recode", + "php7.3-cli", "php7.3-mbstring", + "php7.3-bcmath", "php7.3-mysql", "php7.3-opcache", + "php7.3-zip", "php7.3-xml", "php7.3-soap"] + wo_php_extra = ["php-memcached", "php-imagick", + "graphviz", "php-xdebug", "php-msgpack", "php-redis"] + wo_php_key = '' + else: + wo_php_repo = ( + "deb https://packages.sury.org/php/ {codename} main" + .format(codename=wo_platform_codename)) + wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap", + "php7.2-readline", "php7.2-common", "php7.2-recode", + "php7.2-cli", "php7.2-mbstring", + "php7.2-bcmath", "php7.2-mysql", "php7.2-opcache", + "php7.2-zip", "php7.2-xml", "php7.2-soap"] + wo_php73 = ["php7.3-fpm", "php7.3-curl", "php7.3-gd", "php7.3-imap", + "php7.3-readline", "php7.3-common", "php7.3-recode", + "php7.3-cli", "php7.3-mbstring", + "php7.3-bcmath", "php7.3-mysql", "php7.3-opcache", + "php7.3-zip", "php7.3-xml", "php7.3-soap"] + wo_php_extra = ["php-memcached", "php-imagick", + "graphviz", "php-xdebug", "php-msgpack", "php-redis"] + + wo_php_key = 'AC0E47584A7A714D' + + # MySQL repo and packages + if wo_platform_distro == 'ubuntu': + wo_mysql_repo = ("deb [arch=amd64,ppc64el] " + "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" + "10.3/ubuntu {codename} main" + .format(codename=wo_platform_codename)) + else: + wo_mysql_repo = ("deb [arch=amd64,ppc64el] " + "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" + "10.3/debian {codename} main" + .format(codename=wo_platform_codename)) + wo_mysql = ["mariadb-server", "percona-toolkit", "python3-mysqldb"] + + wo_fail2ban = "fail2ban" + + # Redis repo details + if wo_platform_distro == 'ubuntu': + wo_redis_repo = ("ppa:chris-lea/redis-server") + + else: + wo_redis_repo = ("deb https://packages.sury.org/php/ {codename} all" + .format(codename=wo_platform_codename)) + + wo_redis = ['redis-server', 'php-redis'] + + # Repo path + wo_repo_file = "wo-repo.list" + wo_repo_file_path = ("/etc/apt/sources.list.d/" + wo_repo_file) + + # Application dabase file path + basedir = os.path.abspath(os.path.dirname('/var/lib/wo/')) + wo_db_uri = 'sqlite:///' + os.path.join(basedir, 'dbase.db') + + def __init__(self): + pass From 4f5b61e7fea0b6176555d931b482527fa7ab7555 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 14 Jul 2019 22:53:48 +0200 Subject: [PATCH 08/44] Fix dns validation letsencrypt --- wo/cli/plugins/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 8d1f69f..e20a6d6 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -812,7 +812,7 @@ class WOSiteUpdateController(CementBaseController): (['--dns'], dict(help="choose dns provider api for letsencrypt", action='store' or 'store_const', - const='cf', nargs='?')), + const='dns_cf', nargs='?')), (['--hsts'], dict(help="configure hsts for the site", action='store' or 'store_const', From 945677bdd6b0b10e6cb11e971383b217d9a1e3f6 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 00:45:24 +0200 Subject: [PATCH 09/44] Remove reference to memcached --- wo/cli/plugins/stack_services.py | 39 -------------------------------- 1 file changed, 39 deletions(-) diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py index 872e518..a51410d 100644 --- a/wo/cli/plugins/stack_services.py +++ b/wo/cli/plugins/stack_services.py @@ -12,11 +12,6 @@ class WOStackStatusController(CementBaseController): stacked_on = 'stack' stacked_type = 'embedded' description = 'Check the stack status' - arguments = [ - (['--memcached'], - dict(help='start/stop/restart memcached', - action='store_true')), - ] @expose(help="Start stack services") def start(self): @@ -25,7 +20,6 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or - self.app.pargs.memcached or self.app.pargs.redis or self.app.pargs.fail2ban or self.app.pargs.netdata): @@ -68,12 +62,6 @@ class WOStackStatusController(CementBaseController): Log.warn(self, "Remote MySQL found, " "Unable to check MySQL service status") - if self.app.pargs.memcached: - if WOAptGet.is_installed(self, 'memcached'): - services = services + ['memcached'] - else: - Log.info(self, "Memcached is not installed") - if self.app.pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): services = services + ['redis-server'] @@ -97,7 +85,6 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or - self.app.pargs.memcached or self.app.pargs.redis): self.app.pargs.nginx = True self.app.pargs.php = True @@ -139,12 +126,6 @@ class WOStackStatusController(CementBaseController): Log.warn(self, "Remote MySQL found, " "Unable to check MySQL service status") - if self.app.pargs.memcached: - if WOAptGet.is_installed(self, 'memcached'): - services = services + ['memcached'] - else: - Log.info(self, "Memcached is not installed") - if self.app.pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): services = services + ['redis-server'] @@ -212,12 +193,6 @@ class WOStackStatusController(CementBaseController): Log.warn(self, "Remote MySQL found, " "Unable to check MySQL service status") - if self.app.pargs.memcached: - if WOAptGet.is_installed(self, 'memcached'): - services = services + ['memcached'] - else: - Log.info(self, "Memcached is not installed") - if self.app.pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): services = services + ['redis-server'] @@ -241,7 +216,6 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or - self.app.pargs.memcached or self.app.pargs.redis or self.app.pargs.fail2ban): self.app.pargs.nginx = True @@ -284,12 +258,6 @@ class WOStackStatusController(CementBaseController): Log.warn(self, "Remote MySQL found, " "Unable to check MySQL service status") - if self.app.pargs.memcached: - if WOAptGet.is_installed(self, 'memcached'): - services = services + ['memcached'] - else: - Log.info(self, "Memcached is not installed") - if self.app.pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): services = services + ['redis-server'] @@ -313,7 +281,6 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or - self.app.pargs.memcached or self.app.pargs.redis or self.app.pargs.fail2ban): self.app.pargs.nginx = True @@ -357,12 +324,6 @@ class WOStackStatusController(CementBaseController): Log.warn(self, "Remote MySQL found, " "Unable to check MySQL service status") - if self.app.pargs.memcached: - if WOAptGet.is_installed(self, 'memcached'): - services = services + ['memcached'] - else: - Log.info(self, "Memcached is not installed") - if self.app.pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): services = services + ['redis-server'] From 65332103e17cb6dc0988d7e2d30f8ca9e12f3220 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 05:02:49 +0200 Subject: [PATCH 10/44] Fix typo --- wo/cli/plugins/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index e20a6d6..327f587 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1271,7 +1271,7 @@ class WOSiteUpdateController(CementBaseController): if self.app.pargs.letsencrypt == "on": setupLetsEncrypt(self, wo_domain) httpsRedirect(self, wo_domain) - elif self.app.pargs.letsencrypt == "subodmain": + elif self.app.pargs.letsencrypt == "subdomain": setupLetsEncryptSubdomain(self, wo_domain) httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": From 53b85719f13a09ada74a1338d3c05a10f45fa5ef Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 10:43:40 +0200 Subject: [PATCH 11/44] Refactor Letsencrypt stack --- CHANGELOG.md | 12 +- README.md | 10 +- wo/cli/plugins/site.py | 19 ++- wo/cli/plugins/site_functions.py | 257 +++++++------------------------ 4 files changed, 88 insertions(+), 210 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ca4ab..67640ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Releases -### v3.9.x - [Unreleased] +### v3.9.6 - [Unreleased] ---- +#### Added + +- New Nginx package on Ubuntu with Cloudflare HTTP/2 HPACK and Dynamic TLS records +- phpMyAdmin upgrade with `wo stack upgrade --phpmyadmin` +- Wildcard SSL Certificates support with DNS validation + +#### Fixed + +- Nginx was not reloaded after enabling HSTS ### v3.9.5.4 - 2019-07-13 diff --git a/README.md b/README.md index 9a9f7d1..177cb75 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,12 @@ - **Easy to install** : One step automated installer with migration from EasyEngine v3 support - **Fast deployment** : Fast and automated WordPress, Nginx, PHP, MySQL & Redis installation -- **Up-to-date** : Nginx 1.16.0 with TLS v1.3 & Brotli support, PHP 7.2 & 7.3, MariaDB 10.3 & Redis 5.0 +- **Custom Nginx build** : Nginx 1.16.0 - TLS v1.3 Cloudflare HTTP/2 HPACK & Brotli support +- **Up-to-date** : PHP 7.2 & 7.3, MariaDB 10.3 & Redis 5.0 - **Secured** : Hardened WordPress security with strict Nginx location directives - **Powerful** : Optimized Nginx configurations with multiple cache backends support -- **SSL** : Let's Encrypt SSL certificates handled by acme.sh -- **Modern** : Secured SSL/TLS encryption with strong ciphers_suite, modern TLS protocols and HSTS support +- **SSL** : Domain, Subdomain & Wildcard Let's Encrypt SSL certificates handled by acme.sh +- **Modern** : Strong ciphers_suite, modern TLS protocols and HSTS support (Grade A+ on ssllabs) - **Monitoring** : Live Nginx vhost traffic with ngx_vts_module and server monitoring with Netdata ## Requirements @@ -167,12 +168,11 @@ Apps & Tools shipped with WordOps : - [Composer](https://github.com/composer/composer) - [Adminer](https://www.adminer.org/) - [phpRedisAdmin](https://github.com/erikdubbelboer/phpRedisAdmin) -- [PHPMemcachedAdmin](https://github.com/elijaa/phpmemcachedadmin) - [opcacheGUI](https://github.com/amnuts/opcache-gui) - [eXtplorer](https://github.com/soerennb/extplorer) - [MySQLTuner](https://github.com/major/MySQLTuner-perl/) - [Webgrind](https://github.com/jokkedk/webgrind) - +- [MySQLTuner](https://github.com/major/MySQLTuner-perl) ## License diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 327f587..2bfe7cf 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1264,19 +1264,30 @@ class WOSiteUpdateController(CementBaseController): " http://{0}".format(wo_domain)) return 0 + if self.app.pargs.dns: + wo_acme_dns = pargs.dns + if pargs.letsencrypt: if data['letsencrypt'] is True: if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot)): if self.app.pargs.letsencrypt == "on": - setupLetsEncrypt(self, wo_domain) + if self.app.pargs.dns: + setupLetsEncrypt(self, wo_domain, False, + False, True, wo_acme_dns) + else: + setupLetsEncrypt(self, wo_domain) httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "subdomain": - setupLetsEncryptSubdomain(self, wo_domain) + if self.app.pargs.dns: + setupLetsEncrypt(self, wo_domain, True, False, + True, wo_acme_dns) + else: + setupLetsEncrypt(self, wo_domain, True) httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": - wo_acme_dns = pargs.dns - setupLetsEncryptWildcard(self, wo_domain, wo_acme_dns) + setupLetsEncrypt(self, wo_domain, false, True, + True, wo_acme_dns) httpsRedirect(self, wo_domain, True, True) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 8697692..89d09bc 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -339,7 +339,8 @@ def setupwordpress(self, data): .format(WOVariables.wo_wpcli_path) + "config create " + "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbhost=\'{2}\' " - .format(data['wo_db_name'], wo_wp_prefix, data['wo_db_host']) + + .format(data['wo_db_name'], + wo_wp_prefix, data['wo_db_host']) + "--dbuser=\'{0}\' --dbpass=\'{1}\' " "--extra-php< Date: Mon, 15 Jul 2019 10:47:20 +0200 Subject: [PATCH 12/44] Fix typo --- wo/cli/plugins/site.py | 2 +- wo/cli/plugins/site_functions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 2bfe7cf..9281f71 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1286,7 +1286,7 @@ class WOSiteUpdateController(CementBaseController): setupLetsEncrypt(self, wo_domain, True) httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": - setupLetsEncrypt(self, wo_domain, false, True, + setupLetsEncrypt(self, wo_domain, False, True, True, wo_acme_dns) httpsRedirect(self, wo_domain, True, True) else: diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 89d09bc..1cf9a70 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1268,8 +1268,8 @@ def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', # setup letsencrypt for domain + www.domain -def setupLetsEncrypt(self, wo_domain_name, subdomain=false, wildcard=false, - wo_dns=false, wo_acme_dns='dns_cf'): +def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, + wo_dns=False, wo_acme_dns='dns_cf'): if os.path.isfile("/etc/letsencrypt/" "renewal/{0}_ecc/" From 90e3ebb88cbf1b7cff0c1f4f34ad83f0531945ba Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 12:36:29 +0200 Subject: [PATCH 13/44] Implement dns validation on site create --- README.md | 1 + install | 6 +++--- wo/cli/plugins/site.py | 39 ++++++++++++++++++++++++++------------- wo/cli/plugins/stack.py | 3 ++- wo/core/variables.py | 4 ++++ 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 177cb75..a15cbc0 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ - Ubuntu 19.04 (Disco) - Debian 8 (Jessie) - Debian 9 (Stretch) +- Debian 10 (Buster) - Raspbian 9 (Stretch) ### Ports requirements diff --git a/install b/install index 842d006..9a06f37 100755 --- a/install +++ b/install @@ -144,9 +144,9 @@ if [ -z "$wo_force_install" ]; then wo_lib_echo_fail "other Linux distributions and perhaps even Unix deratives." exit 100 else - check_wo_linux_distro=$(lsb_release -sc | grep -E "trusty|xenial|bionic|disco|jessie|stretch") + check_wo_linux_distro=$(lsb_release -sc | grep -E "trusty|xenial|bionic|disco|jessie|stretch|buster") if [ -z "$check_wo_linux_distro" ]; then - wo_lib_echo_fail "WordOps (wo) only supports Ubuntu 14.04/16.04/18.04/19.04 LTS, Debian 8.x, Debian 9.x and Raspbian 9.x" + wo_lib_echo_fail "WordOps (wo) only supports Ubuntu 14.04/16.04/18.04/19.04 LTS, Debian 8.x/9.x/10.x and Raspbian 9.x" exit 100 fi fi @@ -633,7 +633,7 @@ wo_tweak_kernel() { if [ "$WO_ARCH" = "x86_64" ]; then rm -f /etc/sysctl.d/60-ubuntu-nginx-web-server.conf wget -qO /etc/sysctl.d/60-wo-tweaks.conf https://raw.githubusercontent.com/WordOps/WordOps/updating-configuration/wo/cli/templates/sysctl.mustache - if [ "$wo_distro_version" = "bionic" ] || [ "$wo_distro_version" = "disco" ]; then + if [ "$wo_distro_version" = "bionic" ] || [ "$wo_distro_version" = "disco" ] || [ "$wo_distro_version" = "buster" ]; then modprobe tcp_bbr && echo 'tcp_bbr' >> /etc/modules-load.d/bbr.conf echo -e '\nnet.ipv4.tcp_congestion_control = bbr\nnet.ipv4.tcp_notsent_lowat = 16384' >> /etc/sysctl.d/60-wo-tweaks.conf else diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 9281f71..6457bdd 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -319,7 +319,8 @@ class WOSiteCreateController(CementBaseController): dict(help="create WordPress multisite with subdomain setup", action='store_true')), (['--wpfc'], - dict(help="create WordPress single/multi site with wpfc cache", + dict(help="create WordPress single/multi site with " + "Nginx fastcgi_cache", action='store_true')), (['--wpsc'], dict(help="create WordPress single/multi site with wpsc cache", @@ -328,7 +329,7 @@ class WOSiteCreateController(CementBaseController): dict(help="create WordPress single/multi site " "with redis cache", action='store_true')), - (['-le', '--letsencrypt'], + (['--le', '--letsencrypt'], dict(help="configure letsencrypt ssl for the site", action='store' or 'store_const', choices=('on', 'subdomain', 'wildcard'), @@ -336,8 +337,7 @@ class WOSiteCreateController(CementBaseController): (['--dns'], dict(help="choose dns provider api for letsencrypt", action='store' or 'store_const', - choices=('cf', 'do'), - const='cf', nargs='?')), + const='dns_cf', nargs='?')), (['--hsts'], dict(help="enable HSTS for site secured with letsencrypt", action='store_true')), @@ -731,18 +731,30 @@ class WOSiteCreateController(CementBaseController): Log.error(self, "Check the log for details: " "`tail /var/log/wo/wordops.log` and please try again") + if self.app.pargs.dns: + wo_acme_dns = pargs.dns + if self.app.pargs.letsencrypt: data['letsencrypt'] = True letsencrypt = True if data['letsencrypt'] is True: if self.app.pargs.letsencrypt == "on": - setupLetsEncrypt(self, wo_domain) + if self.app.pargs.dns: + setupLetsEncrypt(self, wo_domain, False, + False, True, wo_acme_dns) + else: + setupLetsEncrypt(self, wo_domain) httpsRedirect(self, wo_domain) - elif self.app.pargs.letsencrypt == "subodmain": - setupLetsEncryptSubdomain(self, wo_domain) + elif self.app.pargs.letsencrypt == "subdomain": + if self.app.pargs.dns: + setupLetsEncrypt(self, wo_domain, True, False, + True, wo_acme_dns) + else: + setupLetsEncrypt(self, wo_domain, True) httpsRedirect(self, wo_domain) elif self.app.pargs.letsencrypt == "wildcard": - setupLetsEncryptWildcard(self, wo_domain) + setupLetsEncrypt(self, wo_domain, False, True, + True, wo_acme_dns) httpsRedirect(self, wo_domain, True, True) if self.app.pargs.hsts: @@ -804,7 +816,7 @@ class WOSiteUpdateController(CementBaseController): dict(help="update to wpsc cache", action='store_true')), (['--wpredis'], dict(help="update to redis cache", action='store_true')), - (['-le', '--letsencrypt'], + (['--le', '--letsencrypt'], dict(help="configure letsencrypt ssl for the site", action='store' or 'store_const', choices=('on', 'off', 'renew', 'subdomain', 'wildcard'), @@ -844,7 +856,8 @@ class WOSiteUpdateController(CementBaseController): if not (pargs.php or pargs.php73 or pargs.mysql or pargs.wp or pargs.wpsubdir or pargs.wpsubdomain or pargs.wpfc or pargs.wpsc or - pargs.wpredis or pargs.letsencrypt or pargs.hsts): + pargs.wpredis or pargs.letsencrypt or pargs.hsts or + pargs.dns): Log.error(self, "Please provide options to update sites.") if pargs.all: @@ -1264,10 +1277,10 @@ class WOSiteUpdateController(CementBaseController): " http://{0}".format(wo_domain)) return 0 - if self.app.pargs.dns: - wo_acme_dns = pargs.dns - if pargs.letsencrypt: + if self.app.pargs.dns: + wo_acme_dns = pargs.dns + if data['letsencrypt'] is True: if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot)): diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index c0c623e..0666209 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -94,7 +94,8 @@ class WOStackController(CementBaseController): if set(WOVariables.wo_mysql).issubset(set(apt_packages)): # add mariadb repository excepted on raspbian and ubuntu 19.04 if ((not WOVariables.wo_platform_codename == 'disco') and - (not WOVariables.wo_platform_distro == 'raspbian')): + (not WOVariables.wo_platform_distro == 'raspbian') and + (not WOVariables.wo_platform_codename == 'buster')): Log.info(self, "Adding repository for MySQL, please wait...") mysql_pref = ("Package: *\nPin: origin " "sfo1.mirrors.digitalocean.com" diff --git a/wo/core/variables.py b/wo/core/variables.py index 6df473c..1963bca 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -108,6 +108,10 @@ class WOVariables(): wo_nginx_repo = ("deb http://download.opensuse.org" "/repositories/home:" "/virtubox:/WordOps/Debian_9.0/ /") + elif wo_platform_codename == 'buster': + wo_nginx_repo = ("deb http://download.opensuse.org" + "/repositories/home:" + "/virtubox:/WordOps/Debian_10/ /") else: wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:" "/virtubox:/WordOps/Raspbian_9.0/ /") From 7e69933bdee62cea92eed861d6c2f8fa7d873ce4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 12:50:23 +0200 Subject: [PATCH 14/44] Fix letsencrypt --- wo/cli/plugins/site.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 6457bdd..3937284 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -731,10 +731,9 @@ class WOSiteCreateController(CementBaseController): Log.error(self, "Check the log for details: " "`tail /var/log/wo/wordops.log` and please try again") - if self.app.pargs.dns: - wo_acme_dns = pargs.dns - if self.app.pargs.letsencrypt: + if self.app.pargs.dns: + wo_acme_dns = pargs.dns data['letsencrypt'] = True letsencrypt = True if data['letsencrypt'] is True: @@ -1277,10 +1276,9 @@ class WOSiteUpdateController(CementBaseController): " http://{0}".format(wo_domain)) return 0 - if pargs.letsencrypt: + if self.app.pargs.letsencrypt: if self.app.pargs.dns: wo_acme_dns = pargs.dns - if data['letsencrypt'] is True: if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot)): From 2052dd96725122bb04b6f854188a18ab9b1b823c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 14:00:15 +0200 Subject: [PATCH 15/44] Fix -le --- wo/cli/plugins/site.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 3937284..af96b3e 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -329,7 +329,7 @@ class WOSiteCreateController(CementBaseController): dict(help="create WordPress single/multi site " "with redis cache", action='store_true')), - (['--le', '--letsencrypt'], + (['-le', '--letsencrypt'], dict(help="configure letsencrypt ssl for the site", action='store' or 'store_const', choices=('on', 'subdomain', 'wildcard'), @@ -732,19 +732,12 @@ class WOSiteCreateController(CementBaseController): "`tail /var/log/wo/wordops.log` and please try again") if self.app.pargs.letsencrypt: - if self.app.pargs.dns: - wo_acme_dns = pargs.dns data['letsencrypt'] = True letsencrypt = True + if self.app.pargs.dns: + wo_acme_dns = pargs.dns if data['letsencrypt'] is True: - if self.app.pargs.letsencrypt == "on": - if self.app.pargs.dns: - setupLetsEncrypt(self, wo_domain, False, - False, True, wo_acme_dns) - else: - setupLetsEncrypt(self, wo_domain) - httpsRedirect(self, wo_domain) - elif self.app.pargs.letsencrypt == "subdomain": + if self.app.pargs.letsencrypt == "subdomain": if self.app.pargs.dns: setupLetsEncrypt(self, wo_domain, True, False, True, wo_acme_dns) @@ -755,6 +748,13 @@ class WOSiteCreateController(CementBaseController): setupLetsEncrypt(self, wo_domain, False, True, True, wo_acme_dns) httpsRedirect(self, wo_domain, True, True) + else: + if self.app.pargs.dns: + setupLetsEncrypt(self, wo_domain, False, + False, True, wo_acme_dns) + else: + setupLetsEncrypt(self, wo_domain) + httpsRedirect(self, wo_domain) if self.app.pargs.hsts: setupHsts(self, wo_domain) @@ -815,7 +815,7 @@ class WOSiteUpdateController(CementBaseController): dict(help="update to wpsc cache", action='store_true')), (['--wpredis'], dict(help="update to redis cache", action='store_true')), - (['--le', '--letsencrypt'], + (['-le', '--letsencrypt'], dict(help="configure letsencrypt ssl for the site", action='store' or 'store_const', choices=('on', 'off', 'renew', 'subdomain', 'wildcard'), From 26abac5a934456da3594b1c87a21f6da83813e08 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 15:39:45 +0200 Subject: [PATCH 16/44] add --letsencrypt=clean --- CHANGELOG.md | 2 ++ config/bash_completion.d/wo_auto.rc | 18 +++++------ wo/cli/plugins/site.py | 46 +++++++++++++++++++---------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67640ba..2c5ba34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New Nginx package on Ubuntu with Cloudflare HTTP/2 HPACK and Dynamic TLS records - phpMyAdmin upgrade with `wo stack upgrade --phpmyadmin` - Wildcard SSL Certificates support with DNS validation +- Acme challenge validation with DNS API (Cloudflare, DigitalOcean, etc ..) on domain, subdomain, and wildcard +- Flag `--letsencrypt=clean` to purge a previous SSL configuration #### Fixed diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index c8f4d45..42f039c 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -159,13 +159,13 @@ _wo_complete() "create") COMPREPLY=( $(compgen \ - -W "--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --proxy= --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" \ + -W "--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --proxy= --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" \ -- $cur) ) ;; "update") COMPREPLY=( $(compgen \ - -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" \ + -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le --le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" \ -- $cur) ) ;; "delete") @@ -211,9 +211,9 @@ _wo_complete() "--wp") if [ ${COMP_WORDS[1]} != "debug" ]; then if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --letsencrypt --php73" + retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --letsencrypt -le --php73" elif [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=subdomain --le=off --le=wildcard --dns --dns=cf --dns=do" + retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=subdomain -le=off -le=wildcard --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -230,9 +230,9 @@ _wo_complete() "--wpsubdir" | "--wpsubdomain") if [ ${COMP_WORDS[1]} != "debug" ]; then if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--wpsc --wpfc --user --email --pass --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --le --le=subdomain --le=wildcard --php73 --dns --dns=cf --dns=do" + retlist="--wpsc --wpfc --user --email --pass --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le -le=subdomain -le=wildcard --php73 --dns --dns=dns_cf --dns=dns_do" elif [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=off --le=subdomain --le=wildcard --dns --dns=cf --dns=do" + retlist="--wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=off -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -248,7 +248,7 @@ _wo_complete() "--wpredis" | "--wpfc" | "--wpsc" | "--wpsubdir" | "--wpsubdomain" | "--user" | "--pass" | "--email" | "--wp") if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --php73 --letsencrypt --letsencrypt=subdomain --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" + retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --php73 --letsencrypt --letsencrypt=subdomain -le -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -261,7 +261,7 @@ _wo_complete() "--wpredis" | "--wpfc") if [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew --le --le=off --le=subdomain --le=wildcard --dns --dns=cf --dns=do" + retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=off -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -363,7 +363,7 @@ _wo_complete() case "$mprev" in "--user" | "--email" | "--pass") if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --le --le=subdomain --le=wildcard --dns --dns=cf --dns=do" + retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" fi ret="${retlist[@]/$prev}" COMPREPLY=( $(compgen \ diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index af96b3e..b0aab8e 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -818,7 +818,8 @@ class WOSiteUpdateController(CementBaseController): (['-le', '--letsencrypt'], dict(help="configure letsencrypt ssl for the site", action='store' or 'store_const', - choices=('on', 'off', 'renew', 'subdomain', 'wildcard'), + choices=('on', 'off', 'renew', 'subdomain', + 'wildcard', 'clean'), const='on', nargs='?')), (['--dns'], dict(help="choose dns provider api for letsencrypt", @@ -1184,6 +1185,9 @@ class WOSiteUpdateController(CementBaseController): elif pargs.letsencrypt == 'off': data['letsencrypt'] = False letsencrypt = False + elif pargs.letsencrypt == 'clean': + data['letsencrypt'] = False + letsencrypt = False if letsencrypt is check_ssl: if letsencrypt is False: @@ -1324,23 +1328,35 @@ class WOSiteUpdateController(CementBaseController): ".PLEASE renew soon . ") elif data['letsencrypt'] is False: - if os.path.isfile("{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot)): - Log.info(self, 'Setting Nginx configuration') - WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot), - '{0}/conf/nginx/ssl.conf.disabled' - .format(wo_site_webroot)) - httpsRedirect(self, wo_domain, False) - if os.path.isfile("{0}/conf/nginx/hsts.conf" + if self.app.pargs.letsencrypt == "off": + if os.path.isfile("{0}/conf/nginx/ssl.conf" .format(wo_site_webroot)): - WOFileUtils.mvfile(self, "{0}/conf/nginx/hsts.conf" + Log.info(self, 'Setting Nginx configuration') + WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf" .format(wo_site_webroot), - '{0}/conf/nginx/hsts.conf.disabled' + '{0}/conf/nginx/ssl.conf.disabled' .format(wo_site_webroot)) - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") + httpsRedirect(self, wo_domain, False) + if os.path.isfile("{0}/conf/nginx/hsts.conf" + .format(wo_site_webroot)): + WOFileUtils.mvfile(self, "{0}/conf/nginx/hsts.conf" + .format(wo_site_webroot), + '{0}/conf/nginx/' + 'hsts.conf.disabled' + .format(wo_site_webroot)) + if self.app.pargs.letsencrypt == "clean": + if os.path.isfile("{0}/conf/nginx/ssl.conf" + .format(wo_site_webroot)): + WOFileUtils.remove("{0}/conf/nginx/ssl.conf" + .format(wo_site_webroot)) + WOFileUtils.remove("/etc/letsencrypt/live" + "/{0}".format(wo_domain)) + WOFileUtils.remove("/etc/nginx/conf.d/" + "force-ssl-{0}.conf" + .format(wo_domain_name)) + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") # Log.info(self,"Removing Cron Job set for cert # auto-renewal") WOCron.remove_cron(self,'wo site # update {0} --le=renew --min_expiry_limit 30 From 838a78f8ba9fb0856f6b50d0392a43febe31c4b4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 16:10:24 +0200 Subject: [PATCH 17/44] Add missing message about Nginx on debian --- wo/cli/plugins/stack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 0666209..02bb8a4 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -195,6 +195,7 @@ class WOStackController(CementBaseController): WORepo.add(self, ppa=WOVariables.wo_nginx_repo) Log.debug(self, 'Adding ppa for Nginx') else: + Log.info(self, "Adding repository for NGINX, please wait...") WORepo.add(self, repo_url=WOVariables.wo_nginx_repo) Log.debug(self, 'Adding repository for Nginx') WORepo.add_key(self, WOVariables.wo_nginx_key) From dc703c56e0c99b2bdef24d52b7e852fb0f805cf0 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 17:54:56 +0200 Subject: [PATCH 18/44] Force bash-completion update --- CHANGELOG.md | 1 + docs/wo.8 | 4 ++-- install | 2 ++ wo/core/variables.py | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c5ba34..533e712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Wildcard SSL Certificates support with DNS validation - Acme challenge validation with DNS API (Cloudflare, DigitalOcean, etc ..) on domain, subdomain, and wildcard - Flag `--letsencrypt=clean` to purge a previous SSL configuration +- Support for Debian 10 (buster) in beta #### Fixed diff --git a/docs/wo.8 b/docs/wo.8 index 6c168b2..97e48b8 100644 --- a/docs/wo.8 +++ b/docs/wo.8 @@ -11,9 +11,9 @@ wo stack [ status | start | stop | reload | restart ] [--all | --nginx | --php | .TP wo site [ list | info | show | enable | disable | edit | cd | show ] [ example.com ] .TP -wo site create example.com [ --html | --php | --php73 | --mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis | --letsencrypt/--le/--letsencrypt=subdomain/wildcard]] +wo site create example.com [ --html | --php | --php73 | --mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis | --letsencrypt/-le/--letsencrypt=subdomain/wildcard][--dns=dns_cf/dns_do]] .TP -wo site update example.com [ --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ] [--password] [--le/--letsencrypt=on/off/subdomain/renew]] +wo site update example.com [ --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ] [--password] [-le/--letsencrypt=on/off/subdomain/renew/wildcard] [--dns=dns_cf/dns_do]] .TP wo site delete example.com [--db | --files | --all | --no-prompt | --force/-f ] .TP diff --git a/install b/install index 9a06f37..3448d0b 100755 --- a/install +++ b/install @@ -401,10 +401,12 @@ wo_install_acme_sh() { # Clone Github repository if it doesn't exist wo_install() { { + rm -f /etc/bash_completion.d/wo_auto.rc rm -rf /tmp/WordOps git clone https://github.com/WordOps/WordOps.git /tmp/WordOps -b "$wo_branch" cd /tmp/WordOps || exit 1 + } >> "$wo_install_log" 2>&1 if [ -f $HOME/.gitconfig ]; then diff --git a/wo/core/variables.py b/wo/core/variables.py index 1963bca..e52136d 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -150,7 +150,8 @@ class WOVariables(): "php7.3-bcmath", "php7.3-mysql", "php7.3-opcache", "php7.3-zip", "php7.3-xml", "php7.3-soap"] wo_php_extra = ["php-memcached", "php-imagick", - "graphviz", "php-xdebug", "php-msgpack", "php-redis"] + "graphviz", "php-xdebug", "php-msgpack", + "php-redis", "php-mysql"] wo_php_key = 'AC0E47584A7A714D' From 0d02a1ceef3be473c4368b9a4b4e3da34340650e Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 15 Jul 2019 18:51:58 +0200 Subject: [PATCH 19/44] Fix --le --- config/bash_completion.d/wo_auto.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index 42f039c..10b7af0 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -165,7 +165,7 @@ _wo_complete() "update") COMPREPLY=( $(compgen \ - -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le --le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" \ + -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" \ -- $cur) ) ;; "delete") From ed4df166ce3b227cd5ae7a908539d18c9550cbaa Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 16 Jul 2019 02:07:52 +0200 Subject: [PATCH 20/44] Update version --- install | 8 ++++---- setup.py | 2 +- wo/core/variables.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/install b/install index 3448d0b..c2adacd 100755 --- a/install +++ b/install @@ -7,10 +7,10 @@ # Copyright (c) 2019 - WordOps # This script is licensed under M.I.T # ------------------------------------------------------------------------- -# Version 3.9.5.4 - 2019-07-09 +# Version 3.9.6 - 2019-07-16 # ------------------------------------------------------------------------- readonly wo_version_old="2.2.3" -readonly wo_version_new="3.9.5.4" +readonly wo_version_new="3.9.6" # CONTENTS # --- # 1. VARIABLES AND DECLARATIONS @@ -406,8 +406,8 @@ wo_install() { git clone https://github.com/WordOps/WordOps.git /tmp/WordOps -b "$wo_branch" cd /tmp/WordOps || exit 1 - - } >> "$wo_install_log" 2>&1 + } \ + >> "$wo_install_log" 2>&1 if [ -f $HOME/.gitconfig ]; then python3 setup.py install >> $wo_install_log 2>&1 diff --git a/setup.py b/setup.py index 1f098e4..2198da8 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ if not os.path.isfile('/root/.gitconfig'): shutil.copy2(os.path.expanduser("~")+'/.gitconfig', '/root/.gitconfig') setup(name='wo', - version='3.9.5.4', + version='3.9.6', description=long_description, long_description=long_description, classifiers=[], diff --git a/wo/core/variables.py b/wo/core/variables.py index e52136d..3b96a17 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -11,7 +11,7 @@ class WOVariables(): """Intialization of core variables""" # WordOps version - wo_version = "3.9.5.4" + wo_version = "3.9.6" # WordOps packages versions wo_wp_cli = "2.2.0" wo_adminer = "4.7.1" From 67c9c096cefde52c62f8375b468e359f919a43f5 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 16 Jul 2019 17:15:17 +0200 Subject: [PATCH 21/44] Fix pargs.letsencyrpt --- install | 2 +- wo/cli/plugins/site.py | 1 + wo/cli/plugins/stack.py | 58 ++++++++++++++++++--------------- wo/cli/plugins/stack_upgrade.py | 20 +++++++++++- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/install b/install index c2adacd..ca6db89 100755 --- a/install +++ b/install @@ -185,9 +185,9 @@ wo_install_dep() { wget https://download.opensuse.org/repositories/home:virtubox:WordOps/Debian_9.0/Release.key -O Release.key apt-key add - < Release.key rm -f Release.key - [ -d /etc/apt/trusted.gpg.d ] && { wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg; } # install dependencies DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confmiss" -o Dpkg::Options::="--force-confold" -y install build-essential curl gzip dirmngr sudo python3 python3-apt python3-setuptools python3-dev ca-certificates sqlite3 git tar software-properties-common pigz apt-transport-https gnupg2 cron ccze rsync tree haveged ufw > /dev/null 2>&1 + [ -d /etc/apt/trusted.gpg.d ] && { wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg; } fi locale-gen en diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index b0aab8e..6be0fcb 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -360,6 +360,7 @@ class WOSiteCreateController(CementBaseController): @expose(hide=True) def default(self): + pargs = self.app.pargs # self.app.render((data), 'default.mustache') # Check domain name validation data = dict() diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 02bb8a4..24aa932 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1538,12 +1538,14 @@ class WOStackController(CementBaseController): # PHPMYADMIN if self.app.pargs.phpmyadmin: - Log.debug(self, "Setting packages variable for phpMyAdmin ") - self.app.pargs.composer = True - packages = packages + [["https://github.com/phpmyadmin/" - "phpmyadmin/archive/STABLE.tar.gz", - "/var/lib/wo/tmp/pma.tar.gz", - "phpMyAdmin"]] + if not os.path.isdir('/var/www/22222/htdocs/db/pma'): + Log.debug(self, "Setting packages variable " + "for phpMyAdmin ") + self.app.pargs.composer = True + packages = packages + [["https://github.com/phpmyadmin/" + "phpmyadmin/archive/STABLE.tar.gz", + "/var/lib/wo/tmp/pma.tar.gz", + "phpMyAdmin"]] # Composer if self.app.pargs.composer: Log.debug(self, "Setting packages variable for Composer ") @@ -1552,16 +1554,16 @@ class WOStackController(CementBaseController): "Composer"]] # PHPREDISADMIN if self.app.pargs.phpredisadmin: - Log.debug(self, "Setting packages variable for phpRedisAdmin") - self.app.pargs.composer = True - packages = packages + [["https://github.com/erikdubbelboer/" - "phpRedisAdmin/archive/v1.11.3.tar.gz", - "/var/lib/wo/tmp/pra.tar.gz", - "phpRedisAdmin"], - ["https://github.com/nrk/predis/" - "archive/v1.1.1.tar.gz", - "/var/lib/wo/tmp/predis.tar.gz", - "Predis"]] + if not os.path.isdir('/var/www/22222/htdocs/cache/redis'): + Log.debug( + self, "Setting packages variable for phpRedisAdmin") + self.app.pargs.composer = True + packages = packages + [["https://github.com/" + "erikdubbelboer/" + "phpRedisAdmin/archive" + "/ v1.11.3.tar.gz", + "/var/lib/wo/tmp/pra.tar.gz", + "phpRedisAdmin"]] # ADMINER if self.app.pargs.adminer: Log.debug(self, "Setting packages variable for Adminer ") @@ -1592,17 +1594,19 @@ class WOStackController(CementBaseController): # WordOps Dashboard if self.app.pargs.dashboard: - Log.debug(self, "Setting packages variable for WO-Dashboard") - packages = packages + \ - [["https://github.com/WordOps/" - "wordops-dashboard/releases/" - "download/v1.0/wo-dashboard.tar.gz", - "/var/lib/wo/tmp/wo-dashboard.tar.gz", - "WordOps Dashboard"], - ["https://github.com/soerennb/" - "extplorer/archive/v2.1.11.tar.gz", - "/var/lib/wo/tmp/extplorer.tar.gz", - "eXtplorer"]] + if not os.path.isfile('/var/www/22222/htdocs/index.php'): + Log.debug( + self, "Setting packages variable for WO-Dashboard") + packages = packages + \ + [["https://github.com/WordOps/" + "wordops-dashboard/releases/" + "download/v1.0/wo-dashboard.tar.gz", + "/var/lib/wo/tmp/wo-dashboard.tar.gz", + "WordOps Dashboard"], + ["https://github.com/soerennb/" + "extplorer/archive/v2.1.11.tar.gz", + "/var/lib/wo/tmp/extplorer.tar.gz", + "eXtplorer"]] # UTILS if self.app.pargs.utils: diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index 8eb0343..d44b884 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -85,7 +85,7 @@ class WOStackUpgradeController(CementBaseController): if ((not self.app.pargs.web) and (not self.app.pargs.nginx) and (not self.app.pargs.php) and (not self.app.pargs.mysql) and (not self.app.pargs.all) and (not self.app.pargs.wpcli) and - (not self.app.pargs.netdata) and + (not self.app.pargs.netdata) and (not self.app.pargs.composer) (not self.app.pargs.phpmyadmin) and (not self.app.pargs.redis)): self.app.pargs.web = True @@ -161,6 +161,14 @@ class WOStackUpgradeController(CementBaseController): else: Log.error(self, "phpMyAdmin isn't installed") + if self.app.pargs.composer: + if os.path.isfile('/usr/local/bin/composer'): + packages = packages + [["https://getcomposer.org/installer", + "/var/lib/wo/tmp/composer-install", + "Composer"]] + else: + Log.error(self, "Composer isn't installed") + if len(packages) or len(apt_packages): Log.info(self, "During package update process non nginx-cached" @@ -206,6 +214,16 @@ class WOStackUpgradeController(CementBaseController): WOShellExec.cmd_exec(self, "/bin/bash /var/lib/wo/tmp/" "kickstart.sh " "--dont-wait") + + if self.app.pargs.composer: + Log.info(self, "Upgrading Composer, please wait...") + WOShellExec.cmd_exec(self, "php -q /var/lib/wo" + "/tmp/composer-install " + "--install-dir=/var/lib/wo/tmp/") + shutil.copyfile('/var/lib/wo/tmp/composer.phar', + '/usr/local/bin/composer') + WOFileUtils.chmod(self, "/usr/local/bin/composer", 0o775) + if self.app.pargs.phpmyadmin: Log.info(self, "Upgrading phpMyAdmin, please wait...") WOExtract.extract( From 0f8c9739fc9d3d3dc26cab1fb7a7d3a649dbc90f Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 16 Jul 2019 18:51:04 +0200 Subject: [PATCH 22/44] Fix redis link and split apt_packages and packages install process --- wo/cli/plugins/stack.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 24aa932..adeb28e 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1561,7 +1561,7 @@ class WOStackController(CementBaseController): packages = packages + [["https://github.com/" "erikdubbelboer/" "phpRedisAdmin/archive" - "/ v1.11.3.tar.gz", + "/v1.11.3.tar.gz", "/var/lib/wo/tmp/pra.tar.gz", "phpRedisAdmin"]] # ADMINER @@ -1654,19 +1654,23 @@ class WOStackController(CementBaseController): pass if (apt_packages) or (packages): - Log.debug(self, "Calling pre_pref") - self.pre_pref(apt_packages) if (apt_packages): + Log.debug(self, "Calling pre_pref") + self.pre_pref(apt_packages) WOSwap.add(self) Log.info(self, "Updating apt-cache, please wait...") WOAptGet.update(self) Log.info(self, "Installing packages, please wait...") - WOAptGet.install(self, apt_packages) + OAptGet.install(self, apt_packages) + Log.debug(self, "Calling post_pref") + self.post_pref(apt_packages) + if (packages): Log.debug(self, "Downloading following: {0}".format(packages)) WODownload.download(self, packages) - Log.debug(self, "Calling post_pref") - self.post_pref(apt_packages, packages) + Log.debug(self, "Calling post_pref") + self.post_pref(packages) + if 'redis-server' in apt_packages: # set redis.conf parameter # set maxmemory 10% for ram below 512MB and 20% for others From f6e4d1bbed147e38b356c8038eb1af06e6ec3cd5 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Wed, 17 Jul 2019 03:00:53 +0200 Subject: [PATCH 23/44] Revert changes and fix link --- wo/cli/plugins/stack.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index adeb28e..f654053 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1654,23 +1654,19 @@ class WOStackController(CementBaseController): pass if (apt_packages) or (packages): + Log.debug(self, "Calling pre_pref") + self.pre_pref(apt_packages) if (apt_packages): - Log.debug(self, "Calling pre_pref") - self.pre_pref(apt_packages) WOSwap.add(self) Log.info(self, "Updating apt-cache, please wait...") WOAptGet.update(self) Log.info(self, "Installing packages, please wait...") - OAptGet.install(self, apt_packages) - Log.debug(self, "Calling post_pref") - self.post_pref(apt_packages) - + WOAptGet.install(self, apt_packages) if (packages): Log.debug(self, "Downloading following: {0}".format(packages)) WODownload.download(self, packages) - Log.debug(self, "Calling post_pref") - self.post_pref(packages) - + Log.debug(self, "Calling post_pref") + self.post_pref(apt_packages, packages) if 'redis-server' in apt_packages: # set redis.conf parameter # set maxmemory 10% for ram below 512MB and 20% for others From 595f80f132c31af85cabb2b0671a22b761e87e69 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Wed, 17 Jul 2019 03:08:03 +0200 Subject: [PATCH 24/44] Fix composer namespace --- wo/cli/plugins/stack_upgrade.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index d44b884..70876c0 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -38,6 +38,8 @@ class WOStackUpgradeController(CementBaseController): dict(help='Upgrade Redis', action='store_true')), (['--netdata'], dict(help='Upgrade Netdata', action='store_true')), + (['--composer'], + dict(help='Upgrade Composer', action='store_true')), (['--phpmyadmin'], dict(help='Upgrade phpMyAdmin', action='store_true')), (['--no-prompt'], @@ -85,7 +87,7 @@ class WOStackUpgradeController(CementBaseController): if ((not self.app.pargs.web) and (not self.app.pargs.nginx) and (not self.app.pargs.php) and (not self.app.pargs.mysql) and (not self.app.pargs.all) and (not self.app.pargs.wpcli) and - (not self.app.pargs.netdata) and (not self.app.pargs.composer) + (not self.app.pargs.netdata) and (not self.app.pargs.composer) and (not self.app.pargs.phpmyadmin) and (not self.app.pargs.redis)): self.app.pargs.web = True From 8906fa2c61855ed37041cc3556c71654a585400c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 14:32:41 +0200 Subject: [PATCH 25/44] Fix Fail2ban --- wo/cli/plugins/stack.py | 11 +++++++++++ wo/cli/plugins/stack_services.py | 10 +++++----- wo/cli/templates/fail2ban.mustache | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index f654053..3962f3f 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1093,6 +1093,17 @@ class WOStackController(CementBaseController): out=wo_fail2ban) wo_fail2ban.close() + Log.debug(self, "Setting up fail2ban wp filter") + wo_fail2ban = open('/etc/fail2ban/filter.d/' + 'nginx-forbidden.conf', + encoding='utf-8', mode='w') + self.app.render((data), 'fail2ban-forbidden.mustache', + out=wo_fail2ban) + wo_fail2ban.close() + WOGit.add(self, ["/etc/fail2ban"], + msg="Adding Fail2ban into Git") + WOService.reload_service(self, 'fail2ban') + if (packages): if any('/usr/local/bin/wp' == x[1] for x in packages): Log.debug(self, "Setting Privileges" diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py index a51410d..9b7d604 100644 --- a/wo/cli/plugins/stack_services.py +++ b/wo/cli/plugins/stack_services.py @@ -70,7 +70,7 @@ class WOStackStatusController(CementBaseController): if self.app.pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): - services = services + ['fail2ban-client'] + services = services + ['fail2ban'] else: Log.info(self, "fail2ban is not installed") @@ -134,7 +134,7 @@ class WOStackStatusController(CementBaseController): if self.app.pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): - services = services + ['fail2ban-client'] + services = services + ['fail2ban'] else: Log.info(self, "fail2ban is not installed") @@ -201,7 +201,7 @@ class WOStackStatusController(CementBaseController): if self.app.pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): - services = services + ['fail2ban-client'] + services = services + ['fail2ban'] else: Log.info(self, "fail2ban is not installed") @@ -266,7 +266,7 @@ class WOStackStatusController(CementBaseController): if self.app.pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): - services = services + ['fail2ban-client'] + services = services + ['fail2ban'] else: Log.info(self, "fail2ban is not installed") @@ -332,7 +332,7 @@ class WOStackStatusController(CementBaseController): if self.app.pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): - services = services + ['fail2ban-client'] + services = services + ['fail2ban'] else: Log.info(self, "fail2ban is not installed") diff --git a/wo/cli/templates/fail2ban.mustache b/wo/cli/templates/fail2ban.mustache index f7eb611..554cc6e 100644 --- a/wo/cli/templates/fail2ban.mustache +++ b/wo/cli/templates/fail2ban.mustache @@ -17,7 +17,7 @@ maxretry = 5 [nginx-forbidden] enabled = true filter = nginx-forbidden -port = http,https +action = iptables-multiport[name="wo-wordpress", port="http,https"] logpath = /var/log/nginx/*error*.log findtime = 60 bantime = 6000 From f7bf0df50c006e562a5fb2bc258448b082df761a Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 17:04:20 +0200 Subject: [PATCH 26/44] add fail2ban removal --- wo/cli/plugins/stack.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 3962f3f..e29dc29 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1420,6 +1420,7 @@ class WOStackController(CementBaseController): (not self.app.pargs.composer) and (not self.app.pargs.netdata) and (not self.app.pargs.dashboard) and + (not self.app.pargs.fail2ban) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin) and @@ -1450,6 +1451,7 @@ class WOStackController(CementBaseController): self.app.pargs.netdata = True self.app.pargs.dashboard = True self.app.pargs.phpredisadmin = True + self.app.pargs.fail2ban = True # Redis if self.app.pargs.redis: @@ -1741,6 +1743,7 @@ class WOStackController(CementBaseController): (not self.app.pargs.wpcli) and (not self.app.pargs.phpmyadmin) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.composer) and (not self.app.pargs.netdata) and + (not self.app.pargs.fail2ban) and (not self.app.pargs.all) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin)): self.app.pargs.web = True @@ -1765,6 +1768,7 @@ class WOStackController(CementBaseController): self.app.pargs.netdata = True self.app.pargs.dashboard = True self.app.pargs.phpredisadmin = True + self.app.pargs.fail2ban = True # NGINX if self.app.pargs.nginx: @@ -1808,6 +1812,12 @@ class WOStackController(CementBaseController): Log.debug(self, "Removing apt_packages variable of MySQL") apt_packages = apt_packages + WOVariables.wo_mysql packages = packages + ['/usr/bin/mysqltuner'] + + # fail2ban + if self.app.pargs.redis: + Log.debug(self, "Remove apt_packages variable of Fail2ban") + apt_packages = apt_packages + WOVariables.wo_fail2ban + # WPCLI if self.app.pargs.wpcli: Log.debug(self, "Removing package variable of WPCLI ") @@ -1912,6 +1922,7 @@ class WOStackController(CementBaseController): (not self.app.pargs.wpcli) and (not self.app.pargs.phpmyadmin) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.composer) and (not self.app.pargs.netdata) and + (not self.app.pargs.fail2ban) and (not self.app.pargs.all) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin)): self.app.pargs.web = True @@ -1970,6 +1981,11 @@ class WOStackController(CementBaseController): else: Log.error(self, "Cannot Purge PHP 7.3. not found.") + # fail2ban + if self.app.pargs.redis: + Log.debug(self, "Remove apt_packages variable of Fail2ban") + apt_packages = apt_packages + WOVariables.wo_fail2ban + # WP-CLI if self.app.pargs.wpcli: Log.debug(self, "Purge package variable WPCLI") From 15dd961cd3bb40e354aeeb7d36646a66d257ed1d Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 17:48:05 +0200 Subject: [PATCH 27/44] Fix fail2ban install --- wo/core/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wo/core/variables.py b/wo/core/variables.py index 3b96a17..94b6a86 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -168,7 +168,7 @@ class WOVariables(): .format(codename=wo_platform_codename)) wo_mysql = ["mariadb-server", "percona-toolkit", "python3-mysqldb"] - wo_fail2ban = "fail2ban" + wo_fail2ban = ["fail2ban", "python3-pyinotify"] # Redis repo details if wo_platform_distro == 'ubuntu': From 99c6cc8d583643b53ae39c9b43fe9a6daa145fd5 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 17:54:45 +0200 Subject: [PATCH 28/44] Fix fail2ban variable --- wo/cli/plugins/stack.py | 13 ++++++------- wo/cli/plugins/stack_services.py | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index e29dc29..9c137e3 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1077,29 +1077,28 @@ class WOStackController(CementBaseController): # create fail2ban configuration files if set(WOVariables.wo_fail2ban).issubset(set(apt_packages)): if not os.path.isfile("/etc/fail2ban/jail.d/custom.conf"): - data = dict() Log.debug(self, "Setting up fail2ban jails configuration") - wo_fail2ban = open('/etc/fail2ban/jail.d/custom.conf', + fail2ban_config = open('/etc/fail2ban/jail.d/custom.conf', encoding='utf-8', mode='w') self.app.render((data), 'fail2ban.mustache', out=wo_fail2ban) - wo_fail2ban.close() + fail2ban_config.close() Log.debug(self, "Setting up fail2ban wp filter") - wo_fail2ban = open('/etc/fail2ban/filter.d/' + fail2ban_config = open('/etc/fail2ban/filter.d/' 'wo-wordpress.conf', encoding='utf-8', mode='w') self.app.render((data), 'fail2ban-wp.mustache', out=wo_fail2ban) - wo_fail2ban.close() + fail2ban_config.close() Log.debug(self, "Setting up fail2ban wp filter") - wo_fail2ban = open('/etc/fail2ban/filter.d/' + fail2ban_config = open('/etc/fail2ban/filter.d/' 'nginx-forbidden.conf', encoding='utf-8', mode='w') self.app.render((data), 'fail2ban-forbidden.mustache', out=wo_fail2ban) - wo_fail2ban.close() + fail2ban_config.close() WOGit.add(self, ["/etc/fail2ban"], msg="Adding Fail2ban into Git") WOService.reload_service(self, 'fail2ban') diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py index 9b7d604..2def4af 100644 --- a/wo/cli/plugins/stack_services.py +++ b/wo/cli/plugins/stack_services.py @@ -85,6 +85,7 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or + self.app.pargs.fail2ban or self.app.pargs.redis): self.app.pargs.nginx = True self.app.pargs.php = True From cb2bd24d5470d04d3a220f3a07d5bd75a069b7d5 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 18:19:04 +0200 Subject: [PATCH 29/44] Fix wo_fail2ban --- wo/cli/plugins/stack.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 9c137e3..adb2283 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1079,25 +1079,25 @@ class WOStackController(CementBaseController): if not os.path.isfile("/etc/fail2ban/jail.d/custom.conf"): Log.debug(self, "Setting up fail2ban jails configuration") fail2ban_config = open('/etc/fail2ban/jail.d/custom.conf', - encoding='utf-8', mode='w') + encoding='utf-8', mode='w') self.app.render((data), 'fail2ban.mustache', - out=wo_fail2ban) + out=fail2ban_config) fail2ban_config.close() Log.debug(self, "Setting up fail2ban wp filter") fail2ban_config = open('/etc/fail2ban/filter.d/' - 'wo-wordpress.conf', - encoding='utf-8', mode='w') + 'wo-wordpress.conf', + encoding='utf-8', mode='w') self.app.render((data), 'fail2ban-wp.mustache', - out=wo_fail2ban) + out=fail2ban_config) fail2ban_config.close() Log.debug(self, "Setting up fail2ban wp filter") fail2ban_config = open('/etc/fail2ban/filter.d/' - 'nginx-forbidden.conf', - encoding='utf-8', mode='w') + 'nginx-forbidden.conf', + encoding='utf-8', mode='w') self.app.render((data), 'fail2ban-forbidden.mustache', - out=wo_fail2ban) + out=fail2ban_config) fail2ban_config.close() WOGit.add(self, ["/etc/fail2ban"], msg="Adding Fail2ban into Git") @@ -1813,7 +1813,7 @@ class WOStackController(CementBaseController): packages = packages + ['/usr/bin/mysqltuner'] # fail2ban - if self.app.pargs.redis: + if self.app.pargs.fail2ban: Log.debug(self, "Remove apt_packages variable of Fail2ban") apt_packages = apt_packages + WOVariables.wo_fail2ban @@ -1981,7 +1981,7 @@ class WOStackController(CementBaseController): Log.error(self, "Cannot Purge PHP 7.3. not found.") # fail2ban - if self.app.pargs.redis: + if self.app.pargs.fail2ban: Log.debug(self, "Remove apt_packages variable of Fail2ban") apt_packages = apt_packages + WOVariables.wo_fail2ban From 785fc7da7a254f465c59f6147a9159708043f6be Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 18:55:23 +0200 Subject: [PATCH 30/44] update bash_completion --- config/bash_completion.d/wo_auto.rc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index 10b7af0..297d227 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -211,9 +211,9 @@ _wo_complete() "--wp") if [ ${COMP_WORDS[1]} != "debug" ]; then if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --letsencrypt -le --php73" + retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --letsencrypt -le --letsencrypt=subdomain --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_do --php73" elif [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=subdomain -le=off -le=wildcard --dns --dns=dns_cf --dns=dns_do" + retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --letsencrypt=off --letsencrypt=renew -le -le=off -le=wildcard --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -230,9 +230,9 @@ _wo_complete() "--wpsubdir" | "--wpsubdomain") if [ ${COMP_WORDS[1]} != "debug" ]; then if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--wpsc --wpfc --user --email --pass --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le -le=subdomain -le=wildcard --php73 --dns --dns=dns_cf --dns=dns_do" + retlist="--wpsc --wpfc --user --email --pass --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le --php73 --dns --dns=dns_cf --dns=dns_do" elif [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=off -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" + retlist="--wpfc --wpsc --php73 --php73=off --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard --letsencrypt=off --letsencrypt=renew -le --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -248,7 +248,7 @@ _wo_complete() "--wpredis" | "--wpfc" | "--wpsc" | "--wpsubdir" | "--wpsubdomain" | "--user" | "--pass" | "--email" | "--wp") if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --php73 --letsencrypt --letsencrypt=subdomain -le -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" + retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --php73 --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -261,7 +261,7 @@ _wo_complete() "--wpredis" | "--wpfc") if [ ${COMP_WORDS[2]} == "update" ]; then - retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le -le=off -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" + retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=off --letsencrypt=renew -le --dns --dns=dns_cf --dns=dns_do" else retlist="" fi @@ -272,9 +272,9 @@ _wo_complete() -- $cur) ) ;; - "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--redis | --phpredisadmin | --netdata") + "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--fail2ban" | "--redis | --phpredisadmin | --netdata") if [[ ${COMP_WORDS[2]} == "install" || ${COMP_WORDS[2]} == "purge" || ${COMP_WORDS[2]} == "remove" ]]; then - retlist="--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --netdata" + retlist="--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --phpredisadmin --netdata" elif [[ ${COMP_WORDS[2]} == "start" || ${COMP_WORDS[2]} == "reload" || ${COMP_WORDS[2]} == "restart" || ${COMP_WORDS[2]} == "stop" ]]; then retlist="--nginx --php --php73 --mysql --redis --netdata" elif [[ ${COMP_WORDS[1]} == "debug" ]]; then @@ -363,7 +363,7 @@ _wo_complete() case "$mprev" in "--user" | "--email" | "--pass") if [ ${COMP_WORDS[2]} == "create" ]; then - retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le -le=subdomain -le=wildcard --dns --dns=dns_cf --dns=dns_do" + retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --letsencrypt --letsencrypt=subdomain --letsencrypt=wildcard -le --dns --dns=dns_cf --dns=dns_do" fi ret="${retlist[@]/$prev}" COMPREPLY=( $(compgen \ From 4b00416fe14c37fef4c8ab7fc8b1103a8772f6b2 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 19:14:24 +0200 Subject: [PATCH 31/44] Force nginx config install only for old releases --- install | 6 ++++-- wo/cli/plugins/stack.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/install b/install index ca6db89..d3df375 100755 --- a/install +++ b/install @@ -681,8 +681,10 @@ if [ -x /usr/local/bin/wo ]; then fi if [ -z "$wo_preserve_config" ]; then if [ -n "$(command -v nginx)" ]; then - wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log - wo_upgrade_nginx | tee -ai $wo_install_log + if [ ! -f /etc/nginx/common/release ] || [[ -z $(grep "v3.9.5." /etc/nginx/common/release) && -z $(grep "v3.9.6" /etc/nginx/common/release) ]]; then + wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log + wo_upgrade_nginx | tee -ai $wo_install_log + fi fi fi wo_update_latest | tee -ai $wo_install_log diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index adb2283..e4ab4b7 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1520,7 +1520,6 @@ class WOStackController(CementBaseController): "/master/mysqltuner.pl", "/usr/bin/mysqltuner", "MySQLTuner"]] - else: Log.debug(self, "MySQL connection is already alive") Log.info(self, "MySQL connection is already alive") @@ -2067,6 +2066,9 @@ class WOStackController(CementBaseController): "libexec/netdata-" "uninstaller.sh -y -f") + if (set(["fail2ban"]).issubset(set(apt_packages))): + WOService.stop_service(self, 'fail2ban') + if (apt_packages): Log.info(self, "Purging packages, please wait...") WOAptGet.remove(self, apt_packages, purge=True) From e3138432e5d68fae8e63c0dae9b354b5971a3f0a Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Thu, 18 Jul 2019 20:49:42 +0200 Subject: [PATCH 32/44] Improve code quality --- install | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/install b/install index d3df375..6408a5e 100755 --- a/install +++ b/install @@ -250,8 +250,8 @@ wo_sync_db() { wo_site_current_type=$(grep "common/" /etc/nginx/sites-available/$site | awk -F "/" '{print $2}') - if [ -n "$(echo $wo_site_current_type | grep php)" ]; then - if [ "$(echo $wo_site_current_type | grep php7)" ]; then + if echo "$wo_site_current_type" | grep -q "php"; then + if echo "$wo_site_current_type" | grep -q "php7"; then wo_php_version="7.0" else wo_php_version="5.6" @@ -260,31 +260,31 @@ wo_sync_db() { wo_php_version="" fi - if [ "$(echo $wo_site_current_type | grep redis)" ]; then + if echo "$wo_site_current_type" | grep -q "redis"; then wo_site_current_cache="wpredis" - elif [ -z "$(echo $wo_site_current_type | grep wpsc)" ]; then + elif echo "$wo_site_current_type" | grep -q wpsc; then wo_site_current_cache="wpsc" - elif [ -z "$(echo $wo_site_current_type | grep wpfc)" ]; then + elif echo "$wo_site_current_type" | grep -q wpfc; then wo_site_current_cache="wpfc" else wo_site_current_cache="basic" fi - if [ "$(echo $wo_site_current_type | grep wp)" ]; then - if [ -z "$(echo $wo_site_current_type | grep wpsubdir)" ]; then + if echo "$wo_site_current_type" | grep -q wp; then + if echo "$wo_site_current_type" | grep -q wpsubdir; then wo_site_current="wpsubdir" - elif [ -z "$(echo $wo_site_current_type | grep wpsudomain)" ]; then + elif echo "$wo_site_current_type" | grep -q wpsudomain; then wo_site_current="wpsubdomain" else wo_site_current="wp" fi else - if [ -z "$(echo $wo_site_current_type | grep location)" ]; then + if echo "$wo_site_current_type" | grep -q location; then wo_site_current="proxy" - elif [ -z "$(echo $wo_site_current_type | grep php)" ]; then + elif echo "$wo_site_current_type" | grep -q php; then wo_site_current="html" else - if [ -f /var/www/${site}/ee-config.php ] || [ -f /var/www/${site}/wo-config.php ]; then + if [ -f "/var/www/${site}/ee-config.php" ] || [ -f "/var/www/${site}/wo-config.php" ]; then wo_site_current="mysql" else wo_site_current="php" @@ -409,11 +409,11 @@ wo_install() { } \ >> "$wo_install_log" 2>&1 - if [ -f $HOME/.gitconfig ]; then + if [ -f "$HOME/.gitconfig" ]; then python3 setup.py install >> $wo_install_log 2>&1 else if [ "$wo_force_install" = "y" ]; then - [ ! -f $HOME/.gitconfig ] && { bash -c 'echo -e "[user]\n\tname = $USER\n\temail = root@$HOSTNAME" > $HOME/.gitconfig'; } + [ ! -f "$HOME/.gitconfig" ] && { bash -c 'echo -e "[user]\n\tname = $USER\n\temail = root@$HOSTNAME" > $HOME/.gitconfig'; } fi python3 setup.py install fi @@ -550,8 +550,8 @@ wo_update_latest() { mkdir -p /etc/mysql/conf.d chmod 755 /etc/mysql/conf.d } - if [ -f $HOME/.my.cnf ]; then - cp -f $HOME/.my.cnf /etc/mysql/conf.d/my.cnf + if [ -f "$HOME/.my.cnf" ]; then + cp -f "$HOME/.my.cnf" /etc/mysql/conf.d/my.cnf chmod 600 /etc/mysql/conf.d/my.cnf elif [ -f /root/.my.cnf ]; then @@ -681,7 +681,7 @@ if [ -x /usr/local/bin/wo ]; then fi if [ -z "$wo_preserve_config" ]; then if [ -n "$(command -v nginx)" ]; then - if [ ! -f /etc/nginx/common/release ] || [[ -z $(grep "v3.9.5." /etc/nginx/common/release) && -z $(grep "v3.9.6" /etc/nginx/common/release) ]]; then + if ! grep -q "v3.9.5." /etc/nginx/common/release; then wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log wo_upgrade_nginx | tee -ai $wo_install_log fi From dce0618e430e072716806eff59ecf8b288c4d569 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 00:57:32 +0200 Subject: [PATCH 33/44] Refactor update --- wo/cli/plugins/update.py | 73 ++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/wo/cli/plugins/update.py b/wo/cli/plugins/update.py index b7a6934..7f040db 100644 --- a/wo/cli/plugins/update.py +++ b/wo/cli/plugins/update.py @@ -25,6 +25,9 @@ class WOUpdateController(CementBaseController): (['--preserve'], dict(help='Preserve current Nginx configuration', action='store_true')), + (['--beta'], + dict(help='Update WordOps to latest beta release', + action='store_true')), (['--travis'], dict(help='Argument used only for WordOps development', action='store_true')), @@ -36,51 +39,33 @@ class WOUpdateController(CementBaseController): filename = "woupdate" + time.strftime("%Y%m%d-%H%M%S") if self.app.pargs.travis: - WODownload.download(self, [["https://raw.githubusercontent.com/" - "WordOps/WordOps/updating-configuration/install", - "/tmp/{0}".format(filename), - "update script"]]) - try: - Log.info(self, "updating WordOps, please wait...") - os.system("bash /tmp/{0} --travis --force".format(filename)) - except OSError as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") + wo_branch = "updating-configuration" + install_args = "--travis --force " + elif self.app.pargs.beta: + wo_branch = "beta" + install_args = "" else: - WODownload.download(self, [["https://raw.githubusercontent.com/" - "WordOps/WordOps/master/install", - "/tmp/{0}".format(filename), - "update script"]]) - if self.app.pargs.force: - try: - Log.info(self, "updating WordOps, please wait...") - os.system("bash /tmp/{0} --force".format(filename)) - except OSError as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") - except Exception as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") - elif self.app.pargs.preserve: - try: - Log.info(self, "updating WordOps, please wait...") - os.system("bash /tmp/{0} --preserve".format(filename)) - except OSError as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") - except Exception as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") - else: - try: - Log.info(self, "updating WordOps, please wait...") - os.system("bash /tmp/{0}".format(filename)) - except OSError as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") - except Exception as e: - Log.debug(self, str(e)) - Log.error(self, "WordOps update failed !") + wo_branch = "master " + install_args = "" + if self.app.pargs.force: + install_args = install_args + "--force " + if self.app.pargs.preserve: + install_args = install_args + "--preserve " + + WODownload.download(self, [["https://raw.githubusercontent.com/" + "WordOps/WordOps/{0}/install" + .format(wo_branch), + "/var/lib/wo/tmp/{0}".format(filename), + "update script"]]) + + try: + Log.info(self, "updating WordOps, please wait...") + os.system("/bin/bash /var/lib/wo/tmp/{0} " + "-b {1} {2}".format(filename, + wo_branch, install_args)) + except OSError as e: + Log.debug(self, str(e)) + Log.error(self, "WordOps update failed !") def load(app): From 4c663dfa9febe881636ffe42222e800e57d03406 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 01:02:00 +0200 Subject: [PATCH 34/44] Fix issues, simplify code --- config/wo.conf | 4 ++ wo/cli/plugins/site.py | 6 +-- wo/cli/plugins/site_functions.py | 79 ++++++++++++++------------------ wo/cli/plugins/stack_upgrade.py | 2 +- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/config/wo.conf b/config/wo.conf index 4390e4d..b1e6b3c 100644 --- a/config/wo.conf +++ b/config/wo.conf @@ -68,6 +68,10 @@ password = ### EMail for WordPress sites email = +[letsencrypt] + +key-length = "ec-384" + [update] ### If enabled, load a plugin named `update` either from the Python module diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 6be0fcb..08343db 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1348,11 +1348,11 @@ class WOSiteUpdateController(CementBaseController): if self.app.pargs.letsencrypt == "clean": if os.path.isfile("{0}/conf/nginx/ssl.conf" .format(wo_site_webroot)): - WOFileUtils.remove("{0}/conf/nginx/ssl.conf" + WOFileUtils.remove(self, "{0}/conf/nginx/ssl.conf" .format(wo_site_webroot)) - WOFileUtils.remove("/etc/letsencrypt/live" + WOFileUtils.remove(self, "/etc/letsencrypt/live" "/{0}".format(wo_domain)) - WOFileUtils.remove("/etc/nginx/conf.d/" + WOFileUtils.remove(self, "/etc/nginx/conf.d/" "force-ssl-{0}.conf" .format(wo_domain_name)) if not WOService.reload_service(self, 'nginx'): diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 1cf9a70..94ca80f 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1266,8 +1266,9 @@ def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', raise SiteError("dbhost not provided") deleteDB(self, dbname, dbuser, dbhost) - # setup letsencrypt for domain + www.domain + + def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, wo_dns=False, wo_acme_dns='dns_cf'): @@ -1279,54 +1280,42 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, .format(wo_domain_name)) ssl = archivedCertificateHandle(self, wo_domain_name) else: + keylenght = "{0}".format(self.app.config.get('letsencrypt', + 'keylength')) + if wo_dns: + acme_mode = "--dns {0}".format(wo_acme_dns) + else: + acme_mode = "-w /var/www/html" Log.info(self, "Issuing SSL cert with acme.sh") if subdomain: - if wo_dns: - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} --dns {1} " - "-k ec-384 -f" - .format(wo_domain_name, - wo_acme_dns)) - else: - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} {1}" + "-k {3} -f" + .format(wo_domain_name, + acme_mode, + keylenght)) elif wildcard: - if wo_dns: - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d *.{0} --dns {1} " - "-k ec-384 -f" - .format(wo_domain_name, - wo_acme_dns)) + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d *.{0} --dns {1} " + "-k {2} -f" + .format(wo_domain_name, + wo_acme_dns, + keylenght)) else: - if wo_dns: - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d www.{0} --dns {1} " - "-k ec-384 -f" - .format(wo_domain_name, - wo_acme_dns)) - else: - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--issue " - "-d {0} -d www.{0} " - "-w /var/www/html " - "-k ec-384 -f" - .format(wo_domain_name)) + ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--issue " + "-d {0} -d www.{0} {1} " + "-k {2} -f" + .format(wo_domain_name, + acme_mode, keylenght)) if ssl: try: Log.info(self, "Deploying SSL cert with acme.sh") diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index 70876c0..ab20c5e 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -237,7 +237,7 @@ class WOStackUpgradeController(CementBaseController): '-all-languages/config.inc.php' .format(WOVariables.wo_phpmyadmin) ) - WOFileUtils.remove('{0}22222/htdocs/db/pma' + WOFileUtils.remove(self, '{0}22222/htdocs/db/pma' .format(WOVariables.wo_webroot)) shutil.move('/var/lib/wo/tmp/phpMyAdmin-{0}' '-all-languages/' From 27ad2ae2aee236ee63f006b4d11695ce417589aa Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 01:28:11 +0200 Subject: [PATCH 35/44] Fix variable keylength --- config/wo.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/wo.conf b/config/wo.conf index b1e6b3c..8d4a3ad 100644 --- a/config/wo.conf +++ b/config/wo.conf @@ -70,7 +70,7 @@ email = [letsencrypt] -key-length = "ec-384" +keylength = "ec-384" [update] From f97b51cd0780149620cc4c18ac27cf2117cd2773 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 13:47:29 +0200 Subject: [PATCH 36/44] Add ProFTPd setup --- CHANGELOG.md | 4 +- README.md | 22 ++----- wo/cli/plugins/stack.py | 79 ++++++++++++++++++++-- wo/cli/plugins/stack_services.py | 87 ++++++++++++++++++++++++- wo/cli/templates/wpcommon-php7.mustache | 16 +++++ wo/cli/templates/wpcommon.mustache | 20 +++++- 6 files changed, 200 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 533e712..39a9a87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Wildcard SSL Certificates support with DNS validation - Acme challenge validation with DNS API (Cloudflare, DigitalOcean, etc ..) on domain, subdomain, and wildcard - Flag `--letsencrypt=clean` to purge a previous SSL configuration -- Support for Debian 10 (buster) in beta +- Support for Debian 10 buster (testing - not ready for production) +- Fail2ban with custom jails to secure WordPress & SSH +- Variable `keylength` in /etc/wo/wo.conf to define letsencrypt certificate keylenght #### Fixed diff --git a/README.md b/README.md index a15cbc0..11433b9 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ - Ubuntu 19.04 (Disco) - Debian 8 (Jessie) - Debian 9 (Stretch) -- Debian 10 (Buster) +- Debian 10 (Buster) - Not ready for production - Raspbian 9 (Stretch) ### Ports requirements @@ -78,20 +78,7 @@ sudo wo site create example.com --wp # Install required packages & setup Wor ## Must read -WordOps made some fundamental changes: - -- We've deprecated the mail stack. As an alternative, you can take a look at [Mail-in-a-Box](https://github.com/mail-in-a-box/mailinabox), [iRedMail](https://www.iredmail.org/) or [Caesonia](https://github.com/vedetta-com/caesonia). As Roundcube alternative, there is [Rainloop](https://www.rainloop.net/) or [Afterlogic WebMail](https://github.com/afterlogic/webmail-lite-8) -- Support for w3tc is dropped as a security precaution. -- PHP 5.6 has been replaced by PHP 7.2 and PHP 7.0 has been replaced by PHP 7.3. -- Nginx-ee package has been replaced by Nginx-wo (based on Nginx stable v1.16.0 with Brotli support) -- HHVM stack has been removed -- Let's Encrypt stack isn't based on letsencrypt-auto anymore, we use acme.sh to handle SSL certificates - -If you are going to migrate from EasyEngine v3, here some important informations : - -- Previous php upstreams in Nginx will not be overwritted -- php5.6 and php7.0 will not be removed or uninstalled -- previous Nginx common configurations will not be overwritted +[From EasyEngine to WordOps](https://docs.wordops.net/about/from-easyengine-to-wordops/) ## Usage @@ -135,9 +122,10 @@ wo site create example.com --proxy=127.0.0.1:3000 # create example.com with ngi ### Sites secured with Let's Encrypt ```bash -wo site create example.com --wp --letsencrypt # install wordpress & secure site with letsencrypt -wo site create sub.example.com --wp --letsencrypt=subdomain # install wordpress and secure subdomain with letsencrypt +wo site create example.com --wp --letsencrypt # wordpress secured with letsencrypt +wo site create sub.example.com --wp --letsencrypt=subdomain # wordpress + letsencrypt subdomain wo site create site.tld --wp --letsencrypt --hsts # install wordpress & secure site with letsencrypt with HSTS +wo site create site.tld --wp --letsencrypt=wildcard --dns=dns_cf # install wordpress & issue a wildcard SSL certificate with Cloudflare DNS API ``` ## Update WordOps diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index e4ab4b7..9fcd480 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -79,6 +79,8 @@ class WOStackController(CementBaseController): dict(help='Install Redis', action='store_true')), (['--phpredisadmin'], dict(help='Install phpRedisAdmin', action='store_true')), + (['--proftpd'], + dict(help='Install ProFTPd', action='store_true')), ] usage = "wo stack (command) [options]" @@ -1103,6 +1105,37 @@ class WOStackController(CementBaseController): msg="Adding Fail2ban into Git") WOService.reload_service(self, 'fail2ban') + if set(["proftpd-basic"]).issubset(set(apt_packages)): + if os.path.isfile("/etc/proftpd/proftpd.conf"): + Log.debug(self, "Setting up Proftpd configuration") + WOFileUtils.searchreplace(self, "/etc/proftpd/" + "proftpd.conf", + "# DefaultRoot", + "DefaultRoot") + WOFileUtils.searchreplace(self, "/etc/proftpd/" + "proftpd.conf", + "# RequireValidShell", + "RequireValidShell") + WOFileUtils.searchreplace(self, "/etc/proftpd/" + "proftpd.conf", + "# PassivePorts " + " " + "49152 65534", + "PassivePorts " + " " + " 49000 50000") + + if WOAptGet.is_installed(self, 'ufw'): + try: + WOShellExec.cmd_exec(self, "ufw allow " + "49000:50000/tcp") + except CommandExecutionError as e: + Log.error(self, "Unable to add UFW rules") + + WOGit.add(self, ["/etc/proftpd"], + msg="Adding ProFTPd into Git") + WOService.reload_service(self, 'proftpd') + if (packages): if any('/usr/local/bin/wp' == x[1] for x in packages): Log.debug(self, "Setting Privileges" @@ -1421,7 +1454,7 @@ class WOStackController(CementBaseController): (not self.app.pargs.dashboard) and (not self.app.pargs.fail2ban) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and - (not self.app.pargs.redis) and + (not self.app.pargs.redis) and (not self.app.pargs.proftpd) and (not self.app.pargs.phpredisadmin) and (not self.app.pargs.php73)): self.app.pargs.web = True @@ -1432,6 +1465,7 @@ class WOStackController(CementBaseController): self.app.pargs.admin = True self.app.pargs.php73 = True self.app.pargs.redis = True + self.app.pargs.proftpd = True if self.app.pargs.web: self.app.pargs.nginx = True @@ -1547,6 +1581,15 @@ class WOStackController(CementBaseController): Log.debug(self, "Fail2ban already installed") Log.info(self, "Fail2ban already installed") + # proftpd + if self.app.pargs.proftpd: + Log.debug(self, "Setting apt_packages variable for ProFTPd") + if not WOAptGet.is_installed(self, 'proftpd-basic'): + apt_packages = apt_packages + ["proftpd-basic"] + else: + Log.debug(self, "ProFTPd already installed") + Log.info(self, "ProFTPd already installed") + # PHPMYADMIN if self.app.pargs.phpmyadmin: if not os.path.isdir('/var/www/22222/htdocs/db/pma'): @@ -1741,7 +1784,7 @@ class WOStackController(CementBaseController): (not self.app.pargs.wpcli) and (not self.app.pargs.phpmyadmin) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.composer) and (not self.app.pargs.netdata) and - (not self.app.pargs.fail2ban) and + (not self.app.pargs.fail2ban) and (not self.app.pargs.proftpd) and (not self.app.pargs.all) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin)): self.app.pargs.web = True @@ -1813,8 +1856,19 @@ class WOStackController(CementBaseController): # fail2ban if self.app.pargs.fail2ban: - Log.debug(self, "Remove apt_packages variable of Fail2ban") - apt_packages = apt_packages + WOVariables.wo_fail2ban + if WOAptGet.is_installed(self, 'fail2ban'): + Log.debug(self, "Remove apt_packages variable of Fail2ban") + apt_packages = apt_packages + WOVariables.wo_fail2ban + else: + Log.error(self, "Fail2ban not found") + + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + Log.debug(self, "Remove apt_packages variable for ProFTPd") + apt_packages = apt_packages + ["proftpd-basic"] + else: + Log.error(self, "ProFTPd not found") # WPCLI if self.app.pargs.wpcli: @@ -1920,7 +1974,7 @@ class WOStackController(CementBaseController): (not self.app.pargs.wpcli) and (not self.app.pargs.phpmyadmin) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.composer) and (not self.app.pargs.netdata) and - (not self.app.pargs.fail2ban) and + (not self.app.pargs.fail2ban) and (not self.app.pargs.proftpd) (not self.app.pargs.all) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin)): self.app.pargs.web = True @@ -1981,8 +2035,19 @@ class WOStackController(CementBaseController): # fail2ban if self.app.pargs.fail2ban: - Log.debug(self, "Remove apt_packages variable of Fail2ban") - apt_packages = apt_packages + WOVariables.wo_fail2ban + if WOAptGet.is_installed(self, 'fail2ban'): + Log.debug(self, "Purge apt_packages variable of Fail2ban") + apt_packages = apt_packages + WOVariables.wo_fail2ban + else: + Log.error(self, "Fail2ban not found") + + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + Log.debug(self, "Purge apt_packages variable for ProFTPd") + apt_packages = apt_packages + ["proftpd-basic"] + else: + Log.error(self, "ProFTPd not found") # WP-CLI if self.app.pargs.wpcli: diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py index 2def4af..56985b8 100644 --- a/wo/cli/plugins/stack_services.py +++ b/wo/cli/plugins/stack_services.py @@ -4,6 +4,7 @@ from wo.core.services import WOService from wo.core.logging import Log from wo.core.variables import WOVariables from wo.core.aptget import WOAptGet +import os class WOStackStatusController(CementBaseController): @@ -22,6 +23,7 @@ class WOStackStatusController(CementBaseController): self.app.pargs.mysql or self.app.pargs.redis or self.app.pargs.fail2ban or + self.app.pargs.proftpd or self.app.pargs.netdata): self.app.pargs.nginx = True self.app.pargs.php = True @@ -74,6 +76,20 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "fail2ban is not installed") + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + services = services + ['proftpd'] + else: + Log.info(self, "ProFTPd is not installed") + + # netdata + if self.app.pargs.netdata: + if os.path.isdir("/opt/netdata"): + services = services + ['netdata'] + else: + Log.info(self, "Netdata is not installed") + for service in services: Log.debug(self, "Starting service: {0}".format(service)) WOService.start_service(self, service) @@ -86,17 +102,21 @@ class WOStackStatusController(CementBaseController): self.app.pargs.php73 or self.app.pargs.mysql or self.app.pargs.fail2ban or + self.app.pargs.netdata or + self.app.pargs.proftpd or self.app.pargs.redis): self.app.pargs.nginx = True self.app.pargs.php = True self.app.pargs.mysql = True + # nginx if self.app.pargs.nginx: if (WOAptGet.is_installed(self, 'nginx-custom')): services = services + ['nginx'] else: Log.info(self, "Nginx is not installed") + # php7.2 if self.app.pargs.php: if WOAptGet.is_installed(self, 'php7.2-fpm'): services = services + ['php7.2-fpm'] @@ -108,12 +128,14 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "PHP7.3-FPM is not installed") + # php7.3 if self.app.pargs.php73: if WOAptGet.is_installed(self, 'php7.3-fpm'): services = services + ['php7.3-fpm'] else: Log.info(self, "PHP7.3-FPM is not installed") + # mysql if self.app.pargs.mysql: if ((WOVariables.wo_mysql_host is "localhost") or (WOVariables.wo_mysql_host is "127.0.0.1")): @@ -127,18 +149,34 @@ class WOStackStatusController(CementBaseController): Log.warn(self, "Remote MySQL found, " "Unable to check MySQL service status") + # redis if self.app.pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): services = services + ['redis-server'] else: Log.info(self, "Redis server is not installed") + # fail2ban if self.app.pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): services = services + ['fail2ban'] else: Log.info(self, "fail2ban is not installed") + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + services = services + ['proftpd'] + else: + Log.info(self, "ProFTPd is not installed") + + # netdata + if self.app.pargs.netdata: + if os.path.isdir("/opt/netdata"): + services = services + ['netdata'] + else: + Log.info(self, "Netdata is not installed") + for service in services: Log.debug(self, "Stopping service: {0}".format(service)) WOService.stop_service(self, service) @@ -150,7 +188,8 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or - self.app.pargs.memcached or + self.app.pargs.netdata or + self.app.pargs.proftpd or self.app.pargs.redis or self.app.pargs.fail2ban): self.app.pargs.nginx = True @@ -206,6 +245,20 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "fail2ban is not installed") + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + services = services + ['proftpd'] + else: + Log.info(self, "ProFTPd is not installed") + + # netdata + if self.app.pargs.netdata: + if os.path.isdir("/opt/netdata"): + services = services + ['netdata'] + else: + Log.info(self, "Netdata is not installed") + for service in services: Log.debug(self, "Restarting service: {0}".format(service)) WOService.restart_service(self, service) @@ -217,6 +270,8 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or + self.app.pargs.netdata or + self.app.pargs.proftpd or self.app.pargs.redis or self.app.pargs.fail2ban): self.app.pargs.nginx = True @@ -271,6 +326,20 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "fail2ban is not installed") + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + services = services + ['proftpd'] + else: + Log.info(self, "ProFTPd is not installed") + + # netdata + if self.app.pargs.netdata: + if os.path.isdir("/opt/netdata"): + services = services + ['netdata'] + else: + Log.info(self, "Netdata is not installed") + for service in services: if WOService.get_service_status(self, service): Log.info(self, "{0:10}: {1}".format(service, "Running")) @@ -282,6 +351,8 @@ class WOStackStatusController(CementBaseController): if not (self.app.pargs.nginx or self.app.pargs.php or self.app.pargs.php73 or self.app.pargs.mysql or + self.app.pargs.netdata or + self.app.pargs.proftpd or self.app.pargs.redis or self.app.pargs.fail2ban): self.app.pargs.nginx = True @@ -337,6 +408,20 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "fail2ban is not installed") + # proftpd + if self.app.pargs.proftpd: + if WOAptGet.is_installed(self, 'proftpd-basic'): + services = services + ['proftpd'] + else: + Log.info(self, "ProFTPd is not installed") + + # netdata + if self.app.pargs.netdata: + if os.path.isdir("/opt/netdata"): + services = services + ['netdata'] + else: + Log.info(self, "Netdata is not installed") + for service in services: Log.debug(self, "Reloading service: {0}".format(service)) WOService.reload_service(self, service) diff --git a/wo/cli/templates/wpcommon-php7.mustache b/wo/cli/templates/wpcommon-php7.mustache index d0ee50d..85d2739 100644 --- a/wo/cli/templates/wpcommon-php7.mustache +++ b/wo/cli/templates/wpcommon-php7.mustache @@ -40,6 +40,22 @@ location /wp-content/uploads { deny all; } } +# webp rewrite rules for EWWW testing image +location /wp-content/plugins/ewww-image-optimizer/images { + location ~ \.(png|jpe?g)$ { + add_header Vary "Accept-Encoding"; + add_header "Access-Control-Allow-Origin" "*"; + add_header Cache-Control "public, no-transform"; + access_log off; + log_not_found off; + expires max; + try_files $uri$webp_suffix $uri =404; + } + location ~ \.php$ { +#Prevent Direct Access Of PHP Files From Web Browsers + deny all; + } +} # Deny access to any files with a .php extension in the uploads directory # Works in sub-directory installs and also in multisite network # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) diff --git a/wo/cli/templates/wpcommon.mustache b/wo/cli/templates/wpcommon.mustache index 581710c..7e6edb3 100644 --- a/wo/cli/templates/wpcommon.mustache +++ b/wo/cli/templates/wpcommon.mustache @@ -21,7 +21,7 @@ location = /robots.txt { } # fallback for robots.txt with default wordpress rules location @robots { - return 200 "User-agent: *\nDisallow: /wp-admin/\nAllow: /wp-admin/admin-ajax.php\n"; + return 200 "User-agent: *\nDisallow: /wp-admin/\nAllow: /wp-admin/admin-ajax.php\n"; } # webp rewrite rules for jpg and png images # try to load alternative image.png.webp before image.png @@ -36,7 +36,23 @@ location /wp-content/uploads { try_files $uri$webp_suffix $uri =404; } location ~ \.php$ { - #Prevent Direct Access Of PHP Files From Web Browsers +#Prevent Direct Access Of PHP Files From Web Browsers + deny all; + } +} +# webp rewrite rules for EWWW testing image +location /wp-content/plugins/ewww-image-optimizer/images { + location ~ \.(png|jpe?g)$ { + add_header Vary "Accept-Encoding"; + add_header "Access-Control-Allow-Origin" "*"; + add_header Cache-Control "public, no-transform"; + access_log off; + log_not_found off; + expires max; + try_files $uri$webp_suffix $uri =404; + } + location ~ \.php$ { +#Prevent Direct Access Of PHP Files From Web Browsers deny all; } } From a1b11a39dfc70411d3c1e6d6b357152e4dabf419 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 13:52:58 +0200 Subject: [PATCH 37/44] [skip travis] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a9a87..49aca97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,10 +18,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Support for Debian 10 buster (testing - not ready for production) - Fail2ban with custom jails to secure WordPress & SSH - Variable `keylength` in /etc/wo/wo.conf to define letsencrypt certificate keylenght +- ProFTPd stack +- Beta branch and command `wo update --beta` for beta releases #### Fixed - Nginx was not reloaded after enabling HSTS +- Netdata, Composer & Fail2Ban stack remove and purge ### v3.9.5.4 - 2019-07-13 From 120bf4eff1922773ad73cac71f29eb5b0d69fb69 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 14:29:54 +0200 Subject: [PATCH 38/44] [skip travis] update bash_completion --- config/bash_completion.d/wo_auto.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index 297d227..ec38270 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -74,7 +74,7 @@ _wo_complete() # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE "install" | "purge" | "remove" ) COMPREPLY=( $(compgen \ - -W "--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --all --redis --phpredisadmin --composer --netdata --fail2ban --dashboard" \ + -W "--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --all --redis --phpredisadmin --composer --netdata --fail2ban --dashboard --proftpd" \ -- $cur) ) ;; "upgrade" ) @@ -84,7 +84,7 @@ _wo_complete() ;; "start" | "stop" | "reload" | "restart" | "status") COMPREPLY=( $(compgen \ - -W "--nginx --php --php73 --mysql --redis --fail2ban --netdata" \ + -W "--nginx --php --php73 --mysql --redis --fail2ban --netdata -proftpd" \ -- $cur) ) ;; "migrate") From 91212b7e8299deea444f475c580ae10e3b007cad Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 15:21:17 +0200 Subject: [PATCH 39/44] Add security stack --- CHANGELOG.md | 8 ++++---- wo/cli/plugins/stack.py | 27 ++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49aca97..cbdbc6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Support for Debian 10 buster (testing - not ready for production) - Fail2ban with custom jails to secure WordPress & SSH - Variable `keylength` in /etc/wo/wo.conf to define letsencrypt certificate keylenght -- ProFTPd stack +- ProFTPd stack with UFW & Fail2ban configuration - Beta branch and command `wo update --beta` for beta releases #### Fixed @@ -36,14 +36,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Changed -- phpRedisAdmin is now installed with the stack "--admin" +- phpRedisAdmin is now installed with the stack `--admin` - Remove memcached - not required anymore #### Fixed - phpRedisAdmin installation - Duplicated locations /robots.txt after upgrade to v3.9.5.3 -- Let's Encrypt stack "wo site update --letsencrypt/--letsencrypt=off" +- Let's Encrypt stack `wo site update --letsencrypt/--letsencrypt=off` - pt-query-advisor dead link - Netdata persistant configuration @@ -92,7 +92,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Install script handle migration from EEv3 - load-balancing on unix socket for php-fpm - stub_status vhost for metrics -- "--letsencrypt=subdomain" option +- `--letsencrypt=subdomain` option - opcache optimization for php-fpm - EasyEngine configuration backup before migration - EasyEngine configuration cleanup after migration diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 9fcd480..ec1db13 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -50,6 +50,8 @@ class WOStackController(CementBaseController): dict(help='Install web stack', action='store_true')), (['--admin'], dict(help='Install admin tools stack', action='store_true')), + (['--security'], + dict(help='Install security tools stack', action='store_true')), (['--nginx'], dict(help='Install Nginx stack', action='store_true')), (['--php'], @@ -1105,6 +1107,7 @@ class WOStackController(CementBaseController): msg="Adding Fail2ban into Git") WOService.reload_service(self, 'fail2ban') + # Proftpd configuration if set(["proftpd-basic"]).issubset(set(apt_packages)): if os.path.isfile("/etc/proftpd/proftpd.conf"): Log.debug(self, "Setting up Proftpd configuration") @@ -1124,13 +1127,19 @@ class WOStackController(CementBaseController): "PassivePorts " " " " 49000 50000") - + # add rule for proftpd with UFW if WOAptGet.is_installed(self, 'ufw'): try: WOShellExec.cmd_exec(self, "ufw allow " "49000:50000/tcp") except CommandExecutionError as e: - Log.error(self, "Unable to add UFW rules") + Log.error(self, "Unable to add UFW rule") + + if os.path.isfile("/etc/fail2ban/jail.d/custom.conf"): + with open("/etc/fail2ban/jail.d/custom.conf", + encoding='utf-8', mode='a') as f2bproftpd: + f2bproftpd.write("\n\n[proftpd]\nenabled = true\n") + WOService.reload_service(self, 'fail2ban') WOGit.add(self, ["/etc/proftpd"], msg="Adding ProFTPd into Git") @@ -1453,12 +1462,14 @@ class WOStackController(CementBaseController): (not self.app.pargs.netdata) and (not self.app.pargs.dashboard) and (not self.app.pargs.fail2ban) and + (not self.app.pargs.security) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.redis) and (not self.app.pargs.proftpd) and (not self.app.pargs.phpredisadmin) and (not self.app.pargs.php73)): self.app.pargs.web = True self.app.pargs.admin = True + self.app.pargs.security = True if self.app.pargs.all: self.app.pargs.web = True @@ -1484,6 +1495,8 @@ class WOStackController(CementBaseController): self.app.pargs.netdata = True self.app.pargs.dashboard = True self.app.pargs.phpredisadmin = True + + if self.app.pargs.security: self.app.pargs.fail2ban = True # Redis @@ -1785,10 +1798,12 @@ class WOStackController(CementBaseController): (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.composer) and (not self.app.pargs.netdata) and (not self.app.pargs.fail2ban) and (not self.app.pargs.proftpd) and + (not self.app.pargs.security) and (not self.app.pargs.all) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin)): self.app.pargs.web = True self.app.pargs.admin = True + self.app.pargs.security = True if self.app.pargs.all: self.app.pargs.web = True @@ -1809,6 +1824,8 @@ class WOStackController(CementBaseController): self.app.pargs.netdata = True self.app.pargs.dashboard = True self.app.pargs.phpredisadmin = True + + if self.app.pargs.security: self.app.pargs.fail2ban = True # NGINX @@ -1974,11 +1991,13 @@ class WOStackController(CementBaseController): (not self.app.pargs.wpcli) and (not self.app.pargs.phpmyadmin) and (not self.app.pargs.adminer) and (not self.app.pargs.utils) and (not self.app.pargs.composer) and (not self.app.pargs.netdata) and - (not self.app.pargs.fail2ban) and (not self.app.pargs.proftpd) + (not self.app.pargs.fail2ban) and (not self.app.pargs.proftpd) and + (not self.app.pargs.security) and (not self.app.pargs.all) and (not self.app.pargs.redis) and (not self.app.pargs.phpredisadmin)): self.app.pargs.web = True self.app.pargs.admin = True + self.app.pargs.security = True if self.app.pargs.all: self.app.pargs.web = True @@ -2000,6 +2019,8 @@ class WOStackController(CementBaseController): self.app.pargs.dashboard = True self.app.pargs.phpredisadmin = True + if self.app.pargs.security: + self.app.pargs.fail2ban = True # NGINX if self.app.pargs.nginx: if WOAptGet.is_installed(self, 'nginx-custom'): From aafc0092abeec4cdb3ff5e3fc4b37731a61aabed Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 17:42:57 +0200 Subject: [PATCH 40/44] Improve stack install and add extra directive for wp --- .travis.yml | 4 +- CHANGELOG.md | 3 +- wo/cli/plugins/site_functions.py | 83 +++++++++++++++++++++++++++++--- wo/cli/plugins/stack.py | 45 +++++++++++++---- 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3356f34..5cb4b30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ script: - sudo echo "Travis Banch = $TRAVIS_BRANCH" - sudo apt-get install -y --force-yes git python3-setuptools python3-dev python3-apt ccze tree - sudo bash install -b $TRAVIS_BRANCH --travis - - sudo wo --help && sudo wo stack install + - sudo wo --help && sudo wo stack install && sudo stack install --proftpd - sudo wo stack upgrade --netdata --no-prompt - sudo wo site create html.net --html && sudo wo site create php.com --php && sudo wo site create mysql.com --mysql && sudo wo site create proxy.com --proxy=127.0.0.1:3000 - sudo wo site create wp1.com --wp && sudo wo site create wpsc1.net --wpsc && sudo wo site create wpfc1.com --wpfc @@ -48,5 +48,7 @@ script: - sudo wp --allow-root --info - sudo wo info - sudo tree -L 2 /etc/nginx + - sudo cat /var/www/wp1.com/wp-config.php - sudo wo update --travis + - sudo wo stack remove --netdata --proftpd -- - sudo wo stack status \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cbdbc6d..cb3e92c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Support for Debian 10 buster (testing - not ready for production) - Fail2ban with custom jails to secure WordPress & SSH - Variable `keylength` in /etc/wo/wo.conf to define letsencrypt certificate keylenght -- ProFTPd stack with UFW & Fail2ban configuration +- ProFTPd stack with UFW & Fail2ban configurationz - Beta branch and command `wo update --beta` for beta releases +- Extra directives in wp-config.php (limit posts revisions, set max_memory, enable auto-update for minor-releases) #### Fixed diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 94ca80f..995b648 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -327,11 +327,44 @@ def setupwordpress(self, data): raise SiteError("generate wp-config failed for wp single site") except CommandExecutionError as e: raise SiteError("generate wp-config failed for wp single site") + try: - WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " - .format(WOVariables.wo_wpcli_path) + - "config set WP_CACHE_KEY_SALT " - "\'{0}:\'\"".format(wo_domain_name)) + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_CACHE_KEY_SALT " + "\'{0}:\'\"".format(wo_domain_name)) + + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_MEMORY_LIMIT " + "\'128M\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_MAX_MEMORY_LIMIT " + "\'256M\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set CONCATENATE_SCRIPTS " + "false\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_POST_REVISIONS " + "\'10\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set MEDIA_TRASH " + "true\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set EMPTY_TRASH_DAYS " + "\'15\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_AUTO_UPDATE_CORE " + "minor\"") + + except CommandExecutionError as e: + Log.error(self, "Unable to define extra variable in wp-config.php") else: Log.debug(self, "Generating wp-config for WordPress multisite") @@ -373,10 +406,44 @@ def setupwordpress(self, data): except CommandExecutionError as e: raise SiteError("generate wp-config failed for wp multi site") - WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " - .format(WOVariables.wo_wpcli_path) + - "config set WP_CACHE_KEY_SALT " - "\'{0}:\'\"".format(wo_domain_name)) + try: + + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_CACHE_KEY_SALT " + "\'{0}:\'\"".format(wo_domain_name)) + + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_MEMORY_LIMIT " + "\'128M\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_MAX_MEMORY_LIMIT " + "\'256M\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set CONCATENATE_SCRIPTS " + "false\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_POST_REVISIONS " + "\'10\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set MEDIA_TRASH " + "true\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set EMPTY_TRASH_DAYS " + "\'15\'\"") + WOShellExec.cmd_exec(self, "bash -c \"php {0} --allow-root " + .format(WOVariables.wo_wpcli_path) + + "config set WP_AUTO_UPDATE_CORE " + "minor\"") + + except CommandExecutionError as e: + Log.error(self, "Unable to define extra variable in wp-config.php") # WOFileUtils.mvfile(self, os.getcwd()+'/wp-config.php', # os.path.abspath(os.path.join(os.getcwd(), os.pardir))) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index ec1db13..f318bee 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1613,12 +1613,22 @@ class WOStackController(CementBaseController): "phpmyadmin/archive/STABLE.tar.gz", "/var/lib/wo/tmp/pma.tar.gz", "phpMyAdmin"]] + else: + Log.debug(self, "phpMyAdmin already installed") + Log.info(self, "phpMyAdmin already installed") + # Composer if self.app.pargs.composer: - Log.debug(self, "Setting packages variable for Composer ") - packages = packages + [["https://getcomposer.org/installer", - "/var/lib/wo/tmp/composer-install", - "Composer"]] + if not os.path.isfile('/usr/local/bin/composer'): + Log.debug(self, "Setting packages variable for Composer ") + packages = packages + [["https://getcomposer.org/" + "installer", + "/var/lib/wo/tmp/composer-install", + "Composer"]] + else: + Log.debug(self, "Composer already installed") + Log.info(self, "Composer already installed") + # PHPREDISADMIN if self.app.pargs.phpredisadmin: if not os.path.isdir('/var/www/22222/htdocs/cache/redis'): @@ -1631,11 +1641,17 @@ class WOStackController(CementBaseController): "/v1.11.3.tar.gz", "/var/lib/wo/tmp/pra.tar.gz", "phpRedisAdmin"]] + else: + Log.debug(self, "phpRedisAdmin already installed") + Log.info(self, "phpRedisAdmin already installed") + # ADMINER if self.app.pargs.adminer: - Log.debug(self, "Setting packages variable for Adminer ") - packages = packages + [["https://github.com/vrana/adminer/" - "releases/download/v{0}" + if not os.path.isdir('{0}22222/htdocs/db/adminer' + .format(WOVariables.wo_webroot)): + Log.debug(self, "Setting packages variable for Adminer ") + packages = packages + [["https://github.com/vrana/adminer/" + "releases/download/v{0}" "/adminer-{0}.php" .format(WOVariables.wo_adminer), "{0}22222/" @@ -1646,9 +1662,12 @@ class WOStackController(CementBaseController): "/vrana/adminer/master/designs/" "pepa-linha/adminer.css", "{0}22222/" - "htdocs/db/adminer/adminer.css" - .format(WOVariables.wo_webroot), - "Adminer theme"]] + "htdocs/db/adminer/adminer.css" + .format(WOVariables.wo_webroot), + "Adminer theme"]] + else: + Log.debug(self, "Adminer already installed") + Log.info(self, "Adminer already installed") # Netdata if self.app.pargs.netdata: @@ -1658,6 +1677,9 @@ class WOStackController(CementBaseController): 'kickstart-static64.sh', '/var/lib/wo/tmp/kickstart.sh', 'Netdata']] + else: + Log.debug(self, "Netdata already installed") + Log.info(self, "Netdata already installed") # WordOps Dashboard if self.app.pargs.dashboard: @@ -1674,6 +1696,9 @@ class WOStackController(CementBaseController): "extplorer/archive/v2.1.11.tar.gz", "/var/lib/wo/tmp/extplorer.tar.gz", "eXtplorer"]] + else: + Log.debug(self, "WordOps dashboard already installed") + Log.info(self, "WordOps dashboard already installed") # UTILS if self.app.pargs.utils: From e96b2477c18b1c9c578653a64b46e04caae1bf15 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 18:33:10 +0200 Subject: [PATCH 41/44] Fix travis --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5cb4b30..4c03c69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,9 +30,9 @@ script: - unset LANG - sudo bash -c 'echo -e "[user]\n\tname = abc\n\temail = root@localhost.com" > /home/travis/.gitconfig' - sudo echo "Travis Banch = $TRAVIS_BRANCH" - - sudo apt-get install -y --force-yes git python3-setuptools python3-dev python3-apt ccze tree + - sudo apt-get install -qq --force-yes git python3-setuptools python3-dev python3-apt ccze tree - sudo bash install -b $TRAVIS_BRANCH --travis - - sudo wo --help && sudo wo stack install && sudo stack install --proftpd + - sudo wo --help && sudo wo stack install && sudo wo stack install --proftpd - sudo wo stack upgrade --netdata --no-prompt - sudo wo site create html.net --html && sudo wo site create php.com --php && sudo wo site create mysql.com --mysql && sudo wo site create proxy.com --proxy=127.0.0.1:3000 - sudo wo site create wp1.com --wp && sudo wo site create wpsc1.net --wpsc && sudo wo site create wpfc1.com --wpfc @@ -50,5 +50,5 @@ script: - sudo tree -L 2 /etc/nginx - sudo cat /var/www/wp1.com/wp-config.php - sudo wo update --travis - - sudo wo stack remove --netdata --proftpd -- + - sudo wo stack remove --all - sudo wo stack status \ No newline at end of file From 70c9ae2d558d15a643c2a017c8cb061bbd8ac54a Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 19:06:52 +0200 Subject: [PATCH 42/44] Travis fix --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4c03c69..b630e9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,5 +50,4 @@ script: - sudo tree -L 2 /etc/nginx - sudo cat /var/www/wp1.com/wp-config.php - sudo wo update --travis - - sudo wo stack remove --all - sudo wo stack status \ No newline at end of file From e74ddff7f9df7cc11a618cadea6df1e59b955fd3 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 19 Jul 2019 22:29:18 +0200 Subject: [PATCH 43/44] Cleanup site --- CHANGELOG.md | 2 +- wo/cli/plugins/site.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb3e92c..c342b65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New Nginx package on Ubuntu with Cloudflare HTTP/2 HPACK and Dynamic TLS records - phpMyAdmin upgrade with `wo stack upgrade --phpmyadmin` - Wildcard SSL Certificates support with DNS validation -- Acme challenge validation with DNS API (Cloudflare, DigitalOcean, etc ..) on domain, subdomain, and wildcard +- Let's Encrypt DNS API support (Cloudflare, DigitalOcean, etc ..) on domain, subdomain, and wildcard - Flag `--letsencrypt=clean` to purge a previous SSL configuration - Support for Debian 10 buster (testing - not ready for production) - Fail2ban with custom jails to secure WordPress & SSH diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 08343db..5fd383d 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -833,9 +833,6 @@ class WOSiteUpdateController(CementBaseController): const='on', nargs='?')), (['--proxy'], dict(help="update to proxy site", nargs='+')), - (['--experimental'], - dict(help="Enable Experimenal packages without prompt", - action='store_true')), (['--all'], dict(help="update all sites", action='store_true')), (['--force'], @@ -858,7 +855,7 @@ class WOSiteUpdateController(CementBaseController): pargs.mysql or pargs.wp or pargs.wpsubdir or pargs.wpsubdomain or pargs.wpfc or pargs.wpsc or pargs.wpredis or pargs.letsencrypt or pargs.hsts or - pargs.dns): + pargs.dns or pargs.force): Log.error(self, "Please provide options to update sites.") if pargs.all: @@ -970,7 +967,7 @@ class WOSiteUpdateController(CementBaseController): (stype == 'wpsubdir' and oldsitetype in ['wpsubdomain']) or (stype == 'wpsubdomain' and oldsitetype in ['wpsubdir']) or (stype == oldsitetype and cache == oldcachetype) and not - (pargs.php73 or pargs.hsts or pargs.letsencrypt)): + pargs.php73): Log.info(self, Log.FAIL + "can not update {0} {1} to {2} {3}". format(oldsitetype, oldcachetype, stype, cache)) return 1 @@ -1211,12 +1208,15 @@ class WOSiteUpdateController(CementBaseController): if pargs.php73 == "on": data['php73'] = True php73 = True + else: + data['php73'] = False + php73 = False if pargs.letsencrypt == "on": if oldsitetype in ['wpsubdomain']: data['letsencrypt'] = True letsencrypt = True - wildcard = True + pargs.letsencrypt == 'wildcard' else: data['letsencrypt'] = True letsencrypt = True @@ -1449,7 +1449,8 @@ class WOSiteUpdateController(CementBaseController): return 1 # Setup WordPress if old sites are html/php/mysql sites - if data['wp'] and oldsitetype in ['html', 'proxy', 'php', 'mysql']: + if data['wp'] and oldsitetype in ['html', 'proxy', 'php', + 'mysql', 'php73']: try: wo_wp_creds = setupwordpress(self, data) except SiteError as e: From 0b74622c190caaf12d0f857b80bf4506940c4e67 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 20 Jul 2019 00:58:08 +0200 Subject: [PATCH 44/44] Fix MariaDB install on Debian 10 --- CHANGELOG.md | 5 ++++- install | 2 +- wo/cli/plugins/site_functions.py | 15 ++++++++++++--- wo/cli/plugins/stack.py | 11 ++++++++--- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c342b65..bf93a33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Releases -### v3.9.6 - [Unreleased] +### v3.9.x - [Unreleased] + +### v3.9.6 - 2019-07-20 #### Added @@ -26,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Nginx was not reloaded after enabling HSTS - Netdata, Composer & Fail2Ban stack remove and purge +- WordPress not installed by `wo site update` with basic php73 sites ### v3.9.5.4 - 2019-07-13 diff --git a/install b/install index 6408a5e..813473d 100755 --- a/install +++ b/install @@ -7,7 +7,7 @@ # Copyright (c) 2019 - WordOps # This script is licensed under M.I.T # ------------------------------------------------------------------------- -# Version 3.9.6 - 2019-07-16 +# Version 3.9.6 - 2019-07-20 # ------------------------------------------------------------------------- readonly wo_version_old="2.2.3" readonly wo_version_new="3.9.6" diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 995b648..525f273 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -924,7 +924,7 @@ def site_package_check(self, stype): apt_packages = apt_packages + WOVariables.wo_php73 if (os.path.isdir("/etc/nginx/common") and - not os.path.isfile("/etc/nginx/common/php73.conf")): + not os.path.isfile("/etc/nginx/common/locations-wo.conf")): data = dict() Log.debug(self, 'Writting the nginx configuration to ' 'file /etc/nginx/common/locations-wo.conf') @@ -1299,9 +1299,18 @@ def removeAcmeConf(self, domain): if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc' .format(domain)): Log.debug(self, "Removing Acme configuration") - WOFileUtils.rm(self, '/etc/letsencrypt/renewal/{0}_ecc' + try: + WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " + "--config-home " + "'/etc/letsencrypt/config' " + "--remove " + "-d {0} --ecc" + .format(domain)) + except CommandExecutionError as e: + Log.error(self, "Cert removal failed") + WOFileUtils.remove(self, '/etc/letsencrypt/renewal/{0}_ecc' .format(domain)) - WOFileUtils.rm(self, '/etc/letsencrypt/live/{0}' + WOFileUtils.remove(self, '/etc/letsencrypt/live/{0}' .format(domain)) WOGit.add(self, ["/etc/letsencrypt"], msg="Deleted {0} " diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index f318bee..bbd036a 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -97,9 +97,7 @@ class WOStackController(CementBaseController): if set(WOVariables.wo_mysql).issubset(set(apt_packages)): # add mariadb repository excepted on raspbian and ubuntu 19.04 - if ((not WOVariables.wo_platform_codename == 'disco') and - (not WOVariables.wo_platform_distro == 'raspbian') and - (not WOVariables.wo_platform_codename == 'buster')): + if (not WOVariables.wo_platform_distro == 'raspbian'): Log.info(self, "Adding repository for MySQL, please wait...") mysql_pref = ("Package: *\nPin: origin " "sfo1.mirrors.digitalocean.com" @@ -214,6 +212,13 @@ class WOStackController(CementBaseController): else: Log.info(self, "Adding repository for PHP, please wait...") # Add repository for php + if (WOVariables.wo_platform_codename == 'buster'): + php_pref = ("Package: *\nPin: origin " + "packages.sury.org" + "\nPin-Priority: 1000\n") + with open('/etc/apt/preferences.d/' + 'PHP.pref', 'w') as php_pref_file: + php_pref_file.write(php_pref) Log.debug(self, 'Adding repo_url of php for debian') WORepo.add(self, repo_url=WOVariables.wo_php_repo) Log.debug(self, 'Adding deb.sury GPG key')