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:
|
if validation_target is None:
|
||||||
raise Exception('No token_destination configured for this authority. Cant complete HTTP-01 challenge')
|
raise Exception('No token_destination configured for this authority. Cant complete HTTP-01 challenge')
|
||||||
|
|
||||||
|
validations = {}
|
||||||
for challenge in chall:
|
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)
|
acme_client.answer_challenge(challenge, response)
|
||||||
|
|
||||||
current_app.logger.info("Uploaded HTTP-01 challenge tokens, trying to poll and finalize the order")
|
current_app.logger.info("Uploaded HTTP-01 challenge tokens, trying to poll and finalize the order")
|
||||||
@ -123,6 +124,9 @@ class AcmeHttpChallenge(AcmeChallenge):
|
|||||||
else:
|
else:
|
||||||
pem_certificate_chain = finalized_orderr.fullchain_pem[len(pem_certificate):].lstrip()
|
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
|
# 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
|
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)
|
destination_plugin.upload_acme_token(challenge.chall.path, validation, destination.options)
|
||||||
current_app.logger.info("Uploaded HTTP-01 challenge token.")
|
current_app.logger.info("Uploaded HTTP-01 challenge token.")
|
||||||
|
|
||||||
return response
|
return response, validation
|
||||||
|
|
||||||
def cleanup(self, challenge, acme_client, validation_target):
|
def cleanup(self, token_path, token, validation_target):
|
||||||
pass
|
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):
|
class AcmeDnsChallenge(AcmeChallenge):
|
||||||
|
@ -134,6 +134,81 @@ class SFTPDestinationPlugin(DestinationPlugin):
|
|||||||
|
|
||||||
self.upload_file(dst_path, files, options)
|
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
|
# here the file is uploaded for real, this helps to keep this class DRY
|
||||||
def upload_file(self, dst_path, files, options):
|
def upload_file(self, dst_path, files, options):
|
||||||
|
|
||||||
@ -200,7 +275,8 @@ class SFTPDestinationPlugin(DestinationPlugin):
|
|||||||
try:
|
try:
|
||||||
sftp.mkdir(remote_path)
|
sftp.mkdir(remote_path)
|
||||||
except IOError as ioerror:
|
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
|
# upload certificate files to the sftp destination
|
||||||
for filename, data in files.items():
|
for filename, data in files.items():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user