Mini Shell
Direktori : /opt/sharedrads/ |
|
Current File : //opt/sharedrads/check_autossl |
#!/opt/imh-python/bin/python3
# pylint: disable=F0401
"""Checks for failed AutoSSL renewals"""
import sys
import datetime
import time
import argparse
import socket
import logging
import os
from pathlib import Path
import json
import pp_api
from cpapis import whmapi1, CpAPIError
class NotificationFile:
"""Provides simple interface for updating users notification file"""
def __init__(self, user):
"""NotificationFile constructor"""
self.user = user
self.filepath = Path('/home', user, '.imh/autossl_notified')
self.filepath.parent.mkdir(mode=0o755, parents=True, exist_ok=True)
if not self.filepath.is_file():
self.filepath.write_bytes(b'{}')
def read(self) -> dict:
"""Read notification file and return JSON representation"""
try:
with open(self.filepath, encoding='ascii') as handle:
return json.load(handle)
except ValueError: # did not read valid json, start fresh
return {}
def write(self, domains: dict):
"""Write dict domains to notification file"""
with open(self.filepath, 'w', encoding='ascii') as handle:
json.dump(domains, handle)
def email_cx(user, domains):
"""Email user the list of domains using PP API
domains (dict): [expiration_status][domain] = expiration timestamp"""
loguser = {'current_user': user}
pp_connect = pp_api.PowerPanel()
if 'webhostinghub' in socket.gethostname():
template_id = 710
else:
template_id = 709
for expstatus in domains:
variable_text = ""
if len(domains[expstatus]) == 0:
logger.debug(
"No %s notifications, skipping", expstatus, extra=loguser
)
continue
for domain in domains[expstatus]:
human_expiration = datetime.datetime.fromtimestamp(
domains[expstatus][domain]
).strftime('%m-%d-%Y')
variable_text += "Domain: {} - Expiration: {}, ".format(
domain, human_expiration
)
# verbiage by CE
if expstatus == 'expiring':
exp_verbiage = 'expire'
else:
exp_verbiage = 'remain expired'
results = pp_connect.call(
"notification.send",
template=template_id,
cpanelUser=user,
variable_1=variable_text,
variable_2=exp_verbiage,
)
if results.status != 0:
logger.warning(
"Failed to send notification: %s",
results.message,
extra=loguser,
)
else:
logger.info("Notification for %s sent", expstatus, extra=loguser)
try:
notification_file = NotificationFile(user)
user_notifications = notification_file.read()
for expstatus in domains:
for domain in domains[expstatus]:
user_notifications[domain] = domains[expstatus][domain]
notification_file.write(user_notifications)
except Exception:
logger.warning("Unable to record notification!", extra=loguser)
def main():
"""Main logic"""
parser = argparse.ArgumentParser(description='AutoSSL monitor')
parser.add_argument('--noop', action='store_true', default=False)
args = parser.parse_args()
loguser = {'current_user': 'MAIN'}
try:
result = whmapi1('fetch_ssl_vhosts', check=True)
except CpAPIError as exc:
logger.debug(exc)
logger.error("Unable to fetch installed SSLs!")
sys.exit(1)
notifications = {}
for vhost in result['data']['vhosts']:
is_autossl = False
if vhost['crt']['issuer.organizationName'] == "cPanel, Inc.":
is_autossl = True
# if vhost['crt']['issuer.organizationName'] == "Let's Encrypt":
# is_autossl = True
if not is_autossl:
continue # skip vhost if its not autossl issued
owner = vhost['user']
domain = vhost['crt']['subject.commonName']['commonName']
expire = int(vhost['crt']['not_after'])
# DEBUG
# owner = 'josephmteer333'
# expire = 1499988912 #expired
# expire = 1501113600 #will expire
loguser = {'current_user': owner}
if (expire - int(time.time())) > 432000: # not expiring in 5 days
continue
if (expire - int(time.time())) < 0:
expiration_status = 'expired'
else:
expiration_status = 'expiring'
notification_file = NotificationFile(owner)
notified_list = notification_file.read()
# check autossl feature status
try:
whmapi1(
'get_users_features_settings',
{'user': owner, 'feature': 'autossl'},
check=True,
)
except CpAPIError as exc:
logger.debug(exc)
logger.warning(
"Unable to fetch AutoSSL status! Ignoring...", extra=loguser
)
continue
feature_settings = result['data']['users_features_settings'][0]
if feature_settings['cpuser_setting']: # returned None = no override
if int(feature_settings['cpuser_setting']) == 1:
# AutoSSL enabled via override
logger.debug("AutoSSL enabled via override", extra=loguser)
else:
logger.info("AutoSSL disabled via override", extra=loguser)
continue
elif int(feature_settings['feature_list_setting']) == 1:
# AutoSSL enabled via featurelist
logger.debug("AutoSSL enabled via featurelist", extra=loguser)
else:
logger.info("AutoSSL not enabled! Skipping...", extra=loguser)
continue
human_expiration = datetime.datetime.fromtimestamp(expire).strftime(
'%m-%d-%Y'
)
logger.info(
"Expiring - Domain: %s Expiration: %s",
domain,
human_expiration,
extra=loguser,
)
# domain is expired or expiring at this point, notify accordingly
if owner not in notifications:
# create user in dict if not already there
notifications[owner] = {}
notifications[owner]['expiring'] = {}
notifications[owner]['expired'] = {}
if (domain in notified_list) and (expire == notified_list[domain]):
# already notified
logger.debug("Already notified about %s", domain, extra=loguser)
continue
notifications[owner][expiration_status][domain] = expire
if not args.noop:
for user, domains in notifications.items():
email_cx(user, domains)
if __name__ == "__main__":
if os.getuid() != 0:
sys.exit("This tool must be run as root. Exiting...")
logformat = logging.Formatter('%(asctime)s [%(current_user)s]: %(message)s')
logger = logging.getLogger('autossl_check')
logger.setLevel(logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(logformat)
logger.addHandler(console)
mainlog = logging.FileHandler('/var/log/autossl_check.log')
mainlog.setLevel(logging.DEBUG)
mainlog.setFormatter(logformat)
logger.addHandler(mainlog)
main()
Zerion Mini Shell 1.0