Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
svalgaard committed Jun 3, 2021
1 parent 506e377 commit 88309e0
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 0 deletions.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include LICENSE.txt
include README.md
global-exclude __pycache__
global-exclude *.py[cod]
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
certbot-dns-infoblox
====================

Infoblox DNS Authenticator plugin for Certbot

This plugin automates the process of completing a ``dns-01`` challenge by
creating, and subsequently removing, TXT records using the Infoblox Remote API.


Installation
------------
```
pip install certbot-dns-infoblox
```

Named Arguments
---------------

To start using DNS authentication for Infoblox, pass the following arguments on
certbot's command line:

============================================================ ==============================================
``--authenticator certbot-dns-infoblox:dns-infoblox`` Select the authenticator plugin (Required)

``--certbot-dns-infoblox:dns-infoblox-credentials`` Infoblox remote user credentials INI file.
(Default: /etc/letsencrypt/infoblox.ini)

``--certbot-dns-infoblox:dns-infoblox-propagation-seconds`` Waiting time for DNS to propagate before asking
the ACME server to verify the DNS record.
(Default: 10)
============================================================ ==============================================

If you are using certbot >= 1.0, you can skip the `certbot-dns-infoblox:`
in the above arguments.


Credentials
-----------
An example ``credentials.ini`` file:

.. code-block:: ini
#
# Sample Infoblox INI file
#
dns_infoblox_hostname="infoblox.example.net"
dns_infoblox_username="my-wapi-user"
dns_infoblox_password="5f4dcc3b5aa765d61d8327deb882cf99"
dns_infoblox_view=""

The path to this file can be provided interactively or using the
``--dns-infoblox-credentials`` command-line argument. Certbot
records the path to this file for use during renewal, but does not store the
file's contents.

**CAUTION:** You should protect these API credentials as you would the
password to your infoblox account. Users who can read this file can use these
credentials to issue arbitrary API calls on your behalf. Users who can cause
Certbot to run using these credentials can complete a ``dns-01`` challenge to
acquire new certificates or revoke existing certificates for associated
domains, even if those domains aren't being managed by this server.

Certbot will emit a warning if it detects that the credentials file can be
accessed by other users on your system. The warning reads "Unsafe permissions
on credentials configuration file", followed by the path to the credentials
file. This warning will be emitted each time Certbot uses the credentials file,
including for renewal, and cannot be silenced except by addressing the issue
(e.g., by using a command like ``chmod 600`` to restrict access to the file).


Examples
--------
To acquire a single certificate for both ``example.com`` and
``*.example.com``, waiting 100 seconds for DNS propagation:

.. code-block:: bash

certbot certonly \
--authenticator dns-infoblox \
--dns-infoblox-credentials /etc/letsencrypt/.secrets/domain.tld.ini \
--dns-infoblox-propagation-seconds 100 \
--agree-tos \
--rsa-key-size 4096 \
-d 'example.com' \
-d '*.example.com'

Notes
-----

