diff --git a/files/new_wrapper/create_challenge.py b/files/new_wrapper/create_challenge.py
new file mode 100644
index 0000000000000000000000000000000000000000..b205b09b077db70076929baa9b25e2be9c0fa859
--- /dev/null
+++ b/files/new_wrapper/create_challenge.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+""" Add Acme challenges to Infoblox """
+import os
+import time
+import configparser
+import requests
+
+
+def create_acme(iblox_domain, acme_token, iblox_user, iblox_pw):
+ """ upload txt record """
+ post_req = requests.post(
+ 'https://infoblox.geant.org/wapi/v2.6.1/record:txt',
+ auth=(iblox_user, iblox_pw),
+ data={
+ 'name': '_acme-challenge.{}'.format(iblox_domain),
+ 'text': acme_token,
+ 'ttl': '60',
+ "view": "External"
+ }
+ )
+ return post_req.status_code
+
+
+# Here we Go.
+if __name__ == "__main__":
+
+ CONFIG = configparser.RawConfigParser()
+ CONFIG.read_file(open('/root/.geant_acme.ini'))
+ IBLOX_PASS = CONFIG.get('geant_acme', 'iblox_pass')
+ IBLOX_USER = CONFIG.get('geant_acme', 'iblox_user')
+
+ ARGS = os.sys.argv
+ _ = ARGS.pop(0)
+
+ for host in ARGS:
+ if ARGS.index(host) % 2 == 0:
+ token = ARGS.index(host)+1
+ http_code = create_acme(host, ARGS[token], IBLOX_USER, IBLOX_PASS)
+ if http_code != 201:
+ print('could not create {} for {}'.format(ARGS[token], host))
+ os.sys.exit(1)
+
+ time.sleep(10)
diff --git a/files/new_wrapper/geant_acme.py b/files/new_wrapper/geant_acme.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6031de18feefa6f8883ace0d941f17dbff8f944
--- /dev/null
+++ b/files/new_wrapper/geant_acme.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python3
+"""Geant Acme
+
+Usage:
+ geant_acme.py --domain <DOMAIN>... (--client <CLIENT>... | --wildcard) [--verbose]
+ geant_acme.py (-h | --help)
+
+Options:
+ -h --help Show this screen.
+ -c CLIENT --client=CLIENT Client
+ -d DOMAIN --domain=DOMAIN Domain
+ -w --wildcard Use wildcard
+ -v --verbose Print out messages
+"""
+import os
+import subprocess as sp
+import configparser
+from docopt import docopt
+import requests
+
+BASE_URL = 'https://infoblox.geant.org/wapi/v2.6.1'
+
+
+def os_exit(verbose=None):
+ """ exit """
+ if verbose:
+ print('+' + 72*'-' + '+')
+ os.sys.exit()
+
+
+def get_reference(iblox_domain, iblox_user, iblox_pw, verbose=None):
+ """ grab reference for txt object """
+ ref_obj = requests.get(
+ '{}/record:txt?name=_acme-challenge.{}&_return_as_object=1'.format(
+ BASE_URL, iblox_domain),
+ auth=(iblox_user, iblox_pw)
+ )
+
+ if ref_obj.status_code != 200:
+ return 'error'
+
+ try:
+ ref = ref_obj.json()['result'][0]['_ref']
+ 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*'-' + '+')
+
+ return ref
+
+
+def delete_challenge(object_reference, iblox_user, iblox_pw, verbose=None):
+ """ 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*'-' + '+')
+
+ return del_req.status_code
+
+
+def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw, verbose=None):
+ """ upload txt record """
+ if verbose:
+ print('+' + 72*'-' + '+')
+ print('creating challenge _acme-challenge.{}'.format(iblox_domain))
+
+ post_req = requests.post(
+ '{}/record:txt'.format(BASE_URL),
+ auth=(iblox_user, iblox_pw),
+ data={
+ 'name': '_acme-challenge.{}'.format(iblox_domain),
+ 'text': acme_token,
+ 'ttl': '30',
+ "view": "External"
+ }
+ )
+ if verbose:
+ 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):
+ """ get certificate from letsencrypt """
+ if wild_card:
+ domain_list = '*.{}'.format(cbot_domain)
+ else:
+ domain_list = ' -d '.join(list(reversed(cbot_domain)))
+
+ cbot_cmd = '/usr/local/bin/certbot certonly -c /etc/letsencrypt/cli.ini' \
+ + ' --manual-auth-hook=/root/bin/create_challenge.py' \
+ + ' --cert-name {} -d {}'.format(cbot_domain[0], domain_list)
+
+ if verbose:
+ print('+' + 72*'-' + '+')
+ 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()
+ if cbot_child.returncode != 0:
+ msg = "error running certbot: {}".format(cbot_err.decode("utf-8"))
+ else:
+ decoded_msg = cbot_out.decode("utf-8")
+ msg = decoded_msg[:decoded_msg.rfind('\n')]
+
+ if verbose:
+ print('\n{}'.format(msg))
+
+ return msg
+
+
+# Here we Go.
+if __name__ == "__main__":
+
+ ARGS = docopt(__doc__)
+ DOMAIN = ARGS['--domain']
+ CLIENTS = ARGS['--client']
+ WILDCARD = ARGS['--wildcard']
+ VERBOSE = ARGS['--verbose']
+
+ CONFIG = configparser.RawConfigParser()
+ CONFIG.read_file(open('/root/.geant_acme.ini'))
+ IBLOX_PASS = CONFIG.get('geant_acme', 'iblox_pass')
+ IBLOX_USER = CONFIG.get('geant_acme', 'iblox_user')
+
+ if WILDCARD:
+ REAL_DOMAIN = 'domain *.{}'.format(DOMAIN[0])
+ else:
+ REAL_DOMAIN = 'host {}'.format(DOMAIN[0])
+
+ if VERBOSE:
+ 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)
+ if REF_OBJ == 'error':
+ print('error retrieving reference object')
+ os_exit(VERBOSE)
+ if REF_OBJ:
+ DEL_STATUS = delete_challenge(
+ REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE)
+ if DEL_STATUS != 200:
+ print('{}: {} error deleting challenge on Infoblox'.format(
+ domain_item, DEL_STATUS))
+ os_exit(VERBOSE)
+
+ # run certbot
+ run_certbot(DOMAIN, WILDCARD, VERBOSE)
+
+ if VERBOSE:
+ print('+' + 72*'-' + '+')
+
+ # remove the Acme challenges from Infoblox
+ for domain_item in DOMAIN:
+ REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE)
+ if REF_OBJ == 'error':
+ print('error retrieving reference object')
+ os_exit(VERBOSE)
+ if REF_OBJ:
+ DEL_STATUS = delete_challenge(
+ REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE)
+ if DEL_STATUS != 200:
+ print('{}: {} error deleting challenge on Infoblox'.format(
+ domain_item, DEL_STATUS))
+ os_exit(VERBOSE)
+
+ # if we are here, everything went fine and we can upload the certificates
+ if WILDCARD:
+ UPLOADER = '/root/bin/upload_wildcards.py'
+ if VERBOSE:
+ UPLOADER += ' -v'
+ os.system(UPLOADER)
+ else:
+ for client in CLIENTS:
+ UPLOADER = '/root/bin/geant_acme_uploader.py -d {} -c {}'.format(
+ DOMAIN[0], client)
+ if VERBOSE:
+ UPLOADER += ' -v'
+ os.system(UPLOADER)
+
+ os_exit(VERBOSE)