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