- compatible with python3.6
- adapted to allow for wildcard-certs - adapted to allow for more than one Order per domain
This commit is contained in:
parent
6d423a99cb
commit
476530bd4f
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Michael Porter
|
||||
Copyright (c) 2018 Michael Porter, with modifications from Markus Pawlata
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -2,6 +2,10 @@ import requests
|
||||
import urllib
|
||||
from collections import namedtuple
|
||||
from certbot.plugins import dns_common
|
||||
try:
|
||||
from urllib import quote # Python 2.X
|
||||
except ImportError:
|
||||
from urllib.parse import quote # Python 3+
|
||||
|
||||
|
||||
_GandiConfig = namedtuple('_GandiConfig', ('api_key',))
|
||||
@ -33,7 +37,7 @@ def _headers(cfg):
|
||||
|
||||
def _get_url(*segs):
|
||||
return 'https://dns.api.gandi.net/api/v5/{}'.format(
|
||||
'/'.join(urllib.quote(seg, safe='') for seg in segs)
|
||||
'/'.join(quote(seg, safe='') for seg in segs)
|
||||
)
|
||||
|
||||
|
||||
@ -61,7 +65,10 @@ def _get_relative_name(base_domain, name):
|
||||
|
||||
|
||||
def _del_txt_record(cfg, base_domain, relative_name):
|
||||
return _request(cfg, 'DELETE', ('zones', base_domain.zone_uuid, 'records', relative_name, 'TXT'))
|
||||
return _request(
|
||||
cfg,
|
||||
'DELETE',
|
||||
('zones', base_domain.zone_uuid, 'records', relative_name, 'TXT'))
|
||||
|
||||
|
||||
def _update_record(cfg, domain, name, request_runner):
|
||||
@ -78,13 +85,36 @@ def _update_record(cfg, domain, name, request_runner):
|
||||
return None if response.ok else _get_response_message(response)
|
||||
|
||||
|
||||
def get_txt_records(cfg, domain, name):
|
||||
|
||||
base_domain = _get_base_domain(cfg, domain)
|
||||
if base_domain is None:
|
||||
return 'Unable to get base domain for "{}"'.format(domain)
|
||||
relative_name = _get_relative_name(base_domain, name)
|
||||
if relative_name is None:
|
||||
return 'Unable to derive relative name for "{}"'.format(name)
|
||||
|
||||
response = _request(
|
||||
cfg,
|
||||
'GET',
|
||||
('zones', base_domain.zone_uuid, 'records', relative_name, 'TXT'))
|
||||
if response.ok:
|
||||
return response.json().get('rrset_values')
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def add_txt_record(cfg, domain, name, value):
|
||||
|
||||
def requester(base_domain, relative_name):
|
||||
_del_txt_record(cfg, base_domain, relative_name)
|
||||
return _request(cfg, 'POST',
|
||||
('zones', base_domain.zone_uuid, 'records', relative_name, 'TXT'),
|
||||
json={'rrset_values': [value]})
|
||||
return _request(
|
||||
cfg,
|
||||
'POST',
|
||||
('zones', base_domain.zone_uuid, 'records', relative_name, 'TXT'),
|
||||
json={
|
||||
'rrset_values': value if isinstance(value, list) else [value]
|
||||
})
|
||||
|
||||
return _update_record(cfg, domain, name, requester)
|
||||
|
||||
@ -95,5 +125,3 @@ def del_txt_record(cfg, domain, name):
|
||||
return _del_txt_record(cfg, base_domain, relative_name)
|
||||
|
||||
return _update_record(cfg, domain, name, requester)
|
||||
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import zope.interface
|
||||
import logging
|
||||
|
||||
from certbot import interfaces, errors
|
||||
from certbot import interfaces
|
||||
from certbot.errors import PluginError
|
||||
from certbot.plugins import dns_common
|
||||
|
||||
from . import gandi_api
|
||||
@ -15,24 +16,21 @@ logger = logging.getLogger(__name__)
|
||||
class Authenticator(dns_common.DNSAuthenticator):
|
||||
"""DNS Authenticator for Gandi (using LiveDNS)."""
|
||||
|
||||
description = 'Obtain certificates using a DNS TXT record (if you are using Gandi for DNS).'
|
||||
|
||||
description = ('Obtain certificates using a DNS TXT record (if you are '
|
||||
'using Gandi for DNS).')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Authenticator, self).__init__(*args, **kwargs)
|
||||
self.credentials = None
|
||||
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
|
||||
super(Authenticator, cls).add_parser_arguments(add)
|
||||
add('credentials', help='Gandi credentials INI file.')
|
||||
|
||||
|
||||
def more_info(self): # pylint: disable=missing-docstring,no-self-use
|
||||
return 'This plugin configures a DNS TXT record to respond to a dns-01 challenge using ' + \
|
||||
'the Gandi LiveDNS API.'
|
||||
|
||||
return ('This plugin configures a DNS TXT record to respond to a '
|
||||
'dns-01 challenge using the Gandi LiveDNS API.')
|
||||
|
||||
def _setup_credentials(self):
|
||||
self.credentials = self._configure_credentials(
|
||||
@ -43,18 +41,25 @@ class Authenticator(dns_common.DNSAuthenticator):
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _perform(self, domain, validation_name, validation):
|
||||
error = gandi_api.add_txt_record(self._get_gandi_config(), domain, validation_name, validation)
|
||||
previous_values = gandi_api.get_txt_records(
|
||||
self._get_gandi_config(), domain, validation_name)
|
||||
previous_values.append(validation)
|
||||
error = gandi_api.add_txt_record(
|
||||
self._get_gandi_config(), domain, validation_name, previous_values)
|
||||
if error is not None:
|
||||
raise errors.PluginError('An error occurred adding the DNS TXT record: {0}'.format(error))
|
||||
|
||||
raise PluginError(
|
||||
'An error occurred adding the DNS TXT record: {}'.format(
|
||||
error))
|
||||
|
||||
def _cleanup(self, domain, validation_name, validation):
|
||||
error = gandi_api.del_txt_record(self._get_gandi_config(), domain, validation_name)
|
||||
error = gandi_api.del_txt_record(
|
||||
self._get_gandi_config(), domain, validation_name)
|
||||
if error is not None:
|
||||
logger.warn('Unable to find or delete the DNS TXT record: %s', error)
|
||||
|
||||
logger.warn(
|
||||
('Unable to find or delete the DNS TXT record: {}, this '
|
||||
'is normal if you had multiple challenges in the same '
|
||||
'zone.').format(error))
|
||||
|
||||
def _get_gandi_config(self):
|
||||
return gandi_api.get_config(api_key = self.credentials.conf('api-key'))
|
||||
return gandi_api.get_config(api_key=self.credentials.conf('api-key'))
|
||||
|
Loading…
Reference in New Issue
Block a user