Support LetsEncrypt accounts

This commit is contained in:
Curtis Castrapel
2018-07-30 15:25:02 -07:00
parent b70885595f
commit 0889076d3b
6 changed files with 75 additions and 22 deletions

View File

@ -108,12 +108,13 @@ def fetch_all_acme():
error_log["message"] = "Deleting pending certificate"
send_pending_failure_notification(pending_cert, notify_owner=pending_cert.notify)
pending_certificate_service.delete_by_id(pending_cert.id)
else:
pending_certificate_service.increment_attempt(pending_cert)
pending_certificate_service.update(
cert.get("pending_cert").id,
status=str(cert.get("last_error"))[0:128]
)
current_app.logger.error(error_log)
pending_certificate_service.increment_attempt(pending_cert)
pending_certificate_service.update(
cert.get("pending_cert").id,
status=str(cert.get("last_error"))[0:128]
)
log_data["message"] = "Complete"
log_data["new"] = new
log_data["failed"] = failed

View File

@ -140,14 +140,27 @@ def setup_acme_client(authority):
tel = options.get('telephone', current_app.config.get('ACME_TEL'))
directory_url = options.get('acme_url', current_app.config.get('ACME_DIRECTORY_URL'))
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
existing_key = options.get('acme_private_key', current_app.config.get('ACME_PRIVATE_KEY'))
existing_regr = options.get('acme_regr', current_app.config.get('ACME_REGR'))
print(existing_key)
if existing_key and existing_regr:
# Reuse the same account for each certificate issuance
key = jose.JWK.json_loads(existing_key)
regr = messages.RegistrationResource.json_loads(existing_regr)
current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
net = ClientNetwork(key, account=regr)
client = BackwardsCompatibleClientV2(net, key, directory_url)
return client, {}
else:
# Create an account for each certificate issuance
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
net = ClientNetwork(key, account=None)
client = BackwardsCompatibleClientV2(net, key, directory_url)
registration = client.new_account_and_tos(messages.NewRegistration.from_data(email=email))
current_app.logger.debug("Connected: {0}".format(registration.uri))
net = ClientNetwork(key, account=None)
client = BackwardsCompatibleClientV2(net, key, directory_url)
registration = client.new_account_and_tos(messages.NewRegistration.from_data(email=email))
current_app.logger.debug("Connected: {0}".format(registration.uri))
return client, registration
@ -196,6 +209,35 @@ def finalize_authorizations(acme_client, account_number, dns_provider, authoriza
return authorizations
def cleanup_dns_challenges(acme_client, account_number, dns_provider, authorizations, dns_provider_options):
"""
Best effort attempt to delete DNS challenges that may not have been deleted previously. This is usually called
on an exception
:param acme_client:
:param account_number:
:param dns_provider:
:param authorizations:
:param dns_provider_options:
:return:
"""
for authz_record in authorizations:
dns_challenges = authz_record.dns_challenge
host_to_validate = maybe_remove_wildcard(authz_record.host)
host_to_validate = maybe_add_extension(host_to_validate, dns_provider_options)
for dns_challenge in dns_challenges:
try:
dns_provider.delete_txt_record(
authz_record.change_id,
account_number,
dns_challenge.validation_domain_name(host_to_validate),
dns_challenge.validation(acme_client.client.net.key)
)
except Exception:
# If this fails, it's most likely because the record doesn't exist or we're not authorized to modify it.
pass
class ACMEIssuerPlugin(IssuerPlugin):
title = 'Acme'
slug = 'acme-issuer'
@ -333,12 +375,21 @@ class ACMEIssuerPlugin(IssuerPlugin):
"cert": cert,
"pending_cert": entry["pending_cert"],
})
except (PollError, AcmeError, Exception):
except (PollError, AcmeError, Exception) as e:
current_app.logger.error("Unable to resolve pending cert: {}".format(pending_cert), exc_info=True)
certs.append({
"cert": False,
"pending_cert": entry["pending_cert"],
"last_error": e,
})
# Ensure DNS records get deleted
cleanup_dns_challenges(
entry["acme_client"],
entry["account_number"],
entry["dns_provider_type"],
entry["authorizations"],
entry["dns_provider_options"],
)
return certs
def create_certificate(self, csr, issuer_options):

View File

@ -134,6 +134,7 @@ class TestAcme(unittest.TestCase):
mock_client.register = mock_registration
mock_client.agree_to_tos = Mock(return_value=True)
mock_acme.return_value = mock_client
mock_current_app.config = {}
result_client, result_registration = plugin.setup_acme_client(mock_authority)
assert result_client
assert result_registration