Implement cleanup_acme_token for http challenge
This commit is contained in:
parent
99ca0ac78d
commit
960b8e78e3
|
@ -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):
|
||||
|
|
|
@ -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():
|
||||
|
|
Loading…
Reference in New Issue