Merge pull request #1497 from castrapel/letsencrypt_account_support
Letsencrypt account support
This commit is contained in:
commit
3f9d66bd51
|
@ -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
|
||||
|
|
|
@ -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'))
|
||||
|
||||
current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
|
||||
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'))
|
||||
|
||||
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))
|
||||
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))
|
||||
|
||||
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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,8 +15,8 @@ asyncpool==1.0
|
|||
babel==2.6.0 # via sphinx
|
||||
bcrypt==3.1.4
|
||||
blinker==1.4
|
||||
boto3==1.7.62
|
||||
botocore==1.10.62
|
||||
boto3==1.7.65
|
||||
botocore==1.10.65
|
||||
certifi==2018.4.16
|
||||
cffi==1.11.5
|
||||
chardet==3.0.4
|
||||
|
@ -55,11 +55,11 @@ mock==2.0.0
|
|||
ndg-httpsclient==0.5.1
|
||||
packaging==17.1 # via sphinx
|
||||
paramiko==2.4.1
|
||||
pbr==4.1.1
|
||||
pbr==4.2.0
|
||||
pem==18.1.0
|
||||
psycopg2==2.7.5
|
||||
pyasn1-modules==0.2.2
|
||||
pyasn1==0.4.3
|
||||
pyasn1==0.4.4
|
||||
pycparser==2.18
|
||||
pygments==2.2.0 # via sphinx
|
||||
pyjwt==1.6.4
|
||||
|
|
|
@ -8,9 +8,9 @@ asn1crypto==0.24.0 # via cryptography
|
|||
atomicwrites==1.1.5 # via pytest
|
||||
attrs==18.1.0 # via pytest
|
||||
aws-xray-sdk==0.95 # via moto
|
||||
boto3==1.7.65 # via moto
|
||||
boto3==1.7.66 # via moto
|
||||
boto==2.49.0 # via moto
|
||||
botocore==1.10.65 # via boto3, moto, s3transfer
|
||||
botocore==1.10.66 # via boto3, moto, s3transfer
|
||||
certifi==2018.4.16 # via requests
|
||||
cffi==1.11.5 # via cryptography
|
||||
chardet==3.0.4 # via requests
|
||||
|
@ -37,14 +37,14 @@ more-itertools==4.2.0 # via pytest
|
|||
moto==1.3.3
|
||||
nose==1.3.7
|
||||
pbr==4.2.0 # via mock
|
||||
pluggy==0.6.0 # via pytest
|
||||
pluggy==0.7.1 # via pytest
|
||||
py==1.5.4 # via pytest
|
||||
pyaml==17.12.1 # via moto
|
||||
pycparser==2.18 # via cffi
|
||||
pyflakes==2.0.0
|
||||
pytest-flask==0.10.0
|
||||
pytest-mock==1.10.0
|
||||
pytest==3.6.3
|
||||
pytest==3.6.4
|
||||
python-dateutil==2.6.1 # via botocore, faker, freezegun, moto
|
||||
pytz==2018.5 # via moto
|
||||
pyyaml==3.13 # via pyaml
|
||||
|
|
|
@ -13,8 +13,8 @@ asn1crypto==0.24.0 # via cryptography
|
|||
asyncpool==1.0
|
||||
bcrypt==3.1.4 # via flask-bcrypt, paramiko
|
||||
blinker==1.4 # via flask-mail, flask-principal, raven
|
||||
boto3==1.7.65
|
||||
botocore==1.10.65 # via boto3, s3transfer
|
||||
boto3==1.7.66
|
||||
botocore==1.10.66 # via boto3, s3transfer
|
||||
certifi==2018.4.16
|
||||
cffi==1.11.5 # via bcrypt, cryptography, pynacl
|
||||
chardet==3.0.4 # via requests
|
||||
|
|
Loading…
Reference in New Issue