Beispiel #1
0
                        type=int,
                        default=2)
    parser.add_argument('-c',
                        '--controller',
                        help='controller ip',
                        default='127.0.0.1')
    parser.add_argument('-u', '--username', help='user name', default='admin')
    parser.add_argument('-p', '--password', help='password', default='abc')

    args = parser.parse_args()
    print('parsed args', args)

    mq = {
        'metric_id': args.vs_metrics,
        'serviceengine_uuid': args.se_uuid,
        'tenant': (args.tenant if args.tenant else 'admin'),
        'step': args.step,
        'limit': args.limit,
        'aggregate_entity': not args.per_vs_metrics,
        'entity_uuid': '*',
        'pad_missing_data': False
    }

    api_ssn = ApiSession(args.controller, args.username, args.password,
                         args.tenant)
    api_utils = ApiUtils(api_ssn)

    rsp = api_utils.get_metrics_collection(tenant=args.tenant,
                                           metric_requests=[mq])
    print 'metrics query', mq
    print json.dumps(rsp, indent=2)
Beispiel #2
0
 def test_reuse_api_session(self):
     api1 = ApiSession(avi_credentials=api.avi_credentials,
                       verify=False)
     api2 = ApiSession.get_session(avi_credentials=api.avi_credentials,
                                   verify=False)
     assert api1 == api2
Beispiel #3
0
# Get session on the basis of authentication token
token = os.environ.get('API_TOKEN')
user = os.environ.get('USER')
tenant = os.environ.get('TENANT')
print "[INFO] token: %s" % token
print "[INFO] user: %s" % user
print "[INFO] tenant: %s" % tenant

ibx_server = '10.56.70.250'
ibx_username = '******'
ibx_password = '******'
ibx_version = '2.0'
ibx_dns_view = 'default'
ibx_net_view = 'default'

with ApiSession("localhost", user, token=token, tenant=tenant) as session:
    vs_name = ParseAviParams(session, tenant, sys.argv)
    vs = GetVSObject(session, vs_name, tenant)
    vs_hostnames = vs['vh_domain_name']
    vs_parent_ref = vs['vh_parent_vs_ref']
    vs_parent_vip = GetVSParentObject(session, vs_parent_ref)
    #Open connection to Infoblox
    ibx = infoblox.Infoblox(ibx_server,
                            ibx_username,
                            ibx_password,
                            ibx_version,
                            ibx_dns_view,
                            ibx_net_view,
                            iba_verify_ssl=False)
    AddHosts(vs_hostnames, vs_parent_vip, ibx)
 def __init__(self, avi_ip, avi_user, avi_pswd, avi_version):
     self.avi_api = ApiSession(avi_ip,
                               avi_user,
                               avi_pswd,
                               api_version=avi_version)
Beispiel #5
0
    def test_get_key_token(self):
        api1 = ApiSession(avi_credentials=api.avi_credentials, verify=False)

        api2 = ApiSession.get_session(avi_credentials=api.avi_credentials,
                                      verify=False)
        assert api1.keystone_token == api2.keystone_token
Beispiel #6
0
    def test_tenant(self):
        api1 = ApiSession(avi_credentials=api.avi_credentials, verify=False)

        api2 = ApiSession.get_session(avi_credentials=api.avi_credentials,
                                      verify=False)
        assert api1.tenant == api2.tenant
Beispiel #7
0
    def test_get_controller_ip(self):
        api1 = ApiSession(avi_credentials=api.avi_credentials, verify=False)

        api2 = ApiSession.get_session(avi_credentials=api.avi_credentials,
                                      verify=False)
        assert api1.controller_ip == api2.controller_ip
