def submit_domain_validation(client, regr, account, challenges_file, domain, log): # Get challenges for the domain. challg1 = get_challenges(client, regr, domain, challenges_file, log) challg = challg1.body if challg.status.name == "valid": # This is already valid. Return it immediately. return challg1 elif challg.status.name != "pending": raise ChallengesUnknownStatus() # Look for a challenge combination that we can fulfill. for combination in challg.combinations: if len(combination) == 1: chg = challg.challenges[combination[0]] if isinstance(chg.chall, acme.challenges.SimpleHTTP): if chg.status.name != "pending": # We can't submit twice. If this challenge is still pending # but the overall challg object is not valid, then I'm not # sure how to proceed. raise ChallengesUnknownStatus() # Submit the SimpleHTTP challenge, raising NeedToInstallFile if # the conditions are not yet met. chg = answer_challenge_simplehttp( domain, chg.chall, client, account, chg, log) # The ChallengeResource probably comes back still pending because # it doesn't go THAT fast. Give it a moment, then poll. time.sleep(1) challg1, resp = client.poll(challg1) if challg1.body.status.name == "valid": # It's valid now. That was fast. return challg1 # It's not valid. Tell the user they must want. retry_after = client.retry_after(resp, default=60) raise WaitABit(retry_after) raise NoChallengeMethodsSupported()
def get_challenges(client, regr, domain, challenges_file, log): # Load any existing challenges we've requested for domains so we # can track the challenges we've requested across sessions. existing_challenges = [] if os.path.exists(challenges_file): with open(challenges_file) as f: existing_challenges = json.load(f) # Load. for i in range(len(existing_challenges)): existing_challenges[i] = \ acme.messages.AuthorizationResource.from_json(existing_challenges[i]) # Drop any challenges that have expired. existing_challenges = list(filter(lambda challg: is_still_valid(challg.body.expires), existing_challenges)) # If challenges exist for this domain, reuse it. for i, challg in enumerate(existing_challenges): if challg.body.identifier.typ.name == "dns" and challg.body.identifier.value == domain: log("Reusing existing challenges for %s." % domain) # Refresh the record because it may have been updated with validated challenges. challg, resp = client.poll(challg) existing_challenges[i] = challg break else: # None found. challg = None if challg is None: # Get new challenges for a domain. log("Requesting new challenges for %s." % domain) challg = client.request_domain_challenges(domain, regr.new_authzr_uri) # Add into our existing challenges. existing_challenges.append(challg) # Save new set of challenges. with open(challenges_file, 'w') as f: f.write(json.dumps([c.to_json() for c in existing_challenges], sort_keys=True, indent=4)) # Return the new challenges for this domain. return challg
def get_challenges(client, regr, domain, challenges_file, log): # Load the cache of challenges. challenges = load_challenges_file(challenges_file) # If challenges exist for this domain, reuse it. # We've already dropped expired and revoked challenges, so we don't have # to check that here. for i, challg in enumerate(challenges): if challg.body.identifier.typ.name == "dns" and challg.body.identifier.value == domain: log("Reusing existing challenges for %s." % domain) # Refresh the record because it may have been updated with validated challenges. try: challg, resp = client.poll(challg) except acme.messages.Error as e: if e.typ in ("urn:acme:error:unauthorized", "urn:acme:error:malformed"): # There is a problem accessing our own account. This probably # means the stored registration information is not valid. raise AccountDataIsCorrupt(challenges_file) raise # Check that the refreshed record is not expired/revoked. Those # aren't helpful. It might be "invalid", meaning a challenge # failed. We'll percolate up an invalid challenge so the user # gets a ChallengeFailed exception, but we'll also drop it from # the cache so that it doesn't prevent further attempts to get # a certificate from proceeding. if is_still_valid_challenge(challg): if challg.body.status.name != "invalid": # Update cache. challenges[i] = challg else: # Drop from cache. challenges.pop(i) # Stop loop here: Use this challenge. break else: # None found. challg = None resp = None if challg is None: # Get new challenges for a domain. log("Requesting new challenges for %s." % domain) try: challg = client.request_domain_challenges(domain, regr.new_authzr_uri) except acme.messages.Error as e: if e.typ == "urn:acme:error:malformed": raise InvalidDomainName(domain, e.detail) raise # Add into our existing challenges. challenges.append(challg) # Write a cache of challenges. save_challenges_file(challenges, challenges_file) # Return the new challenges for this domain, and if we updated it, # then the response object so we can know how long to wait before # polling again. return (challg, resp)
def _getChallenges(hostname): # Load the cache of challenges. # Load any existing challenges we've requested for domains so we # can track the challenges we've requested across sessions. challenges = [] domain = hostname + DOMAIN info = _recallHost(hostname) key = serialization.load_pem_private_key(base64.b64decode(info['acct_privkey']),password=None, backend=default_backend()) client = acme.client.Client(CA,jose.JWKRSA(key=jose.ComparableRSAKey(key))) regr = acme.messages.RegistrationResource.json_loads(info['reg_json']) if not info['authz_json'] == None: challenges = json.loads(info['authz_json']) # Convert from JSON to ACME objects. for i in range(len(challenges)): challenges[i] = acme.messages.AuthorizationResource.from_json(challenges[i]) # Drop any challenges that have expired or have been revoked. challenges = [challg for challg in challenges if not challg.body.status.name == "revoked" and ((challg.body.expires.replace(tzinfo=None)-datetime.now()) > timedelta(seconds=60))] # If challenges exist for this domain, reuse it. # We've already dropped expired and revoked challenges, so we don't have # to check that here. for i, challg in enumerate(challenges): if challg.body.identifier.typ.name == "dns" and challg.body.identifier.value == domain: print ("Reusing existing challenges for %s." % domain) # Refresh the record because it may have been updated with validated challenges. try: challg, resp = client.poll(challg) except acme.messages.Error as e: if e.typ in ("urn:acme:error:unauthorized", "urn:acme:error:malformed"): # There is a problem accessing our own account. This probably # means the stored registration information is not valid. raise AccountDataIsCorrupt(challenges_file) raise # Check that the refreshed record is still valid. if not challg.body.status.name == "revoked" and ((challg.body.expires.replace(tzinfo=None)-datetime.now()) > timedelta(seconds=60)): # If so, keep it. challenges[i] = challg break else: # None found. challg = None resp = None if challg is None: # Get new challenges for a domain. print ("Requesting new challenges for %s." % domain) try: challg = client.request_domain_challenges(domain, regr.new_authzr_uri) except acme.messages.Error as e: #if e.typ == "urn:acme:error:malformed": #print e.detail print e.detail raise # Add into our existing challenges. challenges.append(challg) # Write a cache of challenges. _updateHost(hostname, 'authz_json', json.dumps([c.to_json() for c in challenges], sort_keys=True, indent=4)) # Return the new challenges for this domain, and if we updated it, # then the response object so we can know how long to wait before # polling again. return (challg, resp)