This is based on the work in [certbot-dns-ipsconfig](https://github.com/m42e/certbot-dns-ispconfig)
and the package [infoblox-client](https://github.com/infobloxopen/infoblox-client) python package.

Note that all your issued certificates can be found in public databases, e.g., [crt.sh](https://crt.sh/).
79 changes: 79 additions & 0 deletions certbot_dns_infoblox/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
The `~certbot_dns_infoblox.dns_infoblox` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the Infoblox REST API.
Named Arguments
---------------
======================================== =====================================
``--dns-infoblox-credentials`` Infoblox Remote API credentials
INI file. (Default:
/etc/letsencrypt/infoblox.ini)
``--dns-infoblox-propagation-seconds`` The number of seconds to wait for DNS
to propagate before asking the ACME
server to verify the DNS record.
(Default: 10)
======================================== =====================================
Credentials
-----------
Use of this plugin requires a configuration file containing Infoblox Remote
WAPI credentials.
.. code-block:: ini
:name: credentials.ini
:caption: Example credentials file:
# INFOBLOX API credentials used by Certbot
dns_infoblox_hostname = "infoblox.example.org:8080"
dns_infoblox_username = "myapiuser"
dns_infoblox_password = "mysecretpassword"
dns_infoblox_view = "external"
The path to this file can be provided interactively or using the
``--dns-infoblox-credentials`` command-line argument. Certbot records the path
to this file for use during renewal, but does not store the file's contents.
.. caution::
You should protect these API credentials as you would a password. Users who
can read this file can use these credentials to issue arbitrary API calls on
your behalf. Users who can cause Certbot to run using these credentials can
complete a ``dns-01`` challenge to acquire new certificates or revoke
existing certificates for associated domains, even if those domains aren't
being managed by this server.
Certbot will emit a warning if it detects that the credentials file can be
accessed by other users on your system. The warning reads "Unsafe permissions
on credentials configuration file", followed by the path to the credentials
file. This warning will be emitted each time Certbot uses the credentials file,
including for renewal, and cannot be silenced except by addressing the issue
(e.g., by using a command like ``chmod 600`` to restrict access to the file).
Examples
--------
.. code-block:: bash
:caption: To acquire a certificate for ``example.com``
certbot certonly \\
--dns-infoblox \\
--dns-infoblox-credentials ~/.secrets/certbot/infoblox.ini \\
-d example.com
.. code-block:: bash
:caption: To acquire a single certificate for both ``example.com`` and
``www.example.com``
certbot certonly \\
--dns-infoblox \\
--dns-infoblox-credentials ~/.secrets/certbot/infoblox.ini \\
-d example.com \\
-d www.example.com
.. code-block:: bash
:caption: To acquire a certificate for ``example.com``, waiting 240 seconds
for DNS propagation
certbot certonly \\
--dns-infoblox \\
--dns-infoblox-credentials ~/.secrets/certbot/infoblox.ini \\
--dns-infoblox-propagation-seconds 240 \\
-d example.com
"""
1 change: 1 addition & 0 deletions certbot_dns_infoblox/_internal/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Internal implementation of `~certbot_dns_infoblox.dns_infoblox` plugin."""
105 changes: 105 additions & 0 deletions certbot_dns_infoblox/_internal/dns_infoblox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""DNS Authenticator for Infoblox."""
import logging
import time

import infoblox_client.connector
import infoblox_client.objects
import zope.interface
from certbot import interfaces
from certbot.plugins import dns_common

logger = logging.getLogger(__name__)


@zope.interface.implementer(interfaces.IAuthenticator)
@zope.interface.provider(interfaces.IPluginFactory)
class Authenticator(dns_common.DNSAuthenticator):
"""DNS Authenticator for Infoblox
This Authenticator uses the Infoblox REST API to fulfill a
dns-01 challenge.
"""

description = ("Obtain certificates using a DNS TXT record "
"(if you are using Infoblox for DNS).")
ttl = 120

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, default_propagation_seconds=10
)
add("credentials", help="Infoblox credentials INI file.",
default='/etc/letsencrypt/infoblox.ini')

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 Infoblox Remote REST API."
)

def _setup_credentials(self):
self.credentials = self._configure_credentials(
"credentials",
"Infoblox credentials INI file",
{
"hostname": "Hostname for Infoblox REST API.",
"username": "Username for Infoblox REST API.",
"password": "Password for Infoblox REST API.",
"view": "View to use for TXT entries "
"(leave blank is view is not necessary)"
},
)

infoclient = None
infotxts = []

def _get_infoblox_client(self):
if not self.infoclient:
self.infoclient = {
'connector': infoblox_client.connector.Connector({
'host': self.credentials.conf("hostname"),
'username': self.credentials.conf("username"),
'password': self.credentials.conf("password"),
'ssl_verify': True
})
}
if self.credentials.conf("view"):
self.infoclient['view'] = self.credentials.conf("view")

return self.infoclient.copy()

def _get_infoblox_record(self, validation_name, validation, create):
record = self._get_infoblox_client()
record['name'] = validation_name
record['text'] = validation
if create:
record['ttl'] = self.ttl
username = self.credentials.conf("username")
record['comment'] = time.strftime(
f'%Y-%m-%d %H:%M:%S: certbot-auto-{username}')

return record

def _perform(self, domain, validation_name, validation):
txt = infoblox_client.objects.TXTRecord.create(
**self._get_infoblox_record(validation_name, validation, True)
)
self.infotxts.append(txt)

def _cleanup(self, domain, validation_name, validation):
for txt in self.infotxts:
if txt.name == validation_name and txt.text == validation:
txt.delete()
return

txts = infoblox_client.objects.TXTRecord.search_all(
**self._get_infoblox_record(validation_name, validation, False)
)
for txt in txts:
# FIXME
print(f'Please delete this txt record by yourself: {txt}')
7 changes: 7 additions & 0 deletions infoblox.sample.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Sample Infoblox INI file
#
dns_infoblox_hostname="infoblox.example.net"
dns_infoblox_username="my-wapi-user"
dns_infoblox_password="5f4dcc3b5aa765d61d8327deb882cf99"
dns_infoblox_view=""
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[bdist_wheel]
universal = 1
59 changes: 59 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

from setuptools import find_packages, setup

install_requires = [
"acme>=0.29.0",
"certbot>=0.34.0",
'infoblox-client>=0.5.0',
"setuptools",
]

# read the contents of the README file
with open("README.md") as f:
long_description = f.read()

docs_extras = [
]

setup(
name="certbot-dns-infoblox",
use_scm_version=True,
setup_requires=['setuptools_scm'],

description="Infoblox DNS Authenticator plugin for Certbot",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/svalgaard/certbot-dns-infoblox",
author="Jens Svalgaard Kohrt",
author_email="github@svalgaard.net",
license="Apache License 2.0",
python_requires=">=3.6",
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Plugins",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: Apache Software License",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Security",
"Topic :: System :: Installation/Setup",
"Topic :: System :: Networking",
"Topic :: System :: Systems Administration",
"Topic :: Utilities",
],

packages=find_packages(),
include_package_data=True,
install_requires=install_requires,
entry_points={
"certbot.plugins": [
"dns-infoblox = certbot_dns_infoblox.dns_infoblox:Authenticator",
]
},
)

0 comments on commit 88309e0

Please sign in to comment.