diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a9b5f..da20f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### v3.9.x - [Unreleased] +#### Changed + +- Improve Let's Encrypt certificate issuance logging informations + #### Fixed - Fix cheat.sh install [PR #139](https://github.com/WordOps/WordOps/pull/139) - sslutils error when trying to display SSL certificate expiration -- Fix cheat.sh symbolink link check before creation +- Fix cheat.sh symbolic link check before creation ### v3.9.8.9 - 2019-09-03 diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index b4c027c..d26235e 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -163,9 +163,9 @@ class WOSiteController(CementBaseController): if os.path.islink("{0}/conf/nginx/ssl.conf" .format(wo_site_webroot)): sslexpiry = str( - SSL.getExpirationDate(self, wo_root_domain)) + SSL.getexpirationdays(self, wo_root_domain)) else: - sslexpiry = str(SSL.getExpirationDate(self, wo_domain)) + sslexpiry = str(SSL.getexpirationdays(self, wo_domain)) else: sslprovider = '' sslexpiry = '' @@ -745,9 +745,9 @@ class WOSiteCreateController(CementBaseController): # check if a wildcard cert for the root domain exist Log.debug(self, "checkWildcardExist on *.{0}" .format(wo_root_domain)) - isWildcard = checkWildcardExist(self, wo_root_domain) - Log.debug(self, "isWildcard = {0}".format(isWildcard)) - if isWildcard: + iswildcard = SSL.checkwildcardexist(self, wo_root_domain) + Log.debug(self, "iswildcard = {0}".format(iswildcard)) + if iswildcard: Log.info(self, "Using existing Wildcard SSL " "certificate from {0} to secure {1}" .format(wo_root_domain, wo_domain)) @@ -769,7 +769,7 @@ class WOSiteCreateController(CementBaseController): if pargs.hsts: setupHsts(self, wo_domain) - site_url_https(self, wo_domain) + SSL.siteurlhttps(self, wo_domain) if not WOService.reload_service(self, 'nginx'): Log.error(self, "service nginx reload failed. " "check issues with `nginx -t` command") @@ -1174,7 +1174,7 @@ class WOSiteUpdateController(CementBaseController): # --letsencrypt=renew code goes here if pargs.letsencrypt == "renew" and not pargs.all: - expiry_days = SSL.getExpirationDays(self, wo_domain) + expiry_days = SSL.getexpirationdays(self, wo_domain) min_expiry_days = 45 if check_ssl: if (expiry_days <= min_expiry_days): @@ -1196,12 +1196,12 @@ class WOSiteUpdateController(CementBaseController): "check issues with `nginx -t` command") Log.info(self, "SUCCESS: Certificate was successfully renewed For" " https://{0}".format(wo_domain)) - if (SSL.getExpirationDays(self, wo_domain) > 0): + if (SSL.getexpirationdays(self, wo_domain) > 0): Log.info(self, "Your cert will expire within " + - str(SSL.getExpirationDays(self, wo_domain)) + + str(SSL.getexpirationdays(self, wo_domain)) + " days.") Log.info(self, "Expiration date: " + - str(SSL.getExpirationDate(self, wo_domain))) + str(SSL.getexpirationdate(self, wo_domain))) else: Log.warn( @@ -1236,12 +1236,12 @@ class WOSiteUpdateController(CementBaseController): self, "You have more than 45 days with the current " "certificate - refusing to run.\n") - if (SSL.getExpirationDays(self, wo_domain) > 0): + if (SSL.getexpirationdays(self, wo_domain) > 0): Log.info(self, "Your cert will expire within " + - str(SSL.getExpirationDays(self, wo_domain)) + + str(SSL.getexpirationdays(self, wo_domain)) + " days.") Log.info(self, "Expiration date: \n\n" + - str(SSL.getExpirationDate(self, wo_domain))) + str(SSL.getexpirationdate(self, wo_domain))) return 0 # else: # Log.warn(self, "Your cert already EXPIRED ! @@ -1354,11 +1354,11 @@ class WOSiteUpdateController(CementBaseController): # check if a wildcard cert for the root domain exist Log.debug(self, "checkWildcardExist on *.{0}" .format(wo_root_domain)) - isWildcard = checkWildcardExist(self, wo_root_domain) - Log.debug(self, "isWildcard = {0}".format(isWildcard)) + iswildcard = SSL.checkwildcardexist(self, wo_root_domain) + Log.debug(self, "iswildcard = {0}".format(iswildcard)) if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled"): if wo_subdomain: - if isWildcard: + if iswildcard: Log.info(self, "Using existing Wildcard SSL " "certificate from {0} to secure {1}" .format(wo_root_domain, wo_domain)) @@ -1375,9 +1375,6 @@ class WOSiteUpdateController(CementBaseController): else: setupLetsEncrypt(self, wo_domain, wo_subdomain, wo_wildcard, wo_dns, wo_acme_dns) - - httpsRedirect(self, wo_domain, True, wo_wildcard) - site_url_https(self, wo_domain) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot), @@ -1389,8 +1386,8 @@ class WOSiteUpdateController(CementBaseController): '/etc/nginx/conf.d/force-ssl-{0}.conf' .format(wo_domain)) - httpsRedirect(self, wo_domain, True, wo_wildcard) - site_url_https(self, wo_domain) + httpsRedirect(self, wo_domain, True, wo_wildcard) + SSL.siteUrlHttps(self, wo_domain) if not WOService.reload_service(self, 'nginx'): Log.error(self, "service nginx reload failed. " @@ -1398,20 +1395,20 @@ class WOSiteUpdateController(CementBaseController): Log.info(self, "Congratulations! Successfully " "Configured SSL for Site " " https://{0}".format(wo_domain)) - if wo_subdomain and isWildcard: - if (SSL.getExpirationDays(self, wo_root_domain) > 0): + if wo_subdomain and iswildcard: + if (SSL.getexpirationdays(self, wo_root_domain) > 0): Log.info( self, "Your cert will expire within " + - str(SSL.getExpirationDays(self, wo_root_domain)) + + str(SSL.getexpirationdays(self, wo_root_domain)) + " days.") else: Log.warn( self, "Your cert already EXPIRED ! " ".PLEASE renew soon . ") else: - if (SSL.getExpirationDays(self, wo_domain) > 0): + if (SSL.getexpirationdays(self, wo_domain) > 0): Log.info(self, "Your cert will expire within " + - str(SSL.getExpirationDays(self, wo_domain)) + + str(SSL.getexpirationdays(self, wo_domain)) + " days.") else: Log.warn( diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 99d11d3..3a0eee2 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -6,7 +6,6 @@ import json import re import string import subprocess -import csv from subprocess import CalledProcessError from wo.cli.plugins.sitedb import getSiteInfo @@ -1290,37 +1289,6 @@ def removeAcmeConf(self, domain): WOService.restart_service(self, "nginx") -def site_url_https(self, domain): - if os.path.isfile('/var/www/{0}/wp-config.php'.format(domain)): - wo_site_webroot = ('/var/www/{0}'.format(domain)) - Log.info(self, "Checking if site url already " - "use https, please wait...") - WOFileUtils.chdir(self, '{0}/htdocs/'.format(wo_site_webroot)) - wo_siteurl = \ - WOShellExec.cmd_exec_stdout(self, - "{0} option get siteurl " - .format(WOVariables.wo_wpcli_path) + - "--allow-root --quiet") - test_url = re.split(":", wo_siteurl) - if not (test_url[0] == 'https'): - try: - WOShellExec.cmd_exec(self, "{0} option update siteurl " - "\'https://{1}\' --allow-root".format( - WOVariables.wo_wpcli_path, domain)) - WOShellExec.cmd_exec(self, "{0} option update home " - "\'https://{1}\' --allow-root".format( - WOVariables.wo_wpcli_path, domain)) - except CommandExecutionError as e: - Log.debug(self, "{0}".format(e)) - raise SiteError("migration to https failed") - Log.info( - self, "Site address updated " - "successfully to https://{0}".format(domain)) - else: - Log.info( - self, "Site address was already using https") - - def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', dbhost=''): """ @@ -1350,7 +1318,7 @@ def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, - wo_dns=False, wo_acme_dns='dns_cf', backend=False): + wo_dns=False, wo_acme_dns='dns_cf'): if os.path.isfile("/etc/letsencrypt/" "renewal/{0}_ecc/" @@ -1371,11 +1339,12 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, self, "Validation : DNS mode with {0}".format(wo_acme_dns)) else: acme_mode = "-w /var/www/html" - validation_mode = "Subdomain Webroot challenge" + validation_mode = "Webroot challenge" Log.debug(self, "Validation : Webroot mode") if subdomain: - Log.info(self, "Issuing subdomain SSL cert with acme.sh") + Log.info(self, "Certificate type: Subdomain") Log.info(self, "Validation mode : {0}".format(validation_mode)) + Log.wait(self, "Issuing SSL certificate with acme.sh") ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + "--issue " "-d {0} {1} " @@ -1384,8 +1353,9 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, acme_mode, keylenght)) elif wildcard: - Log.info(self, "Issuing Wildcard SSL cert with acme.sh") + Log.info(self, "Certificate type: Wildcard") Log.info(self, "Validation mode : {0}".format(validation_mode)) + Log.wait(self, "Issuing SSL certificate with acme.sh") ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + "--issue " "-d {0} -d '*.{0}' --dns {1} " @@ -1394,8 +1364,9 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, wo_acme_dns, keylenght)) else: - Log.info(self, "Issuing domain SSL cert with acme.sh") + Log.info(self, "Certificate type: Domain + www") Log.info(self, "Validation mode : {0}".format(validation_mode)) + Log.wait(self, "Issuing SSL certificate with acme.sh") ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + "--issue " "-d {0} -d www.{0} {1} " @@ -1403,7 +1374,8 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, .format(wo_domain_name, acme_mode, keylenght)) if ssl: - Log.info(self, "Deploying SSL cert with acme.sh") + Log.valide(self, "Issuing SSL certificate with acme.sh") + Log.wait(self, "Deploying SSL cert with acme.sh") Log.debug(self, "Cert deployment for domain: {0}" .format(wo_domain_name)) try: @@ -1423,21 +1395,22 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, "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)) + Log.valide(self, "Deploying SSL cert with acme.sh") + if os.path.isdir('/var/www/{0}/conf/nginx' + .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() + 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) if not WOFileUtils.grep(self, '/var/www/22222/conf/nginx/ssl.conf', '/etc/letsencrypt'): @@ -1466,34 +1439,6 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, "you are running Let\'s Encrypt Client " "\n to allow it to verify the site automatically.") -# check if a wildcard exist to secure a new subdomain - - -def checkWildcardExist(self, wo_domain_name): - - wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " - "'/etc/letsencrypt/config'") - # export certificates list from acme.sh - WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + - "--list --listraw > /var/lib/wo/cert.csv") - - # define new csv dialect - csv.register_dialect('acmeconf', delimiter='|') - # open file - certfile = open('/var/lib/wo/cert.csv', mode='r', encoding='utf-8') - reader = csv.reader(certfile, 'acmeconf') - wo_wildcard_domain = ("*.{0}".format(wo_domain_name)) - for row in reader: - if wo_wildcard_domain in row[2]: - isWildcard = True - break - else: - isWildcard = False - certfile.close() - - return isWildcard - - # copy wildcard certificate to a subdomain @@ -1537,9 +1482,9 @@ def renewLetsEncrypt(self, wo_domain_name): if not ssl: Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!", False) - if (SSL.getExpirationDays(self, wo_domain_name) > 0): + if (SSL.getexpirationdays(self, wo_domain_name) > 0): Log.error(self, "Your current certificate will expire within " + - str(SSL.getExpirationDays(self, wo_domain_name)) + + str(SSL.getexpirationdays(self, wo_domain_name)) + " days.", False) else: Log.error(self, "Your current certificate already expired!", False) diff --git a/wo/core/logging.py b/wo/core/logging.py index 84a3508..19787ff 100644 --- a/wo/core/logging.py +++ b/wo/core/logging.py @@ -45,3 +45,23 @@ class Log: Logs debug messages into log file """ self.app.log.debug(Log.HEADER + msg + Log.ENDC) + + def wait(self, msg, end='\r', log=True): + """ + Logs info messages with validation step + """ + print( + Log.OKBLUE + msg + + "[" + Log.ENDC + ".." + Log.OKBLUE + "]", end=end) + if log: + self.app.log.info(Log.OKBLUE + msg + Log.ENDC) + + def valide(self, msg, end='\n', log=True): + """ + Logs info messages after validation step + """ + print( + Log.OKBLUE + msg + + "[" + Log.ENDC + "OK" + Log.OKBLUE + "]", end=end) + if log: + self.app.log.info(Log.OKBLUE + msg + Log.ENDC) diff --git a/wo/core/sslutils.py b/wo/core/sslutils.py index b164913..d380557 100644 --- a/wo/core/sslutils.py +++ b/wo/core/sslutils.py @@ -1,12 +1,16 @@ +import csv import os +import re +from wo.core.fileutils import WOFileUtils from wo.core.logging import Log from wo.core.shellexec import WOShellExec +from wo.core.variables import WOVariables class SSL: - def getExpirationDays(self, domain, returnonerror=False): + def getexpirationdays(self, domain, returnonerror=False): # check if exist if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem' .format(domain)): @@ -33,7 +37,7 @@ class SSL: # return "Certificate Already Expired ! Please Renew soon." return -1 - def getExpirationDate(self, domain): + def getexpirationdate(self, domain): # check if exist if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem' .format(domain)): @@ -49,3 +53,61 @@ class SSL: "\"Not After\" | cut -c 25-)\" " .format(domain)) return expiration_date + + def siteurlhttps(self, domain): + wo_site_webroot = ('/var/www/{0}'.format(domain)) + WOFileUtils.chdir( + self, '{0}/htdocs/'.format(wo_site_webroot)) + if WOShellExec.cmd_exec( + self, "{0} --allow-root core is-installed" + .format(WOVariables.wo_wp_cli)): + wo_siteurl = ( + WOShellExec.cmd_exec_stdout( + self, "{0} option get siteurl " + .format(WOVariables.wo_wpcli_path) + + "--allow-root --quiet")) + test_url = re.split(":", wo_siteurl) + if not (test_url[0] == 'https'): + WOShellExec.cmd_exec( + self, "{0} option update siteurl " + "\'https://{1}\' --allow-root".format( + WOVariables.wo_wpcli_path, domain)) + WOShellExec.cmd_exec( + self, "{0} option update home " + "\'https://{1}\' --allow-root".format( + WOVariables.wo_wpcli_path, domain)) + WOShellExec.cmd_exec( + self, "{0} search-replace \'http://{0}\'" + "\'https://{0}\' --skip-columns=guid " + "--skip-tables=wp_users" + .format(domain)) + Log.info( + self, "Site address updated " + "successfully to https://{0}".format(domain)) + + # check if a wildcard exist to secure a new subdomain + + def checkwildcardexist(self, wo_domain_name): + + wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " + "'/etc/letsencrypt/config'") + # export certificates list from acme.sh + WOShellExec.cmd_exec( + self, "{0} ".format(wo_acme_exec) + + "--list --listraw > /var/lib/wo/cert.csv") + + # define new csv dialect + csv.register_dialect('acmeconf', delimiter='|') + # open file + certfile = open('/var/lib/wo/cert.csv', mode='r', encoding='utf-8') + reader = csv.reader(certfile, 'acmeconf') + wo_wildcard_domain = ("*.{0}".format(wo_domain_name)) + for row in reader: + if wo_wildcard_domain in row[2]: + iswildcard = True + break + else: + iswildcard = False + certfile.close() + + return iswildcard