Compare commits

..

15 Commits

Author SHA1 Message Date
Brad Warren
0360ac47d6 test docker only 2020-12-16 10:34:10 -08:00
Adrien Ferrand
2ced0de4fe Use proper flags with pip 2020-12-11 19:04:13 +01:00
Adrien Ferrand
693abd4a27 Update snap/snapcraft.yaml
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
2020-12-11 19:00:58 +01:00
Adrien Ferrand
ef6d46d808 Merge branch 'master' into reenable-build-isolation 2020-12-11 12:57:42 +01:00
Adrien Ferrand
ec2441f773 Merge branch 'master' into reenable-build-isolation
# Conflicts:
#	snap/snapcraft.yaml
2020-12-10 21:37:15 +01:00
Adrien Ferrand
b5104e9168 Add back --no-build-cache option in Docker images build 2020-12-10 21:28:37 +01:00
Adrien Ferrand
75299aa1ff Add a comment 2020-12-10 21:23:57 +01:00
Adrien Ferrand
4aeda88633 Fix pipstrap constraints in snap build 2020-11-11 23:20:44 +01:00
Adrien Ferrand
09ec53ccf2 Prepare installer build 2020-11-11 22:35:26 +01:00
Adrien Ferrand
429bbfbf39 Update snapcraft build 2020-11-11 20:16:41 +01:00
Adrien Ferrand
f10d4cca62 Update docker build 2020-11-11 20:16:23 +01:00
Adrien Ferrand
602a74e452 Inject pipstrap constraints during pip_install 2020-11-11 20:16:04 +01:00
Adrien Ferrand
6fa3472db5 Externalize pipstrap reqs to be reusable 2020-11-11 19:53:11 +01:00
Adrien Ferrand
1e3f56ef1b Clean imports in pipstrap 2020-11-11 18:29:52 +01:00
Adrien Ferrand
96cf1e7ea9 Use workaround to fix build requirements in build isolation, and renable build isolation 2020-11-11 17:49:51 +01:00
151 changed files with 1255 additions and 1480 deletions

View File

@@ -5,4 +5,3 @@ pr:
jobs:
- template: templates/jobs/standard-tests-jobs.yml

View File

@@ -22,21 +22,25 @@ jobs:
TOXENV: py37
CERTBOT_NO_PIN: 1
linux-boulder-v1-integration-certbot-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-certbot-oldest
ACME_SERVER: boulder-v1
linux-boulder-v2-integration-certbot-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-certbot-oldest
ACME_SERVER: boulder-v2
linux-boulder-v1-integration-nginx-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-nginx-oldest
ACME_SERVER: boulder-v1
linux-boulder-v2-integration-nginx-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-nginx-oldest
ACME_SERVER: boulder-v2
linux-boulder-v1-py27-integration:
PYTHON_VERSION: 2.7
TOXENV: integration
ACME_SERVER: boulder-v1
linux-boulder-v2-py27-integration:
PYTHON_VERSION: 2.7
TOXENV: integration
ACME_SERVER: boulder-v2
linux-boulder-v1-py36-integration:
PYTHON_VERSION: 3.6
TOXENV: integration

View File

