Compare commits
6 Commits
dbm_test
...
python37-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc85d309a9 | ||
|
|
c1b4f21325 | ||
|
|
29a75eb8a7 | ||
|
|
309a70c3fe | ||
|
|
e5d35099a7 | ||
|
|
9fade9c85c |
@@ -34,7 +34,7 @@ matrix:
|
||||
- python: "3.5"
|
||||
env: TOXENV=mypy
|
||||
- python: "2.7"
|
||||
env: TOXENV='py27-{acme,apache,certbot,dns,nginx,postfix}-oldest'
|
||||
env: TOXENV='py27-{acme,apache,certbot,dns,nginx}-oldest'
|
||||
sudo: required
|
||||
services: docker
|
||||
- python: "3.4"
|
||||
@@ -90,12 +90,12 @@ addons:
|
||||
- nginx-light
|
||||
- openssl
|
||||
|
||||
install: "travis_retry $(command -v pip || command -v pip3) install codecov tox"
|
||||
install: "travis_retry $(command -v pip || command -v pip3) install tox coveralls"
|
||||
script:
|
||||
- travis_retry tox
|
||||
- '[ -z "${BOULDER_INTEGRATION+x}" ] || (travis_retry tests/boulder-fetch.sh && tests/tox-boulder-integration.sh)'
|
||||
|
||||
after_success: '[ "$TOXENV" == "cover" ] && codecov'
|
||||
after_success: '[ "$TOXENV" == "cover" ] && coveralls'
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -2,67 +2,6 @@
|
||||
|
||||
Certbot adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 0.26.1 - 2018-07-17
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix a bug that was triggered when users who had previously manually set `--server` to get ACMEv2 certs tried to renew ACMEv1 certs.
|
||||
|
||||
Despite us having broken lockstep, we are continuing to release new versions of all Certbot components during releases for the time being, however, the only package with changes other than its version number was:
|
||||
|
||||
* certbot
|
||||
|
||||
More details about these changes can be found on our GitHub repo:
|
||||
https://github.com/certbot/certbot/milestone/58?closed=1
|
||||
|
||||
## 0.26.0 - 2018-07-11
|
||||
|
||||
### Added
|
||||
|
||||
* A new security enhancement which we're calling AutoHSTS has been added to
|
||||
Certbot's Apache plugin. This enhancement configures your webserver to send a
|
||||
HTTP Strict Transport Security header with a low max-age value that is slowly
|
||||
increased over time. The max-age value is not increased to a large value
|
||||
until you've successfully managed to renew your certificate. This enhancement
|
||||
can be requested with the --auto-hsts flag.
|
||||
* New official DNS plugins have been created for Gehirn Infrastracture Service,
|
||||
Linode, OVH, and Sakura Cloud. These plugins can be found on our Docker Hub
|
||||
page at https://hub.docker.com/u/certbot and on PyPI.
|
||||
* The ability to reuse ACME accounts from Let's Encrypt's ACMEv1 endpoint on
|
||||
Let's Encrypt's ACMEv2 endpoint has been added.
|
||||
* Certbot and its components now support Python 3.7.
|
||||
* Certbot's install subcommand now allows you to interactively choose which
|
||||
certificate to install from the list of certificates managed by Certbot.
|
||||
* Certbot now accepts the flag `--no-autorenew` which causes any obtained
|
||||
certificates to not be automatically renewed when it approaches expiration.
|
||||
* Support for parsing the TLS-ALPN-01 challenge has been added back to the acme
|
||||
library.
|
||||
|
||||
### Changed
|
||||
|
||||
* Certbot's default ACME server has been changed to Let's Encrypt's ACMEv2
|
||||
endpoint. By default, this server will now be used for both new certificate
|
||||
lineages and renewals.
|
||||
* The Nginx plugin is no longer marked labeled as an "Alpha" version.
|
||||
* The `prepare` method of Certbot's plugins is no longer called before running
|
||||
"Updater" enhancements that are run on every invocation of `certbot renew`.
|
||||
|
||||
Despite us having broken lockstep, we are continuing to release new versions of
|
||||
all Certbot components during releases for the time being, however, the only
|
||||
packages with functional changes were:
|
||||
|
||||
* acme
|
||||
* certbot
|
||||
* certbot-apache
|
||||
* certbot-dns-gehirn
|
||||
* certbot-dns-linode
|
||||
* certbot-dns-ovh
|
||||
* certbot-dns-sakuracloud
|
||||
* certbot-nginx
|
||||
|
||||
More details about these changes can be found on our GitHub repo:
|
||||
https://github.com/certbot/certbot/milestone/55?closed=1
|
||||
|
||||
## 0.25.1 - 2018-06-13
|
||||
|
||||
### Fixed
|
||||
@@ -796,7 +735,7 @@ https://github.com/certbot/certbot/pulls?q=is%3Apr%20milestone%3A0.11.1%20is%3Ac
|
||||
|
||||
### Added
|
||||
|
||||
* When using the standalone plugin while running Certbot interactively
|
||||
* When using the standalone plugin while running Certbot interactively
|
||||
and a required port is bound by another process, Certbot will give you
|
||||
the option to retry to grab the port rather than immediately exiting.
|
||||
* You are now able to deactivate your account with the Let's Encrypt
|
||||
|
||||
@@ -107,8 +107,8 @@ ACME working area in github: https://github.com/ietf-wg-acme/acme
|
||||
:target: https://travis-ci.org/certbot/certbot
|
||||
:alt: Travis CI status
|
||||
|
||||
.. |coverage| image:: https://codecov.io/gh/certbot/certbot/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/certbot/certbot
|
||||
.. |coverage| image:: https://coveralls.io/repos/certbot/certbot/badge.svg?branch=master
|
||||
:target: https://coveralls.io/r/certbot/certbot
|
||||
:alt: Coverage status
|
||||
|
||||
.. |docs| image:: https://readthedocs.org/projects/letsencrypt/badge/
|
||||
|
||||
@@ -50,6 +50,7 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
||||
:ivar .ClientNetwork net: Client network.
|
||||
:ivar int acme_version: ACME protocol version. 1 or 2.
|
||||
"""
|
||||
|
||||
def __init__(self, directory, net, acme_version):
|
||||
"""Initialize.
|
||||
|
||||
@@ -577,57 +578,16 @@ class ClientV2(ClientBase):
|
||||
|
||||
:param .NewRegistration new_account:
|
||||
|
||||
:raises .ConflictError: in case the account already exists
|
||||
|
||||
:returns: Registration Resource.
|
||||
:rtype: `.RegistrationResource`
|
||||
"""
|
||||
response = self._post(self.directory['newAccount'], new_account)
|
||||
# if account already exists
|
||||
if response.status_code == 200 and 'Location' in response.headers:
|
||||
raise errors.ConflictError(response.headers.get('Location'))
|
||||
# "Instance of 'Field' has no key/contact member" bug:
|
||||
# pylint: disable=no-member
|
||||
regr = self._regr_from_response(response)
|
||||
self.net.account = regr
|
||||
return regr
|
||||
|
||||
def query_registration(self, regr):
|
||||
"""Query server about registration.
|
||||
|
||||
:param messages.RegistrationResource: Existing Registration
|
||||
Resource.
|
||||
|
||||
"""
|
||||
self.net.account = regr
|
||||
updated_regr = super(ClientV2, self).query_registration(regr)
|
||||
self.net.account = updated_regr
|
||||
return updated_regr
|
||||
|
||||
def update_registration(self, regr, update=None):
|
||||
"""Update registration.
|
||||
|
||||
:param messages.RegistrationResource regr: Registration Resource.
|
||||
:param messages.Registration update: Updated body of the
|
||||
resource. If not provided, body will be taken from `regr`.
|
||||
|
||||
:returns: Updated Registration Resource.
|
||||
:rtype: `.RegistrationResource`
|
||||
|
||||
"""
|
||||
# https://github.com/certbot/certbot/issues/6155
|
||||
new_regr = self._get_v2_account(regr)
|
||||
return super(ClientV2, self).update_registration(new_regr, update)
|
||||
|
||||
def _get_v2_account(self, regr):
|
||||
self.net.account = None
|
||||
only_existing_reg = regr.body.update(only_return_existing=True)
|
||||
response = self._post(self.directory['newAccount'], only_existing_reg)
|
||||
updated_uri = response.headers['Location']
|
||||
new_regr = regr.update(uri=updated_uri)
|
||||
self.net.account = new_regr
|
||||
return new_regr
|
||||
|
||||
def new_order(self, csr_pem):
|
||||
"""Request a new Order object from the server.
|
||||
|
||||
@@ -950,7 +910,6 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
if acme_version == 2:
|
||||
kwargs["url"] = url
|
||||
# newAccount and revokeCert work without the kid
|
||||
# newAccount must not have kid
|
||||
if self.account is not None:
|
||||
kwargs["kid"] = self.account["uri"]
|
||||
kwargs["key"] = self.key
|
||||
|
||||
@@ -134,18 +134,12 @@ class BackwardsCompatibleClientV2Test(ClientTestBase):
|
||||
client = self._init()
|
||||
self.assertEqual(client.acme_version, 2)
|
||||
|
||||
def test_query_registration_client_v2(self):
|
||||
self.response.json.return_value = DIRECTORY_V2.to_json()
|
||||
client = self._init()
|
||||
self.response.json.return_value = self.regr.body.to_json()
|
||||
self.assertEqual(self.regr, client.query_registration(self.regr))
|
||||
|
||||
def test_forwarding(self):
|
||||
self.response.json.return_value = DIRECTORY_V1.to_json()
|
||||
client = self._init()
|
||||
self.assertEqual(client.directory, client.client.directory)
|
||||
self.assertEqual(client.key, KEY)
|
||||
self.assertEqual(client.deactivate_registration, client.client.deactivate_registration)
|
||||
self.assertEqual(client.update_registration, client.client.update_registration)
|
||||
self.assertRaises(AttributeError, client.__getattr__, 'nonexistent')
|
||||
self.assertRaises(AttributeError, client.__getattr__, 'new_account_and_tos')
|
||||
self.assertRaises(AttributeError, client.__getattr__, 'new_account')
|
||||
@@ -276,13 +270,6 @@ class BackwardsCompatibleClientV2Test(ClientTestBase):
|
||||
client.revoke(messages_test.CERT, self.rsn)
|
||||
mock_client().revoke.assert_called_once_with(messages_test.CERT, self.rsn)
|
||||
|
||||
def test_update_registration(self):
|
||||
self.response.json.return_value = DIRECTORY_V1.to_json()
|
||||
with mock.patch('acme.client.Client') as mock_client:
|
||||
client = self._init()
|
||||
client.update_registration(mock.sentinel.regr, None)
|
||||
mock_client().update_registration.assert_called_once_with(mock.sentinel.regr, None)
|
||||
|
||||
|
||||
class ClientTest(ClientTestBase):
|
||||
"""Tests for acme.client.Client."""
|
||||
@@ -712,11 +699,6 @@ class ClientV2Test(ClientTestBase):
|
||||
|
||||
self.assertEqual(self.regr, self.client.new_account(self.new_reg))
|
||||
|
||||
def test_new_account_conflict(self):
|
||||
self.response.status_code = http_client.OK
|
||||
self.response.headers['Location'] = self.regr.uri
|
||||
self.assertRaises(errors.ConflictError, self.client.new_account, self.new_reg)
|
||||
|
||||
def test_new_order(self):
|
||||
order_response = copy.deepcopy(self.response)
|
||||
order_response.status_code = http_client.CREATED
|
||||
@@ -807,19 +789,6 @@ class ClientV2Test(ClientTestBase):
|
||||
self.net.post.assert_called_once_with(
|
||||
self.directory["revokeCert"], mock.ANY, acme_version=2)
|
||||
|
||||
def test_update_registration(self):
|
||||
# "Instance of 'Field' has no to_json/update member" bug:
|
||||
# pylint: disable=no-member
|
||||
self.response.headers['Location'] = self.regr.uri
|
||||
self.response.json.return_value = self.regr.body.to_json()
|
||||
self.assertEqual(self.regr, self.client.update_registration(self.regr))
|
||||
self.assertNotEqual(self.client.net.account, None)
|
||||
self.assertEqual(self.client.net.post.call_count, 2)
|
||||
self.assertTrue(DIRECTORY_V2.newAccount in self.net.post.call_args_list[0][0])
|
||||
|
||||
self.response.json.return_value = self.regr.body.update(
|
||||
contact=()).to_json()
|
||||
|
||||
|
||||
class MockJSONDeSerializable(jose.JSONDeSerializable):
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
@@ -110,8 +110,6 @@ class ConflictError(ClientError):
|
||||
|
||||
In the version of ACME implemented by Boulder, this is used to find an
|
||||
account if you only have the private key, but don't know the account URL.
|
||||
|
||||
Also used in V2 of the ACME client for the same purpose.
|
||||
"""
|
||||
def __init__(self, location):
|
||||
self.location = location
|
||||
|
||||
@@ -274,7 +274,6 @@ class Registration(ResourceBody):
|
||||
agreement = jose.Field('agreement', omitempty=True)
|
||||
status = jose.Field('status', omitempty=True)
|
||||
terms_of_service_agreed = jose.Field('termsOfServiceAgreed', omitempty=True)
|
||||
only_return_existing = jose.Field('onlyReturnExisting', omitempty=True)
|
||||
|
||||
phone_prefix = 'tel:'
|
||||
email_prefix = 'mailto:'
|
||||
|
||||
@@ -3,7 +3,7 @@ from setuptools import find_packages
|
||||
from setuptools.command.test import test as TestCommand
|
||||
import sys
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
@@ -58,7 +58,7 @@ setup(
|
||||
license='Apache License 2.0',
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Programming Language :: Python',
|
||||
@@ -68,7 +68,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
""" Utility functions for certbot-apache plugin """
|
||||
import binascii
|
||||
import os
|
||||
import six
|
||||
import struct
|
||||
import time
|
||||
|
||||
from certbot import crypto_util
|
||||
from certbot import util
|
||||
|
||||
def get_mod_deps(mod_name):
|
||||
@@ -108,47 +104,3 @@ def parse_define_file(filepath, varname):
|
||||
def unique_id():
|
||||
""" Returns an unique id to be used as a VirtualHost identifier"""
|
||||
return binascii.hexlify(os.urandom(16)).decode("utf-8")
|
||||
|
||||
|
||||
def get_apache_ocsp_struct(ttl, ocsp_response):
|
||||
"""Create Apache OCSP response structure to be used in response cache
|
||||
|
||||
:param int ttl: Time-To-Live in seocnds
|
||||
:param str ocsp_response: OCSP response data
|
||||
|
||||
:returns: Apache OCSP structure
|
||||
:rtype: `str`
|
||||
|
||||
"""
|
||||
|
||||
ttl = time.time() + ttl
|
||||
# As microseconds
|
||||
ttl_struct = struct.pack('l', int(ttl*1000000))
|
||||
return b'\x01'.join([ttl_struct, ocsp_response])
|
||||
|
||||
|
||||
def certid_sha1_hex(cert_path):
|
||||
"""Hex representation of certificate SHA1 fingerprint
|
||||
|
||||
:param str cert_path: File path to certificate
|
||||
|
||||
:returns: Hex representation SHA1 fingerprint of certificate
|
||||
:rtype: `str`
|
||||
|
||||
"""
|
||||
sha1_hex = binascii.hexlify(certid_sha1(cert_path))
|
||||
if isinstance(sha1_hex, six.binary_type):
|
||||
return sha1_hex.decode('utf-8') # pragma: no cover
|
||||
return sha1_hex # pragma: no cover
|
||||
|
||||
|
||||
def certid_sha1(cert_path):
|
||||
"""SHA1 fingerprint of certificate
|
||||
|
||||
:param str cert_path: File path to certificate
|
||||
|
||||
:returns: SHA1 fingerprint bytestring
|
||||
:rtype: `str`
|
||||
|
||||
"""
|
||||
return crypto_util.cert_sha1_fingerprint(cert_path)
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"""Apache Configuration based off of Augeas Configurator."""
|
||||
# pylint: disable=too-many-lines
|
||||
import dbm
|
||||
import copy
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import pkg_resources
|
||||
import re
|
||||
import shutil
|
||||
import six
|
||||
import socket
|
||||
import time
|
||||
@@ -20,13 +17,12 @@ from acme.magic_typing import Any, DefaultDict, Dict, List, Set, Union # pylint
|
||||
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import ocsp
|
||||
from certbot import util
|
||||
|
||||
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge # pylint: disable=unused-import
|
||||
from certbot.plugins import common
|
||||
from certbot.plugins.util import path_surgery
|
||||
from certbot.plugins.enhancements import AutoHSTSEnhancement, OCSPPrefetchEnhancement
|
||||
from certbot.plugins.enhancements import AutoHSTSEnhancement
|
||||
|
||||
from certbot_apache import apache_util
|
||||
from certbot_apache import augeas_configurator
|
||||
@@ -39,12 +35,6 @@ from certbot_apache import tls_sni_01
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
try:
|
||||
import dbm.ndbm as dbm # pragma: no cover
|
||||
except ImportError: # pragma: no cover
|
||||
# dbm.ndbm only available on Python3
|
||||
import dbm # pragma: no cover
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -107,72 +97,48 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
vhost_root="/etc/apache2/sites-available",
|
||||
vhost_files="*",
|
||||
logs_root="/var/log/apache2",
|
||||
ctl="apache2ctl",
|
||||
version_cmd=['apache2ctl', '-v'],
|
||||
apache_cmd="apache2ctl",
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_modules=False,
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"certbot_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
|
||||
def option(self, key):
|
||||
"""Get a value from options"""
|
||||
return self.options.get(key)
|
||||
|
||||
def _prepare_options(self):
|
||||
"""
|
||||
Set the values possibly changed by command line parameters to
|
||||
OS_DEFAULTS constant dictionary
|
||||
"""
|
||||
opts = ["enmod", "dismod", "le_vhost_ext", "server_root", "vhost_root",
|
||||
"logs_root", "challenge_location", "handle_modules", "handle_sites",
|
||||
"ctl"]
|
||||
for o in opts:
|
||||
# Config options use dashes instead of underscores
|
||||
if self.conf(o.replace("_", "-")) is not None:
|
||||
self.options[o] = self.conf(o.replace("_", "-"))
|
||||
else:
|
||||
self.options[o] = self.OS_DEFAULTS[o]
|
||||
|
||||
# Special cases
|
||||
self.options["version_cmd"][0] = self.option("ctl")
|
||||
self.options["restart_cmd"][0] = self.option("ctl")
|
||||
self.options["conftest_cmd"][0] = self.option("ctl")
|
||||
def constant(self, key):
|
||||
"""Get constant for OS_DEFAULTS"""
|
||||
return self.OS_DEFAULTS.get(key)
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, add):
|
||||
# When adding, modifying or deleting command line arguments, be sure to
|
||||
# include the changes in the list used in method _prepare_options() to
|
||||
# ensure consistent behavior.
|
||||
add("enmod", default=cls.OS_DEFAULTS["enmod"],
|
||||
help="Path to the Apache 'a2enmod' binary")
|
||||
help="Path to the Apache 'a2enmod' binary.")
|
||||
add("dismod", default=cls.OS_DEFAULTS["dismod"],
|
||||
help="Path to the Apache 'a2dismod' binary")
|
||||
help="Path to the Apache 'a2dismod' binary.")
|
||||
add("le-vhost-ext", default=cls.OS_DEFAULTS["le_vhost_ext"],
|
||||
help="SSL vhost configuration extension")
|
||||
help="SSL vhost configuration extension.")
|
||||
add("server-root", default=cls.OS_DEFAULTS["server_root"],
|
||||
help="Apache server root directory")
|
||||
help="Apache server root directory.")
|
||||
add("vhost-root", default=None,
|
||||
help="Apache server VirtualHost configuration root")
|
||||
add("logs-root", default=cls.OS_DEFAULTS["logs_root"],
|
||||
help="Apache server logs directory")
|
||||
add("challenge-location",
|
||||
default=cls.OS_DEFAULTS["challenge_location"],
|
||||
help="Directory path for challenge configuration")
|
||||
add("handle-modules", default=cls.OS_DEFAULTS["handle_modules"],
|
||||
help="Let installer handle enabling required modules for you " +
|
||||
help="Directory path for challenge configuration.")
|
||||
add("handle-modules", default=cls.OS_DEFAULTS["handle_mods"],
|
||||
help="Let installer handle enabling required modules for you. " +
|
||||
"(Only Ubuntu/Debian currently)")
|
||||
add("handle-sites", default=cls.OS_DEFAULTS["handle_sites"],
|
||||
help="Let installer handle enabling sites for you " +
|
||||
help="Let installer handle enabling sites for you. " +
|
||||
"(Only Ubuntu/Debian currently)")
|
||||
add("ctl", default=cls.OS_DEFAULTS["ctl"],
|
||||
help="Full path to Apache control script")
|
||||
util.add_deprecated_argument(add, argument_name="ctl", nargs=1)
|
||||
util.add_deprecated_argument(
|
||||
add, argument_name="init-script", nargs=1)
|
||||
|
||||
@@ -197,15 +163,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
self._enhanced_vhosts = defaultdict(set) # type: DefaultDict[str, Set[obj.VirtualHost]]
|
||||
# Temporary state for AutoHSTS enhancement
|
||||
self._autohsts = {} # type: Dict[str, Dict[str, Union[int, float]]]
|
||||
self._ocsp_prefetch = {} # type: Dict[str, str]
|
||||
self._ocsp_dbm_bsddb = False
|
||||
|
||||
# These will be set in the prepare function
|
||||
self._prepared = False
|
||||
self.parser = None
|
||||
self.version = version
|
||||
self.vhosts = None
|
||||
self.options = copy.deepcopy(self.OS_DEFAULTS)
|
||||
self.vhostroot = None
|
||||
self._enhance_func = {"redirect": self._enable_redirect,
|
||||
"ensure-http-header": self._set_http_header,
|
||||
"staple-ocsp": self._enable_ocsp_stapling}
|
||||
@@ -237,10 +200,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
except ImportError:
|
||||
raise errors.NoInstallationError("Problem in Augeas installation")
|
||||
|
||||
self._prepare_options()
|
||||
|
||||
# Verify Apache is installed
|
||||
self._verify_exe_availability(self.option("ctl"))
|
||||
restart_cmd = self.constant("restart_cmd")[0]
|
||||
if not util.exe_exists(restart_cmd):
|
||||
if not path_surgery(restart_cmd):
|
||||
raise errors.NoInstallationError(
|
||||
'Cannot find Apache control command {0}'.format(restart_cmd))
|
||||
|
||||
# Make sure configuration is valid
|
||||
self.config_test()
|
||||
@@ -260,6 +225,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"version 1.2.0 or higher, please make sure you have you have "
|
||||
"those installed.")
|
||||
|
||||
# Parse vhost-root if defined on cli
|
||||
if not self.conf("vhost-root"):
|
||||
self.vhostroot = self.constant("vhost_root")
|
||||
else:
|
||||
self.vhostroot = os.path.abspath(self.conf("vhost-root"))
|
||||
|
||||
self.parser = self.get_parser()
|
||||
|
||||
# Check for errors in parsing files with Augeas
|
||||
@@ -273,19 +244,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
# Prevent two Apache plugins from modifying a config at once
|
||||
try:
|
||||
util.lock_dir_until_exit(self.option("server_root"))
|
||||
util.lock_dir_until_exit(self.conf("server-root"))
|
||||
except (OSError, errors.LockError):
|
||||
logger.debug("Encountered error:", exc_info=True)
|
||||
raise errors.PluginError(
|
||||
"Unable to lock %s", self.option("server_root"))
|
||||
self._prepared = True
|
||||
|
||||
def _verify_exe_availability(self, exe):
|
||||
"""Checks availability of Apache executable"""
|
||||
if not util.exe_exists(exe):
|
||||
if not path_surgery(exe):
|
||||
raise errors.NoInstallationError(
|
||||
'Cannot find Apache executable {0}'.format(exe))
|
||||
"Unable to lock %s", self.conf("server-root"))
|
||||
|
||||
def _check_aug_version(self):
|
||||
""" Checks that we have recent enough version of libaugeas.
|
||||
@@ -304,9 +267,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
def get_parser(self):
|
||||
"""Initializes the ApacheParser"""
|
||||
# If user provided vhost_root value in command line, use it
|
||||
return parser.ApacheParser(
|
||||
self.aug, self.option("server_root"), self.conf("vhost-root"),
|
||||
self.aug, self.conf("server-root"), self.conf("vhost-root"),
|
||||
self.version, configurator=self)
|
||||
|
||||
def _wildcard_domain(self, domain):
|
||||
@@ -1073,7 +1035,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:param boolean temp: If the change is temporary
|
||||
"""
|
||||
|
||||
if self.option("handle_modules"):
|
||||
if self.conf("handle-modules"):
|
||||
if self.version >= (2, 4) and ("socache_shmcb_module" not in
|
||||
self.parser.modules):
|
||||
self.enable_mod("socache_shmcb", temp=temp)
|
||||
@@ -1102,7 +1064,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
Duplicates vhost and adds default ssl options
|
||||
New vhost will reside as (nonssl_vhost.path) +
|
||||
``self.option("le_vhost_ext")``
|
||||
``self.constant("le_vhost_ext")``
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
@@ -1201,16 +1163,18 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"""
|
||||
|
||||
if self.conf("vhost-root") and os.path.exists(self.conf("vhost-root")):
|
||||
fp = os.path.join(os.path.realpath(self.option("vhost_root")),
|
||||
# Defined by user on CLI
|
||||
|
||||
fp = os.path.join(os.path.realpath(self.vhostroot),
|
||||
os.path.basename(non_ssl_vh_fp))
|
||||
else:
|
||||
# Use non-ssl filepath
|
||||
fp = os.path.realpath(non_ssl_vh_fp)
|
||||
|
||||
if fp.endswith(".conf"):
|
||||
return fp[:-(len(".conf"))] + self.option("le_vhost_ext")
|
||||
return fp[:-(len(".conf"))] + self.conf("le_vhost_ext")
|
||||
else:
|
||||
return fp + self.option("le_vhost_ext")
|
||||
return fp + self.conf("le_vhost_ext")
|
||||
|
||||
def _sift_rewrite_rule(self, line):
|
||||
"""Decides whether a line should be copied to a SSL vhost.
|
||||
@@ -1703,7 +1667,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
self.parser.find_dir("SSLCertificateKeyFile",
|
||||
lineage.key_path, vhost.path))
|
||||
|
||||
def _enable_ocsp_stapling(self, ssl_vhost, unused_options, prefetch=False):
|
||||
def _enable_ocsp_stapling(self, ssl_vhost, unused_options):
|
||||
"""Enables OCSP Stapling
|
||||
|
||||
In OCSP, each client (e.g. browser) would have to query the
|
||||
@@ -1724,8 +1688,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:param unused_options: Not currently used
|
||||
:type unused_options: Not Available
|
||||
|
||||
:param prefetch: Use OCSP prefetching
|
||||
:type prefetch: bool
|
||||
:returns: Success, general_vhost (HTTP vhost)
|
||||
:rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`)
|
||||
|
||||
"""
|
||||
min_apache_ver = (2, 3, 3)
|
||||
@@ -1734,15 +1698,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"Unable to set OCSP directives.\n"
|
||||
"Apache version is below 2.3.3.")
|
||||
|
||||
if prefetch:
|
||||
if "socache_dbm_module" not in self.parser.modules:
|
||||
self.enable_mod("socache_dbm")
|
||||
cache_path = os.path.join(self.config.config_dir, "ocsp", "ocsp_cache.db")
|
||||
cache_dir = ["dbm:"+cache_path]
|
||||
else:
|
||||
if "socache_shmcb_module" not in self.parser.modules:
|
||||
self.enable_mod("socache_shmcb")
|
||||
cache_dir = ["shmcb:/var/run/apache2/stapling_cache(128000)"]
|
||||
if "socache_shmcb_module" not in self.parser.modules:
|
||||
self.enable_mod("socache_shmcb")
|
||||
|
||||
# Check if there's an existing SSLUseStapling directive on.
|
||||
use_stapling_aug_path = self.parser.find_dir("SSLUseStapling",
|
||||
@@ -1763,7 +1720,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
re.sub(r"/\w*$", "", stapling_cache_aug_path[0]))
|
||||
|
||||
self.parser.add_dir_to_ifmodssl(ssl_vhost_aug_path,
|
||||
"SSLStaplingCache", cache_dir)
|
||||
"SSLStaplingCache",
|
||||
["shmcb:/var/run/apache2/stapling_cache(128000)"])
|
||||
|
||||
msg = "OCSP Stapling was enabled on SSL Vhost: %s.\n"%(
|
||||
ssl_vhost.filep)
|
||||
@@ -2065,7 +2023,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
addr in self._get_proposed_addrs(ssl_vhost)),
|
||||
servername, serveralias,
|
||||
" ".join(rewrite_rule_args),
|
||||
self.option("logs_root")))
|
||||
self.conf("logs-root")))
|
||||
|
||||
def _write_out_redirect(self, ssl_vhost, text):
|
||||
# This is the default name
|
||||
@@ -2077,7 +2035,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if len(ssl_vhost.name) < (255 - (len(redirect_filename) + 1)):
|
||||
redirect_filename = "le-redirect-%s.conf" % ssl_vhost.name
|
||||
|
||||
redirect_filepath = os.path.join(self.option("vhost_root"),
|
||||
redirect_filepath = os.path.join(self.vhostroot,
|
||||
redirect_filename)
|
||||
|
||||
# Register the new file that will be created
|
||||
@@ -2188,17 +2146,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
self.config_test()
|
||||
|
||||
if not self._ocsp_prefetch:
|
||||
# Try to populate OCSP prefetch structure from pluginstorage
|
||||
self._ocsp_prefetch_fetch_state()
|
||||
if self._ocsp_prefetch:
|
||||
# OCSP prefetching is enabled, so back up the db
|
||||
self._ocsp_prefetch_backup_db()
|
||||
self._reload()
|
||||
if self._ocsp_prefetch:
|
||||
# Restore the backed up dbm database
|
||||
self._ocsp_prefetch_restore_db()
|
||||
|
||||
def _reload(self):
|
||||
"""Reloads the Apache server.
|
||||
@@ -2208,18 +2156,18 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"""
|
||||
error = ""
|
||||
try:
|
||||
util.run_script(self.option("restart_cmd"))
|
||||
util.run_script(self.constant("restart_cmd"))
|
||||
except errors.SubprocessError as err:
|
||||
logger.info("Unable to restart apache using %s",
|
||||
self.option("restart_cmd"))
|
||||
alt_restart = self.option("restart_cmd_alt")
|
||||
self.constant("restart_cmd"))
|
||||
alt_restart = self.constant("restart_cmd_alt")
|
||||
if alt_restart:
|
||||
logger.debug("Trying alternative restart command: %s",
|
||||
alt_restart)
|
||||
# There is an alternative restart command available
|
||||
# This usually is "restart" verb while original is "graceful"
|
||||
try:
|
||||
util.run_script(self.option(
|
||||
util.run_script(self.constant(
|
||||
"restart_cmd_alt"))
|
||||
return
|
||||
except errors.SubprocessError as secerr:
|
||||
@@ -2235,7 +2183,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
try:
|
||||
util.run_script(self.option("conftest_cmd"))
|
||||
util.run_script(self.constant("conftest_cmd"))
|
||||
except errors.SubprocessError as err:
|
||||
raise errors.MisconfigurationError(str(err))
|
||||
|
||||
@@ -2251,11 +2199,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
try:
|
||||
stdout, _ = util.run_script(self.option("version_cmd"))
|
||||
stdout, _ = util.run_script(self.constant("version_cmd"))
|
||||
except errors.SubprocessError:
|
||||
raise errors.PluginError(
|
||||
"Unable to run %s -v" %
|
||||
self.option("version_cmd"))
|
||||
self.constant("version_cmd"))
|
||||
|
||||
regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE)
|
||||
matches = regex.findall(stdout)
|
||||
@@ -2345,7 +2293,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# certbot for unprivileged users via setuid), this function will need
|
||||
# to be modified.
|
||||
return common.install_version_controlled_file(options_ssl, options_ssl_digest,
|
||||
self.option("MOD_SSL_CONF_SRC"), constants.ALL_SSL_OPTIONS_HASHES)
|
||||
self.constant("MOD_SSL_CONF_SRC"), constants.ALL_SSL_OPTIONS_HASHES)
|
||||
|
||||
def enable_autohsts(self, _unused_lineage, domains):
|
||||
"""
|
||||
@@ -2446,9 +2394,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
continue
|
||||
nextstep = config["laststep"] + 1
|
||||
if nextstep < len(constants.AUTOHSTS_STEPS):
|
||||
# If installer hasn't been prepared yet, do it now
|
||||
if not self._prepared:
|
||||
self.prepare()
|
||||
# Have not reached the max value yet
|
||||
try:
|
||||
vhost = self.find_vhost_by_id(id_str)
|
||||
@@ -2521,201 +2466,5 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# Update AutoHSTS storage (We potentially removed vhosts from managed)
|
||||
self._autohsts_save_state()
|
||||
|
||||
def _ensure_ocsp_dirs(self):
|
||||
"""Makes sure that the OCSP directory paths exist."""
|
||||
ocsp_work = os.path.join(self.config.work_dir, "ocsp")
|
||||
ocsp_save = os.path.join(self.config.config_dir, "ocsp")
|
||||
for path in [ocsp_work, ocsp_save]:
|
||||
if not os.path.isdir(path):
|
||||
os.makedirs(path)
|
||||
os.chmod(path, 0o755)
|
||||
|
||||
def _ocsp_refresh_if_needed(self, pf_obj):
|
||||
"""Refreshes OCSP response for a certiifcate if it's due
|
||||
|
||||
:param dict pf_obj: OCSP prefetch object from pluginstorage
|
||||
|
||||
:returns: If OCSP response was updated
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
ttl = pf_obj["lastupdate"] + constants.OCSP_INTERNAL_TTL
|
||||
if ttl < time.time():
|
||||
self._ocsp_refresh(pf_obj["cert_path"], pf_obj["chain_path"])
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _ocsp_refresh(self, cert_path, chain_path):
|
||||
"""Refresh the OCSP response for a certificate
|
||||
|
||||
:param str cert_path: Filesystem path to certificate file
|
||||
:param str chain_path: Filesystem path to certificate chain file
|
||||
|
||||
"""
|
||||
|
||||
self._ensure_ocsp_dirs()
|
||||
handler = ocsp.OCSPResponseHandler(cert_path, chain_path)
|
||||
ocsp_workfile = os.path.join(
|
||||
self.config.work_dir, "ocsp",
|
||||
apache_util.certid_sha1_hex(cert_path))
|
||||
if handler.ocsp_request_to_file(ocsp_workfile):
|
||||
# Guaranteed good response
|
||||
cache_path = os.path.join(self.config.config_dir, "ocsp", "ocsp_cache")
|
||||
db = self._ocsp_dbm_open(cache_path)
|
||||
cert_sha = apache_util.certid_sha1(cert_path)
|
||||
db[cert_sha] = self._ocsp_response_dbm(ocsp_workfile)
|
||||
self._ocsp_dbm_close(db)
|
||||
else:
|
||||
logger.warning("Encountered an issue while trying to prefetch OCSP "
|
||||
"response for certificate: %s", cert_path)
|
||||
|
||||
def _ensure_ocsp_prefetch_compatibility(self):
|
||||
"""Make sure that the operating system supports the required libraries
|
||||
to manage Apache DBM files.
|
||||
|
||||
:raises: errors.NotSupportedError
|
||||
"""
|
||||
try:
|
||||
import bsddb
|
||||
except ImportError:
|
||||
import dbm
|
||||
if not hasattr(dbm, 'ndbm'):
|
||||
msg = ("Unfortunately your operating system does not have a "
|
||||
"compatible database module available for managing "
|
||||
"Apache cache database.")
|
||||
raise errors.NotSupportedError(msg)
|
||||
|
||||
def _ocsp_dbm_open(self, filepath):
|
||||
"""Helper method to open an DBM file in a way that depends on the platform
|
||||
that Certbot is run on. Returns an open database structure."""
|
||||
try:
|
||||
import bsddb
|
||||
self._ocsp_dbm_bsddb = True
|
||||
cache_path = filepath + ".db"
|
||||
database = bsddb.hashopen(cache_path, 'c')
|
||||
except ImportError:
|
||||
# Python3 doesn't have bsddb module, so we use dbm.ndbm instead
|
||||
import dbm
|
||||
database = dbm.ndbm.open(filepath, 'c')
|
||||
return database
|
||||
|
||||
def _ocsp_dbm_close(self, database):
|
||||
"""Helper method to sync and close a DBM file, in a way required by the
|
||||
used dbm implementation."""
|
||||
if self._ocsp_dbm_bsddb:
|
||||
database.sync()
|
||||
database.close()
|
||||
else:
|
||||
database.close()
|
||||
|
||||
def _ocsp_response_dbm(self, workfile):
|
||||
"""Creates a dbm entry for OCSP response data
|
||||
|
||||
:param str workfile: File path for raw OCSP response
|
||||
|
||||
:returns: OCSP response cache data that Apache can use
|
||||
:rtype: string
|
||||
"""
|
||||
|
||||
with open(workfile, 'rb') as fh:
|
||||
response = fh.read()
|
||||
ttl = constants.OCSP_APACHE_TTL
|
||||
return apache_util.get_apache_ocsp_struct(ttl, response)
|
||||
|
||||
def _ocsp_prefetch_save(self, cert_path, chain_path):
|
||||
"""Saves status of current OCSP prefetch, including the last update
|
||||
time to determine if an update is needed on later run.
|
||||
|
||||
:param str cert_path: Filesystem path to certificate
|
||||
:param str chain_path: Filesystem path to certificate chain file
|
||||
|
||||
"""
|
||||
status = {
|
||||
"lastupdate": time.time(),
|
||||
"cert_path": cert_path,
|
||||
"chain_path": chain_path
|
||||
}
|
||||
cert_id = apache_util.certid_sha1_hex(cert_path)
|
||||
self._ocsp_prefetch[cert_id] = status
|
||||
self.storage.put("ocsp_prefetch", self._ocsp_prefetch)
|
||||
self.storage.save()
|
||||
|
||||
def _ocsp_prefetch_fetch_state(self):
|
||||
"""
|
||||
Populates the OCSP prefetch state from the pluginstorage.
|
||||
"""
|
||||
try:
|
||||
self._ocsp_prefetch = self.storage.fetch("ocsp_prefetch")
|
||||
except KeyError:
|
||||
self._ocsp_prefetch = dict()
|
||||
|
||||
def _ocsp_prefetch_backup_db(self):
|
||||
"""
|
||||
Copies the active dbm file to work directory.
|
||||
"""
|
||||
self._ensure_ocsp_dirs()
|
||||
cache_path = os.path.join(self.config.config_dir, "ocsp", "ocsp_cache.db")
|
||||
try:
|
||||
shutil.copy2(cache_path, os.path.join(self.config.work_dir, "ocsp"))
|
||||
except IOError:
|
||||
logger.debug("Encountered an issue while trying to backup OCSP dbm file")
|
||||
|
||||
def _ocsp_prefetch_restore_db(self):
|
||||
"""
|
||||
Restores the active dbm file from work directory.
|
||||
"""
|
||||
self._ensure_ocsp_dirs()
|
||||
cache_path = os.path.join(self.config.config_dir, "ocsp", "ocsp_cache.db")
|
||||
work_file_path = os.path.join(self.config.work_dir, "ocsp", "ocsp_cache.db")
|
||||
try:
|
||||
shutil.copy2(work_file_path, cache_path)
|
||||
except IOError:
|
||||
logger.debug("Encountered an issue when trying to restore OCSP dbm file")
|
||||
|
||||
def enable_ocsp_prefetch(self, lineage, domains):
|
||||
"""Enable OCSP Stapling and prefetching of the responses.
|
||||
|
||||
In OCSP, each client (e.g. browser) would have to query the
|
||||
OCSP Responder to validate that the site certificate was not revoked.
|
||||
|
||||
Enabling OCSP Stapling, would allow the web-server to query the OCSP
|
||||
Responder, and staple its response to the offered certificate during
|
||||
TLS. i.e. clients would not have to query the OCSP responder.
|
||||
|
||||
"""
|
||||
# Fail early if we are not able to support this
|
||||
self._ensure_ocsp_prefetch_compatibility()
|
||||
prefetch_vhosts = set()
|
||||
for domain in domains:
|
||||
matched_vhosts = self.choose_vhosts(domain, create_if_no_ssl=False)
|
||||
# We should be handling only SSL vhosts
|
||||
for vh in matched_vhosts:
|
||||
if vh.ssl:
|
||||
prefetch_vhosts.add(vh)
|
||||
|
||||
if prefetch_vhosts:
|
||||
for vh in prefetch_vhosts:
|
||||
self._enable_ocsp_stapling(vh, None, prefetch=True)
|
||||
self._ocsp_prefetch_save(lineage.cert_path, lineage.chain_path)
|
||||
self.restart()
|
||||
logger.warning(apache_util.certid_sha1_hex(lineage.cert_path))
|
||||
self._ocsp_refresh(lineage.cert_path, lineage.chain_path)
|
||||
|
||||
def update_ocsp_prefetch(self, _unused_lineage):
|
||||
"""Checks all certificates that are managed by OCSP prefetch, and
|
||||
refreshes OCSP responses for them if required."""
|
||||
|
||||
self._ocsp_prefetch_fetch_state()
|
||||
if not self._ocsp_prefetch:
|
||||
# No OCSP prefetching enabled for any certificate
|
||||
return
|
||||
|
||||
for _, pf in self._ocsp_prefetch.items():
|
||||
if self._ocsp_refresh_if_needed(pf):
|
||||
# Save the status to pluginstorage
|
||||
self._ocsp_prefetch_save(pf["cert_path"], pf["chain_path"])
|
||||
|
||||
|
||||
AutoHSTSEnhancement.register(ApacheConfigurator) # pylint: disable=no-member
|
||||
OCSPPrefetchEnhancement.register(ApacheConfigurator) # pylint: disable=no-member
|
||||
|
||||
@@ -61,9 +61,3 @@ AUTOHSTS_FREQ = 172800
|
||||
MANAGED_COMMENT = "DO NOT REMOVE - Managed by Certbot"
|
||||
MANAGED_COMMENT_ID = MANAGED_COMMENT+", VirtualHost id: {0}"
|
||||
"""Managed by Certbot comments and the VirtualHost identification template"""
|
||||
|
||||
OCSP_APACHE_TTL = 432000
|
||||
"""Apache TTL for OCSP response: 5 days"""
|
||||
|
||||
OCSP_INTERNAL_TTL = 86400
|
||||
"""Internal TTL for OCSP response: 1 day"""
|
||||
|
||||
@@ -113,7 +113,8 @@ def _vhost_menu(domain, vhosts):
|
||||
code, tag = zope.component.getUtility(interfaces.IDisplay).menu(
|
||||
"We were unable to find a vhost with a ServerName "
|
||||
"or Address of {0}.{1}Which virtual host would you "
|
||||
"like to choose?".format(domain, os.linesep),
|
||||
"like to choose?\n(note: conf files with multiple "
|
||||
"vhosts are not yet supported)".format(domain, os.linesep),
|
||||
choices, force_interactive=True)
|
||||
except errors.MissingCommandlineFlag:
|
||||
msg = (
|
||||
|
||||
@@ -6,7 +6,6 @@ from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-m
|
||||
from certbot import errors
|
||||
from certbot.plugins import common
|
||||
from certbot_apache.obj import VirtualHost # pylint: disable=unused-import
|
||||
from certbot_apache.parser import get_aug_path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -173,9 +172,4 @@ class ApacheHttp01(common.TLSSNI01):
|
||||
self.configurator.parser.add_dir(
|
||||
vhost.path, "Include", self.challenge_conf_post)
|
||||
|
||||
if not vhost.enabled:
|
||||
self.configurator.parser.add_dir(
|
||||
get_aug_path(self.configurator.parser.loc["default"]),
|
||||
"Include", vhost.filep)
|
||||
|
||||
self.moded_vhosts.add(vhost)
|
||||
|
||||
@@ -16,14 +16,14 @@ class ArchConfigurator(configurator.ApacheConfigurator):
|
||||
vhost_root="/etc/httpd/conf",
|
||||
vhost_files="*.conf",
|
||||
logs_root="/var/log/httpd",
|
||||
ctl="apachectl",
|
||||
version_cmd=['apachectl', '-v'],
|
||||
apache_cmd="apachectl",
|
||||
restart_cmd=['apachectl', 'graceful'],
|
||||
conftest_cmd=['apachectl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_modules=False,
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/httpd/conf",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
|
||||
@@ -18,33 +18,25 @@ class CentOSConfigurator(configurator.ApacheConfigurator):
|
||||
vhost_root="/etc/httpd/conf.d",
|
||||
vhost_files="*.conf",
|
||||
logs_root="/var/log/httpd",
|
||||
ctl="apachectl",
|
||||
version_cmd=['apachectl', '-v'],
|
||||
apache_cmd="apachectl",
|
||||
restart_cmd=['apachectl', 'graceful'],
|
||||
restart_cmd_alt=['apachectl', 'restart'],
|
||||
conftest_cmd=['apachectl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_modules=False,
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/httpd/conf.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"certbot_apache", "centos-options-ssl-apache.conf")
|
||||
)
|
||||
|
||||
def _prepare_options(self):
|
||||
"""
|
||||
Override the options dictionary initialization in order to support
|
||||
alternative restart cmd used in CentOS.
|
||||
"""
|
||||
super(CentOSConfigurator, self)._prepare_options()
|
||||
self.options["restart_cmd_alt"][0] = self.option("ctl")
|
||||
|
||||
def get_parser(self):
|
||||
"""Initializes the ApacheParser"""
|
||||
return CentOSParser(
|
||||
self.aug, self.option("server_root"), self.option("vhost_root"),
|
||||
self.aug, self.conf("server-root"), self.conf("vhost-root"),
|
||||
self.version, configurator=self)
|
||||
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@ class DarwinConfigurator(configurator.ApacheConfigurator):
|
||||
vhost_root="/etc/apache2/other",
|
||||
vhost_files="*.conf",
|
||||
logs_root="/var/log/apache2",
|
||||
ctl="apachectl",
|
||||
version_cmd=['apachectl', '-v'],
|
||||
version_cmd=['/usr/sbin/httpd', '-v'],
|
||||
apache_cmd="/usr/sbin/httpd",
|
||||
restart_cmd=['apachectl', 'graceful'],
|
||||
conftest_cmd=['apachectl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_modules=False,
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/other",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
|
||||
@@ -23,14 +23,14 @@ class DebianConfigurator(configurator.ApacheConfigurator):
|
||||
vhost_root="/etc/apache2/sites-available",
|
||||
vhost_files="*",
|
||||
logs_root="/var/log/apache2",
|
||||
ctl="apache2ctl",
|
||||
version_cmd=['apache2ctl', '-v'],
|
||||
apache_cmd="apache2ctl",
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod="a2enmod",
|
||||
dismod="a2dismod",
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_modules=True,
|
||||
handle_mods=True,
|
||||
handle_sites=True,
|
||||
challenge_location="/etc/apache2",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
@@ -134,11 +134,11 @@ class DebianConfigurator(configurator.ApacheConfigurator):
|
||||
# Generate reversal command.
|
||||
# Try to be safe here... check that we can probably reverse before
|
||||
# applying enmod command
|
||||
if not util.exe_exists(self.option("dismod")):
|
||||
if not util.exe_exists(self.conf("dismod")):
|
||||
raise errors.MisconfigurationError(
|
||||
"Unable to find a2dismod, please make sure a2enmod and "
|
||||
"a2dismod are configured correctly for certbot.")
|
||||
|
||||
self.reverter.register_undo_command(
|
||||
temp, [self.option("dismod"), "-f", mod_name])
|
||||
util.run_script([self.option("enmod"), mod_name])
|
||||
temp, [self.conf("dismod"), "-f", mod_name])
|
||||
util.run_script([self.conf("enmod"), mod_name])
|
||||
|
||||
@@ -18,33 +18,25 @@ class GentooConfigurator(configurator.ApacheConfigurator):
|
||||
vhost_root="/etc/apache2/vhosts.d",
|
||||
vhost_files="*.conf",
|
||||
logs_root="/var/log/apache2",
|
||||
ctl="apache2ctl",
|
||||
version_cmd=['apache2ctl', '-v'],
|
||||
version_cmd=['/usr/sbin/apache2', '-v'],
|
||||
apache_cmd="apache2ctl",
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
restart_cmd_alt=['apache2ctl', 'restart'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_modules=False,
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/vhosts.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"certbot_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
|
||||
def _prepare_options(self):
|
||||
"""
|
||||
Override the options dictionary initialization in order to support
|
||||
alternative restart cmd used in Gentoo.
|
||||
"""
|
||||
super(GentooConfigurator, self)._prepare_options()
|
||||
self.options["restart_cmd_alt"][0] = self.option("ctl")
|
||||
|
||||
def get_parser(self):
|
||||
"""Initializes the ApacheParser"""
|
||||
return GentooParser(
|
||||
self.aug, self.option("server_root"), self.option("vhost_root"),
|
||||
self.aug, self.conf("server-root"), self.conf("vhost-root"),
|
||||
self.version, configurator=self)
|
||||
|
||||
|
||||
@@ -69,7 +61,7 @@ class GentooParser(parser.ApacheParser):
|
||||
|
||||
def update_modules(self):
|
||||
"""Get loaded modules from httpd process, and add them to DOM"""
|
||||
mod_cmd = [self.configurator.option("ctl"), "modules"]
|
||||
mod_cmd = [self.configurator.constant("apache_cmd"), "modules"]
|
||||
matches = self.parse_from_subprocess(mod_cmd, r"(.*)_module")
|
||||
for mod in matches:
|
||||
self.add_mod(mod.strip())
|
||||
|
||||
@@ -16,8 +16,8 @@ class OpenSUSEConfigurator(configurator.ApacheConfigurator):
|
||||
vhost_root="/etc/apache2/vhosts.d",
|
||||
vhost_files="*.conf",
|
||||
logs_root="/var/log/apache2",
|
||||
ctl="apache2ctl",
|
||||
version_cmd=['apache2ctl', '-v'],
|
||||
apache_cmd="apache2ctl",
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod="a2enmod",
|
||||
|
||||
@@ -69,7 +69,7 @@ class ApacheParser(object):
|
||||
# Must also attempt to parse additional virtual host root
|
||||
if vhostroot:
|
||||
self.parse_file(os.path.abspath(vhostroot) + "/" +
|
||||
self.configurator.option("vhost_files"))
|
||||
self.configurator.constant("vhost_files"))
|
||||
|
||||
# check to see if there were unparsed define statements
|
||||
if version < (2, 4):
|
||||
@@ -152,7 +152,7 @@ class ApacheParser(object):
|
||||
"""Get Defines from httpd process"""
|
||||
|
||||
variables = dict()
|
||||
define_cmd = [self.configurator.option("ctl"), "-t", "-D",
|
||||
define_cmd = [self.configurator.constant("apache_cmd"), "-t", "-D",
|
||||
"DUMP_RUN_CFG"]
|
||||
matches = self.parse_from_subprocess(define_cmd, r"Define: ([^ \n]*)")
|
||||
try:
|
||||
@@ -179,7 +179,7 @@ class ApacheParser(object):
|
||||
# configuration files
|
||||
_ = self.find_dir("Include")
|
||||
|
||||
inc_cmd = [self.configurator.option("ctl"), "-t", "-D",
|
||||
inc_cmd = [self.configurator.constant("apache_cmd"), "-t", "-D",
|
||||
"DUMP_INCLUDES"]
|
||||
matches = self.parse_from_subprocess(inc_cmd, r"\(.*\) (.*)")
|
||||
if matches:
|
||||
@@ -190,7 +190,7 @@ class ApacheParser(object):
|
||||
def update_modules(self):
|
||||
"""Get loaded modules from httpd process, and add them to DOM"""
|
||||
|
||||
mod_cmd = [self.configurator.option("ctl"), "-t", "-D",
|
||||
mod_cmd = [self.configurator.constant("apache_cmd"), "-t", "-D",
|
||||
"DUMP_MODULES"]
|
||||
matches = self.parse_from_subprocess(mod_cmd, r"(.*)_module")
|
||||
for mod in matches:
|
||||
|
||||
@@ -55,9 +55,7 @@ class AutoHSTSTest(util.ApacheTest):
|
||||
|
||||
@mock.patch("certbot_apache.constants.AUTOHSTS_FREQ", 0)
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.prepare")
|
||||
def test_autohsts_increase(self, mock_prepare, _mock_restart):
|
||||
self.config._prepared = False
|
||||
def test_autohsts_increase(self, _mock_restart):
|
||||
maxage = "\"max-age={0}\""
|
||||
initial_val = maxage.format(constants.AUTOHSTS_STEPS[0])
|
||||
inc_val = maxage.format(constants.AUTOHSTS_STEPS[1])
|
||||
@@ -71,7 +69,6 @@ class AutoHSTSTest(util.ApacheTest):
|
||||
# Verify increased value
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
inc_val)
|
||||
self.assertTrue(mock_prepare.called)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._autohsts_increase")
|
||||
@@ -119,9 +116,6 @@ class AutoHSTSTest(util.ApacheTest):
|
||||
cur_val = maxage.format(constants.AUTOHSTS_STEPS[i+1])
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
cur_val)
|
||||
# Ensure that the value is raised to max
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
maxage.format(constants.AUTOHSTS_STEPS[-1]))
|
||||
# Make permanent
|
||||
self.config.deploy_autohsts(mock_lineage)
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
|
||||
@@ -135,7 +135,5 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
|
||||
errors.SubprocessError,
|
||||
errors.SubprocessError]
|
||||
self.assertRaises(errors.MisconfigurationError, self.config.restart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -116,9 +116,8 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
ApacheConfigurator.add_parser_arguments(mock.MagicMock())
|
||||
|
||||
def test_constant(self):
|
||||
self.assertTrue("debian_apache_2_4/multiple_vhosts/apache" in
|
||||
self.config.option("server_root"))
|
||||
self.assertEqual(self.config.option("nonexistent"), None)
|
||||
self.assertEqual(self.config.constant("server_root"), "/etc/apache2")
|
||||
self.assertEqual(self.config.constant("nonexistent"), None)
|
||||
|
||||
@certbot_util.patch_get_utility()
|
||||
def test_get_all_names(self, mock_getutility):
|
||||
@@ -652,10 +651,22 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
self.assertEqual(ssl_vhost_slink.name, "nonsym.link")
|
||||
|
||||
def test_make_vhost_ssl_nonexistent_vhost_path(self):
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[1])
|
||||
self.assertEqual(os.path.dirname(ssl_vhost.filep),
|
||||
os.path.dirname(os.path.realpath(
|
||||
self.vh_truth[1].filep)))
|
||||
def conf_side_effect(arg):
|
||||
""" Mock function for ApacheConfigurator.conf """
|
||||
confvars = {
|
||||
"vhost-root": "/tmp/nonexistent",
|
||||
"le_vhost_ext": "-le-ssl.conf",
|
||||
"handle-sites": True}
|
||||
return confvars[arg]
|
||||
|
||||
with mock.patch(
|
||||
"certbot_apache.configurator.ApacheConfigurator.conf"
|
||||
) as mock_conf:
|
||||
mock_conf.side_effect = conf_side_effect
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[1])
|
||||
self.assertEqual(os.path.dirname(ssl_vhost.filep),
|
||||
os.path.dirname(os.path.realpath(
|
||||
self.vh_truth[1].filep)))
|
||||
|
||||
def test_make_vhost_ssl(self):
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
|
||||
@@ -1572,7 +1583,7 @@ class AugeasVhostsTest(util.ApacheTest):
|
||||
broken_vhost)
|
||||
|
||||
class MultiVhostsTest(util.ApacheTest):
|
||||
"""Test configuration with multiple virtualhosts in a single file."""
|
||||
"""Test vhosts with illegal names dependent on augeas version."""
|
||||
# pylint: disable=protected-access
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
@@ -1692,7 +1703,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
|
||||
self.config.updated_mod_ssl_conf_digest)
|
||||
|
||||
def _current_ssl_options_hash(self):
|
||||
return crypto_util.sha256sum(self.config.option("MOD_SSL_CONF_SRC"))
|
||||
return crypto_util.sha256sum(self.config.constant("MOD_SSL_CONF_SRC"))
|
||||
|
||||
def _assert_current_file(self):
|
||||
self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
|
||||
@@ -1728,7 +1739,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
|
||||
self.assertFalse(mock_logger.warning.called)
|
||||
self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
|
||||
self.assertEqual(crypto_util.sha256sum(
|
||||
self.config.option("MOD_SSL_CONF_SRC")),
|
||||
self.config.constant("MOD_SSL_CONF_SRC")),
|
||||
self._current_ssl_options_hash())
|
||||
self.assertNotEqual(crypto_util.sha256sum(self.config.mod_ssl_conf),
|
||||
self._current_ssl_options_hash())
|
||||
@@ -1744,7 +1755,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
|
||||
"%s has been manually modified; updated file "
|
||||
"saved to %s. We recommend updating %s for security purposes.")
|
||||
self.assertEqual(crypto_util.sha256sum(
|
||||
self.config.option("MOD_SSL_CONF_SRC")),
|
||||
self.config.constant("MOD_SSL_CONF_SRC")),
|
||||
self._current_ssl_options_hash())
|
||||
# only print warning once
|
||||
with mock.patch("certbot.plugins.common.logger") as mock_logger:
|
||||
|
||||
@@ -20,7 +20,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(MultipleVhostsTestDebian, self).setUp()
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.vhost_path, self.config_dir, self.work_dir,
|
||||
self.config_path, None, self.config_dir, self.work_dir,
|
||||
os_info="debian")
|
||||
self.config = self.mock_deploy_cert(self.config)
|
||||
self.vh_truth = util.get_vh_truth(self.temp_dir,
|
||||
|
||||
@@ -117,7 +117,7 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
|
||||
self.config.parser.modules = set()
|
||||
|
||||
with mock.patch("certbot.util.get_os_info") as mock_osi:
|
||||
# Make sure we have the have the Gentoo httpd constants
|
||||
# Make sure we have the have the CentOS httpd constants
|
||||
mock_osi.return_value = ("gentoo", "123")
|
||||
self.config.parser.update_runtime_variables()
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ from certbot import achallenges
|
||||
from certbot import errors
|
||||
|
||||
from certbot.tests import acme_util
|
||||
from certbot_apache.parser import get_aug_path
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
@@ -135,21 +134,6 @@ class ApacheHttp01Test(util.ApacheTest):
|
||||
def test_perform_3_achall_apache_2_4(self):
|
||||
self.combinations_perform_test(num_achalls=3, minor_version=4)
|
||||
|
||||
def test_activate_disabled_vhost(self):
|
||||
vhosts = [v for v in self.config.vhosts if v.name == "certbot.demo"]
|
||||
achalls = [
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.HTTP01(token=((b'a' * 16))),
|
||||
"pending"),
|
||||
domain="certbot.demo", account_key=self.account_key)]
|
||||
vhosts[0].enabled = False
|
||||
self.common_perform_test(achalls, vhosts)
|
||||
matches = self.config.parser.find_dir(
|
||||
"Include", vhosts[0].filep,
|
||||
get_aug_path(self.config.parser.loc["default"]))
|
||||
self.assertEqual(len(matches), 1)
|
||||
|
||||
def combinations_perform_test(self, num_achalls, minor_version):
|
||||
"""Test perform with the given achall count and Apache version."""
|
||||
achalls = self.achalls[:num_achalls]
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
"""Test for certbot_apache.configurator OCSP Prefetching functionality"""
|
||||
import os
|
||||
import unittest
|
||||
import mock
|
||||
# six is used in mock.patch()
|
||||
import six # pylint: disable=unused-import
|
||||
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
class OCSPPrefetchTest(util.ApacheTest):
|
||||
"""Tests for OCSP Prefetch feature"""
|
||||
# pylint: disable=protected-access
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(OCSPPrefetchTest, self).setUp()
|
||||
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.vhost_path, self.config_dir, self.work_dir)
|
||||
|
||||
self.lineage = mock.MagicMock(cert_path="cert", chain_path="chain")
|
||||
self.config.parser.modules.add("headers_module")
|
||||
self.config.parser.modules.add("mod_headers.c")
|
||||
self.config.parser.modules.add("ssl_module")
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.parser.modules.add("socache_dbm_module")
|
||||
self.config.parser.modules.add("mod_socache_dbm.c")
|
||||
|
||||
self.vh_truth = util.get_vh_truth(
|
||||
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
|
||||
|
||||
def call_mocked(self, func, *args, **kwargs):
|
||||
"""Helper method to call functins with mock stack"""
|
||||
|
||||
ver_path = "certbot_apache.configurator.ApacheConfigurator.get_version"
|
||||
cry_path = "certbot.crypto_util.cert_sha1_fingerprint"
|
||||
|
||||
with mock.patch(ver_path) as mock_ver:
|
||||
mock_ver.return_value = (2, 4, 10)
|
||||
with mock.patch(cry_path) as mock_cry:
|
||||
mock_cry.return_value = b'j\x056\x1f\xfa\x08B\xe8D\xa1Bn\xeb*A\xebWx\xdd\xfe'
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod")
|
||||
def test_ocsp_prefetch_enable_mods(self, mock_enable, _restart):
|
||||
self.config.parser.modules.discard("socache_dbm_module")
|
||||
self.config.parser.modules.discard("mod_socache_dbm.c")
|
||||
self.config.parser.modules.discard("headers_module")
|
||||
self.config.parser.modules.discard("mod_header.c")
|
||||
|
||||
ref_path = "certbot_apache.configurator.ApacheConfigurator._ocsp_refresh"
|
||||
with mock.patch(ref_path):
|
||||
self.call_mocked(self.config.enable_ocsp_prefetch,
|
||||
self.lineage,
|
||||
["ocspvhost.com"])
|
||||
self.assertTrue(mock_enable.called)
|
||||
self.assertEquals(len(self.config._ocsp_prefetch), 1)
|
||||
|
||||
@mock.patch("certbot_apache.constants.OCSP_INTERNAL_TTL", 0)
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_ocsp_prefetch_refresh(self, _mock_restart):
|
||||
db_path = os.path.join(self.config_dir, "ocsp", "ocsp_cache")
|
||||
def ocsp_req_mock(workfile):
|
||||
"""Method to mock the OCSP request and write response to file"""
|
||||
with open(workfile, 'w') as fh:
|
||||
fh.write("MOCKRESPONSE")
|
||||
return True
|
||||
|
||||
ocsp_path = "certbot.ocsp.OCSPResponseHandler.ocsp_request_to_file"
|
||||
with mock.patch(ocsp_path, side_effect=ocsp_req_mock):
|
||||
self.call_mocked(self.config.enable_ocsp_prefetch,
|
||||
self.lineage,
|
||||
["ocspvhost.com"])
|
||||
|
||||
odbm = self.config._ocsp_dbm_open(db_path)
|
||||
self.assertEquals(len(odbm.keys()), 1)
|
||||
# The actual response data is prepended by Apache timestamp
|
||||
self.assertTrue(odbm[odbm.keys()[0]].endswith(b'MOCKRESPONSE'))
|
||||
self.config._ocsp_dbm_close(odbm)
|
||||
|
||||
with mock.patch(ocsp_path, side_effect=ocsp_req_mock) as mock_ocsp:
|
||||
self.call_mocked(self.config.update_ocsp_prefetch, None)
|
||||
self.assertTrue(mock_ocsp.called)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_ocsp_prefetch_refresh_noop(self, _mock_restart):
|
||||
def ocsp_req_mock(workfile):
|
||||
"""Method to mock the OCSP request and write response to file"""
|
||||
with open(workfile, 'w') as fh:
|
||||
fh.write("MOCKRESPONSE")
|
||||
return True
|
||||
|
||||
ocsp_path = "certbot.ocsp.OCSPResponseHandler.ocsp_request_to_file"
|
||||
with mock.patch(ocsp_path, side_effect=ocsp_req_mock):
|
||||
self.call_mocked(self.config.enable_ocsp_prefetch,
|
||||
self.lineage,
|
||||
["ocspvhost.com"])
|
||||
self.assertEquals(len(self.config._ocsp_prefetch), 1)
|
||||
refresh_path = "certbot_apache.configurator.ApacheConfigurator._ocsp_refresh"
|
||||
with mock.patch(refresh_path) as mock_refresh:
|
||||
self.call_mocked(self.config.update_ocsp_prefetch, None)
|
||||
self.assertFalse(mock_refresh.called)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.config_test")
|
||||
def test_ocsp_prefetch_backup_db(self, _mock_test):
|
||||
db_path = os.path.join(self.config_dir, "ocsp", "ocsp_cache")
|
||||
def ocsp_del_db():
|
||||
"""Side effect of _reload() that deletes the DBM file, like Apache
|
||||
does when restarting"""
|
||||
full_db_path = db_path + ".db"
|
||||
os.remove(full_db_path)
|
||||
self.assertFalse(os.path.isfile(full_db_path))
|
||||
|
||||
self.config._ensure_ocsp_dirs()
|
||||
odbm = self.config._ocsp_dbm_open(db_path)
|
||||
odbm["mock_key"] = b'mock_value'
|
||||
self.config._ocsp_dbm_close(odbm)
|
||||
|
||||
# Mock OCSP prefetch dict to signify that there should be a db
|
||||
self.config._ocsp_prefetch = {"mock": "value"}
|
||||
rel_path = "certbot_apache.configurator.ApacheConfigurator._reload"
|
||||
with mock.patch(rel_path, side_effect=ocsp_del_db) as mock_reload:
|
||||
self.config.restart()
|
||||
|
||||
odbm = self.config._ocsp_dbm_open(db_path)
|
||||
self.assertEquals(odbm["mock_key"], b'mock_value')
|
||||
self.config._ocsp_dbm_close(odbm)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.config_test")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._reload")
|
||||
def test_ocsp_prefetch_backup_db_error(self, _mock_reload, _mock_test):
|
||||
self.config._ensure_ocsp_dirs()
|
||||
log_path = "certbot_apache.configurator.logger.debug"
|
||||
log_string = "Encountered an issue while trying to backup OCSP dbm file"
|
||||
log_string2 = "Encountered an issue when trying to restore OCSP dbm file"
|
||||
self.config._ocsp_prefetch = {"mock": "value"}
|
||||
with mock.patch("shutil.copy2", side_effect=IOError):
|
||||
with mock.patch(log_path) as mock_log:
|
||||
self.config.restart()
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertEquals(mock_log.call_count, 2)
|
||||
self.assertTrue(log_string in mock_log.call_args_list[0][0][0])
|
||||
self.assertTrue(log_string2 in mock_log.call_args_list[1][0][0])
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_ocsp_prefetch_refresh_fail(self, _mock_restart):
|
||||
ocsp_path = "certbot.ocsp.OCSPResponseHandler.ocsp_request_to_file"
|
||||
log_path = "certbot_apache.configurator.logger.warning"
|
||||
with mock.patch(ocsp_path) as mock_ocsp:
|
||||
mock_ocsp.return_value = False
|
||||
with mock.patch(log_path) as mock_log:
|
||||
self.call_mocked(self.config.enable_ocsp_prefetch,
|
||||
self.lineage,
|
||||
["ocspvhost.com"])
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertTrue(
|
||||
"trying to prefetch OCSP" in mock_log.call_args[0][0])
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._ocsp_refresh_if_needed")
|
||||
def test_ocsp_prefetch_update_noop(self, mock_refresh):
|
||||
self.config.update_ocsp_prefetch(None)
|
||||
self.assertFalse(mock_refresh.called)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -282,11 +282,11 @@ class BasicParserTest(util.ParserTest):
|
||||
self.assertRaises(
|
||||
errors.PluginError, self.parser.update_runtime_variables)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.option")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.constant")
|
||||
@mock.patch("certbot_apache.parser.subprocess.Popen")
|
||||
def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_opt):
|
||||
def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const):
|
||||
mock_popen.side_effect = OSError
|
||||
mock_opt.return_value = "nonexistent"
|
||||
mock_const.return_value = "nonexistent"
|
||||
self.assertRaises(
|
||||
errors.MisconfigurationError,
|
||||
self.parser.update_runtime_variables)
|
||||
|
||||
@@ -97,10 +97,9 @@ def get_apache_configurator( # pylint: disable=too-many-arguments, too-many-loc
|
||||
backups = os.path.join(work_dir, "backups")
|
||||
mock_le_config = mock.MagicMock(
|
||||
apache_server_root=config_path,
|
||||
apache_vhost_root=None,
|
||||
apache_vhost_root=conf_vhost_path,
|
||||
apache_le_vhost_ext="-le-ssl.conf",
|
||||
apache_challenge_location=config_path,
|
||||
apache_enmod=None,
|
||||
backup_dir=backups,
|
||||
config_dir=config_dir,
|
||||
http01_port=80,
|
||||
@@ -108,25 +107,33 @@ def get_apache_configurator( # pylint: disable=too-many-arguments, too-many-loc
|
||||
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
|
||||
work_dir=work_dir)
|
||||
|
||||
with mock.patch("certbot_apache.configurator.util.run_script"):
|
||||
with mock.patch("certbot_apache.configurator.util."
|
||||
"exe_exists") as mock_exe_exists:
|
||||
mock_exe_exists.return_value = True
|
||||
with mock.patch("certbot_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
try:
|
||||
config_class = entrypoint.OVERRIDE_CLASSES[os_info]
|
||||
except KeyError:
|
||||
config_class = configurator.ApacheConfigurator
|
||||
config = config_class(config=mock_le_config, name="apache",
|
||||
version=version)
|
||||
if not conf_vhost_path:
|
||||
config_class.OS_DEFAULTS["vhost_root"] = vhost_path
|
||||
else:
|
||||
# Custom virtualhost path was requested
|
||||
config.config.apache_vhost_root = conf_vhost_path
|
||||
config.config.apache_ctl = config_class.OS_DEFAULTS["ctl"]
|
||||
config.prepare()
|
||||
orig_os_constant = configurator.ApacheConfigurator(mock_le_config,
|
||||
name="apache",
|
||||
version=version).constant
|
||||
|
||||
def mock_os_constant(key, vhost_path=vhost_path):
|
||||
"""Mock default vhost path"""
|
||||
if key == "vhost_root":
|
||||
return vhost_path
|
||||
else:
|
||||
return orig_os_constant(key)
|
||||
|
||||
with mock.patch("certbot_apache.configurator.ApacheConfigurator.constant") as mock_cons:
|
||||
mock_cons.side_effect = mock_os_constant
|
||||
with mock.patch("certbot_apache.configurator.util.run_script"):
|
||||
with mock.patch("certbot_apache.configurator.util."
|
||||
"exe_exists") as mock_exe_exists:
|
||||
mock_exe_exists.return_value = True
|
||||
with mock.patch("certbot_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
try:
|
||||
config_class = entrypoint.OVERRIDE_CLASSES[os_info]
|
||||
except KeyError:
|
||||
config_class = configurator.ApacheConfigurator
|
||||
config = config_class(config=mock_le_config, name="apache",
|
||||
version=version)
|
||||
|
||||
config.prepare()
|
||||
return config
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
install_requires = [
|
||||
'acme>=0.25.0',
|
||||
'certbot>=0.26.0',
|
||||
'certbot>=0.26.0.dev0',
|
||||
'mock',
|
||||
'python-augeas',
|
||||
'setuptools',
|
||||
@@ -31,7 +31,7 @@ setup(
|
||||
license='Apache License 2.0',
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
@@ -43,7 +43,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
83
certbot-auto
83
certbot-auto
@@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then
|
||||
fi
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt"
|
||||
LE_AUTO_VERSION="0.26.1"
|
||||
LE_AUTO_VERSION="0.25.1"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -1060,26 +1060,37 @@ ConfigArgParse==0.12.0 \
|
||||
configobj==5.0.6 \
|
||||
--hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 \
|
||||
--no-binary configobj
|
||||
cryptography==2.2.2 \
|
||||
--hash=sha256:3f3b65d5a16e6b52fba63dc860b62ca9832f51f1a2ae5083c78b6840275f12dd \
|
||||
--hash=sha256:5251e7de0de66810833606439ca65c9b9e45da62196b0c88bfadf27740aac09f \
|
||||
--hash=sha256:551a3abfe0c8c6833df4192a63371aa2ff43afd8f570ed345d31f251d78e7e04 \
|
||||
--hash=sha256:5cb990056b7cadcca26813311187ad751ea644712022a3976443691168781b6f \
|
||||
--hash=sha256:60bda7f12ecb828358be53095fc9c6edda7de8f1ef571f96c00b2363643fa3cd \
|
||||
--hash=sha256:64b5c67acc9a7c83fbb4b69166f3105a0ab722d27934fac2cb26456718eec2ba \
|
||||
--hash=sha256:6fef51ec447fe9f8351894024e94736862900d3a9aa2961528e602eb65c92bdb \
|
||||
--hash=sha256:77d0ad229d47a6e0272d00f6bf8ac06ce14715a9fd02c9a97f5a2869aab3ccb2 \
|
||||
--hash=sha256:808fe471b1a6b777f026f7dc7bd9a4959da4bfab64972f2bbe91e22527c1c037 \
|
||||
--hash=sha256:9b62fb4d18529c84b961efd9187fecbb48e89aa1a0f9f4161c61b7fc42a101bd \
|
||||
--hash=sha256:9e5bed45ec6b4f828866ac6a6bedf08388ffcfa68abe9e94b34bb40977aba531 \
|
||||
--hash=sha256:9fc295bf69130a342e7a19a39d7bbeb15c0bcaabc7382ec33ef3b2b7d18d2f63 \
|
||||
--hash=sha256:abd070b5849ed64e6d349199bef955ee0ad99aefbad792f0c587f8effa681a5e \
|
||||
--hash=sha256:ba6a774749b6e510cffc2fb98535f717e0e5fd91c7c99a61d223293df79ab351 \
|
||||
--hash=sha256:c332118647f084c983c6a3e1dba0f3bcb051f69d12baccac68db8d62d177eb8a \
|
||||
--hash=sha256:d6f46e862ee36df81e6342c2177ba84e70f722d9dc9c6c394f9f1f434c4a5563 \
|
||||
--hash=sha256:db6013746f73bf8edd9c3d1d3f94db635b9422f503db3fc5ef105233d4c011ab \
|
||||
--hash=sha256:f57008eaff597c69cf692c3518f6d4800f0309253bb138b526a37fe9ef0c7471 \
|
||||
--hash=sha256:f6c821ac253c19f2ad4c8691633ae1d1a17f120d5b01ea1d256d7b602bc59887
|
||||
cryptography==2.0.2 \
|
||||
--hash=sha256:187ae17358436d2c760f28c2aeb02fefa3f37647a9c5b6f7f7c3e83cd1c5a972 \
|
||||
--hash=sha256:19e43a13bbf52028dd1e810c803f2ad8880d0692d772f98d42e1eaf34bdee3d6 \
|
||||
--hash=sha256:da9291502cbc87dc0284a20c56876e4d2e68deac61cc43df4aec934e44ca97b1 \
|
||||
--hash=sha256:0954f8813095f581669330e0a2d5e726c33ac7f450c1458fac58bab54595e516 \
|
||||
--hash=sha256:d68b0cc40a8432ed3fc84876c519de704d6001800ec22b136e75ae841910c45b \
|
||||
--hash=sha256:2f8ad9580ab4da645cfea52a91d2da99a49a1e76616d8be68441a986fad652b0 \
|
||||
--hash=sha256:cc00b4511294f5f6b65c4e77a1a9c62f52490a63d2c120f3872176b40a82351e \
|
||||
--hash=sha256:cf896020f6a9f095a547b3d672c8db1ef2ed71fca11250731fa1d4a4cb8b1590 \
|
||||
--hash=sha256:e0fdb8322206fa02aa38f71519ff75dce2eb481b7e1110e2936795cb376bb6ee \
|
||||
--hash=sha256:277538466657ca5d6637f80be100242f9831d75138b788d718edd3aab34621f8 \
|
||||
--hash=sha256:2c77eb0560f54ce654ab82d6b2a64327a71ee969b29022bf9746ca311c9f5069 \
|
||||
--hash=sha256:755a7853b679e79d0a799351c092a9b0271f95ff54c8dd8823d8b527a2926a86 \
|
||||
--hash=sha256:77197a2d525e761cdd4c771180b4bd0d80703654c6385e4311cbbbe2beb56fa1 \
|
||||
--hash=sha256:eb8bb79d0ab00c931c8333b745f06fec481a51c52d70acd4ee95d6093ba5c386 \
|
||||
--hash=sha256:131f61de82ef28f3e20beb4bfc24f9692d28cecfd704e20e6c7f070f7793013a \
|
||||
--hash=sha256:ac35435974b2e27cd4520f29c191d7da36f4189aa3264e52c4c6c6d089ab6142 \
|
||||
--hash=sha256:04b6ea99daa2a8460728794213d76d45ad58ea247dc7e7ff148d7dd726e87863 \
|
||||
--hash=sha256:2b9442f8b4c3d575f6cc3db0e856034e0f5a9d55ecd636f52d8c496795b26952 \
|
||||
--hash=sha256:b3d3b3ecba1fe1bdb6f180770a137f877c8f07571f7b2934bb269475bcf0e5e8 \
|
||||
--hash=sha256:670a58c0d75cb0e78e73dd003bd96d4440bbb1f2bc041dcf7b81767ca4fb0ce9 \
|
||||
--hash=sha256:5af84d23bdb86b5e90aca263df1424b43f1748480bfcde3ac2a3cbe622612468 \
|
||||
--hash=sha256:ba22e8eefabdd7aca37d0c0c00d2274000d2cebb5cce9e5a710cb55bf8797b31 \
|
||||
--hash=sha256:b798b22fa7e92b439547323b8b719d217f1e1b7677585cfeeedf3b55c70bb7fb \
|
||||
--hash=sha256:59cff28af8cce96cb7e94a459726e1d88f6f5fa75097f9dcbebd99118d64ea4c \
|
||||
--hash=sha256:fe859e445abc9ba9e97950ddafb904e23234c4ecb76b0fae6c86e80592ce464a \
|
||||
--hash=sha256:655f3c474067f1e277430f23cc0549f0b1dc99b82aec6e53f80b9b2db7f76f11 \
|
||||
--hash=sha256:0ebc2be053c9a03a2f3e20a466e87bf12a51586b3c79bd2a22171b073a805346 \
|
||||
--hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \
|
||||
--hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \
|
||||
--hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab
|
||||
enum34==1.1.2 ; python_version < '3.4' \
|
||||
--hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \
|
||||
--hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501
|
||||
@@ -1092,9 +1103,9 @@ idna==2.5 \
|
||||
ipaddress==1.0.16 \
|
||||
--hash=sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5 \
|
||||
--hash=sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0
|
||||
josepy==1.1.0 \
|
||||
--hash=sha256:1309a25aac3caeff5239729c58ff9b583f7d022ffdb1553406ddfc8e5b52b76e \
|
||||
--hash=sha256:fb5c62c77d26e04df29cb5ecd01b9ce69b6fcc9e521eb1ca193b7faa2afa7086
|
||||
josepy==1.0.1 \
|
||||
--hash=sha256:354a3513038a38bbcd27c97b7c68a8f3dfaff0a135b20a92c6db4cc4ea72915e \
|
||||
--hash=sha256:9f48b88ca37f0244238b1cc77723989f7c54f7b90b2eee6294390bacfe870acc
|
||||
linecache2==1.0.0 \
|
||||
--hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \
|
||||
--hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c
|
||||
@@ -1197,18 +1208,18 @@ letsencrypt==0.7.0 \
|
||||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.26.1 \
|
||||
--hash=sha256:4e2ffdeebb7f5097600bcb1ca19131441fa021f952b443ca7454a279337af609 \
|
||||
--hash=sha256:4983513d63f7f36e24a07873ca2d6ea1c0101aa6cb1cd825cda02ed520f6ca66
|
||||
acme==0.26.1 \
|
||||
--hash=sha256:d47841e66adc1336ecca2f0d41a247c1b62307c981be6d07996bbf3f95af1dc5 \
|
||||
--hash=sha256:86e7b5f4654cb19215f16c0e6225750db7421f68ef6a0a040a61796f24e690be
|
||||
certbot-apache==0.26.1 \
|
||||
--hash=sha256:c16acb49bd4f84fff25bcbb7eaf74412145efe9b68ce46e1803be538894f2ce3 \
|
||||
--hash=sha256:b7fa327e987b892d64163e7519bdeaf9723d78275ef6c438272848894ace6d87
|
||||
certbot-nginx==0.26.1 \
|
||||
--hash=sha256:c0048dc83672dc90805a8ddf513be3e48c841d6e91607e91e8657c1785d65660 \
|
||||
--hash=sha256:d0c95a32625e0f1612d7fcf9021e6e050ba3d879823489d1edd2478a78ae6624
|
||||
certbot==0.25.1 \
|
||||
--hash=sha256:01689015364685fef3f1e1fb7832ba84eb3b0aa85bc5a71c96661f6d4c59981f \
|
||||
--hash=sha256:5c23e5186133bb1afd805be5e0cd2fb7b95862a8b0459c9ecad4ae60f933e54e
|
||||
acme==0.25.1 \
|
||||
--hash=sha256:26e641a01536705fe5f12d856703b8ef06e5a07981a7b6379d2771dcdb69a742 \
|
||||
--hash=sha256:47b5f3f73d69b7b1d13f918aa2cd75a8093069a68becf4af38e428e4613b2734
|
||||
certbot-apache==0.25.1 \
|
||||
--hash=sha256:a28b7c152cc11474bef5b5e7967aaea42b2c0aaf86fd82ee4082713d33cee5a9 \
|
||||
--hash=sha256:ed012465617073a0f1057fe854dc8d1eb6d2dd7ede1fb2eee765129fed2a095a
|
||||
certbot-nginx==0.25.1 \
|
||||
--hash=sha256:83f82c3ba08c0b1d4bf449ac24018e8e7dd34a6248d35466f2de7da1cd312e15 \
|
||||
--hash=sha256:68f98b41c54e0bf4218ef293079597176617bee3837ae3aa6528ce2ff0bf4f9c
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -59,6 +59,9 @@ class Proxy(configurators_common.Proxy):
|
||||
setattr(self.le_config, "apache_" + k,
|
||||
entrypoint.ENTRYPOINT.OS_DEFAULTS[k])
|
||||
|
||||
# An alias
|
||||
self.le_config.apache_handle_modules = self.le_config.apache_handle_mods
|
||||
|
||||
self._configurator = entrypoint.ENTRYPOINT(
|
||||
config=configuration.NamespaceConfig(self.le_config),
|
||||
name="apache")
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot',
|
||||
@@ -46,7 +46,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -43,7 +43,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM certbot/certbot
|
||||
|
||||
COPY . src/certbot-dns-gehirn
|
||||
|
||||
RUN pip install --no-cache-dir --editable src/certbot-dns-gehirn
|
||||
@@ -1,190 +0,0 @@
|
||||
Copyright 2018 Electronic Frontier Foundation and others
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,3 +0,0 @@
|
||||
include LICENSE.txt
|
||||
include README.rst
|
||||
recursive-include docs *
|
||||
@@ -1 +0,0 @@
|
||||
Gehirn Infrastracture Service DNS Authenticator plugin for Certbot
|
||||
@@ -1,88 +0,0 @@
|
||||
"""
|
||||
The `~certbot_dns_gehirn.dns_gehirn` plugin automates the process of completing
|
||||
a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and subsequently
|
||||
removing, TXT records using the Gehirn Infrastracture Service DNS API.
|
||||
|
||||
|
||||
Named Arguments
|
||||
---------------
|
||||
|
||||
======================================== =====================================
|
||||
``--dns-gehirn-credentials`` Gehirn Infrastracture Service
|
||||
credentials_ INI file.
|
||||
(Required)
|
||||
``--dns-gehirn-propagation-seconds`` The number of seconds to wait for DNS
|
||||
to propagate before asking the ACME
|
||||
server to verify the DNS record.
|
||||
(Default: 30)
|
||||
======================================== =====================================
|
||||
|
||||
|
||||
Credentials
|
||||
-----------
|
||||
|
||||
Use of this plugin requires a configuration file containing
|
||||
Gehirn Infrastracture Service DNS API credentials,
|
||||
obtained from your Gehirn Infrastracture Service
|
||||
`dashboard <https://gis.gehirn.jp/>`_.
|
||||
|
||||
.. code-block:: ini
|
||||
:name: credentials.ini
|
||||
:caption: Example credentials file:
|
||||
|
||||
# Gehirn Infrastracture Service API credentials used by Certbot
|
||||
dns_gehirn_api_token = 00000000-0000-0000-0000-000000000000
|
||||
dns_gehirn_api_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
|
||||
|
||||
The path to this file can be provided interactively or using the
|
||||
``--dns-gehirn-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
|
||||
Gehirn Infrastracture Service 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
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a certificate for ``example.com``
|
||||
|
||||
certbot certonly \\
|
||||
--dns-gehirn \\
|
||||
--dns-gehirn-credentials ~/.secrets/certbot/gehirn.ini \\
|
||||
-d example.com
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a single certificate for both ``example.com`` and
|
||||
``www.example.com``
|
||||
|
||||
certbot certonly \\
|
||||
--dns-gehirn \\
|
||||
--dns-gehirn-credentials ~/.secrets/certbot/gehirn.ini \\
|
||||
-d example.com \\
|
||||
-d www.example.com
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a certificate for ``example.com``, waiting 60 seconds
|
||||
for DNS propagation
|
||||
|
||||
certbot certonly \\
|
||||
--dns-gehirn \\
|
||||
--dns-gehirn-credentials ~/.secrets/certbot/gehirn.ini \\
|
||||
--dns-gehirn-propagation-seconds 60 \\
|
||||
-d example.com
|
||||
|
||||
"""
|
||||
@@ -1,84 +0,0 @@
|
||||
"""DNS Authenticator for Gehirn Infrastracture Service DNS."""
|
||||
import logging
|
||||
|
||||
import zope.interface
|
||||
from lexicon.providers import gehirn
|
||||
|
||||
from certbot import interfaces
|
||||
from certbot.plugins import dns_common
|
||||
from certbot.plugins import dns_common_lexicon
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DASHBOARD_URL = "https://gis.gehirn.jp/"
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
class Authenticator(dns_common.DNSAuthenticator):
|
||||
"""DNS Authenticator for Gehirn Infrastracture Service DNS
|
||||
|
||||
This Authenticator uses the Gehirn Infrastracture Service API to fulfill
|
||||
a dns-01 challenge.
|
||||
"""
|
||||
|
||||
description = 'Obtain certificates using a DNS TXT record ' + \
|
||||
'(if you are using Gehirn Infrastracture Service for DNS).'
|
||||
ttl = 60
|
||||
|
||||
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=30)
|
||||
add('credentials', help='Gehirn Infrastracture Service credentials 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 Gehirn Infrastracture Service API.'
|
||||
|
||||
def _setup_credentials(self):
|
||||
self.credentials = self._configure_credentials(
|
||||
'credentials',
|
||||
'Gehirn Infrastracture Service credentials file',
|
||||
{
|
||||
'api-token': 'API token for Gehirn Infrastracture Service ' + \
|
||||
'API obtained from {0}'.format(DASHBOARD_URL),
|
||||
'api-secret': 'API secret for Gehirn Infrastracture Service ' + \
|
||||
'API obtained from {0}'.format(DASHBOARD_URL),
|
||||
}
|
||||
)
|
||||
|
||||
def _perform(self, domain, validation_name, validation):
|
||||
self._get_gehirn_client().add_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _cleanup(self, domain, validation_name, validation):
|
||||
self._get_gehirn_client().del_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _get_gehirn_client(self):
|
||||
return _GehirnLexiconClient(
|
||||
self.credentials.conf('api-token'),
|
||||
self.credentials.conf('api-secret'),
|
||||
self.ttl
|
||||
)
|
||||
|
||||
|
||||
class _GehirnLexiconClient(dns_common_lexicon.LexiconClient):
|
||||
"""
|
||||
Encapsulates all communication with the Gehirn Infrastracture Service via Lexicon.
|
||||
"""
|
||||
|
||||
def __init__(self, api_token, api_secret, ttl):
|
||||
super(_GehirnLexiconClient, self).__init__()
|
||||
|
||||
self.provider = gehirn.Provider({
|
||||
'auth_token': api_token,
|
||||
'auth_secret': api_secret,
|
||||
'ttl': ttl,
|
||||
})
|
||||
|
||||
def _handle_http_error(self, e, domain_name):
|
||||
if domain_name in str(e) and (str(e).startswith('404 Client Error: Not Found for url:')):
|
||||
return # Expected errors when zone name guess is wrong
|
||||
return super(_GehirnLexiconClient, self)._handle_http_error(e, domain_name)
|
||||
@@ -1,55 +0,0 @@
|
||||
"""Tests for certbot_dns_gehirn.dns_gehirn."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from certbot.plugins import dns_test_common
|
||||
from certbot.plugins import dns_test_common_lexicon
|
||||
from certbot.plugins.dns_test_common import DOMAIN
|
||||
from certbot.tests import util as test_util
|
||||
|
||||
API_TOKEN = '00000000-0000-0000-0000-000000000000'
|
||||
API_SECRET = 'MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw'
|
||||
|
||||
class AuthenticatorTest(test_util.TempDirTestCase,
|
||||
dns_test_common_lexicon.BaseLexiconAuthenticatorTest):
|
||||
|
||||
def setUp(self):
|
||||
super(AuthenticatorTest, self).setUp()
|
||||
|
||||
from certbot_dns_gehirn.dns_gehirn import Authenticator
|
||||
|
||||
path = os.path.join(self.tempdir, 'file.ini')
|
||||
dns_test_common.write(
|
||||
{"gehirn_api_token": API_TOKEN, "gehirn_api_secret": API_SECRET},
|
||||
path
|
||||
)
|
||||
|
||||
self.config = mock.MagicMock(gehirn_credentials=path,
|
||||
gehirn_propagation_seconds=0) # don't wait during tests
|
||||
|
||||
self.auth = Authenticator(self.config, "gehirn")
|
||||
|
||||
self.mock_client = mock.MagicMock()
|
||||
# _get_gehirn_client | pylint: disable=protected-access
|
||||
self.auth._get_gehirn_client = mock.MagicMock(return_value=self.mock_client)
|
||||
|
||||
|
||||
class GehirnLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconClientTest):
|
||||
DOMAIN_NOT_FOUND = HTTPError('404 Client Error: Not Found for url: {0}.'.format(DOMAIN))
|
||||
LOGIN_ERROR = HTTPError('401 Client Error: Unauthorized for url: {0}.'.format(DOMAIN))
|
||||
|
||||
def setUp(self):
|
||||
from certbot_dns_gehirn.dns_gehirn import _GehirnLexiconClient
|
||||
|
||||
self.client = _GehirnLexiconClient(API_TOKEN, API_SECRET, 0)
|
||||
|
||||
self.provider_mock = mock.MagicMock()
|
||||
self.client.provider = self.provider_mock
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
1
certbot-dns-gehirn/docs/.gitignore
vendored
1
certbot-dns-gehirn/docs/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/_build/
|
||||
@@ -1,20 +0,0 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = certbot-dns-gehirn
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -1,8 +0,0 @@
|
||||
=================
|
||||
API Documentation
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
||||
api/**
|
||||
@@ -1,5 +0,0 @@
|
||||
:mod:`certbot_dns_gehirn.dns_gehirn`
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: certbot_dns_gehirn.dns_gehirn
|
||||
:members:
|
||||
@@ -1,180 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# certbot-dns-gehirn documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed May 10 18:30:40 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode']
|
||||
|
||||
autodoc_member_order = 'bysource'
|
||||
autodoc_default_flags = ['show-inheritance', 'private-members']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'certbot-dns-gehirn'
|
||||
copyright = u'2018, Certbot Project'
|
||||
author = u'Certbot Project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'en'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
default_role = 'py:obj'
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
|
||||
# http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs
|
||||
# on_rtd is whether we are on readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
# otherwise, readthedocs.org uses their theme by default, so no need to specify it
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'certbot-dns-gehirndoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'certbot-dns-gehirn.tex', u'certbot-dns-gehirn Documentation',
|
||||
u'Certbot Project', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'certbot-dns-gehirn', u'certbot-dns-gehirn Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'certbot-dns-gehirn', u'certbot-dns-gehirn Documentation',
|
||||
author, 'certbot-dns-gehirn', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.. certbot-dns-gehirn documentation master file, created by
|
||||
sphinx-quickstart on Wed May 10 18:30:40 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to certbot-dns-gehirn's documentation!
|
||||
==============================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api
|
||||
|
||||
.. automodule:: certbot_dns_gehirn
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -1,36 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=certbot-dns-gehirn
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -1,12 +0,0 @@
|
||||
# readthedocs.org gives no way to change the install command to "pip
|
||||
# install -e .[docs]" (that would in turn install documentation
|
||||
# dependencies), but it allows to specify a requirements.txt file at
|
||||
# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259)
|
||||
|
||||
# Although ReadTheDocs certainly doesn't need to install the project
|
||||
# in --editable mode (-e), just "pip install .[docs]" does not work as
|
||||
# expected and "pip install -e .[docs]" must be used instead
|
||||
|
||||
-e acme
|
||||
-e .
|
||||
-e certbot-dns-gehirn[docs]
|
||||
@@ -1,2 +0,0 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
@@ -1,66 +0,0 @@
|
||||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon>=2.1.22',
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
|
||||
'sphinx_rtd_theme',
|
||||
]
|
||||
|
||||
setup(
|
||||
name='certbot-dns-gehirn',
|
||||
version=version,
|
||||
description="Gehirn Infrastracture Service DNS Authenticator plugin for Certbot",
|
||||
url='https://github.com/certbot/certbot',
|
||||
author="Certbot Project",
|
||||
author_email='client-dev@letsencrypt.org',
|
||||
license='Apache License 2.0',
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'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,
|
||||
extras_require={
|
||||
'docs': docs_extras,
|
||||
},
|
||||
entry_points={
|
||||
'certbot.plugins': [
|
||||
'dns-gehirn = certbot_dns_gehirn.dns_gehirn:Authenticator',
|
||||
],
|
||||
},
|
||||
test_suite='certbot_dns_gehirn',
|
||||
)
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -47,7 +47,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM certbot/certbot
|
||||
|
||||
COPY . src/certbot-dns-linode
|
||||
|
||||
RUN pip install --no-cache-dir --editable src/certbot-dns-linode
|
||||
@@ -1,190 +0,0 @@
|
||||
Copyright 2015 Electronic Frontier Foundation and others
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,3 +0,0 @@
|
||||
include LICENSE.txt
|
||||
include README.rst
|
||||
recursive-include docs *
|
||||
@@ -1 +0,0 @@
|
||||
Linode DNS Authenticator plugin for Certbot
|
||||
@@ -1,86 +0,0 @@
|
||||
"""
|
||||
The `~certbot_dns_linode.dns_linode` plugin automates the process of
|
||||
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
|
||||
subsequently removing, TXT records using the Linode API.
|
||||
|
||||
|
||||
Named Arguments
|
||||
---------------
|
||||
|
||||
========================================== ===================================
|
||||
``--dns-linode-credentials`` Linode credentials_ INI file.
|
||||
(Required)
|
||||
``--dns-linode-propagation-seconds`` The number of seconds to wait for
|
||||
DNS to propagate before asking the
|
||||
ACME server to verify the DNS
|
||||
record.
|
||||
(Default: 960)
|
||||
========================================== ===================================
|
||||
|
||||
|
||||
Credentials
|
||||
-----------
|
||||
|
||||
Use of this plugin requires a configuration file containing Linode API
|
||||
credentials, obtained from your Linode account's `Applications & API
|
||||
Tokens page <https://manager.linode.com/profile/api>`_.
|
||||
|
||||
.. code-block:: ini
|
||||
:name: credentials.ini
|
||||
:caption: Example credentials file:
|
||||
|
||||
# Linode API credentials used by Certbot
|
||||
dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64
|
||||
|
||||
The path to this file can be provided interactively or using the
|
||||
``--dns-linode-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
|
||||
Linode 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
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a certificate for ``example.com``
|
||||
|
||||
certbot certonly \\
|
||||
--dns-linode \\
|
||||
--dns-linode-credentials ~/.secrets/certbot/linode.ini \\
|
||||
-d example.com
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a single certificate for both ``example.com`` and
|
||||
``www.example.com``
|
||||
|
||||
certbot certonly \\
|
||||
--dns-linode \\
|
||||
--dns-linode-credentials ~/.secrets/certbot/linode.ini \\
|
||||
-d example.com \\
|
||||
-d www.example.com
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a certificate for ``example.com``, waiting 60 seconds
|
||||
for DNS propagation
|
||||
|
||||
certbot certonly \\
|
||||
--dns-linode \\
|
||||
--dns-linode-credentials ~/.secrets/certbot/linode.ini \\
|
||||
--dns-linode-propagation-seconds 60 \\
|
||||
-d example.com
|
||||
|
||||
"""
|
||||
@@ -1,72 +0,0 @@
|
||||
"""DNS Authenticator for Linode."""
|
||||
import logging
|
||||
|
||||
import zope.interface
|
||||
from lexicon.providers import linode
|
||||
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot.plugins import dns_common
|
||||
from certbot.plugins import dns_common_lexicon
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
API_KEY_URL = 'https://manager.linode.com/profile/api'
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
class Authenticator(dns_common.DNSAuthenticator):
|
||||
"""DNS Authenticator for Linode
|
||||
|
||||
This Authenticator uses the Linode API to fulfill a dns-01 challenge.
|
||||
"""
|
||||
|
||||
description = 'Obtain certs using a DNS TXT record (if you are using Linode 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, default_propagation_seconds=960)
|
||||
add('credentials', help='Linode 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 Linode API.'
|
||||
|
||||
def _setup_credentials(self):
|
||||
self.credentials = self._configure_credentials(
|
||||
'credentials',
|
||||
'Linode credentials INI file',
|
||||
{
|
||||
'key': 'API key for Linode account, obtained from {0}'.format(API_KEY_URL)
|
||||
}
|
||||
)
|
||||
|
||||
def _perform(self, domain, validation_name, validation):
|
||||
self._get_linode_client().add_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _cleanup(self, domain, validation_name, validation):
|
||||
self._get_linode_client().del_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _get_linode_client(self):
|
||||
return _LinodeLexiconClient(self.credentials.conf('key'))
|
||||
|
||||
class _LinodeLexiconClient(dns_common_lexicon.LexiconClient):
|
||||
"""
|
||||
Encapsulates all communication with the Linode API.
|
||||
"""
|
||||
|
||||
def __init__(self, api_key):
|
||||
super(_LinodeLexiconClient, self).__init__()
|
||||
self.provider = linode.Provider({
|
||||
'auth_token': api_key
|
||||
})
|
||||
|
||||
def _handle_general_error(self, e, domain_name):
|
||||
if not str(e).startswith('Domain not found'):
|
||||
return errors.PluginError('Unexpected error determining zone identifier for {0}: {1}'
|
||||
.format(domain_name, e))
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
"""Tests for certbot_dns_linode.dns_linode."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from certbot.plugins import dns_test_common
|
||||
from certbot.plugins import dns_test_common_lexicon
|
||||
from certbot.tests import util as test_util
|
||||
|
||||
TOKEN = 'a-token'
|
||||
|
||||
class AuthenticatorTest(test_util.TempDirTestCase,
|
||||
dns_test_common_lexicon.BaseLexiconAuthenticatorTest):
|
||||
|
||||
def setUp(self):
|
||||
super(AuthenticatorTest, self).setUp()
|
||||
|
||||
from certbot_dns_linode.dns_linode import Authenticator
|
||||
|
||||
path = os.path.join(self.tempdir, 'file.ini')
|
||||
dns_test_common.write({"linode_key": TOKEN}, path)
|
||||
|
||||
self.config = mock.MagicMock(linode_credentials=path,
|
||||
linode_propagation_seconds=0) # don't wait during tests
|
||||
|
||||
self.auth = Authenticator(self.config, "linode")
|
||||
|
||||
self.mock_client = mock.MagicMock()
|
||||
# _get_linode_client | pylint: disable=protected-access
|
||||
self.auth._get_linode_client = mock.MagicMock(return_value=self.mock_client)
|
||||
|
||||
class LinodeLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconClientTest):
|
||||
|
||||
DOMAIN_NOT_FOUND = Exception('Domain not found')
|
||||
|
||||
def setUp(self):
|
||||
from certbot_dns_linode.dns_linode import _LinodeLexiconClient
|
||||
|
||||
self.client = _LinodeLexiconClient(TOKEN)
|
||||
|
||||
self.provider_mock = mock.MagicMock()
|
||||
self.client.provider = self.provider_mock
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
1
certbot-dns-linode/docs/.gitignore
vendored
1
certbot-dns-linode/docs/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/_build/
|
||||
@@ -1,20 +0,0 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = certbot-dns-linode
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -1,8 +0,0 @@
|
||||
=================
|
||||
API Documentation
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
||||
api/**
|
||||
@@ -1,5 +0,0 @@
|
||||
:mod:`certbot_dns_linode.dns_linode`
|
||||
------------------------------------------------
|
||||
|
||||
.. automodule:: certbot_dns_linode.dns_linode
|
||||
:members:
|
||||
@@ -1,180 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# certbot-dns-linode documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed May 10 10:52:06 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode']
|
||||
|
||||
autodoc_member_order = 'bysource'
|
||||
autodoc_default_flags = ['show-inheritance', 'private-members']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'certbot-dns-linode'
|
||||
copyright = u'2017, Certbot Project'
|
||||
author = u'Certbot Project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'en'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
default_role = 'py:obj'
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
|
||||
# http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs
|
||||
# on_rtd is whether we are on readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
# otherwise, readthedocs.org uses their theme by default, so no need to specify it
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'certbot-dns-linodedoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'certbot-dns-linode.tex', u'certbot-dns-linode Documentation',
|
||||
u'Certbot Project', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'certbot-dns-linode', u'certbot-dns-linode Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'certbot-dns-linode', u'certbot-dns-linode Documentation',
|
||||
author, 'certbot-dns-linode', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.. certbot-dns-linode documentation master file, created by
|
||||
sphinx-quickstart on Wed May 10 10:52:06 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to certbot-dns-linode's documentation!
|
||||
====================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api
|
||||
|
||||
.. automodule:: certbot_dns_linode
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -1,36 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=certbot-dns-linode
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -1,2 +0,0 @@
|
||||
acme[dev]==0.21.1
|
||||
certbot[dev]==0.21.1
|
||||
@@ -1,12 +0,0 @@
|
||||
# readthedocs.org gives no way to change the install command to "pip
|
||||
# install -e .[docs]" (that would in turn install documentation
|
||||
# dependencies), but it allows to specify a requirements.txt file at
|
||||
# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259)
|
||||
|
||||
# Although ReadTheDocs certainly doesn't need to install the project
|
||||
# in --editable mode (-e), just "pip install .[docs]" does not work as
|
||||
# expected and "pip install -e .[docs]" must be used instead
|
||||
|
||||
-e acme
|
||||
-e .
|
||||
-e certbot-dns-linode[docs]
|
||||
@@ -1,2 +0,0 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
@@ -1,66 +0,0 @@
|
||||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon>=2.2.1',
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
|
||||
'sphinx_rtd_theme',
|
||||
]
|
||||
|
||||
setup(
|
||||
name='certbot-dns-linode',
|
||||
version=version,
|
||||
description="Linode DNS Authenticator plugin for Certbot",
|
||||
url='https://github.com/certbot/certbot',
|
||||
author="Certbot Project",
|
||||
author_email='client-dev@letsencrypt.org',
|
||||
license='Apache License 2.0',
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'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,
|
||||
extras_require={
|
||||
'docs': docs_extras,
|
||||
},
|
||||
entry_points={
|
||||
'certbot.plugins': [
|
||||
'dns-linode = certbot_dns_linode.dns_linode:Authenticator',
|
||||
],
|
||||
},
|
||||
test_suite='certbot_dns_linode',
|
||||
)
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM certbot/certbot
|
||||
|
||||
COPY . src/certbot-dns-ovh
|
||||
|
||||
RUN pip install --no-cache-dir --editable src/certbot-dns-ovh
|
||||
@@ -1,190 +0,0 @@
|
||||
Copyright 2015 Electronic Frontier Foundation and others
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,3 +0,0 @@
|
||||
include LICENSE.txt
|
||||
include README.rst
|
||||
recursive-include docs *
|
||||
@@ -1 +0,0 @@
|
||||
OVH DNS Authenticator plugin for Certbot
|
||||
@@ -1,98 +0,0 @@
|
||||
"""
|
||||
The `~certbot_dns_ovh.dns_ovh` plugin automates the process of
|
||||
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
|
||||
subsequently removing, TXT records using the OVH API.
|
||||
|
||||
|
||||
Named Arguments
|
||||
---------------
|
||||
|
||||
=================================== ==========================================
|
||||
``--dns-ovh-credentials`` OVH credentials_ INI file.
|
||||
(Required)
|
||||
``--dns-ovh-propagation-seconds`` The number of seconds to wait for DNS
|
||||
to propagate before asking the ACME
|
||||
server to verify the DNS record.
|
||||
(Default: 30)
|
||||
=================================== ==========================================
|
||||
|
||||
|
||||
Credentials
|
||||
-----------
|
||||
|
||||
Use of this plugin requires a configuration file containing OVH API
|
||||
credentials for an account with the following access rules:
|
||||
|
||||
* ``GET /domain/zone/*``
|
||||
* ``PUT /domain/zone/*``
|
||||
* ``POST /domain/zone/*``
|
||||
* ``DELETE /domain/zone/*``
|
||||
|
||||
These credentials can be obtained there:
|
||||
|
||||
* `OVH Europe <https://eu.api.ovh.com/createToken/>`_ (endpoint: ``ovh-eu``)
|
||||
* `OVH North America <https://ca.api.ovh.com/createToken/>`_ (endpoint:
|
||||
``ovh-ca``)
|
||||
|
||||
.. code-block:: ini
|
||||
:name: credentials.ini
|
||||
:caption: Example credentials file:
|
||||
|
||||
# OVH API credentials used by Certbot
|
||||
dns_ovh_endpoint = ovh-eu
|
||||
dns_ovh_application_key = MDAwMDAwMDAwMDAw
|
||||
dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
|
||||
dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
|
||||
|
||||
The path to this file can be provided interactively or using the
|
||||
``--dns-ovh-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
|
||||
OVH 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
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a certificate for ``example.com``
|
||||
|
||||
certbot certonly \\
|
||||
--dns-ovh \\
|
||||
--dns-ovh-credentials ~/.secrets/certbot/ohv.ini \\
|
||||
-d example.com
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a single certificate for both ``example.com`` and
|
||||
``www.example.com``
|
||||
|
||||
certbot certonly \\
|
||||
--dns-ovh \\
|
||||
--dns-ovh-credentials ~/.secrets/certbot/ovh.ini \\
|
||||
-d example.com \\
|
||||
-d www.example.com
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: To acquire a certificate for ``example.com``, waiting 60 seconds
|
||||
for DNS propagation
|
||||
|
||||
certbot certonly \\
|
||||
--dns-ovh \\
|
||||
--dns-ovh-credentials ~/.secrets/certbot/ovh.ini \\
|
||||
--dns-ovh-propagation-seconds 60 \\
|
||||
-d example.com
|
||||
|
||||
"""
|
||||
@@ -1,102 +0,0 @@
|
||||
"""DNS Authenticator for OVH DNS."""
|
||||
import logging
|
||||
|
||||
import zope.interface
|
||||
from lexicon.providers import ovh
|
||||
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot.plugins import dns_common
|
||||
from certbot.plugins import dns_common_lexicon
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TOKEN_URL = 'https://eu.api.ovh.com/createToken/ or https://ca.api.ovh.com/createToken/'
|
||||
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
class Authenticator(dns_common.DNSAuthenticator):
|
||||
"""DNS Authenticator for OVH
|
||||
|
||||
This Authenticator uses the OVH API to fulfill a dns-01 challenge.
|
||||
"""
|
||||
|
||||
description = 'Obtain certificates using a DNS TXT record (if you are using OVH for DNS).'
|
||||
ttl = 60
|
||||
|
||||
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=30)
|
||||
add('credentials', help='OVH 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 OVH API.'
|
||||
|
||||
def _setup_credentials(self):
|
||||
self.credentials = self._configure_credentials(
|
||||
'credentials',
|
||||
'OVH credentials INI file',
|
||||
{
|
||||
'endpoint': 'OVH API endpoint (ovh-eu or ovh-ca)',
|
||||
'application-key': 'Application key for OVH API, obtained from {0}'
|
||||
.format(TOKEN_URL),
|
||||
'application-secret': 'Application secret for OVH API, obtained from {0}'
|
||||
.format(TOKEN_URL),
|
||||
'consumer-key': 'Consumer key for OVH API, obtained from {0}'
|
||||
.format(TOKEN_URL),
|
||||
}
|
||||
)
|
||||
|
||||
def _perform(self, domain, validation_name, validation):
|
||||
self._get_ovh_client().add_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _cleanup(self, domain, validation_name, validation):
|
||||
self._get_ovh_client().del_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _get_ovh_client(self):
|
||||
return _OVHLexiconClient(
|
||||
self.credentials.conf('endpoint'),
|
||||
self.credentials.conf('application-key'),
|
||||
self.credentials.conf('application-secret'),
|
||||
self.credentials.conf('consumer-key'),
|
||||
self.ttl
|
||||
)
|
||||
|
||||
|
||||
class _OVHLexiconClient(dns_common_lexicon.LexiconClient):
|
||||
"""
|
||||
Encapsulates all communication with the OVH API via Lexicon.
|
||||
"""
|
||||
|
||||
def __init__(self, endpoint, application_key, application_secret, consumer_key, ttl):
|
||||
super(_OVHLexiconClient, self).__init__()
|
||||
|
||||
self.provider = ovh.Provider({
|
||||
'auth_entrypoint': endpoint,
|
||||
'auth_application_key': application_key,
|
||||
'auth_application_secret': application_secret,
|
||||
'auth_consumer_key': consumer_key,
|
||||
'ttl': ttl,
|
||||
})
|
||||
|
||||
def _handle_http_error(self, e, domain_name):
|
||||
hint = None
|
||||
if str(e).startswith('400 Client Error:'):
|
||||
hint = 'Is your Application Secret value correct?'
|
||||
if str(e).startswith('403 Client Error:'):
|
||||
hint = 'Are your Application Key and Consumer Key values correct?'
|
||||
|
||||
return errors.PluginError('Error determining zone identifier for {0}: {1}.{2}'
|
||||
.format(domain_name, e, ' ({0})'.format(hint) if hint else ''))
|
||||
|
||||
def _handle_general_error(self, e, domain_name):
|
||||
if domain_name in str(e) and str(e).endswith('not found'):
|
||||
return
|
||||
|
||||
super(_OVHLexiconClient, self)._handle_general_error(e, domain_name)
|
||||
@@ -1,62 +0,0 @@
|
||||
"""Tests for certbot_dns_ovh.dns_ovh."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from certbot.plugins import dns_test_common
|
||||
from certbot.plugins import dns_test_common_lexicon
|
||||
from certbot.tests import util as test_util
|
||||
|
||||
ENDPOINT = 'ovh-eu'
|
||||
APPLICATION_KEY = 'foo'
|
||||
APPLICATION_SECRET = 'bar'
|
||||
CONSUMER_KEY = 'spam'
|
||||
|
||||
|
||||
class AuthenticatorTest(test_util.TempDirTestCase,
|
||||
dns_test_common_lexicon.BaseLexiconAuthenticatorTest):
|
||||
|
||||
def setUp(self):
|
||||
super(AuthenticatorTest, self).setUp()
|
||||
|
||||
from certbot_dns_ovh.dns_ovh import Authenticator
|
||||
|
||||
path = os.path.join(self.tempdir, 'file.ini')
|
||||
credentials = {
|
||||
"ovh_endpoint": ENDPOINT,
|
||||
"ovh_application_key": APPLICATION_KEY,
|
||||
"ovh_application_secret": APPLICATION_SECRET,
|
||||
"ovh_consumer_key": CONSUMER_KEY,
|
||||
}
|
||||
dns_test_common.write(credentials, path)
|
||||
|
||||
self.config = mock.MagicMock(ovh_credentials=path,
|
||||
ovh_propagation_seconds=0) # don't wait during tests
|
||||
|
||||
self.auth = Authenticator(self.config, "ovh")
|
||||
|
||||
self.mock_client = mock.MagicMock()
|
||||
# _get_ovh_client | pylint: disable=protected-access
|
||||
self.auth._get_ovh_client = mock.MagicMock(return_value=self.mock_client)
|
||||
|
||||
|
||||
class OVHLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconClientTest):
|
||||
DOMAIN_NOT_FOUND = Exception('Domain example.com not found')
|
||||
LOGIN_ERROR = HTTPError('403 Client Error: Forbidden for url: https://eu.api.ovh.com/1.0/...')
|
||||
|
||||
def setUp(self):
|
||||
from certbot_dns_ovh.dns_ovh import _OVHLexiconClient
|
||||
|
||||
self.client = _OVHLexiconClient(
|
||||
ENDPOINT, APPLICATION_KEY, APPLICATION_SECRET, CONSUMER_KEY, 0
|
||||
)
|
||||
|
||||
self.provider_mock = mock.MagicMock()
|
||||
self.client.provider = self.provider_mock
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
1
certbot-dns-ovh/docs/.gitignore
vendored
1
certbot-dns-ovh/docs/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/_build/
|
||||
@@ -1,20 +0,0 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = certbot-dns-ovh
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -1,8 +0,0 @@
|
||||
=================
|
||||
API Documentation
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
||||
api/**
|
||||
@@ -1,5 +0,0 @@
|
||||
:mod:`certbot_dns_ovh.dns_ovh`
|
||||
------------------------------
|
||||
|
||||
.. automodule:: certbot_dns_ovh.dns_ovh
|
||||
:members:
|
||||
@@ -1,180 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# certbot-dns-ovh documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Jan 12 10:14:31 2018.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode']
|
||||
|
||||
autodoc_member_order = 'bysource'
|
||||
autodoc_default_flags = ['show-inheritance', 'private-members']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'certbot-dns-ovh'
|
||||
copyright = u'2018, Certbot Project'
|
||||
author = u'Certbot Project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'en'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
default_role = 'py:obj'
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
|
||||
# http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs
|
||||
# on_rtd is whether we are on readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
# otherwise, readthedocs.org uses their theme by default, so no need to specify it
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'certbot-dns-ovhdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'certbot-dns-ovh.tex', u'certbot-dns-ovh Documentation',
|
||||
u'Certbot Project', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'certbot-dns-ovh', u'certbot-dns-ovh Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'certbot-dns-ovh', u'certbot-dns-ovh Documentation',
|
||||
author, 'certbot-dns-ovh', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.. certbot-dns-ovh documentation master file, created by
|
||||
sphinx-quickstart on Fri Jan 12 10:14:31 2018.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to certbot-dns-ovh's documentation!
|
||||
===========================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
.. automodule:: certbot_dns_ovh
|
||||
:members:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -1,36 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=certbot-dns-ovh
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -1,2 +0,0 @@
|
||||
acme[dev]==0.21.1
|
||||
certbot[dev]==0.21.1
|
||||
@@ -1,12 +0,0 @@
|
||||
# readthedocs.org gives no way to change the install command to "pip
|
||||
# install -e .[docs]" (that would in turn install documentation
|
||||
# dependencies), but it allows to specify a requirements.txt file at
|
||||
# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259)
|
||||
|
||||
# Although ReadTheDocs certainly doesn't need to install the project
|
||||
# in --editable mode (-e), just "pip install .[docs]" does not work as
|
||||
# expected and "pip install -e .[docs]" must be used instead
|
||||
|
||||
-e acme
|
||||
-e .
|
||||
-e certbot-dns-ovh[docs]
|
||||
@@ -1,2 +0,0 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
@@ -1,67 +0,0 @@
|
||||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
|
||||
'sphinx_rtd_theme',
|
||||
]
|
||||
|
||||
setup(
|
||||
name='certbot-dns-ovh',
|
||||
version=version,
|
||||
description="OVH DNS Authenticator plugin for Certbot",
|
||||
url='https://github.com/certbot/certbot',
|
||||
author="Certbot Project",
|
||||
author_email='client-dev@letsencrypt.org',
|
||||
license='Apache License 2.0',
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'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,
|
||||
extras_require={
|
||||
'docs': docs_extras,
|
||||
},
|
||||
entry_points={
|
||||
'certbot.plugins': [
|
||||
'dns-ovh = certbot_dns_ovh.dns_ovh:Authenticator',
|
||||
],
|
||||
},
|
||||
test_suite='certbot_dns_ovh',
|
||||
)
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -42,7 +42,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.26.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
@@ -36,7 +36,6 @@ setup(
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM certbot/certbot
|
||||
|
||||
COPY . src/certbot-dns-sakuracloud
|
||||
|
||||
RUN pip install --no-cache-dir --editable src/certbot-dns-sakuracloud
|
||||
@@ -1,190 +0,0 @@
|
||||
Copyright 2018 Electronic Frontier Foundation and others
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,3 +0,0 @@
|
||||
include LICENSE.txt
|
||||
include README.rst
|
||||
recursive-include docs *
|
||||
@@ -1 +0,0 @@
|
||||
Sakura Cloud DNS Authenticator plugin for Certbot
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user