Use Gandi LiveDNS to add and remove the challenge records
This commit is contained in:
parent
0e2e4c9be7
commit
56b693eab4
@ -1,31 +0,0 @@
|
||||
"""Example Certbot plugins.
|
||||
|
||||
For full examples, see `certbot.plugins`.
|
||||
|
||||
"""
|
||||
import zope.interface
|
||||
|
||||
from certbot import interfaces
|
||||
from certbot.plugins import common
|
||||
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
class Authenticator(common.Plugin):
|
||||
"""Example Authenticator."""
|
||||
|
||||
description = "Example Authenticator plugin"
|
||||
|
||||
# Implement all methods from IAuthenticator, remembering to add
|
||||
# "self" as first argument, e.g. def prepare(self)...
|
||||
|
||||
|
||||
@zope.interface.implementer(interfaces.IInstaller)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
class Installer(common.Plugin):
|
||||
"""Example Installer."""
|
||||
|
||||
description = "Example Installer plugin"
|
||||
|
||||
# Implement all methods from IInstaller, remembering to add
|
||||
# "self" as first argument, e.g. def get_all_names(self)...
|
@ -1,17 +0,0 @@
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
setup(
|
||||
name='certbot-example-plugins',
|
||||
package='certbot_example_plugins.py',
|
||||
install_requires=[
|
||||
'certbot',
|
||||
'zope.interface',
|
||||
],
|
||||
entry_points={
|
||||
'certbot.plugins': [
|
||||
'example_authenticator = certbot_example_plugins:Authenticator',
|
||||
'example_installer = certbot_example_plugins:Installer',
|
||||
],
|
||||
},
|
||||
)
|
@ -1,3 +1,4 @@
|
||||
docker run -it --rm --entrypoint /bin/sh -v "$(pwd)/certbot-example-plugins:/tmp/certbot-example-plugins" certbot/certbot:v0.22.0
|
||||
docker run -it --rm --entrypoint /bin/sh -v "$(pwd)/plugin:/tmp/plugin" certbot/certbot:v0.22.0
|
||||
certbot plugins
|
||||
pip install -e /tmp/certbot-example-plugins
|
||||
pip install -e /tmp/plugin
|
||||
certbot certonly --dry-run -a certbot-plugin-gandi:dns --certbot-plugin-gandi:dns-credentials /tmp/creds.ini -d domain.com
|
||||
|
0
plugin/certbot_plugin_gandi/__init__.py
Normal file
0
plugin/certbot_plugin_gandi/__init__.py
Normal file
99
plugin/certbot_plugin_gandi/gandi_api.py
Normal file
99
plugin/certbot_plugin_gandi/gandi_api.py
Normal file
@ -0,0 +1,99 @@
|
||||
import requests
|
||||
import urllib
|
||||
from collections import namedtuple
|
||||
from certbot.plugins import dns_common
|
||||
|
||||
|
||||
_GandiConfig = namedtuple('_GandiConfig', ('api_key',))
|
||||
_BaseDomain = namedtuple('_BaseDomain', ('zone_uuid', 'fqdn'))
|
||||
|
||||
|
||||
def get_config(api_key):
|
||||
return _GandiConfig(api_key=api_key)
|
||||
|
||||
|
||||
def _get_json(response):
|
||||
try:
|
||||
data = response.json()
|
||||
except ValueError:
|
||||
return dict()
|
||||
return data
|
||||
|
||||
|
||||
def _get_response_message(response, default='<No reason given>'):
|
||||
return _get_json(response).get('message', default)
|
||||
|
||||
|
||||
def _headers(cfg):
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': cfg.api_key
|
||||
}
|
||||
|
||||
|
||||
def _get_url(*segs):
|
||||
return 'https://dns.api.gandi.net/api/v5/{}'.format(
|
||||
'/'.join(urllib.quote(seg, safe='') for seg in segs)
|
||||
)
|
||||
|
||||
|
||||
def _request(cfg, method, segs, **kw):
|
||||
headers = _headers(cfg)
|
||||
url = _get_url(*segs)
|
||||
return requests.request(method, url, headers=headers, **kw)
|
||||
|
||||
|
||||
def _get_base_domain(cfg, domain):
|
||||
for candidate_base_domain in dns_common.base_domain_name_guesses(domain):
|
||||
response = _request(cfg, 'GET', ('domains', candidate_base_domain))
|
||||
if response.ok:
|
||||
data = _get_json(response)
|
||||
zone_uuid = data.get('zone_uuid')
|
||||
fqdn = data.get('fqdn')
|
||||
if zone_uuid and fqdn:
|
||||
return _BaseDomain(zone_uuid=zone_uuid, fqdn=fqdn)
|
||||
return None
|
||||
|
||||
|
||||
def _get_relative_name(base_domain, name):
|
||||
suffix = '.' + base_domain.fqdn
|
||||
return name[:-len(suffix)] if name.endswith(suffix) else None
|
||||
|
||||
|
||||
def _del_txt_record(cfg, base_domain, relative_name):
|
||||
return _request(cfg, 'DELETE', ('zones', base_domain.zone_uuid, 'records', relative_name, 'TXT'))
|
||||
|
||||
|
||||
def _update_record(cfg, domain, name, request_runner):
|
||||
|
||||
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_runner(base_domain, relative_name)
|
||||
|
||||
return None if response.ok else _get_response_message(response)
|
||||
|
||||
|
||||
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 _update_record(cfg, domain, name, requester)
|
||||
|
||||
|
||||
def del_txt_record(cfg, domain, name):
|
||||
|
||||
def requester(base_domain, relative_name):
|
||||
return _del_txt_record(cfg, base_domain, relative_name)
|
||||
|
||||
return _update_record(cfg, domain, name, requester)
|
||||
|
||||
|
60
plugin/certbot_plugin_gandi/main.py
Normal file
60
plugin/certbot_plugin_gandi/main.py
Normal file
@ -0,0 +1,60 @@
|
||||
import zope.interface
|
||||
import logging
|
||||
|
||||
from certbot import interfaces, errors
|
||||
from certbot.plugins import dns_common
|
||||
|
||||
from . import gandi_api
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
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).'
|
||||
|
||||
|
||||
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.'
|
||||
|
||||
|
||||
def _setup_credentials(self):
|
||||
self.credentials = self._configure_credentials(
|
||||
'credentials',
|
||||
'Gandi credentials INI file',
|
||||
{
|
||||
'api-key': 'API key for Gandi account'
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _perform(self, domain, validation_name, validation):
|
||||
error = gandi_api.add_txt_record(self._get_gandi_config(), domain, validation_name, validation)
|
||||
if error is not None:
|
||||
raise errors.PluginError('An error occurred adding the DNS TXT record: {0}'.format(error))
|
||||
|
||||
|
||||
def _cleanup(self, domain, validation_name, validation):
|
||||
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)
|
||||
|
||||
|
||||
def _get_gandi_config(self):
|
||||
return gandi_api.get_config(api_key = self.credentials.conf('api-key'))
|
17
plugin/setup.py
Normal file
17
plugin/setup.py
Normal file
@ -0,0 +1,17 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
setup(
|
||||
name='certbot-plugin-gandi',
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
'certbot',
|
||||
'zope.interface',
|
||||
'requests>=2.4.2',
|
||||
],
|
||||
entry_points={
|
||||
'certbot.plugins': [
|
||||
'dns = certbot_plugin_gandi.main:Authenticator',
|
||||
],
|
||||
},
|
||||
)
|
Loading…
Reference in New Issue
Block a user