@@ -50,168 +50,3 @@ jobs:
do docker run --rm "${DOCKER_IMAGE}" plugins --prepare
done
displayName: Run integration tests for Docker images
- job: installer_build
pool:
vmImage: vs2017-win2016
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.8
architecture: x86
addToPath: true
- script: python windows-installer/construct.py
displayName: Build Certbot installer
- task: CopyFiles@2
inputs:
sourceFolder: $(System.DefaultWorkingDirectory)/windows-installer/build/nsis
contents: '*.exe'
targetFolder: $(Build.ArtifactStagingDirectory)
- task: PublishPipelineArtifact@1
inputs:
path: $(Build.ArtifactStagingDirectory)
# If we change the artifact's name, it should also be changed in tools/create_github_release.py
artifact: windows-installer
displayName: Publish Windows installer
- job: installer_run
dependsOn: installer_build
strategy:
matrix:
win2019:
imageName: windows-2019
win2016:
imageName: vs2017-win2016
pool:
vmImage: $(imageName)
steps:
- powershell: |
if ($PSVersionTable.PSVersion.Major -ne 5) {
throw "Powershell version is not 5.x"
}
condition: eq(variables['imageName'], 'vs2017-win2016')
displayName: Check Powershell 5.x is used in vs2017-win2016
- task: UsePythonVersion@0
inputs:
versionSpec: 3.8
addToPath: true
- task: DownloadPipelineArtifact@2
inputs:
artifact: windows-installer
path: $(Build.SourcesDirectory)/bin
displayName: Retrieve Windows installer
- script: |
python -m venv venv
venv\Scripts\python tools\pipstrap.py
venv\Scripts\python tools\pip_install.py -e certbot-ci
env:
PIP_NO_BUILD_ISOLATION: no
displayName: Prepare Certbot-CI
- script: |
set PATH=%ProgramFiles(x86)%\Certbot\bin;%PATH%
venv\Scripts\python -m pytest certbot-ci\windows_installer_integration_tests --allow-persistent-changes --installer-path $(Build.SourcesDirectory)\bin\certbot-beta-installer-win32.exe
displayName: Run windows installer integration tests
- script: |
set PATH=%ProgramFiles(x86)%\Certbot\bin;%PATH%
venv\Scripts\python -m pytest certbot-ci\certbot_integration_tests\certbot_tests -n 4
displayName: Run certbot integration tests
- job: snaps_build
pool:
vmImage: ubuntu-18.04
timeoutInMinutes: 0
variables:
# Do not run the heavy non-amd64 builds for test branches
${{ if not(startsWith(variables['Build.SourceBranchName'], 'test-')) }}:
ARCHS: amd64 arm64 armhf
${{ if startsWith(variables['Build.SourceBranchName'], 'test-') }}:
ARCHS: amd64
steps:
- script: |
set -e
sudo apt-get update
sudo apt-get install -y --no-install-recommends snapd
sudo snap install --classic snapcraft
displayName: Install dependencies
- task: UsePythonVersion@0
inputs:
versionSpec: 3.8
addToPath: true
- task: DownloadSecureFile@1
name: credentials
inputs:
secureFile: launchpad-credentials
- script: |
set -e
git config --global user.email "$(Build.RequestedForEmail)"
git config --global user.name "$(Build.RequestedFor)"
mkdir -p ~/.local/share/snapcraft/provider/launchpad
cp $(credentials.secureFilePath) ~/.local/share/snapcraft/provider/launchpad/credentials
python3 tools/snap/build_remote.py ALL --archs ${ARCHS} --timeout 19800
displayName: Build snaps
- script: |
set -e
mv *.snap $(Build.ArtifactStagingDirectory)
mv certbot-dns-*/*.snap $(Build.ArtifactStagingDirectory)
displayName: Prepare artifacts
- task: PublishPipelineArtifact@1
inputs:
path: $(Build.ArtifactStagingDirectory)
artifact: snaps
displayName: Store snaps artifacts
- job: snap_run
dependsOn: snaps_build
pool:
vmImage: ubuntu-18.04
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.8
addToPath: true
- script: |
set -e
sudo apt-get update
sudo apt-get install -y --no-install-recommends nginx-light snapd
python3 -m venv venv
venv/bin/python tools/pipstrap.py
venv/bin/python tools/pip_install.py -U tox
displayName: Install dependencies
- task: DownloadPipelineArtifact@2
inputs:
artifact: snaps
path: $(Build.SourcesDirectory)/snap
displayName: Retrieve Certbot snaps
- script: |
set -e
sudo snap install --dangerous --classic snap/certbot_*_amd64.snap
displayName: Install Certbot snap
- script: |
set -e
venv/bin/python -m tox -e integration-external,apacheconftest-external-with-pebble
displayName: Run tox
- job: snap_dns_run
dependsOn: snaps_build
pool:
vmImage: ubuntu-18.04
steps:
- script: |
set -e
sudo apt-get update
sudo apt-get install -y --no-install-recommends snapd
displayName: Install dependencies
- task: UsePythonVersion@0
inputs:
versionSpec: 3.8
addToPath: true
- task: DownloadPipelineArtifact@2
inputs:
artifact: snaps
path: $(Build.SourcesDirectory)/snap
displayName: Retrieve Certbot snaps
- script: |
set -e
python3 -m venv venv
venv/bin/python tools/pipstrap.py
venv/bin/python tools/pip_install.py -e certbot-ci
displayName: Prepare Certbot-CI
- script: |
set -e
sudo -E venv/bin/pytest certbot-ci/snap_integration_tests/dns_tests --allow-persistent-changes --snap-folder $(Build.SourcesDirectory)/snap --snap-arch amd64
displayName: Test DNS plugins snaps

View File

@@ -4,10 +4,10 @@ jobs:
PYTHON_VERSION: 3.9
strategy:
matrix:
macos-py36:
macos-py27:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.6
TOXENV: py36
PYTHON_VERSION: 2.7
TOXENV: py27
macos-py39:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.9
@@ -16,22 +16,24 @@ jobs:
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.6
TOXENV: py36
windows-py38-cover:
windows-py37-cover:
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.8
TOXENV: py38-cover
PYTHON_VERSION: 3.7
TOXENV: py37-cover
windows-integration-certbot:
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.8
PYTHON_VERSION: 3.7
TOXENV: integration-certbot
linux-oldest-tests-1:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
TOXENV: '{acme,apache,apache-v2,certbot}-oldest'
TOXENV: py27-{acme,apache,apache-v2,certbot}-oldest
linux-oldest-tests-2:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
TOXENV: '{dns,nginx}-oldest'
TOXENV: py27-{dns,nginx}-oldest
linux-py27:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 2.7
TOXENV: py27
linux-py36:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
@@ -61,18 +63,13 @@ jobs:
TOXENV: modification
apacheconftest:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
PYTHON_VERSION: 2.7
TOXENV: apacheconftest-with-pebble
nginxroundtrip:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
PYTHON_VERSION: 2.7
TOXENV: nginxroundtrip
pool:
vmImage: $(IMAGE_NAME)
steps:
- template: ../steps/tox-steps.yml
- job: test_sphinx_builds
pool:
vmImage: ubuntu-latest
steps:
- template: ../steps/sphinx-steps.yml

View File

@@ -1,6 +1,4 @@
stages:
- stage: TestAndPackage
jobs:
- template: ../jobs/standard-tests-jobs.yml
- template: ../jobs/extended-tests-jobs.yml
- template: ../jobs/packaging-jobs.yml

View File

@@ -1,23 +0,0 @@
steps:
- bash: |
FINAL_STATUS=0
declare -a FAILED_BUILDS
python3 -m venv .venv
source .venv/bin/activate
python tools/pipstrap.py
for doc_path in */docs
do
echo ""
echo "##[group]Building $doc_path"
pip install -q -e $doc_path/..[docs]
if ! sphinx-build -W --keep-going -b html $doc_path $doc_path/_build/html; then
FINAL_STATUS=1
FAILED_BUILDS[${#FAILED_BUILDS[@]}]="${doc_path%/docs}"
fi
echo "##[endgroup]"
done
if [[ $FINAL_STATUS -ne 0 ]]; then
echo "##[error]The following builds failed: ${FAILED_BUILDS[*]}"
exit 1
fi
displayName: Build Sphinx Documentation

View File

@@ -45,7 +45,11 @@ steps:
export TARGET_BRANCH="`echo "${BUILD_SOURCEBRANCH}" | sed -E 's!refs/(heads|tags)/!!g'`"
[ -z "${SYSTEM_PULLREQUEST_TARGETBRANCH}" ] || export TARGET_BRANCH="${SYSTEM_PULLREQUEST_TARGETBRANCH}"
env
python -m tox
if [[ "${TOXENV}" == *"oldest"* ]]; then
tools/run_oldest_tests.sh
else
python -m tox
fi
env:
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)

View File

@@ -1,7 +1,6 @@
Authors
=======
* [Aaron Gable](https://github.com/aarongable)
* [Aaron Zirbes](https://github.com/aaronzirbes)
* Aaron Zuehlke
* Ada Lovelace
@@ -61,7 +60,6 @@ Authors
* [DanCld](https://github.com/DanCld)
* [Daniel Albers](https://github.com/AID)
* [Daniel Aleksandersen](https://github.com/da2x)
* [Daniel Almasi](https://github.com/almasen)
* [Daniel Convissor](https://github.com/convissor)
* [Daniel "Drex" Drexler](https://github.com/aeturnum)
* [Daniel Huang](https://github.com/dhuang)
@@ -151,7 +149,6 @@ Authors
* [Lior Sabag](https://github.com/liorsbg)
* [Lipis](https://github.com/lipis)
* [lord63](https://github.com/lord63)
* [Lorenzo Fundaró](https://github.com/lfundaro)
* [Luca Beltrame](https://github.com/lbeltrame)
* [Luca Ebach](https://github.com/lucebac)
* [Luca Olivetti](https://github.com/olivluca)

View File

@@ -6,6 +6,7 @@ This module is an implementation of the `ACME protocol`_.
"""
import sys
import warnings
# This code exists to keep backwards compatibility with people using acme.jose
# before it became the standalone josepy package.
@@ -19,3 +20,10 @@ for mod in list(sys.modules):
# preserved (acme.jose.* is josepy.*)
if mod == 'josepy' or mod.startswith('josepy.'):
sys.modules['acme.' + mod.replace('josepy', 'jose', 1)] = sys.modules[mod]
if sys.version_info[0] == 2:
warnings.warn(
"Python 2 support will be dropped in the next release of acme. "
"Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover

View File

@@ -150,7 +150,7 @@ class KeyAuthorizationChallenge(_TokenChallenge):
"""Challenge based on Key Authorization.
:param response_cls: Subclass of `KeyAuthorizationChallengeResponse`
that will be used to generate ``response``.
that will be used to generate `response`.
:param str typ: type of the challenge
"""
typ = NotImplemented

View File

@@ -166,7 +166,7 @@ def probe_sni(name, host, port=443, timeout=300, # pylint: disable=too-many-argu
" from {0}:{1}".format(
source_address[0],
source_address[1]
) if any(source_address) else ""
) if socket_kwargs else ""
)
socket_tuple = (host, port) # type: Tuple[str, int]
sock = socket.create_connection(socket_tuple, **socket_kwargs) # type: ignore
@@ -186,7 +186,6 @@ def probe_sni(name, host, port=443, timeout=300, # pylint: disable=too-many-argu
raise errors.Error(error)
return client_ssl.get_peer_certificate()
def make_csr(private_key_pem, domains, must_staple=False):
"""Generate a CSR containing a list of domains as subjectAltNames.
@@ -218,7 +217,6 @@ def make_csr(private_key_pem, domains, must_staple=False):
return crypto.dump_certificate_request(
crypto.FILETYPE_PEM, csr)
def _pyopenssl_cert_or_req_all_names(loaded_cert_or_req):
common_name = loaded_cert_or_req.get_subject().CN
sans = _pyopenssl_cert_or_req_san(loaded_cert_or_req)
@@ -227,7 +225,6 @@ def _pyopenssl_cert_or_req_all_names(loaded_cert_or_req):
return sans
return [common_name] + [d for d in sans if d != common_name]
def _pyopenssl_cert_or_req_san(cert_or_req):
"""Get Subject Alternative Names from certificate or CSR using pyOpenSSL.
@@ -320,7 +317,6 @@ def gen_ss_cert(key, domains, not_before=None,
cert.sign(key, "sha256")
return cert
def dump_pyopenssl_chain(chain, filetype=crypto.FILETYPE_PEM):
"""Dump certificate chain into a bundle.

View File

@@ -49,7 +49,7 @@ class MissingNonce(NonceError):
Replay-Nonce header field in each successful response to a POST it
provides to a client (...)".
:ivar requests.Response ~.response: HTTP Response
:ivar requests.Response response: HTTP Response
"""
def __init__(self, response, *args, **kwargs):

View File

@@ -275,7 +275,7 @@ class Resource(jose.JSONObjectWithFields):
class ResourceWithURI(Resource):
"""ACME Resource with URI.
:ivar unicode ~.uri: Location of the resource.
:ivar unicode uri: Location of the resource.
"""
uri = jose.Field('uri') # no ChallengeResource.uri
@@ -627,7 +627,7 @@ class Order(ResourceBody):
:ivar str finalize: URL to POST to to request issuance once all
authorizations have "valid" status.
:ivar datetime.datetime expires: When the order expires.
:ivar ~.Error error: Any error that occurred during finalization, if applicable.
:ivar .Error error: Any error that occurred during finalization, if applicable.
"""
identifiers = jose.Field('identifiers', omitempty=True)
status = jose.Field('status', decoder=Status.from_json,

View File

@@ -85,10 +85,7 @@ language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = [
'_build',
'man/*'
]
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.

View File

@@ -5,22 +5,25 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
'cryptography>=2.1.4',
# load_pem_private/public_key (>=0.6)
# rsa_recover_prime_factors (>=0.8)
'cryptography>=1.2.3',
# formerly known as acme.jose:
# 1.1.0+ is required to avoid the warnings described at
# https://github.com/certbot/josepy/issues/13.
'josepy>=1.1.0',
'PyOpenSSL>=17.3.0',
# Connection.set_tlsext_host_name (>=0.13) + matching Xenial requirements (>=0.15.1)
'PyOpenSSL>=0.15.1',
'pyrfc3339',
'pytz',
'requests[security]>=2.6.0', # security extras added in 2.4.1
'requests-toolbelt>=0.3.0',
'setuptools>=39.0.1',
'six>=1.11.0',
'setuptools',
'six>=1.9.0', # needed for python_2_unicode_compatible
]
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
@@ -51,12 +54,14 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -14,10 +14,10 @@ class OpenSUSEConfigurator(configurator.ApacheConfigurator):
vhost_root="/etc/apache2/vhosts.d",
vhost_files="*.conf",
logs_root="/var/log/apache2",
ctl="apachectl",
version_cmd=['apachectl', '-v'],
restart_cmd=['apachectl', 'graceful'],
conftest_cmd=['apachectl', 'configtest'],
ctl="apache2ctl",
version_cmd=['apache2ctl', '-v'],
restart_cmd=['apache2ctl', 'graceful'],
conftest_cmd=['apache2ctl', 'configtest'],
enmod="a2enmod",
dismod="a2dismod",
le_vhost_ext="-le-ssl.conf",

View File

@@ -5,7 +5,7 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -13,7 +13,7 @@ install_requires = [
'acme>=0.29.0',
'certbot>=1.6.0',
'python-augeas',
'setuptools>=39.0.1',
'setuptools',
'zope.component',
'zope.interface',
]
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -47,6 +47,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -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="1.11.0"
LE_AUTO_VERSION="1.10.1"
BASENAME=$(basename $0)
USAGE="Usage: $BASENAME [OPTIONS]
A self-updating wrapper script for the Certbot ACME client. When run, updates
@@ -804,7 +804,6 @@ elif [ -f /etc/mageia-release ]; then
# Mageia has both /etc/mageia-release and /etc/redhat-release
DEPRECATED_OS=1
elif [ -f /etc/redhat-release ]; then
DEPRECATED_OS=1
# Run DeterminePythonVersion to decide on the basis of available Python versions
# whether to use 2.x or 3.x on RedHat-like systems.
# Then, revert LE_PYTHON to its previous state.
@@ -837,7 +836,12 @@ elif [ -f /etc/redhat-release ]; then
INTERACTIVE_BOOTSTRAP=1
fi
Bootstrap() {
BootstrapMessage "Legacy RedHat-based OSes that will use Python3"
BootstrapRpmPython3Legacy
}
USE_PYTHON_3=1
BOOTSTRAP_VERSION="BootstrapRpmPython3Legacy $BOOTSTRAP_RPM_PYTHON3_LEGACY_VERSION"
# Try now to enable SCL rh-python36 for systems already bootstrapped
# NB: EnablePython36SCL has been defined along with BootstrapRpmPython3Legacy in certbot-auto
@@ -856,7 +860,18 @@ elif [ -f /etc/redhat-release ]; then
fi
if [ "$RPM_USE_PYTHON_3" = 1 ]; then
Bootstrap() {
BootstrapMessage "RedHat-based OSes that will use Python3"
BootstrapRpmPython3
}
USE_PYTHON_3=1
BOOTSTRAP_VERSION="BootstrapRpmPython3 $BOOTSTRAP_RPM_PYTHON3_VERSION"
else
Bootstrap() {
BootstrapMessage "RedHat-based OSes"
BootstrapRpmCommon
}
BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
fi
fi
@@ -874,7 +889,10 @@ elif uname | grep -iq FreeBSD ; then
elif uname | grep -iq Darwin ; then
DEPRECATED_OS=1
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
DEPRECATED_OS=1
Bootstrap() {
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
}
BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then
DEPRECATED_OS=1
else
@@ -1475,18 +1493,18 @@ letsencrypt==0.7.0 \
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
certbot==1.11.0 \
--hash=sha256:b7faa66c40a1ce5a31bfc8668d8feb5d2db6f7af9e791079a6d95c77b6593bf4 \
--hash=sha256:6b0ce04e55379aff0a47f873fa05c084538ad0f4a9b79f33108dbb0a7a668b43
acme==1.11.0 \
--hash=sha256:77d6ce61b155315d7d7031489bbd245c0ea42c0453a04d4304393414e741a56d \
--hash=sha256:092eb09a074a935da4c10f66cb8634ffb2cc2d2cc1035d2998d608996efab924
certbot-apache==1.11.0 \
--hash=sha256:ea7ac88733aad91a89c700289effda2a0c0658778da1ae2c54a0aefaee351285 \
--hash=sha256:3ed001427ec0b49324f2b9af7170fa6e6e88948fa51c3678b07bf17f8138863d
certbot-nginx==1.11.0 \
--hash=sha256:79de69782a1199e577787ff9790dee02a44aac17dbecd6a7287593030842a306 \
--hash=sha256:9afe611f99a78b8898941b8ad7bdcf7f3c2b6e0fce27125268f7c713e64b34ee
certbot==1.10.1 \
--hash=sha256:011ac980fa21b9f29e02c9b8d8b86e8a4bf4670b51b6ad91656e401e9d2d2231 \
--hash=sha256:0d9ee3fc09e0d03b2d1b1f1c4916e61ecfc6904b4216ddef4e6a5ca1424d9cb7
acme==1.10.1 \
--hash=sha256:752d598e54e98ad1e874de53fd50c61044f1b566d6deb790db5676ce9c573546 \
--hash=sha256:fcbb559aedc96b404edf593e78517dcd7291984d5a37036c3fc77f3c5c122fd8
certbot-apache==1.10.1 \
--hash=sha256:f077b4b7f166627ef5e0921fe7cde57700670fc86e9ad9dbdfaf2c573cc0f2fa \
--hash=sha256:97ed637b4c7b03820db6c69aa90145dc989933351d46a3d62baf6b71674f0a10
certbot-nginx==1.10.1 \
--hash=sha256:7c36459021f8a1ec3b6c062e4c4fc866bfaa1dbf26ccd29e043dd6848003be08 \
--hash=sha256:c0bbeccf85f46b728fd95e6bb8c2649d32d3383d7f47ea4b9c312d12bf04d2f0
UNLIKELY_EOF
# -------------------------------------------------------------------------

View File

@@ -1,4 +1,3 @@
# pylint: disable=missing-module-docstring
import pytest
# Custom assertions defined in the following package need to be registered to be properly

View File

@@ -77,6 +77,6 @@ class IntegrationTestsContext(object):
appending the pytest worker id to the subdomain, using this pattern:
{subdomain}.{worker_id}.wtf
:param subdomain: the subdomain to use in the generated domain (default 'le')
:return: the well-formed domain suitable for redirection on
:return: the well-formed domain suitable for redirection on
"""
return '{0}.{1}.wtf'.format(subdomain, self.worker_id)

View File

@@ -9,7 +9,7 @@ import shutil
import subprocess
import time
from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1, SECP384R1, SECP521R1
from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1, SECP384R1
from cryptography.x509 import NameOID
import pytest
@@ -29,9 +29,8 @@ from certbot_integration_tests.certbot_tests.assertions import EVERYBODY_SID
from certbot_integration_tests.utils import misc
@pytest.fixture(name='context')
def test_context(request):
# pylint: disable=missing-function-docstring
@pytest.fixture()
def context(request):
# Fixture request is a built-in pytest fixture describing current test request.
integration_test_context = certbot_context.IntegrationTestsContext(request)
try:
@@ -148,17 +147,6 @@ def test_certonly(context):
"""Test the certonly verb on certbot."""
context.certbot(['certonly', '--cert-name', 'newname', '-d', context.get_domain('newname')])
assert_cert_count_for_lineage(context.config_dir, 'newname', 1)
def test_certonly_webroot(context):
"""Test the certonly verb with webroot plugin"""
with misc.create_http_server(context.http_01_port) as webroot:
certname = context.get_domain('webroot')
context.certbot(['certonly', '-a', 'webroot', '--webroot-path', webroot, '-d', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 1)
def test_auth_and_install_with_csr(context):
"""Test certificate issuance and install using an existing CSR."""
@@ -234,16 +222,14 @@ def test_renew_files_propagate_permissions(context):
if os.name != 'nt':
os.chmod(privkey1, 0o444)
else:
import win32security # pylint: disable=import-error
import ntsecuritycon # pylint: disable=import-error
import win32security
import ntsecuritycon
# Get the current DACL of the private key
security = win32security.GetFileSecurity(privkey1, win32security.DACL_SECURITY_INFORMATION)
dacl = security.GetSecurityDescriptorDacl()
# Create a read permission for Everybody group
everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID)
dacl.AddAccessAllowedAce(
win32security.ACL_REVISION, ntsecuritycon.FILE_GENERIC_READ, everybody
)
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, ntsecuritycon.FILE_GENERIC_READ, everybody)
# Apply the updated DACL to the private key
security.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(privkey1, win32security.DACL_SECURITY_INFORMATION, security)
@@ -252,14 +238,12 @@ def test_renew_files_propagate_permissions(context):
assert_cert_count_for_lineage(context.config_dir, certname, 2)
if os.name != 'nt':
# On Linux, read world permissions + all group permissions
# will be copied from the previous private key
# On Linux, read world permissions + all group permissions will be copied from the previous private key
assert_world_read_permissions(privkey2)
assert_equals_world_read_permissions(privkey1, privkey2)
assert_equals_group_permissions(privkey1, privkey2)
else:
# On Windows, world will never have any permissions, and
# group permission is irrelevant for this platform
# On Windows, world will never have any permissions, and group permission is irrelevant for this platform
assert_world_no_permissions(privkey2)
@@ -487,28 +471,6 @@ def test_default_curve_type(context):
assert_elliptic_key(key1, SECP256R1)
@pytest.mark.parametrize('curve,curve_cls,skip_servers', [
# Curve name, Curve class, ACME servers to skip
('secp256r1', SECP256R1, []),
('secp384r1', SECP384R1, []),
('secp521r1', SECP521R1, ['boulder-v1', 'boulder-v2'])]
)
def test_ecdsa_curves(context, curve, curve_cls, skip_servers):
"""Test issuance for each supported ECDSA curve"""
if context.acme_server in skip_servers:
pytest.skip('ACME server {} does not support ECDSA curve {}'
.format(context.acme_server, curve))
domain = context.get_domain('curve')
context.certbot([
'certonly',
'--key-type', 'ecdsa', '--elliptic-curve', curve,
'--force-renewal', '-d', domain,
])
key = join(context.config_dir, "live", domain, 'privkey.pem')
assert_elliptic_key(key, curve_cls)
def test_renew_with_ec_keys(context):
"""Test proper renew with updated private key complexity."""
certname = context.get_domain('renew')
@@ -647,22 +609,19 @@ def test_revoke_multiple_lineages(context):
with open(join(context.config_dir, 'renewal', '{0}.conf'.format(cert2)), 'r') as file:
data = file.read()
data = re.sub(
'archive_dir = .*\n',
'archive_dir = {0}\n'.format(
join(context.config_dir, 'archive', cert1).replace('\\', '\\\\')
), data
)
data = re.sub('archive_dir = .*\n',
'archive_dir = {0}\n'.format(join(context.config_dir, 'archive', cert1).replace('\\', '\\\\')),
data)
with open(join(context.config_dir, 'renewal', '{0}.conf'.format(cert2)), 'w') as file:
file.write(data)
context.certbot([
output = context.certbot([
'revoke', '--cert-path', join(context.config_dir, 'live', cert1, 'cert.pem')
])
with open(join(context.workspace, 'logs', 'letsencrypt.log'), 'r') as f:
assert 'Not deleting revoked certificates due to overlapping archive dirs' in f.read()
assert 'Not deleting revoked certs due to overlapping archive dirs' in f.read()
def test_wildcard_certificates(context):

View File

@@ -13,6 +13,7 @@ import sys
from certbot_integration_tests.utils import acme_server as acme_lib
from certbot_integration_tests.utils import dns_server as dns_lib
from certbot_integration_tests.utils.dns_server import DNSServer
def pytest_addoption(parser):
@@ -91,10 +92,8 @@ def _setup_primary_node(config):
try:
subprocess.check_output(['docker-compose', '-v'], stderr=subprocess.STDOUT)
except (subprocess.CalledProcessError, OSError):
raise ValueError(
'Error: docker-compose is required in PATH to launch the integration tests, '
'but is not installed or not available for current user.'
)
raise ValueError('Error: docker-compose is required in PATH to launch the integration tests, '
'but is not installed or not available for current user.')
# Parameter numprocesses is added to option by pytest-xdist
workers = ['primary'] if not config.option.numprocesses\

View File

@@ -1,4 +1,3 @@
"""Module to handle the context of nginx integration tests."""
import os
import subprocess

View File

@@ -2,14 +2,13 @@
import os
import ssl
from typing import List
import pytest
from certbot_integration_tests.nginx_tests import context as nginx_context
@pytest.fixture(name='context')
def test_context(request):
@pytest.fixture()
def context(request):
# Fixture request is a built-in pytest fixture describing current test request.
integration_test_context = nginx_context.IntegrationTestsContext(request)
try:
@@ -28,12 +27,10 @@ def test_context(request):
# No matching server block; default_server does not exist
('nginx5.{0}.wtf', ['--preferred-challenges', 'http'], {'default_server': False}),
# Multiple domains, mix of matching and not
('nginx6.{0}.wtf,nginx7.{0}.wtf', [
'--preferred-challenges', 'http'
], {'default_server': False}),
('nginx6.{0}.wtf,nginx7.{0}.wtf', ['--preferred-challenges', 'http'], {'default_server': False}),
], indirect=['context'])
def test_certificate_deployment(certname_pattern, params, context):
# type: (str, List[str], nginx_context.IntegrationTestsContext) -> None
# type: (str, list, nginx_context.IntegrationTestsContext) -> None
"""
Test various scenarios to deploy a certificate to nginx using certbot.
"""
@@ -44,9 +41,7 @@ def test_certificate_deployment(certname_pattern, params, context):
lineage = domains.split(',')[0]
server_cert = ssl.get_server_certificate(('localhost', context.tls_alpn_01_port))
with open(os.path.join(
context.workspace, 'conf/live/{0}/cert.pem'.format(lineage)), 'r'
) as file:
with open(os.path.join(context.workspace, 'conf/live/{0}/cert.pem'.format(lineage)), 'r') as file:
certbot_cert = file.read()
assert server_cert == certbot_cert

View File

@@ -1,10 +1,7 @@
"""Module to handle the context of RFC2136 integration tests."""
import tempfile
from contextlib import contextmanager
from pkg_resources import resource_filename
from pytest import skip
from pkg_resources import resource_filename
import tempfile
from certbot_integration_tests.certbot_tests import context as certbot_context
from certbot_integration_tests.utils import certbot_call
@@ -36,6 +33,7 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
@contextmanager
def rfc2136_credentials(self, label='default'):
# type: (str) -> str
"""
Produces the contents of a certbot-dns-rfc2136 credentials file.
:param str label: which RFC2136 credential to use
@@ -54,10 +52,10 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
)
with tempfile.NamedTemporaryFile('w+', prefix='rfc2136-creds-{}'.format(label),
suffix='.ini', dir=self.workspace) as fp:
fp.write(contents)
fp.flush()
yield fp.name
suffix='.ini', dir=self.workspace) as f:
f.write(contents)
f.flush()
yield f.name
def skip_if_no_bind9_server(self):
"""Skips the test if there was no RFC2136-capable DNS server configured

View File

@@ -4,9 +4,8 @@ import pytest
from certbot_integration_tests.rfc2136_tests import context as rfc2136_context
@pytest.fixture(name="context")
def pytest_context(request):
# pylint: disable=missing-function-docstring
@pytest.fixture()
def context(request):
# Fixture request is a built-in pytest fixture describing current test request.
integration_test_context = rfc2136_context.IntegrationTestsContext(request)
try:

View File

@@ -7,19 +7,18 @@ import errno
import json
import os
from os.path import join
import re
import shutil
import subprocess
import sys
import tempfile
import time
from typing import List
import requests
from certbot_integration_tests.utils import misc
from certbot_integration_tests.utils import pebble_artifacts
from certbot_integration_tests.utils import proxy
# pylint: disable=wildcard-import,unused-wildcard-import
from certbot_integration_tests.utils.constants import *
@@ -32,11 +31,10 @@ class ACMEServer(object):
ACMEServer gives access the acme_xdist parameter, listing the ports and directory url to use
for each pytest node. It exposes also start and stop methods in order to start the stack, and
stop it with proper resources cleanup.
ACMEServer is also a context manager, and so can be used to ensure ACME server is
started/stopped upon context enter/exit.
ACMEServer is also a context manager, and so can be used to ensure ACME server is started/stopped
upon context enter/exit.
"""
def __init__(self, acme_server, nodes, http_proxy=True, stdout=False,
dns_server=None, http_01_port=DEFAULT_HTTP_01_PORT):
def __init__(self, acme_server, nodes, http_proxy=True, stdout=False, dns_server=None):
"""
Create an ACMEServer instance.
:param str acme_server: the type of acme server used (boulder-v1, boulder-v2 or pebble)
@@ -44,22 +42,15 @@ class ACMEServer(object):
:param bool http_proxy: if False do not start the HTTP proxy
:param bool stdout: if True stream all subprocesses stdout to standard stdout
:param str dns_server: if set, Pebble/Boulder will use it to resolve domains
:param int http_01_port: port to use for http-01 validation; currently
only supported for pebble without an HTTP proxy
"""
self._construct_acme_xdist(acme_server, nodes)
self._acme_type = 'pebble' if acme_server == 'pebble' else 'boulder'
self._proxy = http_proxy
self._workspace = tempfile.mkdtemp()
self._processes = [] # type: List[subprocess.Popen]
self._processes = []
self._stdout = sys.stdout if stdout else open(os.devnull, 'w')
self._dns_server = dns_server
self._http_01_port = http_01_port
if http_01_port != DEFAULT_HTTP_01_PORT:
if self._acme_type != 'pebble' or self._proxy:
raise ValueError('setting http_01_port is not currently supported '
'with boulder or the HTTP proxy')
def start(self):
"""Start the test stack"""
@@ -116,34 +107,26 @@ class ACMEServer(object):
"""Generate and return the acme_xdist dict"""
acme_xdist = {'acme_server': acme_server, 'challtestsrv_port': CHALLTESTSRV_PORT}
# Directory and ACME port are set implicitly in the docker-compose.yml
# files of Boulder/Pebble.
# Directory and ACME port are set implicitly in the docker-compose.yml files of Boulder/Pebble.
if acme_server == 'pebble':
acme_xdist['directory_url'] = PEBBLE_DIRECTORY_URL
else: # boulder
acme_xdist['directory_url'] = BOULDER_V2_DIRECTORY_URL \
if acme_server == 'boulder-v2' else BOULDER_V1_DIRECTORY_URL
acme_xdist['http_port'] = {
node: port for (node, port) in # pylint: disable=unnecessary-comprehension
zip(nodes, range(5200, 5200 + len(nodes)))
}
acme_xdist['https_port'] = {
node: port for (node, port) in # pylint: disable=unnecessary-comprehension
zip(nodes, range(5100, 5100 + len(nodes)))
}
acme_xdist['other_port'] = {
node: port for (node, port) in # pylint: disable=unnecessary-comprehension
zip(nodes, range(5300, 5300 + len(nodes)))
}
acme_xdist['http_port'] = {node: port for (node, port)
in zip(nodes, range(5200, 5200 + len(nodes)))}
acme_xdist['https_port'] = {node: port for (node, port)
in zip(nodes, range(5100, 5100 + len(nodes)))}
acme_xdist['other_port'] = {node: port for (node, port)
in zip(nodes, range(5300, 5300 + len(nodes)))}
self.acme_xdist = acme_xdist
def _prepare_pebble_server(self):
"""Configure and launch the Pebble server"""
print('=> Starting pebble instance deployment...')
pebble_artifacts_rv = pebble_artifacts.fetch(self._workspace, self._http_01_port)
pebble_path, challtestsrv_path, pebble_config_path = pebble_artifacts_rv
pebble_path, challtestsrv_path, pebble_config_path = pebble_artifacts.fetch(self._workspace)
# Configure Pebble at full speed (PEBBLE_VA_NOSLEEP=1) and not randomly refusing valid
# nonce (PEBBLE_WFE_NONCEREJECT=0) to have a stable test environment.
@@ -167,9 +150,9 @@ class ACMEServer(object):
env=environ)
# pebble_ocsp_server is imported here and not at the top of module in order to avoid a
# useless ImportError, in the case where cryptography dependency is too old to support
# ocsp, but Boulder is used instead of Pebble, so pebble_ocsp_server is not used. This is
# the typical situation of integration-certbot-oldest tox testenv.
# useless ImportError, in the case where cryptography dependency is too old to support ocsp,
# but Boulder is used instead of Pebble, so pebble_ocsp_server is not used. This is the
# typical situation of integration-certbot-oldest tox testenv.
from certbot_integration_tests.utils import pebble_ocsp_server
self._launch_process([sys.executable, pebble_ocsp_server.__file__])
@@ -212,16 +195,13 @@ class ACMEServer(object):
if not self._dns_server:
# Configure challtestsrv to answer any A record request with ip of the docker host.
response = requests.post('http://localhost:{0}/set-default-ipv4'.format(
CHALLTESTSRV_PORT), json={'ip': '10.77.77.1'}
)
response = requests.post('http://localhost:{0}/set-default-ipv4'.format(CHALLTESTSRV_PORT),
json={'ip': '10.77.77.1'})
response.raise_for_status()
except BaseException:
# If we failed to set up boulder, print its logs.
print('=> Boulder setup failed. Boulder logs are:')
process = self._launch_process([
'docker-compose', 'logs'], cwd=instance_path, force_stderr=True
)
process = self._launch_process(['docker-compose', 'logs'], cwd=instance_path, force_stderr=True)
process.wait()
raise
@@ -232,7 +212,7 @@ class ACMEServer(object):
print('=> Configuring the HTTP proxy...')
mapping = {r'.+\.{0}\.wtf'.format(node): 'http://127.0.0.1:{0}'.format(port)
for node, port in self.acme_xdist['http_port'].items()}
command = [sys.executable, proxy.__file__, str(DEFAULT_HTTP_01_PORT), json.dumps(mapping)]
command = [sys.executable, proxy.__file__, str(HTTP_01_PORT), json.dumps(mapping)]
self._launch_process(command)
print('=> Finished configuring the HTTP proxy.')
@@ -241,15 +221,12 @@ class ACMEServer(object):
if not env:
env = os.environ
stdout = sys.stderr if force_stderr else self._stdout
process = subprocess.Popen(
command, stdout=stdout, stderr=subprocess.STDOUT, cwd=cwd, env=env
)
process = subprocess.Popen(command, stdout=stdout, stderr=subprocess.STDOUT, cwd=cwd, env=env)
self._processes.append(process)
return process
def main():
# pylint: disable=missing-function-docstring
parser = argparse.ArgumentParser(
description='CLI tool to start a local instance of Pebble or Boulder CA server.')
parser.add_argument('--server-type', '-s',
@@ -260,15 +237,9 @@ def main():
help='specify the DNS server as `IP:PORT` to use by '
'Pebble; if not specified, a local mock DNS server will be used to '
'resolve domains to localhost.')
parser.add_argument('--http-01-port', type=int, default=DEFAULT_HTTP_01_PORT,
help='specify the port to use for http-01 validation; '
'this is currently only supported for Pebble.')
args = parser.parse_args()
acme_server = ACMEServer(
args.server_type, [], http_proxy=False, stdout=True,
dns_server=args.dns_server, http_01_port=args.http_01_port,
)
acme_server = ACMEServer(args.server_type, [], http_proxy=False, stdout=True, dns_server=args.dns_server)
try:
with acme_server as acme_xdist:

View File

@@ -2,13 +2,12 @@
"""Module to call certbot in test mode"""
from __future__ import absolute_import
from distutils.version import LooseVersion
import os
import subprocess
import sys
from distutils.version import LooseVersion
import certbot_integration_tests
# pylint: disable=wildcard-import,unused-wildcard-import
from certbot_integration_tests.utils.constants import *
@@ -36,8 +35,6 @@ def certbot_test(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
def _prepare_environ(workspace):
# pylint: disable=missing-function-docstring
new_environ = os.environ.copy()
new_environ['TMPDIR'] = workspace
@@ -61,13 +58,8 @@ def _prepare_environ(workspace):
# certbot_integration_tests.__file__ is:
# '/path/to/certbot/certbot-ci/certbot_integration_tests/__init__.pyc'
# ... and we want '/path/to/certbot'
certbot_root = os.path.dirname(os.path.dirname(
os.path.dirname(certbot_integration_tests.__file__))
)
python_paths = [
path for path in new_environ['PYTHONPATH'].split(':')
if path != certbot_root
]
certbot_root = os.path.dirname(os.path.dirname(os.path.dirname(certbot_integration_tests.__file__)))
python_paths = [path for path in new_environ['PYTHONPATH'].split(':') if path != certbot_root]
new_environ['PYTHONPATH'] = ':'.join(python_paths)
return new_environ
@@ -78,8 +70,7 @@ def _compute_additional_args(workspace, environ, force_renew):
output = subprocess.check_output(['certbot', '--version'],
universal_newlines=True, stderr=subprocess.STDOUT,
cwd=workspace, env=environ)
# Typical response is: output = 'certbot 0.31.0.dev0'
version_str = output.split(' ')[1].strip()
version_str = output.split(' ')[1].strip() # Typical response is: output = 'certbot 0.31.0.dev0'
if LooseVersion(version_str) >= LooseVersion('0.30.0'):
additional_args.append('--no-random-sleep-on-renew')
@@ -122,12 +113,11 @@ def _prepare_args_env(certbot_args, directory_url, http_01_port, tls_alpn_01_por
def main():
# pylint: disable=missing-function-docstring
args = sys.argv[1:]
# Default config is pebble
directory_url = os.environ.get('SERVER', PEBBLE_DIRECTORY_URL)
http_01_port = int(os.environ.get('HTTP_01_PORT', DEFAULT_HTTP_01_PORT))
http_01_port = int(os.environ.get('HTTP_01_PORT', HTTP_01_PORT))
tls_alpn_01_port = int(os.environ.get('TLS_ALPN_01_PORT', TLS_ALPN_01_PORT))
# Execution of certbot in a self-contained workspace

View File

@@ -1,5 +1,5 @@
"""Some useful constants to use throughout certbot-ci integration tests"""
DEFAULT_HTTP_01_PORT = 5002
HTTP_01_PORT = 5002
TLS_ALPN_01_PORT = 5001
CHALLTESTSRV_PORT = 8055
BOULDER_V1_DIRECTORY_URL = 'http://localhost:4000/directory'
@@ -7,4 +7,4 @@ BOULDER_V2_DIRECTORY_URL = 'http://localhost:4001/directory'
PEBBLE_DIRECTORY_URL = 'https://localhost:14000/dir'
PEBBLE_MANAGEMENT_URL = 'https://localhost:15000'
MOCK_OCSP_SERVER_PORT = 4002
PEBBLE_ALTERNATE_ROOTS = 2
PEBBLE_ALTERNATE_ROOTS = 2

View File

@@ -4,6 +4,7 @@ from __future__ import print_function
import os
import os.path
from pkg_resources import resource_filename
import shutil
import socket
import subprocess
@@ -11,14 +12,13 @@ import sys
import tempfile
import time
from pkg_resources import resource_filename
BIND_DOCKER_IMAGE = "internetsystemsconsortium/bind9:9.16"
BIND_BIND_ADDRESS = ("127.0.0.1", 45953)
BIND_DOCKER_IMAGE = 'internetsystemsconsortium/bind9:9.16'
BIND_BIND_ADDRESS = ('127.0.0.1', 45953)
# A TCP DNS message which is a query for '. CH A' transaction ID 0xcb37. This is used
# by _wait_until_ready to check that BIND is responding without depending on dnspython.
BIND_TEST_QUERY = bytearray.fromhex("0011cb37000000010000000000000000010003")
BIND_TEST_QUERY = bytearray.fromhex('0011cb37000000010000000000000000010003')
class DNSServer(object):
@@ -31,7 +31,7 @@ class DNSServer(object):
future to support parallelization (https://github.com/certbot/certbot/issues/8455).
"""
def __init__(self, unused_nodes, show_output=False):
def __init__(self, nodes, show_output=False):
"""
Create an DNSServer instance.
:param list nodes: list of node names that will be setup by pytest xdist
@@ -40,13 +40,16 @@ class DNSServer(object):
self.bind_root = tempfile.mkdtemp()
self.process = None # type: subprocess.Popen
self.process = None
self.dns_xdist = {"address": BIND_BIND_ADDRESS[0], "port": BIND_BIND_ADDRESS[1]}
self.dns_xdist = {
'address': BIND_BIND_ADDRESS[0],
'port': BIND_BIND_ADDRESS[1]
}
# Unfortunately the BIND9 image forces everything to stderr with -g and we can't
# modify the verbosity.
self._output = sys.stderr if show_output else open(os.devnull, "w")
self._output = sys.stderr if show_output else open(os.devnull, 'w')
def start(self):
"""Start the DNS server"""
@@ -60,11 +63,11 @@ class DNSServer(object):
def stop(self):
"""Stop the DNS server, and clean its resources"""
if self.process:
try:
self.process.terminate()
self.process.wait()
except BaseException as e:
print("BIND9 did not stop cleanly: {}".format(e), file=sys.stderr)
try:
self.process.terminate()
self.process.wait()
except BaseException as e:
print("BIND9 did not stop cleanly: {}".format(e), file=sys.stderr)
shutil.rmtree(self.bind_root, ignore_errors=True)
@@ -73,79 +76,65 @@ class DNSServer(object):
def _configure_bind(self):
"""Configure the BIND9 server based on the prebaked configuration"""
bind_conf_src = resource_filename(
"certbot_integration_tests", "assets/bind-config"
)
for directory in ("conf", "zones"):
shutil.copytree(
os.path.join(bind_conf_src, directory), os.path.join(self.bind_root, directory)
)
bind_conf_src = resource_filename('certbot_integration_tests', 'assets/bind-config')
for dir in ('conf', 'zones'):
shutil.copytree(os.path.join(bind_conf_src, dir), os.path.join(self.bind_root, dir))
def _start_bind(self):
"""Launch the BIND9 server as a Docker container"""
addr_str = "{}:{}".format(BIND_BIND_ADDRESS[0], BIND_BIND_ADDRESS[1])
self.process = subprocess.Popen(
[
"docker",
"run",
"--rm",
"-p",
"{}:53/udp".format(addr_str),
"-p",
"{}:53/tcp".format(addr_str),
"-v",
"{}/conf:/etc/bind".format(self.bind_root),
"-v",
"{}/zones:/var/lib/bind".format(self.bind_root),
BIND_DOCKER_IMAGE,
],
stdout=self._output,
stderr=self._output,
)
addr_str = '{}:{}'.format(BIND_BIND_ADDRESS[0], BIND_BIND_ADDRESS[1])
self.process = subprocess.Popen([
'docker', 'run', '--rm',
'-p', '{}:53/udp'.format(addr_str),
'-p', '{}:53/tcp'.format(addr_str),
'-v', '{}/conf:/etc/bind'.format(self.bind_root),
'-v', '{}/zones:/var/lib/bind'.format(self.bind_root),
BIND_DOCKER_IMAGE
], stdout=self._output, stderr=self._output)
if self.process.poll():
raise ValueError("BIND9 server stopped unexpectedly")
raise("BIND9 server stopped unexpectedly")
try:
self._wait_until_ready()
self._wait_until_ready()
except:
# The container might be running even if we think it isn't
self.stop()
raise
# The container might be running even if we think it isn't
self.stop()
raise
def _wait_until_ready(self, attempts=30):
# type: (int) -> None
"""
Polls the DNS server over TCP until it gets a response, or until
it runs out of attempts and raises a ValueError.
The DNS response message must match the txn_id of the DNS query message,
but otherwise the contents are ignored.
:param int attempts: The number of attempts to make.
"""
for _ in range(attempts):
if self.process.poll():
raise ValueError("BIND9 server stopped unexpectedly")
# type: (int) -> None
"""
Polls the DNS server over TCP until it gets a response, or until
it runs out of attempts and raises a ValueError.
The DNS response message must match the txn_id of the DNS query message,
but otherwise the contents are ignored.
:param int attempts: The number of attempts to make.
"""
for _ in range(attempts):
if self.process.poll():
raise ValueError('BIND9 server stopped unexpectedly')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
try:
sock.connect(BIND_BIND_ADDRESS)
sock.sendall(BIND_TEST_QUERY)
buf = sock.recv(1024)
# We should receive a DNS message with the same tx_id
if buf and len(buf) > 4 and buf[2:4] == BIND_TEST_QUERY[2:4]:
return
# If we got a response but it wasn't the one we wanted, wait a little
time.sleep(1)
except: # pylint: disable=bare-except
# If there was a network error, wait a little
time.sleep(1)
finally:
sock.close()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
try:
sock.connect(BIND_BIND_ADDRESS)
sock.sendall(BIND_TEST_QUERY)
buf = sock.recv(1024)
# We should receive a DNS message with the same tx_id
if buf and len(buf) > 4 and buf[2:4] == BIND_TEST_QUERY[2:4]:
return
# If we got a response but it wasn't the one we wanted, wait a little
time.sleep(1)
except:
# If there was a network error, wait a little
time.sleep(1)
pass
finally:
sock.close()
raise ValueError(
"Gave up waiting for DNS server {} to respond".format(BIND_BIND_ADDRESS)
)
raise ValueError(
'Gave up waiting for DNS server {} to respond'.format(BIND_BIND_ADDRESS))
def __enter__(self):
self.start()

View File

@@ -39,7 +39,6 @@ def _suppress_x509_verification_warnings():
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
except ImportError:
# Handle old versions of request with vendorized urllib3
# pylint: disable=no-member
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
@@ -257,8 +256,7 @@ def generate_csr(domains, key_path, csr_path, key_type=RSA_KEY_TYPE):
def read_certificate(cert_path):
"""
Load the certificate from the provided path, and return a human readable version
of it (TEXT mode).
Load the certificate from the provided path, and return a human readable version of it (TEXT mode).
:param str cert_path: the path to the certificate
:returns: the TEXT version of the certificate, as it would be displayed by openssl binary
"""

View File

@@ -1,5 +1,3 @@
# pylint: disable=missing-module-docstring
import json
import os
import stat
@@ -7,19 +5,18 @@ import stat
import pkg_resources
import requests
from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT, MOCK_OCSP_SERVER_PORT
from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT
PEBBLE_VERSION = 'v2.3.0'
ASSETS_PATH = pkg_resources.resource_filename('certbot_integration_tests', 'assets')
def fetch(workspace, http_01_port=DEFAULT_HTTP_01_PORT):
# pylint: disable=missing-function-docstring
def fetch(workspace):
suffix = 'linux-amd64' if os.name != 'nt' else 'windows-amd64.exe'
pebble_path = _fetch_asset('pebble', suffix)
challtestsrv_path = _fetch_asset('pebble-challtestsrv', suffix)
pebble_config_path = _build_pebble_config(workspace, http_01_port)
pebble_config_path = _build_pebble_config(workspace)
return pebble_path, challtestsrv_path, pebble_config_path
@@ -38,7 +35,7 @@ def _fetch_asset(asset, suffix):
return asset_path
def _build_pebble_config(workspace, http_01_port):
def _build_pebble_config(workspace):
config_path = os.path.join(workspace, 'pebble-config.json')
with open(config_path, 'w') as file_h:
file_h.write(json.dumps({
@@ -47,7 +44,7 @@ def _build_pebble_config(workspace, http_01_port):
'managementListenAddress': '0.0.0.0:15000',
'certificate': os.path.join(ASSETS_PATH, 'cert.pem'),
'privateKey': os.path.join(ASSETS_PATH, 'key.pem'),
'httpPort': http_01_port,
'httpPort': 5002,
'tlsPort': 5001,
'ocspResponderURL': 'http://127.0.0.1:{0}'.format(MOCK_OCSP_SERVER_PORT),
},

View File

@@ -21,7 +21,6 @@ from certbot_integration_tests.utils.misc import GracefulTCPServer
class _ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# pylint: disable=missing-function-docstring
def do_POST(self):
request = requests.get(PEBBLE_MANAGEMENT_URL + '/intermediate-keys/0', verify=False)
issuer_key = serialization.load_pem_private_key(request.content, None, default_backend())
@@ -36,28 +35,20 @@ class _ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
ocsp_request = ocsp.load_der_ocsp_request(self.rfile.read(content_len))
response = requests.get('{0}/cert-status-by-serial/{1}'.format(
PEBBLE_MANAGEMENT_URL, str(hex(ocsp_request.serial_number)).replace('0x', '')),
verify=False
)
PEBBLE_MANAGEMENT_URL, str(hex(ocsp_request.serial_number)).replace('0x', '')), verify=False)
if not response.ok:
ocsp_response = ocsp.OCSPResponseBuilder.build_unsuccessful(
ocsp.OCSPResponseStatus.UNAUTHORIZED
)
ocsp_response = ocsp.OCSPResponseBuilder.build_unsuccessful(ocsp.OCSPResponseStatus.UNAUTHORIZED)
else:
data = response.json()
now = datetime.datetime.utcnow()
cert = x509.load_pem_x509_certificate(data['Certificate'].encode(), default_backend())
if data['Status'] != 'Revoked':
ocsp_status = ocsp.OCSPCertStatus.GOOD
revocation_time = None
revocation_reason = None
ocsp_status, revocation_time, revocation_reason = ocsp.OCSPCertStatus.GOOD, None, None
else:
ocsp_status = ocsp.OCSPCertStatus.REVOKED
revocation_reason = x509.ReasonFlags.unspecified
# "... +0000 UTC" => "+0000"
revoked_at = re.sub(r'( \+\d{4}).*$', r'\1', data['RevokedAt'])
ocsp_status, revocation_reason = ocsp.OCSPCertStatus.REVOKED, x509.ReasonFlags.unspecified
revoked_at = re.sub(r'( \+\d{4}).*$', r'\1', data['RevokedAt']) # "... +0000 UTC" => "+0000"
revocation_time = parser.parse(revoked_at)
ocsp_response = ocsp.OCSPResponseBuilder().add_response(

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env python
# pylint: disable=missing-module-docstring
import json
import re
import sys
@@ -12,9 +10,7 @@ from certbot_integration_tests.utils.misc import GracefulTCPServer
def _create_proxy(mapping):
# pylint: disable=missing-function-docstring
class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# pylint: disable=missing-class-docstring
def do_GET(self):
headers = {key.lower(): value for key, value in self.headers.items()}
backend = [backend for pattern, backend in mapping.items()

View File

@@ -18,7 +18,7 @@ install_requires = [
'python-dateutil',
'pyyaml',
'requests',
'six'
'six',
]
# Add pywin32 on Windows platforms to handle low-level system calls.
@@ -40,12 +40,14 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -5,7 +5,7 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
install_requires = [
'certbot',
@@ -38,12 +38,14 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_cloudflare.dns_cloudflare` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the Cloudflare API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'cloudflare>=1.5.1',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_cloudxns.dns_cloudxns` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the CloudXNS API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_digitalocean.dns_digitalocean` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the DigitalOcean API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -19,8 +19,7 @@ class Authenticator(dns_common.DNSAuthenticator):
This Authenticator uses the DigitalOcean API to fulfill a dns-01 challenge.
"""
description = 'Obtain certificates using a DNS TXT record (if you are ' + \
'using DigitalOcean for DNS).'
description = 'Obtain certs using a DNS TXT record (if you are using DigitalOcean for DNS).'
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,14 +6,14 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'python-digitalocean>=1.11',
'setuptools>=39.0.1',
'six>=1.11.0',
'setuptools',
'six',
'zope.interface',
]
@@ -50,7 +50,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -58,6 +58,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_dnsimple.dns_dnsimple` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the DNSimple API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,12 +6,12 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -60,7 +60,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -68,6 +68,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_dnsmadeeasy.dns_dnsmadeeasy` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the DNS Made Easy API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ 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 Infrastructure Service DNS API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,12 +6,12 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
'dns-lexicon>=2.1.22',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -48,7 +48,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -56,6 +56,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_google.dns_google` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the Google Cloud DNS API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -118,13 +118,10 @@ class _GoogleClient(object):
record_contents = self.get_existing_txt_rrset(zone_id, record_name)
if record_contents is None:
# If it wasn't possible to fetch the records at this label (missing .list permission),
# assume there aren't any (#5678). If there are actually records here, this will fail
# with HTTP 409/412 API errors.
record_contents = {"rrdatas": []}
add_records = record_contents["rrdatas"][:]
record_contents = []
add_records = record_contents[:]
if "\""+record_content+"\"" in record_contents["rrdatas"]:
if "\""+record_content+"\"" in record_contents:
# The process was interrupted previously and validation token exists
return
@@ -143,15 +140,15 @@ class _GoogleClient(object):
],
}
if record_contents["rrdatas"]:
if record_contents:
# We need to remove old records in the same request
data["deletions"] = [
{
"kind": "dns#resourceRecordSet",
"type": "TXT",
"name": record_name + ".",
"rrdatas": record_contents["rrdatas"],
"ttl": record_contents["ttl"],
"rrdatas": record_contents,
"ttl": record_ttl,
},
]
@@ -191,10 +188,7 @@ class _GoogleClient(object):
record_contents = self.get_existing_txt_rrset(zone_id, record_name)
if record_contents is None:
# If it wasn't possible to fetch the records at this label (missing .list permission),
# assume there aren't any (#5678). If there are actually records here, this will fail
# with HTTP 409/412 API errors.
record_contents = {"rrdatas": ["\"" + record_content + "\""], "ttl": record_ttl}
record_contents = ["\"" + record_content + "\""]
data = {
"kind": "dns#change",
@@ -203,15 +197,14 @@ class _GoogleClient(object):
"kind": "dns#resourceRecordSet",
"type": "TXT",
"name": record_name + ".",
"rrdatas": record_contents["rrdatas"],
"ttl": record_contents["ttl"],
"rrdatas": record_contents,
"ttl": record_ttl,
},
],
}
# Remove the record being deleted from the list
readd_contents = [r for r in record_contents["rrdatas"]
if r != "\"" + record_content + "\""]
readd_contents = [r for r in record_contents if r != "\"" + record_content + "\""]
if readd_contents:
# We need to remove old records in the same request
data["additions"] = [
@@ -220,7 +213,7 @@ class _GoogleClient(object):
"type": "TXT",
"name": record_name + ".",
"rrdatas": readd_contents,
"ttl": record_contents["ttl"],
"ttl": record_ttl,
},
]
@@ -242,15 +235,14 @@ class _GoogleClient(object):
:param str zone_id: The ID of the managed zone.
:param str record_name: The record name (typically beginning with '_acme-challenge.').
:returns: The resourceRecordSet corresponding to `record_name` or None
:rtype: `resourceRecordSet <https://cloud.google.com/dns/docs/reference/v1/resourceRecordSets#resource>` or `None` # pylint: disable=line-too-long
:returns: List of TXT record values or None
:rtype: `list` of `string` or `None`
"""
rrs_request = self.dns.resourceRecordSets()
request = rrs_request.list(managedZone=zone_id, project=self.project_id)
# Add dot as the API returns absolute domains
record_name += "."
request = rrs_request.list(project=self.project_id, managedZone=zone_id, name=record_name,
type="TXT")
try:
response = request.execute()
except googleapiclient_errors.Error:
@@ -258,8 +250,10 @@ class _GoogleClient(object):
"requesting a wildcard certificate, this might not work.")
logger.debug("Error was:", exc_info=True)
else:
if response and response["rrsets"]:
return response["rrsets"][0]
if response:
for rr in response["rrsets"]:
if rr["name"] == record_name and rr["type"] == "TXT":
return rr["rrdatas"]
return None
def _find_managed_zone_id(self, domain):

View File

@@ -112,7 +112,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,14 +6,14 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'google-api-python-client>=1.5.5',
'oauth2client>=4.0',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
# already a dependency of google-api-python-client, but added for consistency
'httplib2'
@@ -52,7 +52,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -60,6 +60,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -70,7 +70,7 @@ class GoogleClientTest(unittest.TestCase):
zone = "ZONE_ID"
change = "an-id"
def _setUp_client_with_mock(self, zone_request_side_effect, rrs_list_side_effect=None):
def _setUp_client_with_mock(self, zone_request_side_effect):
from certbot_dns_google._internal.dns_google import _GoogleClient
pwd = os.path.dirname(__file__)
@@ -86,16 +86,9 @@ class GoogleClientTest(unittest.TestCase):
mock_mz.list.return_value.execute.side_effect = zone_request_side_effect
mock_rrs = mock.MagicMock()
def rrs_list(project=None, managedZone=None, name=None, type=None):
response = {"rrsets": []}
if name == "_acme-challenge.example.org.":
response = {"rrsets": [{"name": "_acme-challenge.example.org.", "type": "TXT",
"rrdatas": ["\"example-txt-contents\""], "ttl": 60}]}
mock_return = mock.MagicMock()
mock_return.execute.return_value = response
mock_return.execute.side_effect = rrs_list_side_effect
return mock_return
mock_rrs.list.side_effect = rrs_list
rrsets = {"rrsets": [{"name": "_acme-challenge.example.org.", "type": "TXT",
"rrdatas": ["\"example-txt-contents\""]}]}
mock_rrs.list.return_value.execute.return_value = rrsets
mock_changes = mock.MagicMock()
client.dns.managedZones = mock.MagicMock(return_value=mock_mz)
@@ -180,29 +173,11 @@ class GoogleClientTest(unittest.TestCase):
# pylint: disable=line-too-long
mock_get_rrs = "certbot_dns_google._internal.dns_google._GoogleClient.get_existing_txt_rrset"
with mock.patch(mock_get_rrs) as mock_rrs:
mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": self.record_ttl}
mock_rrs.return_value = ["sample-txt-contents"]
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.assertTrue(changes.create.called)
deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
self.assertTrue("sample-txt-contents" in deletions["rrdatas"])
self.assertEqual(self.record_ttl, deletions["ttl"])
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_add_txt_record_delete_old_ttl_case(self, unused_credential_mock):
client, changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}])
# pylint: disable=line-too-long
mock_get_rrs = "certbot_dns_google._internal.dns_google._GoogleClient.get_existing_txt_rrset"
with mock.patch(mock_get_rrs) as mock_rrs:
custom_ttl = 300
mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": custom_ttl}
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.assertTrue(changes.create.called)
deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
self.assertTrue("sample-txt-contents" in deletions["rrdatas"])
self.assertEqual(custom_ttl, deletions["ttl"]) #otherwise HTTP 412
self.assertTrue("sample-txt-contents" in
changes.create.call_args_list[0][1]["body"]["deletions"][0]["rrdatas"])
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@@ -246,13 +221,14 @@ class GoogleClientTest(unittest.TestCase):
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_del_txt_record_multi_rrdatas(self, unused_credential_mock):
def test_del_txt_record(self, unused_credential_mock):
client, changes = self._setUp_client_with_mock([{'managedZones': [{'id': self.zone}]}])
# pylint: disable=line-too-long
mock_get_rrs = "certbot_dns_google._internal.dns_google._GoogleClient.get_existing_txt_rrset"
with mock.patch(mock_get_rrs) as mock_rrs:
mock_rrs.return_value = {"rrdatas": ["\"sample-txt-contents\"",
"\"example-txt-contents\""], "ttl": self.record_ttl}
mock_rrs.return_value = ["\"sample-txt-contents\"",
"\"example-txt-contents\""]
client.del_txt_record(DOMAIN, "_acme-challenge.example.org",
"example-txt-contents", self.record_ttl)
@@ -282,51 +258,22 @@ class GoogleClientTest(unittest.TestCase):
managedZone=self.zone,
project=PROJECT_ID)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_del_txt_record_single_rrdatas(self, unused_credential_mock):
client, changes = self._setUp_client_with_mock([{'managedZones': [{'id': self.zone}]}])
# pylint: disable=line-too-long
mock_get_rrs = "certbot_dns_google._internal.dns_google._GoogleClient.get_existing_txt_rrset"
with mock.patch(mock_get_rrs) as mock_rrs:
mock_rrs.return_value = {"rrdatas": ["\"example-txt-contents\""], "ttl": self.record_ttl}
client.del_txt_record(DOMAIN, "_acme-challenge.example.org",
"example-txt-contents", self.record_ttl)
expected_body = {
"kind": "dns#change",
"deletions": [
{
"kind": "dns#resourceRecordSet",
"type": "TXT",
"name": "_acme-challenge.example.org.",
"rrdatas": ["\"example-txt-contents\""],
"ttl": self.record_ttl,
},
],
}
changes.create.assert_called_with(body=expected_body,
managedZone=self.zone,
project=PROJECT_ID)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_del_txt_record_error_during_zone_lookup(self, unused_credential_mock):
client, changes = self._setUp_client_with_mock(API_ERROR)
client, unused_changes = self._setUp_client_with_mock(API_ERROR)
client.del_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
changes.create.assert_not_called()
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_del_txt_record_zone_not_found(self, unused_credential_mock):
client, changes = self._setUp_client_with_mock([{'managedZones': []},
client, unused_changes = self._setUp_client_with_mock([{'managedZones': []},
{'managedZones': []}])
client.del_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
changes.create.assert_not_called()
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@@ -340,39 +287,24 @@ class GoogleClientTest(unittest.TestCase):
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_get_existing_found(self, unused_credential_mock):
def test_get_existing(self, unused_credential_mock):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}])
# Record name mocked in setUp
found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertEqual(found["rrdatas"], ["\"example-txt-contents\""])
self.assertEqual(found["ttl"], 60)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_get_existing_not_found(self, unused_credential_mock):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}])
self.assertEqual(found, ["\"example-txt-contents\""])
not_found = client.get_existing_txt_rrset(self.zone, "nonexistent.tld")
self.assertEqual(not_found, None)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_get_existing_with_error(self, unused_credential_mock):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}], API_ERROR)
# Record name mocked in setUp
found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertEqual(found, None)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
mock.mock_open(read_data='{"project_id": "' + PROJECT_ID + '"}'), create=True)
def test_get_existing_fallback(self, unused_credential_mock):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}], API_ERROR)
[{'managedZones': [{'id': self.zone}]}])
mock_execute = client.dns.resourceRecordSets.return_value.list.return_value.execute
mock_execute.side_effect = API_ERROR
rrset = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertFalse(rrset)

View File

@@ -3,10 +3,6 @@ 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.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -24,7 +24,7 @@ class Authenticator(dns_common.DNSAuthenticator):
This Authenticator uses the Linode API to fulfill a dns-01 challenge.
"""
description = 'Obtain certificates using a DNS TXT record (if you are using Linode for DNS).'
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)

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,12 +6,12 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
'dns-lexicon>=2.2.3',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -48,7 +48,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -56,6 +56,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_luadns.dns_luadns` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the LuaDNS API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_nsone.dns_nsone` plugin automates the process of completing
a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and subsequently
removing, TXT records using the NS1 API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ 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.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.7.14', # Correct proxy use on OVH provider
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_rfc2136.dns_rfc2136` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using RFC 2136 Dynamic Updates.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -1,3 +1,13 @@
# type: ignore
# pylint: disable=no-member
# Many attributes of dnspython are now dynamically defined which causes both
# mypy and pylint to error about accessing attributes they think do not exist.
# This is the case even in up-to-date versions of mypy and pylint which as of
# writing this are 0.790 and 2.6.0 respectively. This problem may be fixed in
# dnspython 2.1.0. See https://github.com/rthalley/dnspython/issues/598. For
# now, let's disable these checks. This is done at the very top of the file
# like this because "type: ignore" must be the first line in the file to be
# respected by mypy.
"""DNS Authenticator using RFC 2136 Dynamic Updates."""
import logging

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dnspython',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -49,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +57,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,5 +1,5 @@
include LICENSE.txt
include README.rst
include README
recursive-include docs *
recursive-include tests *
global-exclude __pycache__

View File

@@ -0,0 +1,35 @@
## Route53 plugin for Let's Encrypt client
### Before you start
It's expected that the root hosted zone for the domain in question already
exists in your account.
### Setup
1. Create a virtual environment
2. Update its pip and setuptools (`VENV/bin/pip install -U setuptools pip`)
to avoid problems with cryptography's dependency on setuptools>=11.3.
3. Make sure you have libssl-dev and libffi (or your regional equivalents)
installed. You might have to set compiler flags to pick things up (I have to
use `CPPFLAGS=-I/usr/local/opt/openssl/include
LDFLAGS=-L/usr/local/opt/openssl/lib` on my macOS to pick up brew's openssl,
for example).
4. Install this package.
### How to use it
Make sure you have access to AWS's Route53 service, either through IAM roles or
via `.aws/credentials`. Check out
[sample-aws-policy.json](examples/sample-aws-policy.json) for the necessary permissions.
To generate a certificate:
```
certbot certonly \
-n --agree-tos --email DEVOPS@COMPANY.COM \
--dns-route53 \
-d MY.DOMAIN.NAME
```

View File

@@ -1 +0,0 @@
Amazon Web Services Route 53 DNS Authenticator plugin for Certbot

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_route53.dns_route53` plugin automates the process of
completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and
subsequently removing, TXT records using the Amazon Web Services Route 53 API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,13 +6,13 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'boto3',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -36,11 +36,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
elif sys.version_info < (3,3):
install_requires.append('mock')
docs_extras = [
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
'sphinx_rtd_theme',
]
setup(
name='certbot-dns-route53',
version=version,
@@ -49,7 +44,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,6 +52,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',
@@ -73,9 +70,6 @@ setup(
include_package_data=True,
install_requires=install_requires,
keywords=['certbot', 'route53', 'aws'],
extras_require={
'docs': docs_extras,
},
entry_points={
'certbot.plugins': [
'dns-route53 = certbot_dns_route53._internal.dns_route53:Authenticator',

View File

@@ -3,10 +3,6 @@ The `~certbot_dns_sakuracloud.dns_sakuracloud` plugin automates the process of c
a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and subsequently
removing, TXT records using the Sakura Cloud DNS API.
.. note::
The plugin is not installed by default. It can be installed by heading to
`certbot.eff.org <https://certbot.eff.org/instructions#wildcard>`_, choosing your system and
selecting the Wildcard tab.
Named Arguments
---------------

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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']
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -6,12 +6,12 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
'dns-lexicon>=2.1.23',
'setuptools>=39.0.1',
'setuptools',
'zope.interface',
]
@@ -48,7 +48,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -56,6 +56,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -226,7 +226,7 @@ class NginxConfigurator(common.Installer):
if not fullchain_path:
raise errors.PluginError(
"The nginx plugin currently requires --fullchain-path to "
"install a certificate.")
"install a cert.")
vhosts = self.choose_vhosts(domain, create_if_no_match=True)
for vhost in vhosts:

View File

@@ -5,16 +5,16 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.11.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'acme>=1.4.0',
'certbot>=1.6.0',
'PyOpenSSL>=17.3.0',
'pyparsing>=2.2.0',
'setuptools>=39.0.1',
'PyOpenSSL',
'pyparsing>=1.5.5', # Python3 support
'setuptools',
'zope.interface',
]
@@ -35,7 +35,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -43,6 +43,8 @@ setup(
'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.6',
'Programming Language :: Python :: 3.7',

View File

@@ -2,28 +2,7 @@
Certbot adheres to [Semantic Versioning](https://semver.org/).
## 1.12.0 - master
### Added
*
### Changed
* The `--preferred-chain` flag now only checks the Issuer Common Name of the
topmost (closest to the root) certificate in the chain, instead of checking
every certificate in the chain.
See [#8577](https://github.com/certbot/certbot/issues/8577).
### Fixed
* Fixed the apache component on openSUSE Tumbleweed which no longer provides
an apache2ctl symlink and uses apachectl instead.
* Fixed a typo in `certbot/crypto_util.py` causing an error upon attempting `secp521r1` key generation
More details about these changes can be found on our GitHub repo.
## 1.11.0 - 2021-01-05
## 1.11.0 - master
### Added
@@ -33,9 +12,7 @@ More details about these changes can be found on our GitHub repo.
* We deprecated support for Python 2 in Certbot and its ACME library.
Support for Python 2 will be removed in the next planned release of Certbot.
* certbot-auto was deprecated on all systems. For more information about this
change, see
https://community.letsencrypt.org/t/certbot-auto-no-longer-works-on-debian-based-systems/139702/7.
* certbot-auto was deprecated on all systems.
* We deprecated support for Apache 2.2 in the certbot-apache plugin and it will
be removed in a future release of Certbot.
@@ -43,8 +20,6 @@ More details about these changes can be found on our GitHub repo.
* The Certbot snap no longer loads packages installed via `pip install --user`. This
was unintended and DNS plugins should be installed via `snap` instead.
* `certbot-dns-google` would sometimes crash with HTTP 409/412 errors when used with very large zones. See [#6036](https://github.com/certbot/certbot/issues/6036).
* `certbot-dns-google` would sometimes crash with an HTTP 412 error if preexisting records had an unexpected TTL, i.e.: different than Certbot's default TTL for this plugin. See [#8551](https://github.com/certbot/certbot/issues/8551).
More details about these changes can be found on our GitHub repo.

View File

@@ -18,6 +18,10 @@ systems.
To see the changes made to Certbot between versions please refer to our
`changelog <https://github.com/certbot/certbot/blob/master/certbot/CHANGELOG.md>`_.
Until May 2016, Certbot was named simply ``letsencrypt`` or ``letsencrypt-auto``,
depending on install method. Instructions on the Internet, and some pieces of the
software, may still refer to this older name.
Contributing
------------
@@ -92,7 +96,7 @@ Current Features
- apache/2.x
- nginx/0.8.48+
- webroot (adds files to webroot directories in order to prove control of
domains and obtain certificates)
domains and obtain certs)
- standalone (runs its own simple webserver to prove you control a domain)
- other server software via `third party plugins <https://certbot.eff.org/docs/using.html#third-party-plugins>`_

View File

@@ -1,3 +1,13 @@
"""Certbot client."""
import warnings
import sys
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
__version__ = '1.12.0.dev0'
__version__ = '1.11.0.dev0'
if sys.version_info[0] == 2:
warnings.warn(
"Python 2 support will be dropped in the next release of Certbot. "
"Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover

View File

@@ -20,7 +20,6 @@ from certbot import interfaces
from certbot import util
from certbot._internal import constants
from certbot.compat import os
from certbot.compat import filesystem
logger = logging.getLogger(__name__)
@@ -325,7 +324,7 @@ class AccountFileStorage(interfaces.AccountStorage):
if server_path in reused_servers:
next_server_path = reused_servers[server_path]
next_dir_path = link_func(next_server_path)
if os.path.islink(next_dir_path) and filesystem.readlink(next_dir_path) == dir_path:
if os.path.islink(next_dir_path) and os.readlink(next_dir_path) == dir_path:
possible_next_link = True
server_path = next_server_path
dir_path = next_dir_path
@@ -333,7 +332,7 @@ class AccountFileStorage(interfaces.AccountStorage):
# if there's not a next one up to delete, then delete me
# and whatever I link to
while os.path.islink(dir_path):
target = filesystem.readlink(dir_path)
target = os.readlink(dir_path)
os.unlink(dir_path)
dir_path = target

View File

@@ -369,7 +369,7 @@ def _describe_certs(config, parsed_certs, parse_failures):
notify = out.append
if not parsed_certs and not parse_failures:
notify("No certificates found.")
notify("No certs found.")
else:
if parsed_certs:
match = "matching " if config.certname or config.domains else ""

View File

@@ -5,6 +5,7 @@ from __future__ import print_function
import functools
import logging.handlers
import sys
import warnings
import configobj
import josepy as jose
@@ -253,7 +254,7 @@ def _handle_identical_cert_request(config, # type: configuration.NamespaceConfi
elif config.verb == "certonly":
keep_opt = "Keep the existing certificate for now"
choices = [keep_opt,
"Renew & replace the certificate (may be subject to CA rate limits)"]
"Renew & replace the cert (may be subject to CA rate limits)"]
display = zope.component.getUtility(interfaces.IDisplay)
response = display.menu(question, choices,
@@ -433,8 +434,8 @@ def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains):
_format_list("-", removed),
br=os.linesep))
obj = zope.component.getUtility(interfaces.IDisplay)
if not obj.yesno(msg, "Update certificate", "Cancel", default=True):
raise errors.ConfigurationError("Specified mismatched certificate name and domains.")
if not obj.yesno(msg, "Update cert", "Cancel", default=True):
raise errors.ConfigurationError("Specified mismatched cert name and domains.")
def _find_domains_or_certname(config, installer, question=None):
@@ -512,7 +513,7 @@ def _report_new_cert(config, cert_path, fullchain_path, key_path=None):
# and say something more informative here.
msg = ('Congratulations! Your certificate and chain have been saved at:{br}'
'{0}{br}{1}'
'Your certificate will expire on {2}. To obtain a new or tweaked version of this '
'Your cert will expire on {2}. To obtain a new or tweaked version of this '
'certificate in the future, simply run {3} again{4}. '
'To non-interactively renew *all* of your certificates, run "{3} renew"'
.format(fullchain_path, privkey_statement, expiry, cli.cli_command, verbswitch,
@@ -596,8 +597,8 @@ def _delete_if_appropriate(config):
attempt_deletion = config.delete_after_revoke
if attempt_deletion is None:
msg = ("Would you like to delete the certificate(s) you just revoked, "
"along with all earlier and later versions of the certificate?")
msg = ("Would you like to delete the cert(s) you just revoked, along with all earlier and "
"later versions of the cert?")
attempt_deletion = display.yesno(msg, yes_label="Yes (recommended)", no_label="No",
force_interactive=True, default=True)
@@ -619,8 +620,8 @@ def _delete_if_appropriate(config):
cert_manager.match_and_check_overlaps(config, [lambda x: archive_dir],
lambda x: x.archive_dir, lambda x: x)
except errors.OverlappingMatchFound:
logger.warning("Not deleting revoked certificates due to overlapping archive dirs. "
"More than one certificate is using %s", archive_dir)
logger.warning("Not deleting revoked certs due to overlapping archive dirs. More than "
"one certificate is using %s", archive_dir)
return
except Exception as e:
msg = ('config.default_archive_dir: {0}, config.live_dir: {1}, archive_dir: {2},'
@@ -665,7 +666,7 @@ def unregister(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -705,7 +706,7 @@ def register(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None` or a string indicating and error
:rtype: None or str
@@ -735,7 +736,7 @@ def update_account(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None` or a string indicating and error
:rtype: None or str
@@ -768,7 +769,7 @@ def update_account(config, unused_plugins):
acc.regr = acc.regr.update(uri=prev_regr_uri)
account_storage.update_regr(acc, cb_client.acme)
if not config.email:
if config.email is None:
display_util.notify("Any contact information associated "
"with this account has been removed.")
else:
@@ -812,7 +813,7 @@ def install(config, plugins):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -895,7 +896,7 @@ def plugins_cmd(config, plugins):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -934,7 +935,7 @@ def enhance(config, plugins):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -993,7 +994,7 @@ def rollback(config, plugins):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1011,7 +1012,7 @@ def update_symlinks(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1029,7 +1030,7 @@ def rename(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1047,7 +1048,7 @@ def delete(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1063,7 +1064,7 @@ def certificates(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1080,7 +1081,7 @@ def revoke(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None` or string indicating error in case of error
:rtype: None or str
@@ -1097,7 +1098,7 @@ def revoke(config, unused_plugins):
raise errors.Error("Error! Exactly one of --cert-path or --cert-name must be specified!")
if config.key_path is not None: # revocation by cert key
logger.debug("Revoking %s using certificate key %s",
logger.debug("Revoking %s using cert key %s",
config.cert_path[0], config.key_path[0])
crypto_util.verify_cert_matches_priv_key(config.cert_path[0], config.key_path[0])
key = jose.JWK.load(config.key_path[1])
@@ -1125,7 +1126,7 @@ def run(config, plugins):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1212,7 +1213,7 @@ def renew_cert(config, plugins, lineage):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:param lineage: Certificate lineage object
:type lineage: storage.RenewableCert
@@ -1257,7 +1258,7 @@ def certonly(config, plugins):
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: plugins_disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1307,7 +1308,7 @@ def renew(config, unused_plugins):
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: plugins_disco.PluginsRegistry
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
@@ -1381,7 +1382,6 @@ def main(cli_args=None):
plugins = plugins_disco.PluginsRegistry.find_all()
logger.debug("certbot version: %s", certbot.__version__)
logger.debug("Location of certbot entry point: %s", sys.argv[0])
# do not log `config`, as it contains sensitive data (e.g. revoke --key)!
logger.debug("Arguments: %r", cli_args)
logger.debug("Discovered plugins: %r", plugins)
@@ -1403,6 +1403,13 @@ def main(cli_args=None):
if config.func != plugins_cmd: # pylint: disable=comparison-with-callable
raise
if sys.version_info[0] == 2:
warnings.warn(
"Python 2 support will be dropped in the next release of Certbot. "
"Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover
set_displayer(config)
# Reporter

View File

@@ -157,8 +157,7 @@ to serve all files under specified web root ({0})."""
"--webroot-path and --domains, or --webroot-map. Run with "
" --help webroot for examples.")
for name, path in path_map.items():
self.full_roots[name] = os.path.join(path, os.path.normcase(
challenges.HTTP01.URI_ROOT_PATH))
self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH)
logger.debug("Creating root challenges validation dir at %s",
self.full_roots[name])

View File

@@ -99,7 +99,7 @@ def _reconstitute(config, full_path):
config.domains = [util.enforce_domain_sanity(d)
for d in renewal_candidate.names()]
except errors.ConfigurationError as error:
logger.warning("Renewal configuration file %s references a certificate "
logger.warning("Renewal configuration file %s references a cert "
"that contains an invalid domain name. The problem "
"was: %s. Skipping.", full_path, error)
return None
@@ -293,13 +293,13 @@ def should_renew(config, lineage):
def _avoid_invalidating_lineage(config, lineage, original_server):
"Do not renew a valid cert with one from a staging server!"
# Some lineages may have begun with --staging, but then had production
# certificates added to them
# Some lineages may have begun with --staging, but then had production certs
# added to them
with open(lineage.cert) as the_file:
contents = the_file.read()
latest_cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, contents)
# all our test certificates are from happy hacker fake CA, though maybe one day
# all our test certs are from happy hacker fake CA, though maybe one day
# we should test more methodically
now_valid = "fake" not in repr(latest_cert.get_issuer()).lower()
@@ -366,7 +366,7 @@ def _renew_describe_results(config, renew_successes, renew_failures,
renewal_noun = "simulated renewal" if config.dry_run else "renewal"
if renew_skipped:
notify("The following certificates are not due for renewal yet:")
notify("The following certs are not due for renewal yet:")
notify(report(renew_skipped, "skipped"))
if not renew_successes and not renew_failures:
notify("No {renewal}s were attempted.".format(renewal=renewal_noun))
@@ -377,7 +377,7 @@ def _renew_describe_results(config, renew_successes, renew_failures,
notify("Congratulations, all {renewal}s succeeded: ".format(renewal=renewal_noun))
notify(report(renew_successes, "success"))
elif renew_failures and not renew_successes:
notify_error("All %ss failed. The following certificates could "
notify_error("All %ss failed. The following certs could "
"not be renewed:", renewal_noun)
notify_error(report(renew_failures, "failure"))
elif renew_failures and renew_successes:
@@ -482,7 +482,7 @@ def handle_renewal_request(config):
except Exception as e: # pylint: disable=broad-except
# obtain_cert (presumably) encountered an unanticipated problem.
logger.error(
"Failed to renew certificate %s with error: %s",
"Failed to renew cert %s with error: %s",
lineagename, e
)
logger.debug("Traceback was:\n%s", traceback.format_exc())

View File

@@ -214,7 +214,7 @@ def get_link_target(link):
"""
try:
target = filesystem.readlink(link)
target = os.readlink(link)
except OSError:
raise errors.CertStorageError(
"Expected {0} to be a symlink".format(link))
@@ -223,7 +223,6 @@ def get_link_target(link):
target = os.path.join(os.path.dirname(link), target)
return os.path.abspath(target)
def _write_live_readme_to(readme_path, is_base_dir=False):
prefix = ""
if is_base_dir:
@@ -666,7 +665,7 @@ class RenewableCert(interfaces.RenewableCert):
current_link = getattr(self, kind)
if os.path.lexists(current_link):
os.unlink(current_link)
os.symlink(filesystem.readlink(previous_link), current_link)
os.symlink(os.readlink(previous_link), current_link)
for _, link in previous_symlinks:
if os.path.exists(link):
@@ -810,8 +809,8 @@ class RenewableCert(interfaces.RenewableCert):
May need to recover from rare interrupted / crashed states."""
if self.has_pending_deployment():
logger.warning("Found a new certificate /archive/ that was not "
"linked to in /live/; fixing...")
logger.warning("Found a new cert /archive/ that was not linked to in /live/; "
"fixing...")
self.update_all_links_to(self.latest_common_version())
return False
return True
@@ -847,7 +846,7 @@ class RenewableCert(interfaces.RenewableCert):
link = getattr(self, kind)
filename = "{0}{1}.pem".format(kind, version)
# Relative rather than absolute target directory
target_directory = os.path.dirname(filesystem.readlink(link))
target_directory = os.path.dirname(os.readlink(link))
# TODO: it could be safer to make the link first under a temporary
# filename, then unlink the old link, then rename the new link
# to the old link; this ensures that this process is able to
@@ -884,7 +883,7 @@ class RenewableCert(interfaces.RenewableCert):
"""
target = self.current_target("cert")
if target is None:
raise errors.CertStorageError("could not find the certificate file")
raise errors.CertStorageError("could not find cert file")
with open(target) as f:
return crypto_util.get_names_from_cert(f.read())
@@ -1122,7 +1121,7 @@ class RenewableCert(interfaces.RenewableCert):
# The behavior below keeps the prior key by creating a new
# symlink to the old key or the target of the old key symlink.
if os.path.islink(old_privkey):
old_privkey = filesystem.readlink(old_privkey)
old_privkey = os.readlink(old_privkey)
else:
old_privkey = "privkey{0}.pem".format(prior_version)
logger.debug("Writing symlink to old private key, %s.", old_privkey)

View File

@@ -18,7 +18,7 @@ def run_generic_updaters(config, lineage, plugins):
:type lineage: storage.RenewableCert
:param plugins: List of plugins
:type plugins: certbot._internal.plugins.disco.PluginsRegistry
:type plugins: `list` of `str`
:returns: `None`
:rtype: None

View File

@@ -33,7 +33,7 @@ class AnnotatedChallenge(jose.ImmutableMap):
Wraps around server provided challenge and annotates with data
useful for the client.
:ivar ~.challb: Wrapped `~.ChallengeBody`.
:ivar challb: Wrapped `~.ChallengeBody`.
"""
__slots__ = ('challb',)

View File

@@ -4,7 +4,6 @@ from __future__ import absolute_import
import errno
import os # pylint: disable=os-module-forbidden
import stat
import sys
from acme.magic_typing import List
@@ -362,8 +361,7 @@ def realpath(file_path):
"""
original_path = file_path
# Since Python 3.8, os.path.realpath also resolves symlinks on Windows.
if POSIX_MODE or sys.version_info >= (3, 8):
if POSIX_MODE:
path = os.path.realpath(file_path)
if os.path.islink(path):
# If path returned by realpath is still a link, it means that it failed to
@@ -385,36 +383,8 @@ def realpath(file_path):
return os.path.abspath(file_path)
def readlink(link_path):
# type: (str) -> str
"""
Return a string representing the path to which the symbolic link points.
:param str link_path: The symlink path to resolve
:return: The path the symlink points to
:returns: str
:raise: ValueError if a long path (260> characters) is encountered on Windows
"""
path = os.readlink(link_path)
if POSIX_MODE or not path.startswith('\\\\?\\'):
return path
# At this point, we know we are on Windows and that the path returned uses
# the extended form which is done for all paths in Python 3.8+
# Max length of a normal path is 260 characters on Windows, including the non printable
# termination character "<NUL>". The termination character is not included in Python
# strings, giving a max length of 259 characters, + 4 characters for the extended form
# prefix, to an effective max length 263 characters on a string representing a normal path.
if len(path) < 264:
return path[4:]
raise ValueError("Long paths are not supported by Certbot on Windows.")
# On Windows is_executable run from an unprivileged shell may claim that a path is
# executable when it is executable only if run from a privileged shell. This result
# executable when it is excutable only if run from a privileged shell. This result
# is due to the fact that GetEffectiveRightsFromAcl calculate effective rights
# without taking into consideration if the target user has currently required the
# elevated privileges or not. However this is not a problem since certbot always

Some files were not shown because too many files have changed in this diff Show More