diff --git a/files/geant_acme.py b/files/geant_acme.py index 9e8d9285f451a40fd418772a9b9b0b47b33ee006..9a9959828d6409cb9010d0d1eb5eb2473ee10d8c 100755 --- a/files/geant_acme.py +++ b/files/geant_acme.py @@ -14,6 +14,7 @@ Options: """ import os import time +import datetime import subprocess as sp import configparser from docopt import docopt @@ -22,14 +23,22 @@ import requests BASE_URL = 'https://infoblox.geant.org/wapi/v2.6.1' -def os_exit(verbose=None): +def os_exit(): """ exit """ - if verbose: - print('+' + 72*'-' + '+') + print('+' + 72*'-' + '+') + # close logging + end_date = time_log() + print('[{}] Job ended'.format(end_date)) + print('+' + 72*'=' + '+') os.sys.exit() -def get_reference(iblox_domain, iblox_user, iblox_pw, verbose=None): +def time_log(): + """ return day and time """ + return datetime.datetime.now().strftime("%d %b %Y %H:%M:%S") + + +def get_reference(iblox_domain, iblox_user, iblox_pw): """ grab reference for txt object """ ref_obj = requests.get( '{}/record:txt?name=_acme-challenge.{}&_return_as_object=1'.format( @@ -45,35 +54,32 @@ def get_reference(iblox_domain, iblox_user, iblox_pw, verbose=None): except IndexError: ref = None - if verbose: - if ref: - print('got reference object: {}'.format(ref)) - else: - print('{} empty reference object: no challenge to delete'.format( - iblox_domain)) - print('+' + 72*'-' + '+') + if ref: + print('got reference object: {}'.format(ref)) + else: + print('{} empty reference object: no challenge to delete'.format( + iblox_domain)) + print('+' + 72*'-' + '+') return ref -def delete_challenge(object_reference, iblox_user, iblox_pw, verbose=None): +def delete_challenge(object_reference, iblox_user, iblox_pw): """ delete txt record """ del_req = requests.delete( '{}/{}'.format(BASE_URL, object_reference), auth=(iblox_user, iblox_pw) ) - if verbose: - print('delete challenge - http code (200 expected): {}'.format(del_req.status_code)) - print('+' + 72*'-' + '+') + print('delete challenge - http code (200 expected): {}'.format(del_req.status_code)) + print('+' + 72*'-' + '+') return del_req.status_code -def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw, verbose=None): +def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw): """ upload txt record """ - if verbose: - print('+' + 72*'-' + '+') - print('creating challenge _acme-challenge.{}'.format(iblox_domain)) + print('+' + 72*'-' + '+') + print('creating challenge _acme-challenge.{}'.format(iblox_domain)) post_req = requests.post( '{}/record:txt'.format(BASE_URL), @@ -85,15 +91,14 @@ def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw, verbose=Non "view": "External" } ) - if verbose: - print('create challenge - http code (201 expected): {} {}'.format( - post_req.status_code, post_req.reason)) - print('+' + 72*'-' + '+') + print('create challenge - http code (201 expected): {} {}'.format( + post_req.status_code, post_req.reason)) + print('+' + 72*'-' + '+') return post_req.status_code -def run_certbot(cbot_domain, wild_card=None, verbose=None): +def run_certbot(cbot_domain, wild_card=None): """ get certificate from letsencrypt """ if wild_card: domain_list = '*.{}'.format(cbot_domain) @@ -104,9 +109,7 @@ def run_certbot(cbot_domain, wild_card=None, verbose=None): + ' --manual-auth-hook=/root/bin/infoblox_hook.py' \ + ' --cert-name {} -d {}'.format(cbot_domain[0], domain_list) - if verbose: - print('+' + 72*'-' + '+') - print('running: {}'.format(cbot_cmd)) + print('running: {}'.format(cbot_cmd)) cbot_child = sp.Popen(cbot_cmd, stdout=sp.PIPE, stderr=sp.PIPE, shell=True) cbot_out, cbot_err = cbot_child.communicate() @@ -116,8 +119,7 @@ def run_certbot(cbot_domain, wild_card=None, verbose=None): decoded_msg = cbot_out.decode("utf-8") msg = decoded_msg[:decoded_msg.rfind('\n')] - if verbose: - print('\n{}'.format(msg)) + print('\n{}'.format(msg)) return msg @@ -130,6 +132,14 @@ if __name__ == "__main__": CLIENTS = ARGS['--client'] WILDCARD = ARGS['--wildcard'] VERBOSE = ARGS['--verbose'] + if not VERBOSE: + os.sys.stdout = open('/var/log/acme/acme.log', 'a') + + # start logging + START_DATE = time_log() + CMD_LINE = ' '.join(os.sys.argv) + print('+' + 72*'=' + '+') + print('[{}] Jost started: {}'.format(START_DATE, CMD_LINE)) CONFIG = configparser.RawConfigParser() CONFIG.read_file(open('/root/.geant_acme.ini')) @@ -141,47 +151,44 @@ if __name__ == "__main__": else: REAL_DOMAIN = 'host {}'.format(DOMAIN[0]) - if VERBOSE: - print('+' + 72*'-' + '+') + print('+' + 72*'-' + '+') # Maybe there is an Acme challenge left over. We try to delete it first. for domain_item in DOMAIN: - REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE) + REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS) if REF_OBJ == 'error': print('error retrieving reference object') - os_exit(VERBOSE) + os_exit() if REF_OBJ: DEL_STATUS = delete_challenge( - REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE) + REF_OBJ, IBLOX_USER, IBLOX_PASS) if DEL_STATUS != 200: print('{}: {} error deleting challenge on Infoblox'.format( domain_item, DEL_STATUS)) - os_exit(VERBOSE) + os_exit() # run certbot - run_certbot(DOMAIN, WILDCARD, VERBOSE) - if VERBOSE: - print('sleep 10 seconds to wait for DNS to settle down') - print('+' + 72*'-' + '+') + run_certbot(DOMAIN, WILDCARD) + print('sleep 10 seconds to wait for DNS to settle down') + print('+' + 72*'-' + '+') time.sleep(10) # remove the Acme challenges from Infoblox for domain_item in DOMAIN: - REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE) + REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS) if REF_OBJ == 'error': print('error retrieving reference object') - os_exit(VERBOSE) + os_exit() if REF_OBJ: DEL_STATUS = delete_challenge( - REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE) + REF_OBJ, IBLOX_USER, IBLOX_PASS) if DEL_STATUS != 200: print('{}: {} error deleting challenge on Infoblox'.format( domain_item, DEL_STATUS)) - os_exit(VERBOSE) - + os_exit() - os.sys.exit() # if we are here, everything went fine and we can upload the certificates + os.sys.stdout.flush() if WILDCARD: UPLOADER = '/root/bin/upload_wildcards.py' if VERBOSE: @@ -195,4 +202,4 @@ if __name__ == "__main__": UPLOADER += ' -v' os.system(UPLOADER) - os_exit(VERBOSE) + os_exit() diff --git a/files/geant_acme_uploader.py b/files/geant_acme_uploader.py index a4dc5787cbc477a030eb581382625278ba978654..0674328e915652cd3dcdb1d9d3f9ac509fa3cae7 100755 --- a/files/geant_acme_uploader.py +++ b/files/geant_acme_uploader.py @@ -76,6 +76,8 @@ if __name__ == "__main__": DOMAIN = ARGS['--domain'] CLIENT = ARGS['--client'] VERBOSE = ARGS['--verbose'] + if not VERBOSE: + os.sys.stdout = open('/var/log/geant_acme.log', 'a') BASEDIR = '/etc/letsencrypt/live' @@ -89,8 +91,7 @@ if __name__ == "__main__": redis_full_path = '{}:redis_{}_{}'.format( CLIENT, domain_underscored, certname_renamed) - if VERBOSE: - print('uploading to Redis: {}'.format(redis_full_path)) + print('uploading to Redis: {}'.format(redis_full_path)) redis_upload(REDIS_HOST, REDIS_TOKEN, redis_full_path, certdata) with open(os.path.join(BASEDIR, DOMAIN, 'privkey.pem'), 'r') as keyfile: @@ -99,8 +100,8 @@ if __name__ == "__main__": VAULT_FULL_PATH = 'puppet/{}/vault_wildcard_{}.key'.format( CLIENT, DOMAIN_UNDERSCORED) - if VERBOSE: - print('uploading to Vault: {}'.format(VAULT_FULL_PATH)) + print('uploading to Vault: {}'.format(VAULT_FULL_PATH)) vault_upload(VAULT_HOST, VAULT_TOKEN, VAULT_FULL_PATH, KEYDATA) redis_save(REDIS_HOST, REDIS_TOKEN) + os.sys.stdout.flush() diff --git a/files/logrotate_acme b/files/logrotate_acme new file mode 100644 index 0000000000000000000000000000000000000000..85c55ea7453d19866c3d8ec6e6e353fb2c39735d --- /dev/null +++ b/files/logrotate_acme @@ -0,0 +1,11 @@ +# ACME log rotation +# +/var/log/acme/acme.log { + create 660 root root + notifempty + daily + rotate 10 + missingok + compress + endscript +} diff --git a/files/upload_wildcards.py b/files/upload_wildcards.py index 0897e709a0d610bf19f9b940f0b82e788f38cf2f..1a8e39b508e7173c5fb6de4f8ede10b5176c9f48 100755 --- a/files/upload_wildcards.py +++ b/files/upload_wildcards.py @@ -83,8 +83,7 @@ if __name__ == "__main__": keyname_underscored = keyname.replace('.', '_') redis_full_path = 'common:redis_{}_{}'.format( domain_underscored, keyname_underscored) - if VERBOSE: - print('uploading to Redis: {}'.format(redis_full_path)) + print('uploading to Redis: {}'.format(redis_full_path)) redis_upload(REDIS_HOST, REDIS_TOKEN, redis_full_path, keydata) with open(os.path.join(BASEDIR, domain, 'privkey.pem'), 'r') as keyfile: keydata = keyfile.read() @@ -92,8 +91,7 @@ if __name__ == "__main__": keyname_underscored = 'privkey.pem'.replace('.', '_') vault_full_path = 'puppet/common/vault_wildcard_{}_{}'.format( domain_underscored, keyname_underscored) - if VERBOSE: - print('uploading to Vault: {}'.format(vault_full_path)) + print('uploading to Vault: {}'.format(vault_full_path)) vault_upload(VAULT_HOST, VAULT_TOKEN, vault_full_path, keydata) redis_save(REDIS_HOST, REDIS_TOKEN) diff --git a/manifests/files.pp b/manifests/files.pp index ec63754f3ae0da2ec3ccb080244646103a2c2921..1cfdf6d0d5f3af88563f4400279cf81ca5f9a322 100644 --- a/manifests/files.pp +++ b/manifests/files.pp @@ -34,6 +34,8 @@ class geant_acme::files ( source => "puppet:///modules/${module_name}/geant_acme_uploader.py"; '/root/bin/upload_wildcards.py': source => "puppet:///modules/${module_name}/upload_wildcards.py"; + '/root/bin/infoblox_hook.py': + source => "puppet:///modules/${module_name}/infoblox_hook.py"; '/etc/letsencrypt/cli.ini': mode => '0644', require => File['/etc/letsencrypt'], @@ -41,6 +43,11 @@ class geant_acme::files ( '/root/.geant_acme.ini': mode => '0640', content => Sensitive(template("${module_name}/geant_acme.ini.erb")); + '/var/log/acme': + ensure => directory; + '/etc/logrotate.d/acme': + mode => '0644', + source => "puppet:///modules/${module_name}/logrotate_acme"; } }