Beispiel #8
0
def get_crt(user,
            password,
            tenant,
            api_version,
            account_key,
            csr,
            CA=DEFAULT_CA,
            disable_check=False,
            directory_url=DEFAULT_DIRECTORY_URL,
            contact=None):
    directory, acct_headers, alg, jwk = None, None, None, None  # global variables

    # helper functions - base64 encode for jose spec
    def _b64(b):
        return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "")

    # helper function - run external commands
    def _cmd(cmd_list,
             stdin=None,
             cmd_input=None,
             err_msg="Command Line Error"):
        proc = subprocess.Popen(cmd_list,
                                stdin=stdin,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        out, err = proc.communicate(cmd_input)
        if proc.returncode != 0:
            raise IOError("{0}\n{1}".format(err_msg, err))
        return out

    # helper function - make request and automatically parse json response
    def _do_request(url, data=None, err_msg="Error", depth=0):
        try:
            resp = urlopen(
                Request(url,
                        data=data,
                        headers={
                            "Content-Type": "application/jose+json",
                            "User-Agent": "acme-tiny"
                        }))
            resp_data, code, headers = resp.read().decode(
                "utf8"), resp.getcode(), resp.headers
        except IOError as e:
            resp_data = e.read().decode("utf8") if hasattr(e,
                                                           "read") else str(e)
            code, headers = getattr(e, "code", None), {}
        try:
            resp_data = json.loads(resp_data)  # try to parse json results
        except ValueError:
            pass  # ignore json parsing errors
        if depth < 100 and code == 400 and resp_data[
                'type'] == "urn:ietf:params:acme:error:badNonce":
            raise IndexError(resp_data)  # allow 100 retrys for bad nonces
        if code not in [200, 201, 204]:
            raise ValueError(
                "{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".
                format(err_msg, url, data, code, resp_data))
        return resp_data, code, headers

    # helper function - make signed requests
    def _send_signed_request(url, payload, err_msg, depth=0):
        payload64 = "" if payload is None else _b64(
            json.dumps(payload).encode('utf8'))
        new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
        protected = {"url": url, "alg": alg, "nonce": new_nonce}
        protected.update({"jwk": jwk} if acct_headers is None else
                         {"kid": acct_headers['Location']})
        protected64 = _b64(json.dumps(protected).encode('utf8'))
        protected_input = "{0}.{1}".format(protected64,
                                           payload64).encode('utf8')
        out = _cmd(["openssl", "dgst", "-sha256", "-sign", account_key],
                   stdin=subprocess.PIPE,
                   cmd_input=protected_input,
                   err_msg="OpenSSL Error")
        data = json.dumps({
            "protected": protected64,
            "payload": payload64,
            "signature": _b64(out)
        })
        try:
            return _do_request(url,
                               data=data.encode('utf8'),
                               err_msg=err_msg,
                               depth=depth)
        except IndexError:  # retry bad nonces (they raise IndexError)
            return _send_signed_request(url,
                                        payload,
                                        err_msg,
                                        depth=(depth + 1))

    # helper function - poll until complete
    def _poll_until_not(url, pending_statuses, err_msg):
        result, t0 = None, time.time()
        while result is None or result['status'] in pending_statuses:
            assert (time.time() - t0 <
                    3600), "Polling timeout"  # 1 hour timeout
            time.sleep(0 if result is None else 2)
            result, _, _ = _send_signed_request(url, None, err_msg)
        return result

    session = ApiSession('localhost',
                         user,
                         password,
                         tenant=tenant,
                         api_version=api_version)

    log.info("Generating account key...")
    out = _cmd(["openssl", "genrsa", "4096"], err_msg="OpenSSL Error")
    with open(account_key, 'w') as f:
        f.write(out.decode("utf-8"))

    # parse account key to get public key
    log.info("Parsing account key...")
    out = _cmd(["openssl", "rsa", "-in", account_key, "-noout", "-text"],
               err_msg="OpenSSL Error")
    pub_pattern = r"modulus:[\s]+?00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)"
    pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'),
                                 re.MULTILINE | re.DOTALL).groups()
    pub_exp = "{0:x}".format(int(pub_exp))
    pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp
    alg = "RS256"
    jwk = {
        "e":
        _b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
        "kty":
        "RSA",
        "n":
        _b64(binascii.unhexlify(
            re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
    }
    accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
    thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())

    # find domains
    log.info("Parsing CSR...")
    out = _cmd(["openssl", "req", "-in", csr, "-noout", "-text"],
               err_msg="Error loading {0}".format(csr))
    domains = set([])
    common_name = re.search(r"Subject:.*? CN\s?=\s?([^\s,;/]+)",
                            out.decode('utf8'))
    if common_name is not None:
        domains.add(common_name.group(1))
    subject_alt_names = re.search(
        r"X509v3 Subject Alternative Name: (?:critical)?\n +([^\n]+)\n",
        out.decode('utf8'), re.MULTILINE | re.DOTALL)
    if subject_alt_names is not None:
        for san in subject_alt_names.group(1).split(", "):
            if san.startswith("DNS:"):
                domains.add(san[4:])
    log.info("Found domains: {0}".format(", ".join(domains)))

    # get the ACME directory of urls
    log.info("Getting directory...")
    directory_url = CA + "/directory" if CA != DEFAULT_CA else directory_url  # backwards compatibility with deprecated CA kwarg
    directory, _, _ = _do_request(directory_url,
                                  err_msg="Error getting directory")
    log.info("Directory found!")

    # create account, update contact details (if any), and set the global key identifier
    log.info("Registering account...")
    reg_payload = {"termsOfServiceAgreed": True}
    account, code, acct_headers = _send_signed_request(directory['newAccount'],
                                                       reg_payload,
                                                       "Error registering")
    log.info("Registered!" if code == 201 else "Already registered!")
    if contact is not None:
        account, _, _ = _send_signed_request(acct_headers['Location'],
                                             {"contact": contact},
                                             "Error updating contact details")
        log.info("Updated contact details:\n{0}".format("\n".join(
            account['contact'])))

    # create a new order
    log.info("Creating new order...")
    order_payload = {
        "identifiers": [{
            "type": "dns",
            "value": d
        } for d in domains]
    }
    order, _, order_headers = _send_signed_request(directory['newOrder'],
                                                   order_payload,
                                                   "Error creating new order")
    log.info("Order created!")

    # get the authorizations that need to be completed
    for auth_url in order['authorizations']:
        authorization, _, _ = _send_signed_request(auth_url, None,
                                                   "Error getting challenges")
        domain = authorization['identifier']['value']
        log.info("Verifying {0}...".format(domain))

        # find the http-01 challenge and write the challenge file
        challenge = [
            c for c in authorization['challenges'] if c['type'] == "http-01"
        ][0]
        token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token'])
        keyauthorization = "{0}.{1}".format(token, thumbprint)

        # Update vs
        rsp = session.get("vsvip/?search=(fqdn,{})".format(domain)).json()
        if rsp["count"] == 0:
            raise Exception(
                "Could not find a VSVIP with fqdn = {}".format(domain))
        vsvip_uuid = rsp["results"][0]["uuid"]
        rsp = session.get(
            "virtualservice?search=(vsvip_ref,{})".format(vsvip_uuid)).json()
        if rsp['count'] == 0:
            raise Exception(
                "Could not find a VS with common name = {}".format(domain))

        vs_uuid = rsp["results"][0]["uuid"]
        log.info("Found vs {} with fqdn {}".format(vs_uuid, domain))
        # Check if the vs is servering on port 80
        serving_on_port_80 = False
        service_on_port_80_data = None
        for service in rsp["results"][0]["services"]:
            if service["port"] == "80":
                serving_on_port_80 = True
                break

        # create HTTP policy
        httppolicy_data = {
            "name": (domain + "LetsEncryptHTTPpolicy"),
            "http_security_policy": {
                "rules": [{
                    "name": "Rule 1",
                    "index": 1,
                    "enable": True,
                    "match": {
                        "vs_port": {
                            "match_criteria": "IS_IN",
                            "ports": [80]
                        },
                        "path": {
                            "match_criteria":
                            "CONTAINS",
                            "match_case":
                            "SENSITIVE",
                            "match_str":
                            [".well-known/acme-challenge/{}".format(token)]
                        }
                    },
                    "action": {
                        "action": "HTTP_SECURITY_ACTION_SEND_RESPONSE",
                        "status_code": "HTTP_LOCAL_RESPONSE_STATUS_CODE_200",
                        "file": {
                            "content_type": "text/plain",
                            "file_content": keyauthorization
                        }
                    }
                }]
            },
            "is_internal_policy": False
        }
        rsp = session.post("httppolicyset", data=httppolicy_data).json()
        httppolicy_uuid = rsp["uuid"]
        log.info("Created HTTP policy with uuid {}".format(httppolicy_uuid))

        patch_data = {
            "add": {
                "http_policies": [{
                    "http_policy_set_ref":
                    "/api/httppolicyset/{}".format(httppolicy_uuid),
                    "index":
                    1000001
                }]
            }
        }
        if not serving_on_port_80:
            # Add to port to virtualservice
            service_on_port_80_data = {
                "enable_http2": False,
                "enable_ssl": False,
                "port": 80,
                "port_range_end": 80
            }
            patch_data["add"]["services"] = [service_on_port_80_data]
        rsp = session.patch("virtualservice/{}".format(vs_uuid), patch_data)

        exception_occured = None
        try:
            # check that the file is in place
            try:
                wellknown_url = "http://{0}/.well-known/acme-challenge/{1}".format(
                    domain, token)
                assert (disable_check
                        or _do_request(wellknown_url)[0] == keyauthorization)
            except (AssertionError, ValueError) as e:
                raise ValueError(
                    "Wrote file to {0}, but couldn't download {1}: {2}".format(
                        'wellknown_path', 'wellknown_url', e))

            # say the challenge is done
            _send_signed_request(
                challenge['url'], {},
                "Error submitting challenges: {0}".format(domain))
            authorization = _poll_until_not(
                auth_url, ["pending"],
                "Error checking challenge status for {0}".format(domain))
            if authorization['status'] != "valid":
                raise ValueError("Challenge did not pass for {0}: {1}".format(
                    domain, authorization))

        except Exception as e:
            exception_occured = str(e)
        finally:
            # Update the vs
            patch_data = {
                "delete": {
                    "http_policies": [{
                        "http_policy_set_ref":
                        "/api/httppolicyset/{}".format(httppolicy_uuid),
                        "index":
                        1000001
                    }]
                }
            }
            if not serving_on_port_80:
                patch_data["delete"]["services"] = [service_on_port_80_data]
            rsp = session.patch("virtualservice/{}".format(vs_uuid),
                                patch_data)
            rsp = session.delete("httppolicyset/{}".format(httppolicy_uuid))

        if exception_occured:
            log.error(exception_occured)
            raise Exception(exception_occured)

        log.info("{0} verified!".format(domain))

    # finalize the order with the csr
    log.info("Signing certificate...")
    csr_der = _cmd(["openssl", "req", "-in", csr, "-outform", "DER"],
                   err_msg="DER Export Error")
    _send_signed_request(order['finalize'], {"csr": _b64(csr_der)},
                         "Error finalizing order")

    # poll the order to monitor when it's done
    order = _poll_until_not(order_headers['Location'],
                            ["pending", "processing"],
                            "Error checking order status")
    if order['status'] != "valid":
        raise ValueError("Order failed: {0}".format(order))

    # download the certificate
    certificate_pem, _, _ = _send_signed_request(
        order['certificate'], None, "Certificate download failed")
    log.info("Certificate signed!")

    return certificate_pem
    Example: 
    python change_password_example.py --controller_ip="10.10.10.10" --user="******" --current_password="******" --new_password="******"
    """
    parser = argparse.ArgumentParser(description=HELP_STR)
    parser.add_argument('-c', '--controller_ip', help='controller ip')
    parser.add_argument('-u', '--user', default='admin')
    parser.add_argument('-p', '--current_password', default='admin')
    parser.add_argument('-n', '--new_password', default='admin@123')

    args = parser.parse_args()
    print("Input args: ", args)
    api = None

    try:
        api = ApiSession(controller_ip=args.controller_ip,
                         username=args.user,
                         password=args.current_password,
                         tenant="admin")
    except APIError as error:
        print("Failed to update the password. Error message: " + str(error))
        exit(1)
    change_pwd_example = ChangePasswordExample(api_session=api)
    print("Get pool before changing password")
    change_pwd_example.get_pool()
    change_pwd_example.change_password(args.new_password)
    api_after_updation = ApiSession(controller_ip=args.controller_ip,
                                    username=args.user,
                                    password=args.new_password,
                                    tenant="admin")
    updated_password_example = ChangePasswordExample(
        api_session=api_after_updation)
    print("Get pool after changing password")
Beispiel #10
0
#!/usr/bin/python3

#example PATCH request script to turn off waf policy on vs
#requires avisdk packages. install here #requires avisdk packages. install here https://github.com/avinetworks/sdk/tree/master/python/avi/sdk
#API token generation needed. for token generation help, see https://avinetworks.com/docs/18.2/saas-rest-api-access/
import json
from avi.sdk.avi_api import ApiSession

token = "InsertTokenHere"
user = "******"
tenant = "admin"
controller_ip = "InsertControllerIP"
vs_name = "InsertVSnameHere"

#Desired policy state. Empty string removes WAF policy on VS.
# Input name of policy to turn on, ex: {"waf_policy_ref": "My Waf" } vs {"waf_policy_ref": "" }
request_body = '{ "replace": {"waf_policy_ref": "" }}'

# Get session on the basis of authentication token
with ApiSession(controller_ip, user, token=token, tenant=tenant) as session:
    # Get the virtualservice object by name
    vs_obj = session.get_object_by_name('virtualservice', vs_name)

    if vs_obj:
        # Save the object
        resp = session.patch('virtualservice/%s' % vs_obj['uuid'],
                             data=request_body,
                             api_version="18.2.7")
        print(resp.status_code, resp.json())
def get_crt(user,
            password,
            tenant,
            api_version,
            csr,
            CA=DEFAULT_CA,
            disable_check=False,
            overwrite_vs=None,
            directory_url=DEFAULT_DIRECTORY_URL,
            contact=None,
            debug=False):
    directory, acct_headers, alg, jwk = None, None, None, None  # global variables

    # helper functions - base64 encode for jose spec
    def _b64(b):
        return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "")

    # helper function - run external commands
    def _cmd(cmd_list,
             stdin=None,
             cmd_input=None,
             err_msg="Command Line Error"):
        proc = subprocess.Popen(cmd_list,
                                stdin=stdin,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        out, err = proc.communicate(cmd_input)
        if proc.returncode != 0:
            raise IOError("{0}\n{1}".format(err_msg, err))
        return out

    # helper function - make request and automatically parse json response
    def _do_request(url, data=None, err_msg="Error", depth=0, verify=True):
        try:
            ctx = ssl.create_default_context()
            if not verify:
                # disable certificate verify
                ctx.check_hostname = False
                ctx.verify_mode = ssl.CERT_NONE
            # open request.
            resp = urlopen(Request(url,
                                   data=data,
                                   headers={
                                       "Content-Type": "application/jose+json",
                                       "User-Agent": "acme-tiny"
                                   }),
                           context=ctx)
            resp_data, code, headers = resp.read().decode(
                "utf8"), resp.getcode(), resp.headers
        except IOError as e:
            resp_data = e.read().decode("utf8") if hasattr(e,
                                                           "read") else str(e)
            code, headers = getattr(e, "code", None), {}
        try:
            resp_data = json.loads(resp_data)  # try to parse json results
        except ValueError:
            pass  # ignore json parsing errors
        if depth < 100 and code == 400 and resp_data[
                'type'] == "urn:ietf:params:acme:error:badNonce":
            raise IndexError(resp_data)  # allow 100 retrys for bad nonces
        if code not in [200, 201, 204]:
            raise ValueError(
                "{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".
                format(err_msg, url, data, code, resp_data))
        return resp_data, code, headers

    # helper function - make signed requests
    def _send_signed_request(url, payload, err_msg, depth=0):
        payload64 = "" if payload is None else _b64(
            json.dumps(payload).encode('utf8'))
        new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
        protected = {"url": url, "alg": alg, "nonce": new_nonce}
        protected.update({"jwk": jwk} if acct_headers is None else
                         {"kid": acct_headers['Location']})
        protected64 = _b64(json.dumps(protected).encode('utf8'))
        protected_input = "{0}.{1}".format(protected64,
                                           payload64).encode('utf8')
        out = _cmd(["openssl", "dgst", "-sha256", "-sign", ACCOUNT_KEY_PATH],
                   stdin=subprocess.PIPE,
                   cmd_input=protected_input,
                   err_msg="OpenSSL Error")
        data = json.dumps({
            "protected": protected64,
            "payload": payload64,
            "signature": _b64(out)
        })
        try:
            return _do_request(url,
                               data=data.encode('utf8'),
                               err_msg=err_msg,
                               depth=depth)
        except IndexError:  # retry bad nonces (they raise IndexError)
            return _send_signed_request(url,
                                        payload,
                                        err_msg,
                                        depth=(depth + 1))

    # helper function - poll until complete
    def _poll_until_not(url, pending_statuses, err_msg):
        result, t0 = None, time.time()
        while result is None or result['status'] in pending_statuses:
            assert (time.time() - t0 <
                    3600), "Polling timeout"  # 1 hour timeout
            time.sleep(0 if result is None else 2)
            result, _, _ = _send_signed_request(url, None, err_msg)
        return result

    session = ApiSession(os.environ.get("DOCKER_GATEWAY", 'localhost'),
                         user,
                         password,
                         tenant=tenant,
                         api_version=api_version)

    def _do_request_avi(url, method, data=None, error_msg="Error"):
        rsp = None
        if method == "GET":
            rsp = session.get(url)
        elif method == "POST":
            rsp = session.post(url, data=data)
        elif method == "PATCH":
            rsp = session.patch(url, data)
        elif method == "PUT":
            rsp = session.put(url, data=data)
        elif method == "DELETE":
            rsp = session.delete(url)
        else:
            raise Exception("Unsupported API method")
        if rsp.status_code >= 300:
            err = error_msg + " url - {}. Method - {}. Response status - {}. Response - {}".format(
                url, method, rsp.status_code, rsp.json())
            raise Exception(err)
        return rsp

    if os.path.exists(ACCOUNT_KEY_PATH):
        if debug:
            print("Reusing account key.")
    else:
        print("Account key not found. Generating account key...")
        out = _cmd(["openssl", "genrsa", "4096"], err_msg="OpenSSL Error")
        with open(ACCOUNT_KEY_PATH, 'w') as f:
            f.write(out.decode("utf-8"))

    # Check if we need to overwrite the VS UUID if it was specified
    # We request the info here once, instead in the loop for each SAN entry below.
    if overwrite_vs != None:
        if overwrite_vs.lower().startswith('virtualservice-'):
            search_term = "uuid={}".format(overwrite_vs.lower())
        else:
            search_term = "name={}".format(
                urlparse.quote(overwrite_vs, safe=''))

        overwrite_vs = _do_request_avi(
            "virtualservice/?{}".format(search_term), "GET").json()
        if overwrite_vs['count'] == 0:
            raise Exception("Could not find a VS with {}".format(search_term))

    # parse account key to get public key
    print("Parsing account key...")
    out = _cmd(["openssl", "rsa", "-in", ACCOUNT_KEY_PATH, "-noout", "-text"],
               err_msg="OpenSSL Error")
    pub_pattern = r"modulus:[\s]+?00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)"
    pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'),
                                 re.MULTILINE | re.DOTALL).groups()
    pub_exp = "{0:x}".format(int(pub_exp))
    pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp
    alg = "RS256"
    jwk = {
        "e":
        _b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
        "kty":
        "RSA",
        "n":
        _b64(binascii.unhexlify(
            re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
    }
    accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
    thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())

    # find domains
    print("Parsing CSR...")
    out = _cmd(["openssl", "req", "-in", csr, "-noout", "-text"],
               err_msg="Error loading {0}".format(csr))
    domains = set([])
    common_name = re.search(r"Subject:.*? CN\s?=\s?([^\s,;/]+)",
                            out.decode('utf8'))
    if common_name is not None:
        domains.add(common_name.group(1))
    subject_alt_names = re.search(
        r"X509v3 Subject Alternative Name: (?:critical)?\n +([^\n]+)\n",
        out.decode('utf8'), re.MULTILINE | re.DOTALL)
    if subject_alt_names is not None:
        for san in subject_alt_names.group(1).split(", "):
            if san.startswith("DNS:"):
                domains.add(san[4:])
    print("Found domains: {0}".format(", ".join(domains)))

    # get the ACME directory of urls
    print("Getting directory...")
    directory_url = CA + "/directory" if CA != DEFAULT_CA else directory_url  # backwards compatibility with deprecated CA kwarg
    directory, _, _ = _do_request(directory_url,
                                  err_msg="Error getting directory")
    print("Directory found!")

    # create account, update contact details (if any), and set the global key identifier
    print("Registering account...")
    reg_payload = {"termsOfServiceAgreed": True}
    account, code, acct_headers = _send_signed_request(directory['newAccount'],
                                                       reg_payload,
                                                       "Error registering")
    print("Registered!" if code == 201 else "Already registered!")
    if contact is not None:
        account, _, _ = _send_signed_request(acct_headers['Location'],
                                             {"contact": contact},
                                             "Error updating contact details")
        print("Updated contact details:\n{0}".format("\n".join(
            account['contact'])))

    # create a new order
    print("Creating new order...")
    order_payload = {
        "identifiers": [{
            "type": "dns",
            "value": d
        } for d in domains]
    }
    order, _, order_headers = _send_signed_request(directory['newOrder'],
                                                   order_payload,
                                                   "Error creating new order")
    print("Order created!")

    # get the authorizations that need to be completed
    for auth_url in order['authorizations']:
        if debug:
            print("Authorization URL is: {}".format(auth_url))

        authorization, _, _ = _send_signed_request(auth_url, None,
                                                   "Error getting challenges")
        domain = authorization['identifier']['value']
        print("Verifying {0}...".format(domain))

        # find the http-01 challenge and write the challenge file
        challenge = [
            c for c in authorization['challenges'] if c['type'] == "http-01"
        ][0]
        token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token'])
        keyauthorization = "{0}.{1}".format(token, thumbprint)

        wellknown_url = "http://{0}/.well-known/acme-challenge/{1}".format(
            domain, token)
        if debug:
            print("Validation URL is: {}".format(wellknown_url))

        vhMode = False
        # Check if we need to overwrite VirtualService UUID to something specific
        if overwrite_vs == None:

            # Get VSVIPs/VSs, based on FQDN
            rsp = _do_request_avi("vsvip/?search=(fqdn,{})".format(domain),
                                  "GET").json()
            if debug:
                print("Found {} matching VSVIP FQDNs".format(rsp["count"]))
            if rsp["count"] == 0:
                print("Warning: Could not find a VSVIP with fqdn = {}".format(
                    domain))
                # As a fallback we search for VirtualHosting entries with that domain
                vhMode = True
                search_term = "vh_domain_name.contains={}".format(domain)
            else:
                vsvip_uuid = rsp["results"][0]["uuid"]
                search_term = "vsvip_ref={}".format(vsvip_uuid)

            rsp = _do_request_avi("virtualservice/?{}".format(search_term),
                                  "GET").json()
            if debug:
                print("Found {} matching VSs".format(rsp["count"]))
            if rsp['count'] == 0:
                raise Exception(
                    "Could not find a VS with fqdn = {}".format(domain))

            vs_uuid = rsp["results"][0]["uuid"]

        else:
            # Overwriting VS UUID to what user specified.
            # ALL SANs of the CSR must be reachable on the specified VS to succeed.
            rsp = overwrite_vs
            vs_uuid = rsp["results"][0]["uuid"]
            print("Note: Overwriting VS UUID to {}".format(vs_uuid))

        print("Found VS {} with fqdn {}".format(vs_uuid, domain))

        # Let's check if VS is enabled, otherwise challenge can never successfully complete.
        if not rsp["results"][0]["enabled"]:
            raise Exception("VS with fqdn {} is not enabled.".format(domain))

        # Special handling for virtualHosting: if child, get services from parent.
        if vhMode and rsp["results"][0]["type"] == "VS_TYPE_VH_CHILD":
            # vh_parent_vs_ref is schema of https://avi.domain.tld/api/virtualservice/virtualservice-UUID, hence picking the last part
            vs_uuid_parent = rsp["results"][0]["vh_parent_vs_ref"].split(
                "/")[-1]
            vhRsp = _do_request_avi(
                "virtualservice/?uuid={}".format(vs_uuid_parent),
                "GET").json()
            if debug:
                print(
                    "Parent VS of Child-VS is {} and found {} matches".format(
                        vs_uuid_parent, vhRsp['count']))
            if vhRsp['count'] == 0:
                raise Exception(
                    "Could not find parent VS {} of child VS UUID = {}".format(
                        vs_uuid_parent, vs_uuid))

            # we just copy it over. more transparent for further logic.
            rsp["results"][0]["services"] = vhRsp["results"][0]["services"]

        # Check if the vs is serving on port 80
        serving_on_port_80 = False
        service_on_port_80_data = None
        for service in rsp["results"][0]["services"]:
            if service["port"] == 80 and not service["enable_ssl"]:
                serving_on_port_80 = True
                if debug:
                    print("VS serving on port 80")
                break

        # Update VS
        httpPolicyName = (domain + "-LetsEncryptHTTPpolicy")
        # Check if HTTP policy exists.
        # Can happen in rare cases, when e.g. script fails or removal failed for whatever reasons.
        # To prevent this from failing forever, we clean it up at this stage.
        hpRsp = _do_request_avi(
            "httppolicyset/?name={}".format(httpPolicyName), "GET").json()
        if hpRsp['count'] > 0:
            hp_uuid = hpRsp['results'][0]['uuid']
            print(
                "Stranded httpPolicySet {} found. Deleting...".format(hp_uuid))
            _do_request_avi("httppolicyset/{}".format(hp_uuid), "DELETE")

        # Create HTTP policy
        httppolicy_data = {
            "name": httpPolicyName,
            "http_security_policy": {
                "rules": [{
                    "name": "Rule 1",
                    "index": 1,
                    "enable": True,
                    "match": {
                        "path": {
                            "match_criteria":
                            "CONTAINS",
                            "match_case":
                            "SENSITIVE",
                            "match_str":
                            [".well-known/acme-challenge/{}".format(token)]
                        }
                    },
                    "action": {
                        "action": "HTTP_SECURITY_ACTION_SEND_RESPONSE",
                        "status_code": "HTTP_LOCAL_RESPONSE_STATUS_CODE_200",
                        "file": {
                            "content_type": "text/plain",
                            "file_content": keyauthorization
                        }
                    }
                }]
            },
            "is_internal_policy": False
        }

        httppolicy_uuid = None
        try:
            # Create httpPolicySet
            rsp = _do_request_avi("httppolicyset",
                                  "POST",
                                  data=httppolicy_data).json()
            httppolicy_uuid = rsp["uuid"]
            print("Created httpPolicy with uuid {}".format(httppolicy_uuid))

            patch_data = {
                "add": {
                    "http_policies": [{
                        "http_policy_set_ref":
                        "/api/httppolicyset/{}".format(httppolicy_uuid),
                        "index":
                        1000001
                    }]
                }
            }
            if not serving_on_port_80:
                # Add port to virtualservice
                print("Adding port 80 to VS")
                service_on_port_80_data = {
                    "enable_http2": False,
                    "enable_ssl": False,
                    "port": 80,
                    "port_range_end": 80
                }
                patch_data["add"]["services"] = [service_on_port_80_data]

            # Adding httpPolicySet to VS
            if vhMode:  # if VH, we set the rule on the parent. Without SNI (so HTTP) it will go to the parent.
                _do_request_avi("virtualservice/{}".format(vs_uuid_parent),
                                "PATCH", patch_data)
                print("Added httpPolicySet to parent-VS {}".format(
                    vs_uuid_parent))
            else:
                _do_request_avi("virtualservice/{}".format(vs_uuid), "PATCH",
                                patch_data)
                print("Added httpPolicySet to VS {}".format(vs_uuid))

            # check that the file is in place
            if not disable_check:
                print("Validating token from Avi Controller...")
                try:
                    maxVerifyAttempts = 5  # maximal amount of verification attempts
                    # retrying logic. Otherwise race-condition can occurr between avi controller pushing config and the token validation request
                    for verifyAttempt in range(maxVerifyAttempts):
                        reqToken = _do_request(wellknown_url, verify=False)
                        if reqToken[0] != keyauthorization:
                            print(
                                "Internal token validation failed, {0} of {1} attempts. Retrying in 2 seconds."
                                .format((verifyAttempt + 1),
                                        maxVerifyAttempts))
                            time.sleep(2)
                        else:
                            break
                    else:
                        raise Exception(
                            "All {2} internal token verifications failed. Got '{0}' but expected '{1}'."
                            .format(reqToken[0], keyauthorization,
                                    maxVerifyAttempts))
                except Exception as e:
                    raise ValueError(
                        "Wrote file, but Avi couldn't verify token at {0}. Exception: {1}"
                        .format(wellknown_url, str(e)))
            else:
                print(
                    "Waiting 5 seconds before letting LetsEncrypt validating the challenge as validation disabled. Give controller time to push configs."
                )
                time.sleep(
                    5
                )  # wait 5 secs if not validating, due to above mentioned race condition

            print("Challenge completed, notifying LetsEncrypt")
            # say the challenge is done
            _send_signed_request(
                challenge['url'], {},
                "Error submitting challenges: {0}".format(domain))
            authorization = _poll_until_not(
                auth_url, ["pending"],
                "Error checking challenge status for {0}".format(domain))
            if authorization['status'] != "valid":
                raise ValueError("Challenge did not pass for {0}: {1}".format(
                    domain, authorization))
            print("Challenge passed")

        finally:
            print("Cleaning up...")
            # Update the vs
            if httppolicy_uuid == None:
                print("Error: Failed removing httpPolicy as UUID not defined.")
            else:
                patch_data = {
                    "delete": {
                        "http_policies": [{
                            "http_policy_set_ref":
                            "/api/httppolicyset/{}".format(httppolicy_uuid),
                            "index":
                            1000001
                        }]
                    }
                }
                if not serving_on_port_80:
                    patch_data["delete"]["services"] = [
                        service_on_port_80_data
                    ]

                # Remove httpPolicySet from VS
                if vhMode:  # if VH, we set the rule on the parent. Without SNI (so HTTP) it will go to the parent.
                    _do_request_avi("virtualservice/{}".format(vs_uuid_parent),
                                    "PATCH", patch_data)
                    print("Removed httpPolicySet from parent-VS {}".format(
                        vs_uuid_parent))
                else:
                    _do_request_avi("virtualservice/{}".format(vs_uuid),
                                    "PATCH", patch_data)
                    print("Removed httpPolicySet from VS {}".format(vs_uuid))

                # Remove httpPolicySet
                _do_request_avi("httppolicyset/{}".format(httppolicy_uuid),
                                "DELETE")
                print("Deleted httpPolicySet")

        print("{0} verified!".format(domain))

    # finalize the order with the csr
    print("Signing certificate...")
    csr_der = _cmd(["openssl", "req", "-in", csr, "-outform", "DER"],
                   err_msg="DER Export Error")
    _send_signed_request(order['finalize'], {"csr": _b64(csr_der)},
                         "Error finalizing order")

    # poll the order to monitor when it's done
    order = _poll_until_not(order_headers['Location'],
                            ["pending", "processing"],
                            "Error checking order status")
    if order['status'] != "valid":
        raise ValueError("Order failed: {0}".format(order))

    # download the certificate
    certificate_pem, _, _ = _send_signed_request(
        order['certificate'], None, "Certificate download failed")
    print("Certificate signed!")

    return certificate_pem