Improve letsencrypt

This commit is contained in:
VirtuBox
2019-09-04 03:07:24 +02:00
parent ebdac21cab
commit ce2c63bd64
5 changed files with 139 additions and 111 deletions

View File

@@ -8,11 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### v3.9.x - [Unreleased] ### v3.9.x - [Unreleased]
#### Changed
- Improve Let's Encrypt certificate issuance logging informations
#### Fixed #### Fixed
- Fix cheat.sh install [PR #139](https://github.com/WordOps/WordOps/pull/139) - Fix cheat.sh install [PR #139](https://github.com/WordOps/WordOps/pull/139)
- sslutils error when trying to display SSL certificate expiration - 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 ### v3.9.8.9 - 2019-09-03

View File

@@ -163,9 +163,9 @@ class WOSiteController(CementBaseController):
if os.path.islink("{0}/conf/nginx/ssl.conf" if os.path.islink("{0}/conf/nginx/ssl.conf"
.format(wo_site_webroot)): .format(wo_site_webroot)):
sslexpiry = str( sslexpiry = str(
SSL.getExpirationDate(self, wo_root_domain)) SSL.getexpirationdays(self, wo_root_domain))
else: else:
sslexpiry = str(SSL.getExpirationDate(self, wo_domain)) sslexpiry = str(SSL.getexpirationdays(self, wo_domain))
else: else:
sslprovider = '' sslprovider = ''
sslexpiry = '' sslexpiry = ''
@@ -745,9 +745,9 @@ class WOSiteCreateController(CementBaseController):
# check if a wildcard cert for the root domain exist # check if a wildcard cert for the root domain exist
Log.debug(self, "checkWildcardExist on *.{0}" Log.debug(self, "checkWildcardExist on *.{0}"
.format(wo_root_domain)) .format(wo_root_domain))
isWildcard = checkWildcardExist(self, wo_root_domain) iswildcard = SSL.checkwildcardexist(self, wo_root_domain)
Log.debug(self, "isWildcard = {0}".format(isWildcard)) Log.debug(self, "iswildcard = {0}".format(iswildcard))
if isWildcard: if iswildcard:
Log.info(self, "Using existing Wildcard SSL " Log.info(self, "Using existing Wildcard SSL "
"certificate from {0} to secure {1}" "certificate from {0} to secure {1}"
.format(wo_root_domain, wo_domain)) .format(wo_root_domain, wo_domain))
@@ -769,7 +769,7 @@ class WOSiteCreateController(CementBaseController):
if pargs.hsts: if pargs.hsts:
setupHsts(self, wo_domain) setupHsts(self, wo_domain)
site_url_https(self, wo_domain) SSL.siteurlhttps(self, wo_domain)
if not WOService.reload_service(self, 'nginx'): if not WOService.reload_service(self, 'nginx'):
Log.error(self, "service nginx reload failed. " Log.error(self, "service nginx reload failed. "
"check issues with `nginx -t` command") "check issues with `nginx -t` command")
@@ -1174,7 +1174,7 @@ class WOSiteUpdateController(CementBaseController):
# --letsencrypt=renew code goes here # --letsencrypt=renew code goes here
if pargs.letsencrypt == "renew" and not pargs.all: 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 min_expiry_days = 45
if check_ssl: if check_ssl:
if (expiry_days <= min_expiry_days): if (expiry_days <= min_expiry_days):
@@ -1196,12 +1196,12 @@ class WOSiteUpdateController(CementBaseController):
"check issues with `nginx -t` command") "check issues with `nginx -t` command")
Log.info(self, "SUCCESS: Certificate was successfully renewed For" Log.info(self, "SUCCESS: Certificate was successfully renewed For"
" https://{0}".format(wo_domain)) " 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 " + Log.info(self, "Your cert will expire within " +
str(SSL.getExpirationDays(self, wo_domain)) + str(SSL.getexpirationdays(self, wo_domain)) +
" days.") " days.")
Log.info(self, "Expiration date: " + Log.info(self, "Expiration date: " +
str(SSL.getExpirationDate(self, wo_domain))) str(SSL.getexpirationdate(self, wo_domain)))
else: else:
Log.warn( Log.warn(
@@ -1236,12 +1236,12 @@ class WOSiteUpdateController(CementBaseController):
self, "You have more than 45 days with the current " self, "You have more than 45 days with the current "
"certificate - refusing to run.\n") "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 " + Log.info(self, "Your cert will expire within " +
str(SSL.getExpirationDays(self, wo_domain)) + str(SSL.getexpirationdays(self, wo_domain)) +
" days.") " days.")
Log.info(self, "Expiration date: \n\n" + Log.info(self, "Expiration date: \n\n" +
str(SSL.getExpirationDate(self, wo_domain))) str(SSL.getexpirationdate(self, wo_domain)))
return 0 return 0
# else: # else:
# Log.warn(self, "Your cert already EXPIRED ! # Log.warn(self, "Your cert already EXPIRED !
@@ -1354,11 +1354,11 @@ class WOSiteUpdateController(CementBaseController):
# check if a wildcard cert for the root domain exist # check if a wildcard cert for the root domain exist
Log.debug(self, "checkWildcardExist on *.{0}" Log.debug(self, "checkWildcardExist on *.{0}"
.format(wo_root_domain)) .format(wo_root_domain))
isWildcard = checkWildcardExist(self, wo_root_domain) iswildcard = SSL.checkwildcardexist(self, wo_root_domain)
Log.debug(self, "isWildcard = {0}".format(isWildcard)) Log.debug(self, "iswildcard = {0}".format(iswildcard))
if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled"): if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled"):
if wo_subdomain: if wo_subdomain:
if isWildcard: if iswildcard:
Log.info(self, "Using existing Wildcard SSL " Log.info(self, "Using existing Wildcard SSL "
"certificate from {0} to secure {1}" "certificate from {0} to secure {1}"
.format(wo_root_domain, wo_domain)) .format(wo_root_domain, wo_domain))
@@ -1375,9 +1375,6 @@ class WOSiteUpdateController(CementBaseController):
else: else:
setupLetsEncrypt(self, wo_domain, wo_subdomain, setupLetsEncrypt(self, wo_domain, wo_subdomain,
wo_wildcard, wo_dns, wo_acme_dns) wo_wildcard, wo_dns, wo_acme_dns)
httpsRedirect(self, wo_domain, True, wo_wildcard)
site_url_https(self, wo_domain)
else: else:
WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled"
.format(wo_site_webroot), .format(wo_site_webroot),
@@ -1389,8 +1386,8 @@ class WOSiteUpdateController(CementBaseController):
'/etc/nginx/conf.d/force-ssl-{0}.conf' '/etc/nginx/conf.d/force-ssl-{0}.conf'
.format(wo_domain)) .format(wo_domain))
httpsRedirect(self, wo_domain, True, wo_wildcard) httpsRedirect(self, wo_domain, True, wo_wildcard)
site_url_https(self, wo_domain) SSL.siteUrlHttps(self, wo_domain)
if not WOService.reload_service(self, 'nginx'): if not WOService.reload_service(self, 'nginx'):
Log.error(self, "service nginx reload failed. " Log.error(self, "service nginx reload failed. "
@@ -1398,20 +1395,20 @@ class WOSiteUpdateController(CementBaseController):
Log.info(self, "Congratulations! Successfully " Log.info(self, "Congratulations! Successfully "
"Configured SSL for Site " "Configured SSL for Site "
" https://{0}".format(wo_domain)) " https://{0}".format(wo_domain))
if wo_subdomain and isWildcard: if wo_subdomain and iswildcard:
if (SSL.getExpirationDays(self, wo_root_domain) > 0): if (SSL.getexpirationdays(self, wo_root_domain) > 0):
Log.info( Log.info(
self, "Your cert will expire within " + self, "Your cert will expire within " +
str(SSL.getExpirationDays(self, wo_root_domain)) + str(SSL.getexpirationdays(self, wo_root_domain)) +
" days.") " days.")
else: else:
Log.warn( Log.warn(
self, "Your cert already EXPIRED ! " self, "Your cert already EXPIRED ! "
".PLEASE renew soon . ") ".PLEASE renew soon . ")
else: else:
if (SSL.getExpirationDays(self, wo_domain) > 0): if (SSL.getexpirationdays(self, wo_domain) > 0):
Log.info(self, "Your cert will expire within " + Log.info(self, "Your cert will expire within " +
str(SSL.getExpirationDays(self, wo_domain)) + str(SSL.getexpirationdays(self, wo_domain)) +
" days.") " days.")
else: else:
Log.warn( Log.warn(

View File

@@ -6,7 +6,6 @@ import json
import re import re
import string import string
import subprocess import subprocess
import csv
from subprocess import CalledProcessError from subprocess import CalledProcessError
from wo.cli.plugins.sitedb import getSiteInfo from wo.cli.plugins.sitedb import getSiteInfo
@@ -1290,37 +1289,6 @@ def removeAcmeConf(self, domain):
WOService.restart_service(self, "nginx") 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='', def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='',
dbhost=''): dbhost=''):
""" """
@@ -1350,7 +1318,7 @@ def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='',
def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, 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/" if os.path.isfile("/etc/letsencrypt/"
"renewal/{0}_ecc/" "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)) self, "Validation : DNS mode with {0}".format(wo_acme_dns))
else: else:
acme_mode = "-w /var/www/html" acme_mode = "-w /var/www/html"
validation_mode = "Subdomain Webroot challenge" validation_mode = "Webroot challenge"
Log.debug(self, "Validation : Webroot mode") Log.debug(self, "Validation : Webroot mode")
if subdomain: 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.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) + ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) +
"--issue " "--issue "
"-d {0} {1} " "-d {0} {1} "
@@ -1384,8 +1353,9 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False,
acme_mode, acme_mode,
keylenght)) keylenght))
elif wildcard: 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.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) + ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) +
"--issue " "--issue "
"-d {0} -d '*.{0}' --dns {1} " "-d {0} -d '*.{0}' --dns {1} "
@@ -1394,8 +1364,9 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False,
wo_acme_dns, wo_acme_dns,
keylenght)) keylenght))
else: 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.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) + ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) +
"--issue " "--issue "
"-d {0} -d www.{0} {1} " "-d {0} -d www.{0} {1} "
@@ -1403,7 +1374,8 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False,
.format(wo_domain_name, .format(wo_domain_name,
acme_mode, keylenght)) acme_mode, keylenght))
if ssl: 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}" Log.debug(self, "Cert deployment for domain: {0}"
.format(wo_domain_name)) .format(wo_domain_name))
try: try:
@@ -1423,21 +1395,22 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False,
"service nginx restart\" " "service nginx restart\" "
.format(WOVariables.wo_ssl_live, .format(WOVariables.wo_ssl_live,
wo_domain_name)) wo_domain_name))
Log.info( Log.valide(self, "Deploying SSL cert with acme.sh")
self, "Adding /var/www/{0}/conf/nginx/ssl.conf" if os.path.isdir('/var/www/{0}/conf/nginx'
.format(wo_domain_name)) .format(wo_domain_name)):
sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" sslconf = open("/var/www/{0}/conf/nginx/ssl.conf"
.format(wo_domain_name), .format(wo_domain_name),
encoding='utf-8', mode='w') encoding='utf-8', mode='w')
sslconf.write("listen 443 ssl http2;\n" sslconf.write(
"listen [::]:443 ssl http2;\n" "listen 443 ssl http2;\n"
"ssl_certificate {0}/{1}/fullchain.pem;\n" "listen [::]:443 ssl http2;\n"
"ssl_certificate_key {0}/{1}/key.pem;\n" "ssl_certificate {0}/{1}/fullchain.pem;\n"
"ssl_trusted_certificate {0}/{1}/ca.pem;\n" "ssl_certificate_key {0}/{1}/key.pem;\n"
"ssl_stapling_verify on;\n" "ssl_trusted_certificate {0}/{1}/ca.pem;\n"
.format(WOVariables.wo_ssl_live, wo_domain_name)) "ssl_stapling_verify on;\n"
sslconf.close() .format(WOVariables.wo_ssl_live, wo_domain_name))
sslconf.close()
# updateSiteInfo(self, wo_domain_name, ssl=True) # updateSiteInfo(self, wo_domain_name, ssl=True)
if not WOFileUtils.grep(self, '/var/www/22222/conf/nginx/ssl.conf', if not WOFileUtils.grep(self, '/var/www/22222/conf/nginx/ssl.conf',
'/etc/letsencrypt'): '/etc/letsencrypt'):
@@ -1466,34 +1439,6 @@ def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False,
"you are running Let\'s Encrypt Client " "you are running Let\'s Encrypt Client "
"\n to allow it to verify the site automatically.") "\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 # copy wildcard certificate to a subdomain
@@ -1537,9 +1482,9 @@ def renewLetsEncrypt(self, wo_domain_name):
if not ssl: if not ssl:
Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!", Log.error(self, "ERROR : Let's Encrypt certificate renewal FAILED!",
False) 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 " + 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) " days.", False)
else: else:
Log.error(self, "Your current certificate already expired!", False) Log.error(self, "Your current certificate already expired!", False)

