Implement create_certificate for HTTP-01 challenge

This commit is contained in:
Mathias Petermann 2020-09-29 18:33:40 +02:00
parent 3012995c76
commit e06bdcf2a3
1 changed files with 60 additions and 13 deletions

View File

@ -30,6 +30,8 @@ from lemur.destinations import service as destination_service
from lemur.dns_providers import service as dns_provider_service from lemur.dns_providers import service as dns_provider_service
from lemur.exceptions import InvalidAuthority, InvalidConfiguration, UnknownProvider from lemur.exceptions import InvalidAuthority, InvalidConfiguration, UnknownProvider
from lemur.extensions import metrics, sentry from lemur.extensions import metrics, sentry
from lemur.plugins.base import plugins
from lemur.plugins import lemur_acme as acme from lemur.plugins import lemur_acme as acme
from lemur.plugins.bases import IssuerPlugin from lemur.plugins.bases import IssuerPlugin
from lemur.plugins.lemur_acme import cloudflare, dyn, route53, ultradns, powerdns from lemur.plugins.lemur_acme import cloudflare, dyn, route53, ultradns, powerdns
@ -83,19 +85,19 @@ class AcmeHandler(object):
def maybe_add_extension(self, host, dns_provider_options): def maybe_add_extension(self, host, dns_provider_options):
if dns_provider_options and dns_provider_options.get( if dns_provider_options and dns_provider_options.get(
"acme_challenge_extension" "acme_challenge_extension"
): ):
host = host + dns_provider_options.get("acme_challenge_extension") host = host + dns_provider_options.get("acme_challenge_extension")
return host return host
def start_dns_challenge( def start_dns_challenge(
self, self,
acme_client, acme_client,
account_number, account_number,
host, host,
dns_provider, dns_provider,
order, order,
dns_provider_options, dns_provider_options,
): ):
current_app.logger.debug("Starting DNS challenge for {0}".format(host)) current_app.logger.debug("Starting DNS challenge for {0}".format(host))
@ -210,12 +212,12 @@ class AcmeHandler(object):
if current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA", False) \ if current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA", False) \
and datetime.datetime.now() < datetime.datetime.strptime( and datetime.datetime.now() < datetime.datetime.strptime(
current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA_EXPIRATION_DATE", "17/03/21"), '%d/%m/%y'): current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA_EXPIRATION_DATE", "17/03/21"), '%d/%m/%y'):
pem_certificate_chain = current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA") pem_certificate_chain = current_app.config.get("IDENTRUST_CROSS_SIGNED_LE_ICA")
else: else:
pem_certificate_chain = orderr.fullchain_pem[ pem_certificate_chain = orderr.fullchain_pem[
len(pem_certificate) : # noqa len(pem_certificate): # noqa
].lstrip() ].lstrip()
current_app.logger.debug( current_app.logger.debug(
"{0} {1}".format(type(pem_certificate), type(pem_certificate_chain)) "{0} {1}".format(type(pem_certificate), type(pem_certificate_chain))
@ -692,6 +694,7 @@ class ACMEIssuerPlugin(IssuerPlugin):
account_number = None account_number = None
provider_type = None provider_type = None
acme_client.new_order()
domains = self.acme.get_domains(issuer_options) domains = self.acme.get_domains(issuer_options)
if not create_immediately: if not create_immediately:
# Create pending authorizations that we'll need to do the creation # Create pending authorizations that we'll need to do the creation
@ -810,7 +813,51 @@ class ACMEHttpIssuerPlugin(IssuerPlugin):
self.destination_list.append(destination.label) self.destination_list.append(destination.label)
def create_certificate(self, csr, issuer_options): def create_certificate(self, csr, issuer_options):
pass """
Creates an ACME certificate using the HTTP-01 challenge.
:param csr:
:param issuer_options:
:return: :raise Exception:
"""
self.acme = AcmeHandler()
authority = issuer_options.get("authority")
create_immediately = issuer_options.get("create_immediately", False)
acme_client, registration = self.acme.setup_acme_client(authority)
orderr = acme_client.new_order(csr)
challenge = None
for authz in orderr.authorizations:
# Choosing challenge.
# authz.body.challenges is a set of ChallengeBody objects.
for i in authz.body.challenges:
# Find the supported challenge.
if isinstance(i.chall, challenges.HTTP01):
challenge = i
if challenge is None:
raise Exception('HTTP-01 challenge was not offered by the CA server.')
else:
# Here we probably should create a pending certificate and make use of celery, but for now
# I'll ignore all of that
for option in json.loads(issuer_options["authority"].options):
if option["name"] == "tokenDestination":
token_destination = destination_service.get_by_label(option["value"])
if token_destination is None:
raise Exception('No token_destination configured for this authority. Cant complete HTTP-01 challenge')
destination_plugin = plugins.get(token_destination.plugin_name)
destination_plugin.upload_acme_token(challenge.chall.path, challenge.chall.token, token_destination.options)
current_app.logger.info("Uploaded HTTP-01 challenge token, trying to poll and finalize the order")
pem_certificate, pem_certificate_chain = self.acme.request_certificate(
acme_client, orderr.authorizations, csr
)
# TODO add external ID (if possible)
return pem_certificate, pem_certificate_chain, None
@staticmethod @staticmethod
def create_authority(options): def create_authority(options):
@ -837,4 +884,4 @@ class ACMEHttpIssuerPlugin(IssuerPlugin):
def cancel_ordered_certificate(self, pending_cert, **kwargs): def cancel_ordered_certificate(self, pending_cert, **kwargs):
# Needed to override issuer function. # Needed to override issuer function.
pass pass