Implement cleanup_acme_token for http challenge

This commit is contained in:
Mathias Petermann 2020-11-10 16:22:25 +01:00
parent 99ca0ac78d
commit 960b8e78e3
2 changed files with 95 additions and 6 deletions

View File

@ -100,9 +100,10 @@ class AcmeHttpChallenge(AcmeChallenge):
if validation_target is None:
raise Exception('No token_destination configured for this authority. Cant complete HTTP-01 challenge')
validations = {}
for challenge in chall:
response = self.deploy(challenge, acme_client, validation_target)
response, validation = self.deploy(challenge, acme_client, validation_target)
validations[challenge.chall.path] = validation
acme_client.answer_challenge(challenge, response)
current_app.logger.info("Uploaded HTTP-01 challenge tokens, trying to poll and finalize the order")
@ -123,6 +124,9 @@ class AcmeHttpChallenge(AcmeChallenge):
else:
pem_certificate_chain = finalized_orderr.fullchain_pem[len(pem_certificate):].lstrip()
for token_path, token in validations.items():
self.cleanup(token_path, token, validation_target)
# validation is a random string, we use it as external id, to make it possible to implement revoke_certificate
return pem_certificate, pem_certificate_chain, None
@ -146,10 +150,19 @@ class AcmeHttpChallenge(AcmeChallenge):
destination_plugin.upload_acme_token(challenge.chall.path, validation, destination.options)
current_app.logger.info("Uploaded HTTP-01 challenge token.")
return response
return response, validation
def cleanup(self, challenge, acme_client, validation_target):
pass
def cleanup(self, token_path, token, validation_target):
destination = destination_service.get(validation_target)
if destination is None:
current_app.logger.info(
'Couldn\'t find the destination with name {}, won\'t cleanup the challenge'.format(validation_target))
destination_plugin = plugins.get(destination.plugin_name)
destination_plugin.delete_acme_token(token_path, token, destination.options)
current_app.logger.info("Cleaned up HTTP-01 challenge token.")
class AcmeDnsChallenge(AcmeChallenge):

View File

@ -134,6 +134,81 @@ class SFTPDestinationPlugin(DestinationPlugin):
self.upload_file(dst_path, files, options)
# this is called from the acme http challenge
def delete_acme_token(self, token_path, token, options, **kwargs):
dst_path = self.get_option("destinationPath", options)
_, filename = path.split(token_path)
# prepare files for upload
files = {filename: token}
self.delete_file(dst_path, files, options)
# here the file is uploaded for real, this helps to keep this class DRY
def delete_file(self, dst_path, files, options):
host = self.get_option("host", options)
port = self.get_option("port", options)
user = self.get_option("user", options)
password = self.get_option("password", options)
ssh_priv_key = self.get_option("privateKeyPath", options)
ssh_priv_key_pass = self.get_option("privateKeyPass", options)
# upload files
try:
current_app.logger.debug(
"Connecting to {0}@{1}:{2}".format(user, host, port)
)
ssh = paramiko.SSHClient()
# allow connection to the new unknown host
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# open the ssh connection
if password:
current_app.logger.debug("Using password")
ssh.connect(host, username=user, port=port, password=password)
elif ssh_priv_key:
current_app.logger.debug("Using RSA private key")
pkey = paramiko.RSAKey.from_private_key_file(
ssh_priv_key, ssh_priv_key_pass
)
ssh.connect(host, username=user, port=port, pkey=pkey)
else:
current_app.logger.error(
"No password or private key provided. Can't proceed"
)
raise paramiko.ssh_exception.AuthenticationException
# open the sftp session inside the ssh connection
sftp = ssh.open_sftp()
# upload certificate files to the sftp destination
for filename, data in files.items():
current_app.logger.debug(
"Deleting {0} from {1}".format(filename, dst_path)
)
try:
sftp.remove(path.join(dst_path, filename))
except PermissionError as permerror:
if permerror.errno == 13:
current_app.logger.debug(
"Deleting {0} from {1} returned Permission Denied Error, making file writable and retrying".format(
filename, dst_path)
)
sftp.chmod(path.join(dst_path, filename), 0o600)
sftp.remove(path.join(dst_path, filename))
ssh.close()
except Exception as e:
current_app.logger.error("ERROR in {0}: {1}".format(e.__class__, e))
try:
ssh.close()
except BaseException:
pass
# here the file is uploaded for real, this helps to keep this class DRY
def upload_file(self, dst_path, files, options):
@ -200,7 +275,8 @@ class SFTPDestinationPlugin(DestinationPlugin):
try:
sftp.mkdir(remote_path)
except IOError as ioerror:
current_app.logger.debug("Couldn't create {0}, error message: {1}".format(remote_path, ioerror))
current_app.logger.debug(
"Couldn't create {0}, error message: {1}".format(remote_path, ioerror))
# upload certificate files to the sftp destination
for filename, data in files.items():