View File

@@ -45,3 +45,23 @@ class Log:
Logs debug messages into log file Logs debug messages into log file
""" """
self.app.log.debug(Log.HEADER + msg + Log.ENDC) 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)

View File

@@ -1,12 +1,16 @@
import csv
import os import os
import re
from wo.core.fileutils import WOFileUtils
from wo.core.logging import Log from wo.core.logging import Log
from wo.core.shellexec import WOShellExec from wo.core.shellexec import WOShellExec
from wo.core.variables import WOVariables
class SSL: class SSL:
def getExpirationDays(self, domain, returnonerror=False): def getexpirationdays(self, domain, returnonerror=False):
# check if exist # check if exist
if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem' if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
.format(domain)): .format(domain)):
@@ -33,7 +37,7 @@ class SSL:
# return "Certificate Already Expired ! Please Renew soon." # return "Certificate Already Expired ! Please Renew soon."
return -1 return -1
def getExpirationDate(self, domain): def getexpirationdate(self, domain):
# check if exist # check if exist
if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem' if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
.format(domain)): .format(domain)):
@@ -49,3 +53,61 @@ class SSL:
"\"Not After\" | cut -c 25-)\" " "\"Not After\" | cut -c 25-)\" "
.format(domain)) .format(domain))
return expiration_date 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