Compare commits

..

73 Commits

Author SHA1 Message Date
Adrien Ferrand
a5d8db055f Focus on windows packaging jobs 2021-03-28 23:18:34 +02:00
Adrien Ferrand
7b0e0c7601 Remove wheel hack in windows installer construction script 2021-03-28 23:11:43 +02:00
Brad Warren
f4fc3e636d Redo the majority of Certbot's pinning system (#8741)
* add initial pyproject.toml

* add extra dependencies

* add simple bash script

* polish

* reuse pipstrap

* add requirements.txt

* temporarily remove hashin dep

* Switch to requirements.txt

* remove hashin check

* update requirements.txt again

* remove unnecessary merge

* pin back augeas

* unpin cryptography

* simplify pywin32 pinning

* update comment

* pin back pytest and pylint

* pin back pytest-forked

* pin back coverage

* update script comments

* fix pyopenssl case

* add minimum poetry version

* run pin.sh
2021-03-26 07:51:59 +01:00
Adrien Ferrand
018efc241c Split snap build over three Azure jobs (one per architecture) (#8731)
Fixes #8700

Now that `snapcraft remote-build` truly uses new builds for each call, we can split the builds to have a dedicated Azure job for each target architecture. This PR does that.

* Split snap_build job  on each architecture

* Also parallelize the publish_snap jobs over each architecture
2021-03-25 14:38:34 -07:00
ohemorange
fa25d8356d Remove references to certbot-auto and letsencrypt-auto that we don't need for the final release (#8738)
Fixes #8661.

As mentioned in https://github.com/certbot/certbot/issues/8661#issuecomment-806168214, there are quite a few remaining references, but until we modify the release script, we still need those. The changes here and the list there were created by grepping for the following terms:

```
certbot-auto
cb-auto
cbauto
certbotauto
letsencrypt-auto
le-auto
leauto
letsencryptauto
LEAUTO
LE_AUTO
LETSENCRYPT_AUTO
LETSENCRYPTAUTO
CB_AUTO
CERTBOT_AUTO
CBAUTO
CERTBOTAUTO
```

* Remove references to certbot-auto from certbot code

* Remove references to LEAUTO

* Remove references to CERTBOT_AUTO

* Remove references to letsencrypt-auto

* Remove references to certbot-auto from docs and tools

* remove cli constants header files

* Remove Python virtual environment section
2021-03-24 16:58:15 -07:00
ohemorange
fd62a09197 dump test farm failure logs (#8740) 2021-03-24 16:19:54 -07:00
Brad Warren
8d8b35b7c0 update requirements (#8739) 2021-03-24 15:55:30 -07:00
Brad Warren
74f6f734c8 remove outdated comment (#8736) 2021-03-25 08:00:47 +11:00
Brad Warren
0480959893 use pip in test_sdists.sh (#8737) 2021-03-24 11:50:34 -07:00
ohemorange
f90e93134c Upgrade cryptography to 3.4.6 (#8730)
* Upgrade cryptography to 3.4.6

* Fix comment with instructions for how to use hashin

* run tools/rebuild_certbot_constraints.py

* add deps for building cryptography in snaps

* Update cryptography build dependencies for docker

* Update sources for test farm tests

* Remove rust if it's installed for test farm tests

* source bootstrap script and call sudo as needed
2021-03-24 10:29:12 -07:00
Mads Jensen
d3b74f41e0 Added missing from typing imports. (#8724) 2021-03-23 21:33:47 +01:00
Brad Warren
1d7ddb0c0c fix pylint (#8729) 2021-03-23 13:01:01 -07:00
Brad Warren
54b0b98988 use worker terminology (#8728)
This will be needed for me to update `pytest-xdist` as part of https://github.com/certbot/certbot/issues/8705 since `pytest-xdist` removed the "slave" terminology. See https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst#deprecations-and-removals.
2021-03-23 11:29:01 -07:00
alexzorin
9fdb24331c docs: rewrite "Automated Renewals" in User Guide (#8717) 2021-03-22 15:05:37 -07:00
Adrien Ferrand
84178e2773 Do not reuse existing builds on Launchpad when executing snapcraft remote-build (#8719)
We observed recently several unexpected behavior during the execution of snap jobs in Azure. In particular it seems that `snapcraft remote-build` is tending to reattach to the latest builds on Launchpad triggered by the nightly builds on master, independently from the actual branch, status of the code, or targeted architectures.

Primarily if the builds on Launchpad are stalled for some reason, it blocks effectively any other Azure snap jobs until someone manually cancel the builds on Launchpad. Secondarily it means that the outcome of the builds may be inconsistent, because they can be the result of a build for the master source even if you are on a PR that modifieds these sources (including `snapcraft.yaml`).

After digging in `snapcraft` source code, I realized that the signature computed to understand if a build should be resumed, is not based one some hashes against the snapcraft working directory content, but is simply a hash of the working directory absolute path *itself*. It means that every builds triggered from the working directory `/my/path/certbot` for instance, are recognized as the same unique build on Launchpad side, and may be resumed if they already exist, and so independently from the source code, `snapcraft.yaml` or targeted archs.

For the record, relevant parts in `snapcraft` source code:
82024d3748/snapcraft/project/_project.py (L44)
82024d3748/snapcraft/project/_project.py (L86-L89)
82024d3748/snapcraft/cli/remote.py (L128-L132)

This PR makes effectively the resume build mechanism effectively a noop by moving the source code first in a temporary directory with random name before running `snapcraft remote-build`. This way the signature is never the same and builds are always recognized as brand new builds.

* Invalidate snapcraft remote-build cache by using a temporary workspace.

* Capture one more state in the build
2021-03-22 10:39:09 -07:00
osirisinferi
ae2247163e Remove empty lines from certbot certificates when (#8723)
.. envoked with `--cert-name` or `-d`.
2021-03-22 08:42:23 +11:00
Adrien Ferrand
6bc8b3d2ba Precise the certificate naming convention mechanism in the compatibility document (#8652)
* Precise the certificate naming convention mechanism in a note.

* Add certificate name convention in user guide, refer to it in compatibility page.

* Update certbot/docs/compatibility.rst

Co-authored-by: alexzorin <alex@zor.io>

* Update certbot/docs/using.rst

Co-authored-by: alexzorin <alex@zor.io>

* Update certbot/docs/using.rst

Co-authored-by: alexzorin <alex@zor.io>

* Improve the note about naming conventions

Co-authored-by: alexzorin <alex@zor.io>
2021-03-22 08:39:54 +11:00
ohemorange
40ae5d939e Fix linux-py39-cover test (#8720)
* update setuptools

* upgrade Markupsafe
2021-03-19 14:27:29 -07:00
Brad Warren
1b39d3dc47 switch to wait_until_running (#8715) 2021-03-16 17:53:43 -07:00
Brad Warren
2324c1bb7a Update installing from source instructions (#8713) 2021-03-15 14:10:44 -07:00
Adrien Ferrand
bc892e04c4 Fixing imports in cli module (#8708)
While working on #8640, I realized that there were some hidden circular dependencies in certbot._internal.cli package. Then cerbot could break if the order of these imports changes.

This PR fixes that and apply isort on top of the result.
2021-03-11 13:17:41 -08:00
Adrien Ferrand
0962b0fc83 Kill current snapcraft build when a "Chroot problem" is encountered (#8442)
* Kill snapcraft build when a "Chroot problem" is encountered

* Display specific helper for "Chroot problem" status and cancel retry mechanism in this case.

* Isolate build tmp directories

* Configure XDG_CACHE_HOME

* Kill snapcraftctl with chroot problem is encountered
2021-03-11 13:08:20 -08:00
Adrien Ferrand
dd6f2f565e Convert Python 2 type hints to Python 3 types annotations (#8640)
Fixes #8427

This PR converts the Python 2 types hints into Python 3 types annotations. I have used the project https://github.com/ilevkivskyi/com2ann which has been designed for that specific purpose and did that very well.

The only remaining things to do were to fix broken type hints that became wrong code after migration, and to fix lines too long with the new syntax.

* Raw execution of com2ann

* Fixing broken type annotations

* Cleanup imports
2021-03-10 11:51:27 -08:00
Brad Warren
f2d8c81e9b remove reference to acme.magic_typing from docs (#8709) 2021-03-09 16:53:44 -08:00
Adrien Ferrand
67b65bb2c0 Deprecate acme.typing_magic module, stop using it in certbot (#8643)
* Deprecate acme.magic_typing, stop to use it in certbot

* Isort

* Add a changelog entry

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
2021-03-09 16:12:32 -08:00
alexzorin
76895457c9 dns-digitalocean: use a 30 second TTL (#8693)
Fixes an issue with long TTLs and caching behavior on DigitalOcean's
DNS hosting service.
2021-03-09 15:56:51 -08:00
Mads Jensen
c02b2d30f2 Removed Python legacy __future__ imports (#8697)
There are still some left, but the `modification_check` test fails. Some are still in `tools`, and they can probably be removed as well. `with_statement` was introduced officially in Python 2.5, so there's really old stuff in the code base.
2021-03-05 16:53:20 -08:00
Brad Warren
94dc6936e7 Final update to certbot-auto (#8706)
Fixes https://github.com/certbot/certbot/issues/8690.

After this PR, we'll let the release script make its automated changes to certbot-auto as part of the 1.14.0 release and then never make any code changes to certbot-auto ever again!

* disable upgrades on debian

* update test_leauto_upgrades.sh

* update changelog
2021-03-05 14:14:32 -08:00
Mads Jensen
a3abcc001a Removed a Python 2 fallback in certbot.Reverter. (#8694)
* Removed a Python 2 fallback in certbot.Reverter.

* Removed a Python < 3.6 fallback in certbot-apache._internal.parser.
2021-03-04 08:10:56 +11:00
Brad Warren
9643e85b4c Merge pull request #8699 from certbot/candidate-1.13.0
Release 1.13.0
2021-03-03 13:00:31 -08:00
Erica Portnoy
9d97be3a84 Bump version to 1.14.0 2021-03-02 13:50:04 -08:00
Erica Portnoy
4d6db0eb71 Add contents to certbot/CHANGELOG.md for next version 2021-03-02 13:50:03 -08:00
Erica Portnoy
92a66454b6 Release 1.13.0 2021-03-02 13:49:58 -08:00
Erica Portnoy
976068b5a0 Update changelog for 1.13.0 release 2021-03-02 13:37:04 -08:00
alexzorin
1e30723003 revoke: try determine the server automatically (#8691)
* revoke: try determine the server automatically

When revoking via --cert-name, use the server from the lineage (unless
overriden by the CLI).

* RenewableCert.storage might be None

* guard against an empty lineage server
2021-03-01 12:56:22 -08:00
Brad Warren
496a4ced25 Remove broken test for typing import failure (#8692)
* remove broken test

* fix coverage

* don't worry about getattr test
2021-02-26 16:05:34 -08:00
alexzorin
fab9bfd878 nginx: authenticate all matching vhosts for HTTP01 (#8663)
* nginx: authenticate all matching vhosts for HTTP01

Previously, the nginx authenticator would set up the HTTP-01 challenge
response on a single HTTP vhost which matched the challenge domain.

The nginx authenticator will now set the challenge response on every
vhost which matches the challenge domain, including duplicates and HTTPS
vhosts.

This makes the authenticator usable behind a CDN where all origin
traffic is performed over HTTPS and also makes the authenticator work
more reliably against "invalid" nginx configurations, such as those
where there are duplicate vhosts.

* some typos

* dont authenticate the same vhost twice

One vhost may appear in both the HTTP and HTTPS vhost lists. Use a set()
to avoid trying to mod the same vhost twice.

* fix type annotations

* rewrite changelog entry
2021-02-26 13:43:22 -08:00
Yuma Mihira
d3ca6af982 Insert new line before "More details about these changes can be found on our GitHub repo." (#8645)
Fixing #8634. It's my first time contributing to this repository, if there's something wrong please let me know.

Before this fix

```
$ python3 extract_changelog.py 1.12.0
...
### 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.
```

After this fix

```
$ python3 extract_changelog.py 1.12.0
...
### 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.
```
2021-02-25 16:30:48 -08:00
Mads Jensen
540fd6db93 Dictionaries are ordered by insert by default on Python 3.6. (#8678) 2021-02-25 15:41:05 -08:00
Mads Jensen
b0e35c694e Remove import fallback of urllib2 in tests/modification-check. (#8677) 2021-02-25 14:59:11 -08:00
Mads Jensen
67c2b27af7 Stop inheriting from object. It's unneeded on Python 3+. (#8675) 2021-02-25 14:59:00 -08:00
Mads Jensen
135187f03e Python 3 obsoletes explicit __ne__ methods (#8676)
This shouldn't be needed as of Python 3+.

https://stackoverflow.com/questions/4352244/should-ne-be-implemented-as-the-negation-of-eq-in-python#30676267
2021-02-25 14:50:54 -08:00
Brad Warren
e742cfaa21 dont set required to False (#8689) 2021-02-26 08:39:55 +11:00
alexzorin
f71298f661 cli: make key_path and cert_path always be a str (#8687)
There is some code in [`_paths_parser`](ae3ed200c0/certbot/certbot/_internal/cli/paths_parser.py (L17-L34)) which has the effect of varying the value type of `config.cert_path` and `config.key_path` based on the CLI verb. When the verb is `revoke`, the type is a tuple `(path: str, contents: bytes)`, otherwise it is a single `str` representing the file path. (I wasn't able to find a written reason as to why it works this way).

This commit removes that special `revoke` case and ensures it is always a `str`.

Why change it now?

I am trying to write some changes and there's some code in `cert_manager` which only works if the verb is `revoke`, you hack `config.cert_path` to be a tuple beforehand, or you [(not actually in `master`) try sniff for the value type](49911afaa6/certbot/certbot/_internal/cert_manager.py (L224-L227)). I have a bad feeling about such workarounds. I would prefer to just make these variables simpler to use, but I'm open to opinions.

In addition to the test suites, I've manually tested `revoke` (including by `--key-path`) and `install`. Are there other places I may have missed?

Unblocks #8636 and #8671.
2021-02-25 11:32:21 -08:00
alexzorin
025eb16c7a docs: rewrite "Revoking certificates" (#8657)
* docs: rewrite "Revoking certificates"

- `--cert-name` is supported since a long time ago
- `--delete-after-revoke` is default
- Mention that non-default `--server` must be specified
- Document difference between acme key/cert key revocation methods
- Reshuffle text to keep more important things earlier

* minor edits

* remove revocation note

* remove "preauthorization" revocation method

* rewrite deletion note
2021-02-25 10:22:40 -08:00
ohemorange
ae3ed200c0 Remove check for 'fake' in issuer name when renewing certs (#8685)
Fixes #8680.

We seem to have no existing testing code anywhere in this vicinity, so figured I'd get this up quickly then work on that. Manual tests (renew staging certificate, should allow it; renew non-staging cert as staging, should error) passed.

* Remove check for 'fake' in issuer name when renewing certs

* Change fake issuer name to make sure we're not relying on it anywhere
2021-02-24 14:51:57 -08:00
Adrien Ferrand
c3d6fca3eb Make certbot constraint file independent from certbot-auto + update cryptography (#8649)
* Refactor to not depend on certbot-auto dependencies pinning anymore

* Update constraints

* Replaces references

* Upgrade AWS dependencies pinning

* Fix script

* Fix Windows installer builds

* Fixing sdists letstest script

* Pin cryptography on 3.1.1 specifically for RHEL/CentOS 7 to avoid build failures during test_sdists test.

* Finish fix

* Fix VERSION_ID in RHEL 7
2021-02-23 15:29:52 -08:00
Brad Warren
c43f4fe518 upgrade to 3.8.8 (#8682)
Fixes https://github.com/certbot/certbot/issues/8681. https://python-security.readthedocs.io/vuln/ctypes-buffer-overflow-pycarg_repr.html is the best resource I found linking to the original Python bug, when each Python branch was fixed, etc.
2021-02-23 13:20:04 -08:00
Mads Jensen
0f3f07b5cb Removed backport of unittest.assertLogs (#8673)
* Removed backport of unittest.assertLogs

* Update parser_test.py
2021-02-22 09:34:56 +11:00
Mads Jensen
ef265eccaf Remove import fallback for collections.abc (#8674) 2021-02-22 09:23:42 +11:00
Adrien Ferrand
c0eccdd358 Deprecate certbot-auto specific flags (#8641)
This PR deprecates the certbot-auto specific CLI flags, in the perspective of removing them in a future release as said in #8483.

* Deprecate certbot-auto specific flags

* Update changelog

* Clean tests

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
2021-02-12 16:14:46 -08:00
Adrien Ferrand
c59775c3c0 Disable certbot-auto upgrade on RHEL-like systems (#8653)
Fixes #8637

* Disable upgrade for RHEL-like systems

* Remove letstest on Amazon Linux

* Update changelog
2021-02-10 15:17:51 -08:00
Steffen Neumann
cf062f4c6d Fix ubuntu package name (#8654)
Since Ubuntu 18.04 there is python3-certbot-apache which should be the recommended version. 
The Debian package names should probably be updated accordingly.
2021-02-09 12:18:29 -08:00
Brad Warren
3d0dad8718 Remove dependency on six (#8650)
Fixes https://github.com/certbot/certbot/issues/8494.

I left the `six` dependency pinned in `tests/letstest/requirements.txt` and `tools/oldest_constraints.txt` because `six` is still a transitive dependency with our current pinnings.

The extra moving around of imports is due to me using `isort` to help me keep dependencies in sorted order after replacing imports of `six`.

* remove some six usage in acme

* remove six from acme

* remove six.add_metaclass usage

* fix six.moves.zip

* fix six.moves.builtins.open

* six.moves server fixes

* 's/six\.moves\.range/range/g'

* stop using six.moves.xrange

* fix urllib imports

* s/six\.binary_type/bytes/g

* s/six\.string_types/str/g

* 's/six\.text_type/str/g'

* fix six.iteritems usage

* fix itervalues usage

* switch from six.StringIO to io.StringIO

* remove six imports

* misc fixes

* stop using six.reload_module

* no six.PY2

* rip out six

* keep six pinned in oldest constraints

* fix log_test.py

* update changelog
2021-02-09 11:43:15 -08:00
sommersoft
edad9bd82b Fix Sphinx manpage Building (#8646)
* certbot docs: include & orphan 'man/cerbot.rst'; fixes manpage building

* acme docs: include & orphan 'man/jws.rst'; fixes manpage building
2021-02-09 11:29:31 +01:00
Matt W
2a16aa16c3 Update cli.ini (#8603)
* Update cli.ini

Sharing back some extended examples I desired, did not find,  and derived on my own

* Update cli.ini

Alex,
ok - simplified as requested
Matt

* Update cli.ini

removed trailing quote on line 32

* Update certbot/examples/cli.ini

Co-authored-by: alexzorin <alex@zor.io>

* Update certbot/examples/cli.ini

Co-authored-by: alexzorin <alex@zor.io>

* Update certbot/examples/cli.ini

Co-authored-by: alexzorin <alex@zor.io>

* remove stray newline

Co-authored-by: alexzorin <alex@zor.io>
2021-02-07 18:19:49 +11:00
Brad Warren
711cc95dc4 Remove mock dependency (#8630)
Fixes https://github.com/certbot/certbot/issues/7913.

I only added the deprecation warning to `certbot.tests.util` because that's the only place where I think someone could be using the `mock` module through our API.

* remove external mock from acme

* update Certbot's mock usage

* remove mock dependency in plugins

* remove external mock from compatibility test

* add changelog entry
2021-02-05 15:51:18 -08:00
Brad Warren
c2ee0d2938 Remove requests[security] dependency (#8626)
Fixes https://github.com/certbot/certbot/issues/7901.

* stop using requests[security]

* add changelog entry

* remove unused import
2021-02-05 15:33:45 -08:00
Brad Warren
c668172ef0 merge dev and dev3 (#8639) 2021-02-04 21:31:47 +11:00
Brad Warren
666ee35e29 remove crufty pytest warning (#8638) 2021-02-04 21:04:03 +11:00
Brad Warren
13af3f7ec2 Cleanup venv scripts (#8629)
Fixes https://github.com/certbot/certbot/issues/8387.

* update _venv_common.py

* delete venv.py scripts

* rename venv script

* update relevant venv3 references

* remove set_python_envvars
2021-02-03 12:03:09 -08:00
Brad Warren
5ad0c254ca Merge pull request #8624 from certbot/external-mock
Fixes #8616.

Add tests with external mock
2021-02-03 12:02:43 -08:00
Brad Warren
236062c2d2 Merge pull request #8632 from certbot/candidate-1.12.0
Release 1.12.0
2021-02-02 13:11:27 -08:00
Erica Portnoy
2bcd8c59db Bump version to 1.13.0 2021-02-02 11:06:48 -08:00
Erica Portnoy
57cba3690d Add contents to certbot/CHANGELOG.md for next version 2021-02-02 11:06:47 -08:00
Erica Portnoy
786a130b7d Release 1.12.0 2021-02-02 11:06:40 -08:00
Erica Portnoy
df866b907b Update changelog for 1.12.0 release 2021-02-02 10:58:41 -08:00
Brad Warren
f0b32783f0 Start disabling certbot-auto upgrades (#8623)
* add amazon linux to auto targets

* disable updates outside of debian and rhel

* test certbot-auto with disabled upgrades

* try new approach to testing

* remove bad space

* tweak error text

* add changelog entry

* fix bad certbot-auto commit

* test new error text

* update changelog

* update error text
2021-02-01 13:11:04 -08:00
Brad Warren
534af33a50 add external-mock tests to azure config 2021-01-29 15:32:04 -08:00
Brad Warren
2e33aec8a8 add tests with external mock library 2021-01-29 15:31:11 -08:00
ohemorange
bdfb9f19c4 Remove deprecated options as early as possible using an explicit list (#8617)
* Remove deprecated options as early as possible using an explicit list

* add deprecated options to cli init import list

* use correct dict comprehension syntax for py3

* lint

* add test for renewal reconstitution code

* add test to ensure we're not saving deprecated values

* comment code
2021-01-28 12:34:50 -08:00
Brad Warren
b4e955a60e Switch away from ubuntu-latest (#8606)
I noticed warnings on Azure like [this](https://dev.azure.com/certbot/certbot/_build/results?buildId=3311&view=logs&j=d74e04fe-9740-597d-e9fa-1d0400037dfd) which say:

> ##[warning]Ubuntu-latest pipelines will use Ubuntu-20.04 soon. For more details, see https://github.com/actions/virtual-environments/issues/1816

I was worried about us suddenly switching to Ubuntu 20.04 and things breaking so I tested that `ubuntu-20.04` works and am opening this PR to switch things over explicitly now. I'd rater have our VM images pinned to specific versions than a generic version specification like `latest` which might see an upgrade and break our tests unexpectedly.

I ran the notification code on Ubuntu 20.04 at https://dev.azure.com/certbot/certbot/_build/results?buildId=3315&view=results and you can see the notification at https://opensource.eff.org/eff-open-source/pl/ojjhde5j4jyw7dcurd5zfduymr.
2021-01-25 15:20:51 -08:00
Adrien Ferrand
7399807ff2 Drop Python 2 support (#8591)
Fixes #8389 #8584.

This PR makes the necessary modifications to officially drop Python 2 support in the Certbot project.

I did not remove the specific Python 2 compatibility branches that has been added in various places in the codebase, to reduce the size of this PR and this will be done in a future one

* Update classifiers and python_requires in setup.py

* Remove warnings about Python 2 deprecation

* Remove Azure jobs on Python 2.7

* Remove references to python 2 in documentation

* Pin dnspython to 2.1.0

* Update changelog

* Remove warning ignore
2021-01-25 15:07:43 -08:00
227 changed files with 2506 additions and 2855 deletions

View File

@@ -21,6 +21,8 @@ jobs:
PYTHON_VERSION: 3.7
TOXENV: py37
CERTBOT_NO_PIN: 1
linux-external-mock:
TOXENV: external-mock
linux-boulder-v1-integration-certbot-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-certbot-oldest

View File

@@ -1,55 +1,55 @@
jobs:
- job: docker_build
pool:
vmImage: ubuntu-18.04
strategy:
matrix:
amd64:
DOCKER_ARCH: amd64
# Do not run the heavy non-amd64 builds for test branches
${{ if not(startsWith(variables['Build.SourceBranchName'], 'test-')) }}:
arm32v6:
DOCKER_ARCH: arm32v6
arm64v8:
DOCKER_ARCH: arm64v8
steps:
- bash: set -e && tools/docker/build.sh $(dockerTag) $DOCKER_ARCH
displayName: Build the Docker images
# We don't filter for the Docker Hub organization to continue to allow
# easy testing of these scripts on forks.
- bash: |
set -e
DOCKER_IMAGES=$(docker images --filter reference='*/certbot' --filter reference='*/dns-*' --format '{{.Repository}}')
docker save --output images.tar $DOCKER_IMAGES
displayName: Save the Docker images
# If the name of the tar file or artifact changes, the deploy stage will
# also need to be updated.
- bash: set -e && mv images.tar $(Build.ArtifactStagingDirectory)
displayName: Prepare Docker artifact
- task: PublishPipelineArtifact@1
inputs:
path: $(Build.ArtifactStagingDirectory)
artifact: docker_$(DOCKER_ARCH)
displayName: Store Docker artifact
- job: docker_run
dependsOn: docker_build
pool:
vmImage: ubuntu-18.04
steps:
- task: DownloadPipelineArtifact@2
inputs:
artifact: docker_amd64
path: $(Build.SourcesDirectory)
displayName: Retrieve Docker images
- bash: set -e && docker load --input $(Build.SourcesDirectory)/images.tar
displayName: Load Docker images
- bash: |
set -ex
DOCKER_IMAGES=$(docker images --filter reference='*/certbot' --filter reference='*/dns-*' --format '{{.Repository}}:{{.Tag}}')
for DOCKER_IMAGE in ${DOCKER_IMAGES}
do docker run --rm "${DOCKER_IMAGE}" plugins --prepare
done
displayName: Run integration tests for Docker images
# - job: docker_build
# pool:
# vmImage: ubuntu-18.04
# strategy:
# matrix:
# amd64:
# DOCKER_ARCH: amd64
# # Do not run the heavy non-amd64 builds for test branches
# ${{ if not(startsWith(variables['Build.SourceBranchName'], 'test-')) }}:
# arm32v6:
# DOCKER_ARCH: arm32v6
# arm64v8:
# DOCKER_ARCH: arm64v8
# steps:
# - bash: set -e && tools/docker/build.sh $(dockerTag) $DOCKER_ARCH
# displayName: Build the Docker images
# # We don't filter for the Docker Hub organization to continue to allow
# # easy testing of these scripts on forks.
# - bash: |
# set -e
# DOCKER_IMAGES=$(docker images --filter reference='*/certbot' --filter reference='*/dns-*' --format '{{.Repository}}')
# docker save --output images.tar $DOCKER_IMAGES
# displayName: Save the Docker images
# # If the name of the tar file or artifact changes, the deploy stage will
# # also need to be updated.
# - bash: set -e && mv images.tar $(Build.ArtifactStagingDirectory)
# displayName: Prepare Docker artifact
# - task: PublishPipelineArtifact@1
# inputs:
# path: $(Build.ArtifactStagingDirectory)
# artifact: docker_$(DOCKER_ARCH)
# displayName: Store Docker artifact
# - job: docker_run
# dependsOn: docker_build
# pool:
# vmImage: ubuntu-18.04
# steps:
# - task: DownloadPipelineArtifact@2
# inputs:
# artifact: docker_amd64
# path: $(Build.SourcesDirectory)
# displayName: Retrieve Docker images
# - bash: set -e && docker load --input $(Build.SourcesDirectory)/images.tar
# displayName: Load Docker images
# - bash: |
# set -ex
# DOCKER_IMAGES=$(docker images --filter reference='*/certbot' --filter reference='*/dns-*' --format '{{.Repository}}:{{.Tag}}')
# for DOCKER_IMAGE in ${DOCKER_IMAGES}
# do docker run --rm "${DOCKER_IMAGE}" plugins --prepare
# done
# displayName: Run integration tests for Docker images
- job: installer_build
pool:
vmImage: vs2017-win2016
@@ -113,105 +113,109 @@ jobs:
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
# - job: snaps_build
# pool:
# vmImage: ubuntu-18.04
# strategy:
# matrix:
# amd64:
# SNAP_ARCH: amd64
# # Do not run the heavy non-amd64 builds for test branches
# ${{ if not(startsWith(variables['Build.SourceBranchName'], 'test-')) }}:
# armhf:
# SNAP_ARCH: armhf
# arm64:
# SNAP_ARCH: arm64
# timeoutInMinutes: 0
# 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 ${SNAP_ARCH} --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_$(SNAP_ARCH)
# 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_amd64
# path: $(Build.SourcesDirectory)/snap
# displayName: Retrieve Certbot snaps
# - script: |
# set -e
# sudo snap install --dangerous --classic snap/certbot_*.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_amd64
# 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

@@ -73,6 +73,6 @@ jobs:
- template: ../steps/tox-steps.yml
- job: test_sphinx_builds
pool:
vmImage: ubuntu-latest
vmImage: ubuntu-20.04
steps:
- template: ../steps/sphinx-steps.yml

View File

@@ -37,6 +37,14 @@ stages:
vmImage: ubuntu-18.04
variables:
- group: certbot-common
strategy:
matrix:
amd64:
SNAP_ARCH: amd64
arm32v6:
SNAP_ARCH: armhf
arm64v8:
SNAP_ARCH: arm64
steps:
- bash: |
set -e
@@ -46,7 +54,7 @@ stages:
displayName: Install dependencies
- task: DownloadPipelineArtifact@2
inputs:
artifact: snaps
artifact: snaps_$(SNAP_ARCH)
path: $(Build.SourcesDirectory)/snap
displayName: Retrieve Certbot snaps
- task: DownloadSecureFile@1

View File

@@ -5,7 +5,7 @@ stages:
variables:
- group: certbot-common
pool:
vmImage: ubuntu-latest
vmImage: ubuntu-20.04
steps:
- bash: |
set -e

View File

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

View File

@@ -8,5 +8,4 @@
.git
.tox
venv
venv3
docs

2
.envrc
View File

@@ -3,7 +3,7 @@
# activated and then deactivated when you cd elsewhere. Developers have to have
# direnv set up and run `direnv allow` to allow this file to execute on their
# system. You can find more information at https://direnv.net/.
. venv3/bin/activate
. venv/bin/activate
# direnv doesn't support modifying PS1 so we unset it to squelch the error
# it'll otherwise print about this being done in the activate script. See
# https://github.com/direnv/direnv/wiki/PS1. If you would like your shell

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@ dist*/
letsencrypt.log
certbot.log
letsencrypt-auto-source/letsencrypt-auto.sig.lzma.base64
poetry.lock
# coverage
.coverage

View File

@@ -8,7 +8,10 @@ jobs=0
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# CERTBOT COMMENT
# This is needed for pylint to import linter_plugin.py since
# https://github.com/PyCQA/pylint/pull/3396.
init-hook="import pylint.config, os, sys; sys.path.append(os.path.dirname(pylint.config.PYLINTRC))"
# Profiled execution.
profile=no
@@ -254,7 +257,7 @@ ignore-mixin-members=yes
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis
ignored-modules=pkg_resources,confargparse,argparse,six.moves,six.moves.urllib
ignored-modules=pkg_resources,confargparse,argparse
# import errors ignored only in 1.4.4
# https://bitbucket.org/logilab/pylint/commits/cd000904c9e2

View File

@@ -15,6 +15,6 @@ RUN apt-get update && \
/tmp/* \
/var/tmp/*
RUN VENV_NAME="../venv3" python3 tools/venv3.py
RUN VENV_NAME="../venv" python3 tools/venv.py
ENV PATH /opt/certbot/venv3/bin:$PATH
ENV PATH /opt/certbot/venv/bin:$PATH

View File

@@ -8,15 +8,15 @@ import socket
from cryptography.hazmat.primitives import hashes # type: ignore
import josepy as jose
import requests
import six
from OpenSSL import SSL # type: ignore # https://github.com/python/typeshed/issues/2052
from OpenSSL import crypto
from OpenSSL import SSL # type: ignore # https://github.com/python/typeshed/issues/2052
import requests
from acme import crypto_util
from acme import errors
from acme import fields
from acme.mixins import ResourceMixin, TypeMixin
from acme.mixins import ResourceMixin
from acme.mixins import TypeMixin
logger = logging.getLogger(__name__)
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
class Challenge(jose.TypedJSONObjectWithFields):
# _fields_to_partial_json
"""ACME challenge."""
TYPES = {} # type: dict
TYPES: dict = {}
@classmethod
def from_json(cls, jobj):
@@ -38,7 +38,7 @@ class Challenge(jose.TypedJSONObjectWithFields):
class ChallengeResponse(ResourceMixin, TypeMixin, jose.TypedJSONObjectWithFields):
# _fields_to_partial_json
"""ACME challenge response."""
TYPES = {} # type: dict
TYPES: dict = {}
resource_type = 'challenge'
resource = fields.Resource(resource_type)
@@ -145,8 +145,7 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
return jobj
@six.add_metaclass(abc.ABCMeta)
class KeyAuthorizationChallenge(_TokenChallenge):
class KeyAuthorizationChallenge(_TokenChallenge, metaclass=abc.ABCMeta):
"""Challenge based on Key Authorization.
:param response_cls: Subclass of `KeyAuthorizationChallengeResponse`

View File

@@ -4,10 +4,14 @@ import collections
import datetime
from email.utils import parsedate_tz
import heapq
import http.client as http_client
import logging
import re
import sys
import time
from typing import Dict
from typing import List
from typing import Set
from typing import Text
import josepy as jose
import OpenSSL
@@ -15,38 +19,21 @@ import requests
from requests.adapters import HTTPAdapter
from requests.utils import parse_header_links
from requests_toolbelt.adapters.source import SourceAddressAdapter
import six
from six.moves import http_client
from acme import crypto_util
from acme import errors
from acme import jws
from acme import messages
from acme.magic_typing import Dict
from acme.magic_typing import List
from acme.magic_typing import Set
from acme.magic_typing import Text
from acme.mixins import VersionedLEACMEMixin
logger = logging.getLogger(__name__)
# Prior to Python 2.7.9 the stdlib SSL module did not allow a user to configure
# many important security related options. On these platforms we use PyOpenSSL
# for SSL, which does allow these options to be configured.
# https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
if sys.version_info < (2, 7, 9): # pragma: no cover
try:
requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3() # type: ignore
except AttributeError:
import urllib3.contrib.pyopenssl
urllib3.contrib.pyopenssl.inject_into_urllib3()
DEFAULT_NETWORK_TIMEOUT = 45
DER_CONTENT_TYPE = 'application/pkix-cert'
class ClientBase(object):
class ClientBase:
"""ACME client base object.
:ivar messages.Directory directory:
@@ -125,8 +112,9 @@ class ClientBase(object):
"""
return self.update_registration(regr, update={'status': 'deactivated'})
def deactivate_authorization(self, authzr):
# type: (messages.AuthorizationResource) -> messages.AuthorizationResource
def deactivate_authorization(self,
authzr: messages.AuthorizationResource
) -> messages.AuthorizationResource:
"""Deactivate authorization.
:param messages.AuthorizationResource authzr: The Authorization resource
@@ -260,7 +248,7 @@ class Client(ClientBase):
if net is None:
net = ClientNetwork(key, alg=alg, verify_ssl=verify_ssl)
if isinstance(directory, six.string_types):
if isinstance(directory, str):
directory = messages.Directory.from_json(
net.get(directory).json())
super(Client, self).__init__(directory=directory,
@@ -436,7 +424,7 @@ class Client(ClientBase):
"""
assert max_attempts > 0
attempts = collections.defaultdict(int) # type: Dict[messages.AuthorizationResource, int]
attempts: Dict[messages.AuthorizationResource, int] = collections.defaultdict(int)
exhausted = set()
# priority queue with datetime.datetime (based on Retry-After) as key,
@@ -475,7 +463,7 @@ class Client(ClientBase):
exhausted.add(authzr)
if exhausted or any(authzr.body.status == messages.STATUS_INVALID
for authzr in six.itervalues(updated)):
for authzr in updated.values()):
raise errors.PollError(exhausted, updated)
updated_authzrs = tuple(updated[authzr] for authzr in authzrs)
@@ -549,7 +537,7 @@ class Client(ClientBase):
:rtype: `list` of `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
"""
chain = [] # type: List[jose.ComparableX509]
chain: List[jose.ComparableX509] = []
uri = certr.cert_chain_uri
while uri is not None and len(chain) < max_length:
response, cert = self._get_cert(uri)
@@ -808,7 +796,7 @@ class ClientV2(ClientBase):
if 'rel' in l and 'url' in l and l['rel'] == relation_type]
class BackwardsCompatibleClientV2(object):
class BackwardsCompatibleClientV2:
"""ACME client wrapper that tends towards V2-style calls, but
supports V1 servers.
@@ -951,7 +939,7 @@ class BackwardsCompatibleClientV2(object):
return self.client.external_account_required()
class ClientNetwork(object):
class ClientNetwork:
"""Wrapper around requests that signs POSTs for authentication.
Also adds user agent, and handles Content-Type.
@@ -981,7 +969,7 @@ class ClientNetwork(object):
self.account = account
self.alg = alg
self.verify_ssl = verify_ssl
self._nonces = set() # type: Set[Text]
self._nonces: Set[Text] = set()
self.user_agent = user_agent
self.session = requests.Session()
self._default_timeout = timeout

View File

@@ -5,15 +5,15 @@ import logging
import os
import re
import socket
from typing import Callable
from typing import Tuple
from typing import Union
import josepy as jose
from OpenSSL import crypto
from OpenSSL import SSL # type: ignore # https://github.com/python/typeshed/issues/2052
from acme import errors
from acme.magic_typing import Callable
from acme.magic_typing import Tuple
from acme.magic_typing import Union
logger = logging.getLogger(__name__)
@@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
_DEFAULT_SSL_METHOD = SSL.SSLv23_METHOD # type: ignore
class _DefaultCertSelection(object):
class _DefaultCertSelection:
def __init__(self, certs):
self.certs = certs
@@ -36,7 +36,7 @@ class _DefaultCertSelection(object):
return self.certs.get(server_name, None)
class SSLSocket(object): # pylint: disable=too-few-public-methods
class SSLSocket: # pylint: disable=too-few-public-methods
"""SSL wrapper for sockets.
:ivar socket sock: Original wrapped socket.
@@ -93,7 +93,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
new_context.set_alpn_select_callback(self.alpn_selection)
connection.set_context(new_context)
class FakeConnection(object):
class FakeConnection:
"""Fake OpenSSL.SSL.Connection."""
# pylint: disable=missing-function-docstring
@@ -168,7 +168,7 @@ def probe_sni(name, host, port=443, timeout=300, # pylint: disable=too-many-argu
source_address[1]
) if any(source_address) else ""
)
socket_tuple = (host, port) # type: Tuple[str, int]
socket_tuple: Tuple[str, int] = (host, port)
sock = socket.create_connection(socket_tuple, **socket_kwargs) # type: ignore
except socket.error as error:
raise errors.Error(error)
@@ -256,7 +256,7 @@ def _pyopenssl_cert_or_req_san(cert_or_req):
if isinstance(cert_or_req, crypto.X509):
# pylint: disable=line-too-long
func = crypto.dump_certificate # type: Union[Callable[[int, crypto.X509Req], bytes], Callable[[int, crypto.X509], bytes]]
func: Union[Callable[[int, crypto.X509Req], bytes], Callable[[int, crypto.X509], bytes]] = crypto.dump_certificate
else:
func = crypto.dump_certificate_request
text = func(crypto.FILETYPE_TEXT, cert_or_req).decode("utf-8")

View File

@@ -1,16 +1,17 @@
"""Shim class to not have to depend on typing module in prod."""
import sys
"""Simple shim around the typing module.
This was useful when this code supported Python 2 and typing wasn't always
available. This code is being kept for now for backwards compatibility.
class TypingClass(object):
"""
import warnings
from typing import * # pylint: disable=wildcard-import, unused-wildcard-import
from typing import Collection, IO # type: ignore
warnings.warn("acme.magic_typing is deprecated and will be removed in a future release.",
DeprecationWarning)
class TypingClass:
"""Ignore import errors by getting anything"""
def __getattr__(self, name):
return None
try:
# mypy doesn't respect modifying sys.modules
from typing import * # pylint: disable=wildcard-import, unused-wildcard-import
from typing import Collection, IO # type: ignore
except ImportError:
# mypy complains because TypingClass is not a module
sys.modules[__name__] = TypingClass() # type: ignore
return None # pragma: no cover

View File

@@ -1,8 +1,8 @@
"""ACME protocol messages."""
import json
from collections.abc import Hashable
import josepy as jose
import six
from acme import challenges
from acme import errors
@@ -11,13 +11,6 @@ from acme import jws
from acme import util
from acme.mixins import ResourceMixin
try:
from collections.abc import Hashable
except ImportError: # pragma: no cover
from collections import Hashable
OLD_ERROR_PREFIX = "urn:acme:error:"
ERROR_PREFIX = "urn:ietf:params:acme:error:"
@@ -68,7 +61,6 @@ def is_acme_error(err):
return False
@six.python_2_unicode_compatible
class Error(jose.JSONObjectWithFields, errors.Error):
"""ACME error.
@@ -158,13 +150,10 @@ class _Constant(jose.JSONDeSerializable, Hashable): # type: ignore
def __hash__(self):
return hash((self.__class__, self.name))
def __ne__(self, other):
return not self == other
class Status(_Constant):
"""ACME "status" field."""
POSSIBLE_NAMES = {} # type: dict
POSSIBLE_NAMES: dict = {}
STATUS_UNKNOWN = Status('unknown')
STATUS_PENDING = Status('pending')
STATUS_PROCESSING = Status('processing')
@@ -177,7 +166,7 @@ STATUS_DEACTIVATED = Status('deactivated')
class IdentifierType(_Constant):
"""ACME identifier type."""
POSSIBLE_NAMES = {} # type: dict
POSSIBLE_NAMES: dict = {}
IDENTIFIER_FQDN = IdentifierType('dns') # IdentifierDNS in Boulder
@@ -195,7 +184,7 @@ class Identifier(jose.JSONObjectWithFields):
class Directory(jose.JSONDeSerializable):
"""Directory."""
_REGISTERED_TYPES = {} # type: dict
_REGISTERED_TYPES: dict = {}
class Meta(jose.JSONObjectWithFields):
"""Directory Meta."""
@@ -285,7 +274,7 @@ class ResourceBody(jose.JSONObjectWithFields):
"""ACME Resource Body."""
class ExternalAccountBinding(object):
class ExternalAccountBinding:
"""ACME External Account Binding"""
@classmethod

View File

@@ -1,7 +1,7 @@
"""Useful mixins for Challenge and Resource objects"""
class VersionedLEACMEMixin(object):
class VersionedLEACMEMixin:
"""This mixin stores the version of Let's Encrypt's endpoint being used."""
@property
def le_acme_version(self):

View File

@@ -1,17 +1,16 @@
"""Support for standalone client challenge solvers. """
import collections
import functools
import http.client as http_client
import http.server as BaseHTTPServer
import logging
import socket
import socketserver
import threading
from six.moves import BaseHTTPServer # type: ignore
from six.moves import http_client
from six.moves import socketserver # type: ignore
from typing import List
from acme import challenges
from acme import crypto_util
from acme.magic_typing import List
logger = logging.getLogger(__name__)
@@ -54,7 +53,7 @@ class ACMEServerMixin:
allow_reuse_address = True
class BaseDualNetworkedServers(object):
class BaseDualNetworkedServers:
"""Base class for a pair of IPv6 and IPv4 servers that tries to do everything
it's asked for both servers, but where failures in one server don't
affect the other.
@@ -64,8 +63,8 @@ class BaseDualNetworkedServers(object):
def __init__(self, ServerClass, server_address, *remaining_args, **kwargs):
port = server_address[1]
self.threads = [] # type: List[threading.Thread]
self.servers = [] # type: List[ACMEServerMixin]
self.threads: List[threading.Thread] = []
self.servers: List[ACMEServerMixin] = []
# Must try True first.
# Ubuntu, for example, will fail to bind to IPv4 if we've already bound

View File

@@ -1,7 +1,6 @@
"""ACME utilities."""
import six
def map_keys(dikt, func):
"""Map dictionary keys."""
return {func(key): value for key, value in six.iteritems(dikt)}
return {func(key): value for key, value in dikt.items()}

View File

@@ -87,7 +87,6 @@ language = 'en'
# directories to ignore when looking for source files.
exclude_patterns = [
'_build',
'man/*'
]
# The reST default role (used for this markup: `text`) to use for all

View File

@@ -1 +1,3 @@
:orphan:
.. literalinclude:: ../jws-help.txt

View File

@@ -1,11 +1,9 @@
from distutils.version import LooseVersion
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
@@ -17,21 +15,11 @@ install_requires = [
'PyOpenSSL>=17.3.0',
'pyrfc3339',
'pytz',
'requests[security]>=2.6.0', # security extras added in 2.4.1
'requests>=2.6.0',
'requests-toolbelt>=0.3.0',
'setuptools>=39.0.1',
'six>=1.11.0',
]
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
elif sys.version_info < (3,3):
install_requires.append('mock')
dev_extras = [
'pytest',
'pytest-xdist',

View File

@@ -1,14 +1,11 @@
"""Tests for acme.challenges."""
import urllib.parse as urllib_parse
import unittest
from unittest import mock
import josepy as jose
import OpenSSL
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import requests
from six.moves.urllib import parse as urllib_parse
from acme import errors

View File

@@ -2,17 +2,15 @@
# pylint: disable=too-many-lines
import copy
import datetime
import http.client as http_client
import json
import unittest
from typing import Dict
from unittest import mock
import josepy as jose
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import OpenSSL
import requests
from six.moves import http_client # pylint: disable=import-error
from acme import challenges
from acme import errors
@@ -64,7 +62,7 @@ class ClientTestBase(unittest.TestCase):
self.contact = ('mailto:cert-admin@example.com', 'tel:+12025551212')
reg = messages.Registration(
contact=self.contact, key=KEY.public_key())
the_arg = dict(reg) # type: Dict
the_arg: Dict = dict(reg)
self.new_reg = messages.NewRegistration(**the_arg)
self.regr = messages.RegistrationResource(
body=reg, uri='https://www.letsencrypt-demo.org/acme/reg/1')

View File

@@ -1,14 +1,14 @@
"""Tests for acme.crypto_util."""
import itertools
import socket
import socketserver
import threading
import time
import unittest
from typing import List
import josepy as jose
import OpenSSL
import six
from six.moves import socketserver # type: ignore # pylint: disable=import-error
from acme import errors
import test_util
@@ -27,8 +27,6 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
class _TestServer(socketserver.TCPServer):
# six.moves.* | pylint: disable=attribute-defined-outside-init,no-init
def server_bind(self): # pylint: disable=missing-docstring
self.socket = SSLSocket(socket.socket(),
certs)
@@ -62,7 +60,6 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
self.assertRaises(errors.Error, self._probe, b'bar')
def test_probe_connection_error(self):
# pylint has a hard time with six
self.server.server_close()
original_timeout = socket.getdefaulttimeout()
try:
@@ -121,9 +118,9 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
@classmethod
def _get_idn_names(cls):
"""Returns expected names from '{cert,csr}-idnsans.pem'."""
chars = [six.unichr(i) for i in itertools.chain(range(0x3c3, 0x400),
range(0x641, 0x6fc),
range(0x1820, 0x1877))]
chars = [chr(i) for i in itertools.chain(range(0x3c3, 0x400),
range(0x641, 0x6fc),
range(0x1820, 0x1877))]
return [''.join(chars[i: i + 45]) + '.invalid'
for i in range(0, len(chars), 45)]
@@ -184,7 +181,7 @@ class RandomSnTest(unittest.TestCase):
def setUp(self):
self.cert_count = 5
self.serial_num = [] # type: List[int]
self.serial_num: List[int] = []
self.key = OpenSSL.crypto.PKey()
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)

View File

@@ -1,10 +1,6 @@
"""Tests for acme.errors."""
import unittest
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
from unittest import mock
class BadNonceTest(unittest.TestCase):

View File

@@ -1,11 +1,8 @@
"""Tests for acme.magic_typing."""
import sys
import unittest
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import warnings
from unittest import mock
class MagicTypingTest(unittest.TestCase):
@@ -13,32 +10,21 @@ class MagicTypingTest(unittest.TestCase):
def test_import_success(self):
try:
import typing as temp_typing
except ImportError: # pragma: no cover
temp_typing = None # pragma: no cover
except ImportError: # pragma: no cover
temp_typing = None # pragma: no cover
typing_class_mock = mock.MagicMock()
text_mock = mock.MagicMock()
typing_class_mock.Text = text_mock
sys.modules['typing'] = typing_class_mock
if 'acme.magic_typing' in sys.modules:
del sys.modules['acme.magic_typing'] # pragma: no cover
from acme.magic_typing import Text
del sys.modules['acme.magic_typing'] # pragma: no cover
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
from acme.magic_typing import Text
self.assertEqual(Text, text_mock)
del sys.modules['acme.magic_typing']
sys.modules['typing'] = temp_typing
def test_import_failure(self):
try:
import typing as temp_typing
except ImportError: # pragma: no cover
temp_typing = None # pragma: no cover
sys.modules['typing'] = None
if 'acme.magic_typing' in sys.modules:
del sys.modules['acme.magic_typing'] # pragma: no cover
from acme.magic_typing import Text
self.assertTrue(Text is None)
del sys.modules['acme.magic_typing']
sys.modules['typing'] = temp_typing
if __name__ == '__main__':
unittest.main() # pragma: no cover

View File

@@ -1,11 +1,9 @@
"""Tests for acme.messages."""
from typing import Dict
import unittest
from unittest import mock
import josepy as jose
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
from acme import challenges
import test_util
@@ -84,7 +82,7 @@ class ConstantTest(unittest.TestCase):
from acme.messages import _Constant
class MockConstant(_Constant): # pylint: disable=missing-docstring
POSSIBLE_NAMES = {} # type: Dict
POSSIBLE_NAMES: Dict = {}
self.MockConstant = MockConstant # pylint: disable=invalid-name
self.const_a = MockConstant('a')

View File

@@ -1,16 +1,14 @@
"""Tests for acme.standalone."""
import http.client as http_client
import socket
import socketserver
import threading
import unittest
from typing import Set
from unittest import mock
import josepy as jose
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import requests
from six.moves import http_client # pylint: disable=import-error
from six.moves import socketserver # type: ignore # pylint: disable=import-error
from acme import challenges
from acme import crypto_util
@@ -44,7 +42,7 @@ class HTTP01ServerTest(unittest.TestCase):
def setUp(self):
self.account_key = jose.JWK.load(
test_util.load_vector('rsa1024_key.pem'))
self.resources = set() # type: Set
self.resources: Set = set()
from acme.standalone import HTTP01Server
self.server = HTTP01Server(('', 0), resources=self.resources)
@@ -221,7 +219,7 @@ class HTTP01DualNetworkedServersTest(unittest.TestCase):
def setUp(self):
self.account_key = jose.JWK.load(
test_util.load_vector('rsa1024_key.pem'))
self.resources = set() # type: Set
self.resources: Set = set()
from acme.standalone import HTTP01DualNetworkedServers
self.servers = HTTP01DualNetworkedServers(('', 0), resources=self.resources)

View File

@@ -9,7 +9,6 @@ import pkg_resources
from certbot import errors
from certbot import util
from certbot.compat import os
logger = logging.getLogger(__name__)

View File

@@ -3,7 +3,6 @@ import fnmatch
from certbot_apache._internal import interfaces
PASS = "CERTBOT_PASS_ASSERT"

View File

@@ -64,10 +64,10 @@ Translates over to:
"/files/etc/apache2/apache2.conf/bLoCk[1]",
]
"""
from acme.magic_typing import Set
from typing import Set
from certbot import errors
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import assertions
from certbot_apache._internal import interfaces
@@ -355,7 +355,7 @@ class AugeasBlockNode(AugeasDirectiveNode):
ownpath = self.metadata.get("augeaspath")
directives = self.parser.find_dir(name, start=ownpath, exclude=exclude)
already_parsed = set() # type: Set[str]
already_parsed: Set[str] = set()
for directive in directives:
# Remove the /arg part from the Augeas path
directive = directive.partition("/arg")[0]

View File

@@ -1,28 +1,23 @@
"""Apache Configurator."""
# pylint: disable=too-many-lines
from collections import defaultdict
from distutils.version import LooseVersion
import copy
from distutils.version import LooseVersion
import fnmatch
import logging
import re
import socket
import time
from typing import DefaultDict
from typing import Dict
from typing import List
from typing import Set
from typing import Union
import zope.component
import zope.interface
try:
import apacheconfig
HAS_APACHECONFIG = True
except ImportError: # pragma: no cover
HAS_APACHECONFIG = False
from acme import challenges
from acme.magic_typing import DefaultDict
from acme.magic_typing import Dict
from acme.magic_typing import List
from acme.magic_typing import Set
from acme.magic_typing import Union
from certbot import errors
from certbot import interfaces
from certbot import util
@@ -41,6 +36,13 @@ from certbot_apache._internal import http_01
from certbot_apache._internal import obj
from certbot_apache._internal import parser
try:
import apacheconfig
HAS_APACHECONFIG = True
except ImportError: # pragma: no cover
HAS_APACHECONFIG = False
logger = logging.getLogger(__name__)
@@ -210,23 +212,23 @@ class ApacheConfigurator(common.Installer):
super(ApacheConfigurator, self).__init__(*args, **kwargs)
# Add name_server association dict
self.assoc = {} # type: Dict[str, obj.VirtualHost]
self.assoc: Dict[str, obj.VirtualHost] = {}
# Outstanding challenges
self._chall_out = set() # type: Set[KeyAuthorizationAnnotatedChallenge]
self._chall_out: Set[KeyAuthorizationAnnotatedChallenge] = set()
# List of vhosts configured per wildcard domain on this run.
# used by deploy_cert() and enhance()
self._wildcard_vhosts = {} # type: Dict[str, List[obj.VirtualHost]]
self._wildcard_vhosts: Dict[str, List[obj.VirtualHost]] = {}
# Maps enhancements to vhosts we've enabled the enhancement for
self._enhanced_vhosts = defaultdict(set) # type: DefaultDict[str, Set[obj.VirtualHost]]
self._enhanced_vhosts: DefaultDict[str, Set[obj.VirtualHost]] = defaultdict(set)
# Temporary state for AutoHSTS enhancement
self._autohsts = {} # type: Dict[str, Dict[str, Union[int, float]]]
self._autohsts: Dict[str, Dict[str, Union[int, float]]] = {}
# Reverter save notes
self.save_notes = ""
# Should we use ParserNode implementation instead of the old behavior
self.USE_PARSERNODE = use_parsernode
# Saves the list of file paths that were parsed initially, and
# not added to parser tree by self.conf("vhost-root") for example.
self.parsed_paths = [] # type: List[str]
self.parsed_paths: List[str] = []
# These will be set in the prepare function
self._prepared = False
self.parser = None
@@ -832,7 +834,7 @@ class ApacheConfigurator(common.Installer):
:rtype: set
"""
all_names = set() # type: Set[str]
all_names: Set[str] = set()
vhost_macro = []
@@ -996,8 +998,8 @@ class ApacheConfigurator(common.Installer):
"""
# Search base config, and all included paths for VirtualHosts
file_paths = {} # type: Dict[str, str]
internal_paths = defaultdict(set) # type: DefaultDict[str, Set[str]]
file_paths: Dict[str, str] = {}
internal_paths: DefaultDict[str, Set[str]] = defaultdict(set)
vhs = []
# Make a list of parser paths because the parser_paths
# dictionary may be modified during the loop.
@@ -2156,7 +2158,7 @@ class ApacheConfigurator(common.Installer):
# There can be other RewriteRule directive lines in vhost config.
# rewrite_args_dict keys are directive ids and the corresponding value
# for each is a list of arguments to that directive.
rewrite_args_dict = defaultdict(list) # type: DefaultDict[str, List[str]]
rewrite_args_dict: DefaultDict[str, List[str]] = defaultdict(list)
pat = r'(.*directive\[\d+\]).*'
for match in rewrite_path:
m = re.match(pat, match)
@@ -2250,7 +2252,7 @@ class ApacheConfigurator(common.Installer):
if ssl_vhost.aliases:
serveralias = "ServerAlias " + " ".join(ssl_vhost.aliases)
rewrite_rule_args = [] # type: List[str]
rewrite_rule_args: List[str] = []
if self.get_version() >= (2, 3, 9):
rewrite_rule_args = constants.REWRITE_HTTPS_ARGS_WITH_END
else:

View File

@@ -1,10 +1,10 @@
""" Dual ParserNode implementation """
from certbot_apache._internal import apacheparser
from certbot_apache._internal import assertions
from certbot_apache._internal import augeasparser
from certbot_apache._internal import apacheparser
class DualNodeBase(object):
class DualNodeBase:
""" Dual parser interface for in development testing. This is used as the
base class for dual parser interface classes. This class handles runtime
attribute value assertions."""

View File

@@ -1,9 +1,9 @@
"""A class that performs HTTP-01 challenges for Apache"""
import logging
import errno
import logging
from typing import List
from typing import Set
from acme.magic_typing import List
from acme.magic_typing import Set
from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
@@ -57,7 +57,7 @@ class ApacheHttp01(common.ChallengePerformer):
self.challenge_dir = os.path.join(
self.configurator.config.work_dir,
"http_challenges")
self.moded_vhosts = set() # type: Set[VirtualHost]
self.moded_vhosts: Set[VirtualHost] = set()
def perform(self):
"""Perform all HTTP-01 challenges."""
@@ -93,7 +93,7 @@ class ApacheHttp01(common.ChallengePerformer):
self.configurator.enable_mod(mod, temp=True)
def _mod_config(self):
selected_vhosts = [] # type: List[VirtualHost]
selected_vhosts: List[VirtualHost] = []
http_port = str(self.configurator.config.http01_port)
for chall in self.achalls:
# Search for matching VirtualHosts

View File

@@ -100,12 +100,9 @@ For this reason the internal representation of data should not ignore the case.
"""
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class ParserNode(object):
class ParserNode(object, metaclass=abc.ABCMeta):
"""
ParserNode is the basic building block of the tree of such nodes,
representing the structure of the configuration. It is largely meant to keep
@@ -204,9 +201,7 @@ class ParserNode(object):
"""
# Linter rule exclusion done because of https://github.com/PyCQA/pylint/issues/179
@six.add_metaclass(abc.ABCMeta) # pylint: disable=abstract-method
class CommentNode(ParserNode):
class CommentNode(ParserNode, metaclass=abc.ABCMeta):
"""
CommentNode class is used for representation of comments within the parsed
configuration structure. Because of the nature of comments, it is not able
@@ -249,8 +244,7 @@ class CommentNode(ParserNode):
metadata=kwargs.get('metadata', {})) # pragma: no cover
@six.add_metaclass(abc.ABCMeta)
class DirectiveNode(ParserNode):
class DirectiveNode(ParserNode, metaclass=abc.ABCMeta):
"""
DirectiveNode class represents a configuration directive within the configuration.
It can have zero or more parameters attached to it. Because of the nature of
@@ -325,8 +319,7 @@ class DirectiveNode(ParserNode):
"""
@six.add_metaclass(abc.ABCMeta)
class BlockNode(DirectiveNode):
class BlockNode(DirectiveNode, metaclass=abc.ABCMeta):
"""
BlockNode class represents a block of nested configuration directives, comments
and other blocks as its children. A BlockNode can have zero or more parameters

View File

@@ -1,7 +1,7 @@
"""Module contains classes used by the Apache Configurator."""
import re
from typing import Set
from acme.magic_typing import Set
from certbot.plugins import common
@@ -20,9 +20,6 @@ class Addr(common.Addr):
self.is_wildcard() and other.is_wildcard()))
return False
def __ne__(self, other):
return not self.__eq__(other)
def __repr__(self):
return "certbot_apache._internal.obj.Addr(" + repr(self.tup) + ")"
@@ -98,7 +95,7 @@ class Addr(common.Addr):
return self.get_addr_obj(port)
class VirtualHost(object):
class VirtualHost:
"""Represents an Apache Virtualhost.
:ivar str filep: file path of VH
@@ -140,7 +137,7 @@ class VirtualHost(object):
def get_names(self):
"""Return a set of all names."""
all_names = set() # type: Set[str]
all_names: Set[str] = set()
all_names.update(self.aliases)
# Strip out any scheme:// and <port> field from servername
if self.name is not None:
@@ -191,9 +188,6 @@ class VirtualHost(object):
return False
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self.filep, self.path,
tuple(self.addrs), tuple(self.get_names()),
@@ -251,7 +245,7 @@ class VirtualHost(object):
# already_found acts to keep everything very conservative.
# Don't allow multiple ip:ports in same set.
already_found = set() # type: Set[str]
already_found: Set[str] = set()
for addr in vhost.addrs:
for local_addr in self.addrs:

View File

@@ -1,9 +1,9 @@
""" Distribution specific override class for CentOS family (RHEL, Fedora) """
import logging
from typing import List
import zope.interface
from acme.magic_typing import List
from certbot import errors
from certbot import interfaces
from certbot import util
@@ -102,9 +102,9 @@ class CentOSConfigurator(configurator.ApacheConfigurator):
loadmods = self.parser.find_dir("LoadModule", "ssl_module", exclude=False)
correct_ifmods = [] # type: List[str]
loadmod_args = [] # type: List[str]
loadmod_paths = [] # type: List[str]
correct_ifmods: List[str] = []
loadmod_args: List[str] = []
loadmod_paths: List[str] = []
for m in loadmods:
noarg_path = m.rpartition("/")[0]
path_args = self.parser.get_all_args(noarg_path)

View File

@@ -3,12 +3,9 @@ import copy
import fnmatch
import logging
import re
import sys
from typing import Dict
from typing import List
import six
from acme.magic_typing import Dict
from acme.magic_typing import List
from certbot import errors
from certbot.compat import os
from certbot_apache._internal import apache_util
@@ -17,7 +14,7 @@ from certbot_apache._internal import constants
logger = logging.getLogger(__name__)
class ApacheParser(object):
class ApacheParser:
"""Class handles the fine details of parsing the Apache Configuration.
.. todo:: Make parsing general... remove sites-available etc...
@@ -51,9 +48,9 @@ class ApacheParser(object):
"version 1.2.0 or higher, please make sure you have you have "
"those installed.")
self.modules = {} # type: Dict[str, str]
self.parser_paths = {} # type: Dict[str, List[str]]
self.variables = {} # type: Dict[str, str]
self.modules: Dict[str, str] = {}
self.parser_paths: Dict[str, List[str]] = {}
self.variables: Dict[str, str] = {}
# Find configuration root and make sure augeas can parse it.
self.root = os.path.abspath(root)
@@ -266,7 +263,7 @@ class ApacheParser(object):
the iteration issue. Else... parse and enable mods at same time.
"""
mods = {} # type: Dict[str, str]
mods: Dict[str, str] = {}
matches = self.find_dir("LoadModule")
iterator = iter(matches)
# Make sure prev_size != cur_size for do: while: iteration
@@ -275,7 +272,7 @@ class ApacheParser(object):
while len(mods) != prev_size:
prev_size = len(mods)
for match_name, match_filename in six.moves.zip(
for match_name, match_filename in zip(
iterator, iterator):
mod_name = self.get_arg(match_name)
mod_filename = self.get_arg(match_filename)
@@ -553,7 +550,7 @@ class ApacheParser(object):
else:
arg_suffix = "/*[self::arg=~regexp('%s')]" % case_i(arg)
ordered_matches = [] # type: List[str]
ordered_matches: List[str] = []
# TODO: Wildcards should be included in alphabetical order
# https://httpd.apache.org/docs/2.4/mod/core.html#include
@@ -738,9 +735,6 @@ class ApacheParser(object):
:rtype: str
"""
if sys.version_info < (3, 6):
# This strips off final /Z(?ms)
return fnmatch.translate(clean_fn_match)[:-7] # pragma: no cover
# Since Python 3.6, it returns a different pattern like (?s:.*\.load)\Z
return fnmatch.translate(clean_fn_match)[4:-3] # pragma: no cover

View File

@@ -1,11 +1,7 @@
from distutils.version import LooseVersion
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -18,15 +14,6 @@ install_requires = [
'zope.interface',
]
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
elif sys.version_info < (3,3):
install_requires.append('mock')
dev_extras = [
'apacheconfig>=0.3.2',
]

View File

@@ -1,4 +1,6 @@
"""Tests for AugeasParserNode classes"""
from typing import List
try:
import mock
except ImportError: # pragma: no cover
@@ -107,7 +109,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
def test_set_parameters(self):
servernames = self.config.parser_root.find_directives("servername")
names = [] # type: List[str]
names: List[str] = []
for servername in servernames:
names += servername.parameters
self.assertFalse("going_to_set_this" in names)

View File

@@ -7,7 +7,6 @@ try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import six # pylint: disable=unused-import # six is used in mock.patch()
from certbot import errors
from certbot_apache._internal import constants

View File

@@ -10,7 +10,6 @@ try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import six # pylint: disable=unused-import # six is used in mock.patch()
from acme import challenges
from certbot import achallenges
@@ -726,7 +725,7 @@ class MultipleVhostsTest(util.ApacheTest):
# This calls open
self.config.reverter.register_file_creation = mock.Mock()
mock_open.side_effect = IOError
with mock.patch("six.moves.builtins.open", mock_open):
with mock.patch("builtins.open", mock_open):
self.assertRaises(
errors.PluginError,
self.config.make_vhost_ssl, self.vh_truth[0])
@@ -1834,7 +1833,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
def test_open_module_file(self):
mock_open = mock.mock_open(read_data="testing 12 3")
with mock.patch("six.moves.builtins.open", mock_open):
with mock.patch("builtins.open", mock_open):
self.assertEqual(self.config._open_module_file("/nonsense/"), "testing 12 3")
if __name__ == "__main__":

View File

@@ -1,6 +1,7 @@
"""Test for certbot_apache._internal.http_01."""
import unittest
import errno
from typing import List
try:
import mock
@@ -26,7 +27,7 @@ class ApacheHttp01Test(util.ApacheTest):
super(ApacheHttp01Test, self).setUp(*args, **kwargs)
self.account_key = self.rsa512jwk
self.achalls = [] # type: List[achallenges.KeyAuthorizationAnnotatedChallenge]
self.achalls: List[achallenges.KeyAuthorizationAnnotatedChallenge] = []
vh_truth = util.get_vh_truth(
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
# Takes the vhosts for encryption-example.demo, certbot.demo

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.13.0"
BASENAME=$(basename $0)
USAGE="Usage: $BASENAME [OPTIONS]
A self-updating wrapper script for the Certbot ACME client. When run, updates
@@ -803,8 +803,10 @@ if [ -f /etc/debian_version ]; then
elif [ -f /etc/mageia-release ]; then
# Mageia has both /etc/mageia-release and /etc/redhat-release
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif [ -f /etc/redhat-release ]; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=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.
@@ -863,22 +865,31 @@ elif [ -f /etc/redhat-release ]; then
LE_PYTHON="$prev_le_python"
elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif [ -f /etc/arch-release ]; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif [ -f /etc/manjaro-release ]; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif [ -f /etc/gentoo-release ]; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif uname | grep -iq FreeBSD ; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif uname | grep -iq Darwin ; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
else
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
fi
# We handle this case after determining the normal bootstrap version to allow
@@ -1107,7 +1118,9 @@ if [ "$1" = "--le-auto-phase2" ]; then
fi
if [ -f "$VENV_BIN/letsencrypt" -a "$INSTALL_ONLY" != 1 ]; then
error "Certbot will no longer receive updates."
error "certbot-auto and its Certbot installation will no longer receive updates."
error "You will not receive any bug fixes including those fixing server compatibility"
error "or security problems."
error "Please visit https://certbot.eff.org/ to check for other alternatives."
"$VENV_BIN/letsencrypt" "$@"
exit 0
@@ -1475,18 +1488,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.13.0 \
--hash=sha256:082eb732e1318bb9605afa7aea8db2c2f4c5029d523c73f24c6aa98f03caff76 \
--hash=sha256:64cf41b57df7667d9d849fcaa9031a4f151788246733d1f4c3f37a5aa5e2f458
acme==1.13.0 \
--hash=sha256:93b6365c9425de03497a6b8aee1107814501d2974499b42e9bcc9a7378771143 \
--hash=sha256:6b4257dfd6a6d5f01e8cd4f0b10422c17836bed7c67e9c5b0a0ad6c7d651c088
certbot-apache==1.13.0 \
--hash=sha256:36ed02ac7d2d91febee8dd3181ae9095b3f06434c9ed8959fbc6db24ab4da2e8 \
--hash=sha256:4b5a16e80c1418e2edc05fc2578f522fb24974b2c13eb747cdfeef69e5bd5ae1
certbot-nginx==1.13.0 \
--hash=sha256:3ff271f65321b25c77a868af21f76f58754a7d61529ad565a1d66e29c711120f \
--hash=sha256:9e972cc19c0fa9e5b7863da0423b156fbfb5623fd30b558fd2fd6d21c24c0b08
UNLIKELY_EOF
# -------------------------------------------------------------------------

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import sys

View File

@@ -7,14 +7,14 @@ import tempfile
from certbot_integration_tests.utils import certbot_call
class IntegrationTestsContext(object):
class IntegrationTestsContext:
"""General fixture describing a certbot integration tests context"""
def __init__(self, request):
self.request = request
if hasattr(request.config, 'slaveinput'): # Worker node
self.worker_id = request.config.slaveinput['slaveid']
acme_xdist = request.config.slaveinput['acme_xdist']
if hasattr(request.config, 'workerinput'): # Worker node
self.worker_id = request.config.workerinput['workerid']
acme_xdist = request.config.workerinput['acme_xdist']
else: # Primary node
self.worker_id = 'primary'
acme_xdist = request.config.acme_xdist

View File

@@ -1,5 +1,4 @@
"""Module executing integration tests against certbot core."""
from __future__ import print_function
import os
from os.path import exists

View File

@@ -6,7 +6,6 @@ for a directory a specific configuration using built-in pytest hooks.
See https://docs.pytest.org/en/latest/reference.html#hook-reference
"""
from __future__ import print_function
import contextlib
import subprocess
import sys
@@ -35,7 +34,7 @@ def pytest_configure(config):
Standard pytest hook used to add a configuration logic for each node of a pytest run.
:param config: the current pytest configuration
"""
if not hasattr(config, 'slaveinput'): # If true, this is the primary node
if not hasattr(config, 'workerinput'): # If true, this is the primary node
with _print_on_err():
_setup_primary_node(config)
@@ -45,8 +44,8 @@ def pytest_configure_node(node):
Standard pytest-xdist hook used to configure a worker node.
:param node: current worker node
"""
node.slaveinput['acme_xdist'] = node.config.acme_xdist
node.slaveinput['dns_xdist'] = node.config.dns_xdist
node.workerinput['acme_xdist'] = node.config.acme_xdist
node.workerinput['dns_xdist'] = node.config.dns_xdist
@contextlib.contextmanager

View File

@@ -1,8 +1,8 @@
"""Module executing integration tests against certbot with nginx plugin."""
import os
import ssl
from typing import List
import pytest
from certbot_integration_tests.nginx_tests import context as nginx_context
@@ -32,8 +32,8 @@ def test_context(request):
'--preferred-challenges', 'http'
], {'default_server': False}),
], indirect=['context'])
def test_certificate_deployment(certname_pattern, params, context):
# type: (str, List[str], nginx_context.IntegrationTestsContext) -> None
def test_certificate_deployment(certname_pattern: str, params: List[str],
context: nginx_context.IntegrationTestsContext) -> None:
"""
Test various scenarios to deploy a certificate to nginx using certbot.
"""

View File

@@ -1,7 +1,7 @@
"""Module to handle the context of RFC2136 integration tests."""
import tempfile
from contextlib import contextmanager
import tempfile
from pkg_resources import resource_filename
from pytest import skip
@@ -18,8 +18,8 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
self.request = request
self._dns_xdist = None
if hasattr(request.config, 'slaveinput'): # Worker node
self._dns_xdist = request.config.slaveinput['dns_xdist']
if hasattr(request.config, 'workerinput'): # Worker node
self._dns_xdist = request.config.workerinput['dns_xdist']
else: # Primary node
self._dns_xdist = request.config.dns_xdist

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env python
"""Module to setup an ACME CA server environment able to run multiple tests in parallel"""
from __future__ import print_function
import argparse
import errno
@@ -12,18 +11,18 @@ import subprocess
import sys
import tempfile
import time
from typing import List
import requests
# pylint: disable=wildcard-import,unused-wildcard-import
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 *
class ACMEServer(object):
class ACMEServer:
"""
ACMEServer configures and handles the lifecycle of an ACME CA server and an HTTP reverse proxy
instance, to allow parallel execution of integration tests against the unique http-01 port
@@ -52,7 +51,7 @@ class ACMEServer(object):
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: List[subprocess.Popen] = []
self._stdout = sys.stdout if stdout else open(os.devnull, 'w')
self._dns_server = dns_server
self._http_01_port = http_01_port

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env python
"""Module to call certbot in test mode"""
from __future__ import absolute_import
import os
import subprocess

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env python
"""Module to setup an RFC2136-capable DNS server"""
from __future__ import print_function
import os
import os.path
import shutil
@@ -21,7 +19,7 @@ BIND_BIND_ADDRESS = ("127.0.0.1", 45953)
BIND_TEST_QUERY = bytearray.fromhex("0011cb37000000010000000000000000010003")
class DNSServer(object):
class DNSServer:
"""
DNSServer configures and handles the lifetime of an RFC2136-capable server.
DNServer provides access to the dns_xdist parameter, listing the address and port
@@ -40,7 +38,7 @@ class DNSServer(object):
self.bind_root = tempfile.mkdtemp()
self.process = None # type: subprocess.Popen
self.process: subprocess.Popen = None
self.dns_xdist = {"address": BIND_BIND_ADDRESS[0], "port": BIND_BIND_ADDRESS[1]}
@@ -113,8 +111,7 @@ class DNSServer(object):
self.stop()
raise
def _wait_until_ready(self, attempts=30):
# type: (int) -> None
def _wait_until_ready(self, attempts: int = 30) -> None:
"""
Polls the DNS server over TCP until it gets a response, or until
it runs out of attempts and raises a ValueError.

View File

@@ -4,10 +4,12 @@ or outside during setup/teardown of the integration tests environment.
"""
import contextlib
import errno
import http.server as SimpleHTTPServer
import multiprocessing
import os
import re
import shutil
import socketserver
import stat
import sys
import tempfile
@@ -23,11 +25,9 @@ from cryptography.x509 import load_pem_x509_certificate
from OpenSSL import crypto
import pkg_resources
import requests
from six.moves import SimpleHTTPServer
from six.moves import socketserver
from certbot_integration_tests.utils.constants import \
PEBBLE_ALTERNATE_ROOTS, PEBBLE_MANAGEMENT_URL
from certbot_integration_tests.utils.constants import PEBBLE_ALTERNATE_ROOTS
from certbot_integration_tests.utils.constants import PEBBLE_MANAGEMENT_URL
RSA_KEY_TYPE = 'rsa'
ECDSA_KEY_TYPE = 'ecdsa'
@@ -311,7 +311,7 @@ def echo(keyword, path=None):
if not re.match(r'^\w+$', keyword):
raise ValueError('Error, keyword `{0}` is not a single keyword.'
.format(keyword))
return '{0} -c "from __future__ import print_function; print(\'{1}\')"{2}'.format(
return '{0} -c "print(\'{1}\')"{2}'.format(
os.path.basename(sys.executable), keyword, ' >> "{0}"'.format(path) if path else '')

View File

@@ -7,7 +7,8 @@ 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 DEFAULT_HTTP_01_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')

View File

@@ -4,6 +4,7 @@ This runnable module interfaces itself with the Pebble management interface in o
to serve a mock OCSP responder during integration tests against Pebble.
"""
import datetime
import http.server as BaseHTTPServer
import re
from cryptography import x509
@@ -13,7 +14,6 @@ from cryptography.hazmat.primitives import serialization
from cryptography.x509 import ocsp
from dateutil import parser
import requests
from six.moves import BaseHTTPServer
from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT
from certbot_integration_tests.utils.constants import PEBBLE_MANAGEMENT_URL

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env python
# pylint: disable=missing-module-docstring
import http.server as BaseHTTPServer
import json
import re
import sys
import requests
from six.moves import BaseHTTPServer
from certbot_integration_tests.utils.misc import GracefulTCPServer

View File

@@ -14,11 +14,12 @@ install_requires = [
'pyopenssl',
'pytest',
'pytest-cov',
'pytest-xdist',
# This version is needed for "worker" attributes we currently use like
# "workerinput". See https://github.com/pytest-dev/pytest-xdist/pull/268.
'pytest-xdist>=1.22.1',
'python-dateutil',
'pyyaml',
'requests',
'six'
]
# Add pywin32 on Windows platforms to handle low-level system calls.

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env python3
import pytest
import subprocess
import glob
import os
import re
import subprocess
import pytest
@pytest.fixture(autouse=True, scope="module")

View File

@@ -6,7 +6,7 @@ for a directory a specific configuration using built-in pytest hooks.
See https://docs.pytest.org/en/latest/reference.html#hook-reference
"""
from __future__ import print_function
import os
ROOT_PATH = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))

View File

@@ -1,8 +1,8 @@
import os
import re
import subprocess
import time
import unittest
import subprocess
import re
@unittest.skipIf(os.name != 'nt', reason='Windows installer tests must be run on Windows.')

View File

@@ -8,11 +8,11 @@ RUN apt-get update && \
WORKDIR /opt/certbot/src
# We copy all contents of the build directory to allow us to easily use
# things like tools/venv3.py which expects all of our packages to be available.
# things like tools/venv.py which expects all of our packages to be available.
COPY . .
RUN tools/venv3.py
ENV PATH /opt/certbot/src/venv3/bin:$PATH
RUN tools/venv.py
ENV PATH /opt/certbot/src/venv/bin:$PATH
# install in editable mode (-e) to save space: it's not possible to
# "rm -rf /opt/certbot/src" (it's stays in the underlaying image);

View File

@@ -2,11 +2,8 @@
import os
import shutil
import subprocess
from unittest import mock
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import zope.interface
from certbot import errors as le_errors

View File

@@ -11,7 +11,7 @@ from certbot_compatibility_test import util
logger = logging.getLogger(__name__)
class Proxy(object):
class Proxy:
"""A common base for compatibility test configurators"""
@classmethod

View File

@@ -2,10 +2,10 @@
import os
import shutil
import subprocess
from typing import Set
import zope.interface
from acme.magic_typing import Set
from certbot._internal import configuration
from certbot_compatibility_test import errors
from certbot_compatibility_test import interfaces
@@ -68,7 +68,7 @@ def _get_server_root(config):
def _get_names(config):
"""Returns all and testable domain names in config"""
all_names = set() # type: Set[str]
all_names: Set[str] = set()
for root, _dirs, files in os.walk(config):
for this_file in files:
update_names = _get_server_names(root, this_file)

View File

@@ -8,6 +8,8 @@ import shutil
import sys
import tempfile
import time
from typing import List
from typing import Tuple
import OpenSSL
from urllib3.util import connection
@@ -15,8 +17,6 @@ from urllib3.util import connection
from acme import challenges
from acme import crypto_util
from acme import messages
from acme.magic_typing import List
from acme.magic_typing import Tuple
from certbot import achallenges
from certbot import errors as le_errors
from certbot.tests import acme_util
@@ -178,7 +178,7 @@ def test_enhancements(plugin, domains):
"enhancements")
return False
domains_and_info = [(domain, []) for domain in domains] # type: List[Tuple[str, List[bool]]]
domains_and_info: List[Tuple[str, List[bool]]] = [(domain, []) for domain in domains]
for domain, info in domains_and_info:
try:

View File

@@ -3,8 +3,6 @@ import logging
import socket
import requests
import six
from six.moves import xrange
from acme import crypto_util
from acme import errors as acme_errors
@@ -12,18 +10,18 @@ from acme import errors as acme_errors
logger = logging.getLogger(__name__)
class Validator(object):
class Validator:
"""Collection of functions to test a live webserver's configuration"""
def certificate(self, cert, name, alt_host=None, port=443):
"""Verifies the certificate presented at name is cert"""
if alt_host is None:
host = socket.gethostbyname(name).encode()
elif isinstance(alt_host, six.binary_type):
elif isinstance(alt_host, bytes):
host = alt_host
else:
host = alt_host.encode()
name = name if isinstance(name, six.binary_type) else name.encode()
name = name if isinstance(name, bytes) else name.encode()
try:
presented_cert = crypto_util.probe_sni(name, host, port)
@@ -62,7 +60,7 @@ class Validator(object):
else:
response = requests.get(url, allow_redirects=False)
return response.status_code in xrange(300, 309)
return response.status_code in range(300, 309)
def hsts(self, name):
"""Test for HTTP Strict Transport Security header"""

View File

@@ -1,10 +1,7 @@
"""Tests for certbot_compatibility_test.validator."""
import unittest
from unittest import mock
try:
import mock
except ImportError: # pragma: no cover
from unittest import mock # type: ignore
import OpenSSL
import requests

View File

@@ -1,29 +1,17 @@
from distutils.version import LooseVersion
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
install_requires = [
'certbot',
'certbot-apache',
'six',
'requests',
'zope.interface',
]
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
elif sys.version_info < (3,3):
install_requires.append('mock')
if sys.version_info < (2, 7, 9):
# For secure SSL connexion with Python 2.7 (InsecurePlatformWarning)
install_requires.append('ndg-httpsclient')

View File

@@ -1,13 +1,12 @@
"""DNS Authenticator for Cloudflare."""
import logging
from typing import Any
from typing import Dict
from typing import List
import CloudFlare
import zope.interface
from acme.magic_typing import Any
from acme.magic_typing import Dict
from acme.magic_typing import List
from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
@@ -85,7 +84,7 @@ class Authenticator(dns_common.DNSAuthenticator):
return _CloudflareClient(self.credentials.conf('email'), self.credentials.conf('api-key'))
class _CloudflareClient(object):
class _CloudflareClient:
"""
Encapsulates all communication with the Cloudflare API.
"""
@@ -173,7 +172,7 @@ class _CloudflareClient(object):
"""
zone_name_guesses = dns_common.base_domain_name_guesses(domain)
zones = [] # type: List[Dict[str, Any]]
zones: List[Dict[str, Any]] = []
code = msg = None
for zone_name in zone_name_guesses:

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -21,6 +21,7 @@ class Authenticator(dns_common.DNSAuthenticator):
description = 'Obtain certificates using a DNS TXT record (if you are ' + \
'using DigitalOcean for DNS).'
ttl = 30
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
@@ -45,7 +46,8 @@ class Authenticator(dns_common.DNSAuthenticator):
)
def _perform(self, domain, validation_name, validation):
self._get_digitalocean_client().add_txt_record(domain, validation_name, validation)
self._get_digitalocean_client().add_txt_record(domain, validation_name, validation,
self.ttl)
def _cleanup(self, domain, validation_name, validation):
self._get_digitalocean_client().del_txt_record(domain, validation_name, validation)
@@ -54,7 +56,7 @@ class Authenticator(dns_common.DNSAuthenticator):
return _DigitalOceanClient(self.credentials.conf('token'))
class _DigitalOceanClient(object):
class _DigitalOceanClient:
"""
Encapsulates all communication with the DigitalOcean API.
"""
@@ -62,13 +64,15 @@ class _DigitalOceanClient(object):
def __init__(self, token):
self.manager = digitalocean.Manager(token=token)
def add_txt_record(self, domain_name, record_name, record_content):
def add_txt_record(self, domain_name: str, record_name: str, record_content: str,
record_ttl: int):
"""
Add a TXT record using the supplied information.
:param str domain_name: The domain to use to associate the record with.
:param str record_name: The record name (typically beginning with '_acme-challenge.').
:param str record_content: The record content (typically the challenge validation).
:param int record_ttl: The record TTL.
:raises certbot.errors.PluginError: if an error occurs communicating with the DigitalOcean
API
"""
@@ -89,7 +93,8 @@ class _DigitalOceanClient(object):
result = domain.create_new_domain_record(
type='TXT',
name=self._compute_record_name(domain, record_name),
data=record_content)
data=record_content,
ttl=record_ttl) # ttl kwarg is only effective starting python-digitalocean 1.15.0
record_id = result['domain_record']['id']
@@ -99,7 +104,7 @@ class _DigitalOceanClient(object):
raise errors.PluginError('Error adding TXT record using the DigitalOcean API: {0}'
.format(e))
def del_txt_record(self, domain_name, record_name, record_content):
def del_txt_record(self, domain_name: str, record_name: str, record_content: str):
"""
Delete a TXT record using the supplied information.

View File

@@ -1,19 +1,16 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'python-digitalocean>=1.11',
'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support
'setuptools>=39.0.1',
'six>=1.11.0',
'zope.interface',
]
@@ -28,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -40,7 +40,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
def test_perform(self):
self.auth.perform([self.achall])
expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY)]
expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, 30)]
self.assertEqual(expected, self.mock_client.mock_calls)
def test_cleanup(self):
@@ -58,6 +58,7 @@ class DigitalOceanClientTest(unittest.TestCase):
record_prefix = "_acme-challenge"
record_name = record_prefix + "." + DOMAIN
record_content = "bar"
record_ttl = 60
def setUp(self):
from certbot_dns_digitalocean._internal.dns_digitalocean import _DigitalOceanClient
@@ -78,25 +79,27 @@ class DigitalOceanClientTest(unittest.TestCase):
self.manager.get_all_domains.return_value = [wrong_domain_mock, domain_mock]
self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content)
self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content,
self.record_ttl)
domain_mock.create_new_domain_record.assert_called_with(type='TXT',
name=self.record_prefix,
data=self.record_content)
data=self.record_content,
ttl=self.record_ttl)
def test_add_txt_record_fail_to_find_domain(self):
self.manager.get_all_domains.return_value = []
self.assertRaises(errors.PluginError,
self.digitalocean_client.add_txt_record,
DOMAIN, self.record_name, self.record_content)
DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_error_finding_domain(self):
self.manager.get_all_domains.side_effect = API_ERROR
self.assertRaises(errors.PluginError,
self.digitalocean_client.add_txt_record,
DOMAIN, self.record_name, self.record_content)
DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_error_creating_record(self):
domain_mock = mock.MagicMock()
@@ -107,7 +110,7 @@ class DigitalOceanClientTest(unittest.TestCase):
self.assertRaises(errors.PluginError,
self.digitalocean_client.add_txt_record,
DOMAIN, self.record_name, self.record_content)
DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_del_txt_record(self):
first_record_mock = mock.MagicMock()

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -26,15 +24,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
elif sys.version_info < (3,3):
install_requires.append('mock')
# This package normally depends on dns-lexicon>=3.2.1 to address the
# problem described in https://github.com/AnalogJ/lexicon/issues/387,
# however, the fix there has been backported to older versions of

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
@@ -26,15 +24,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -76,7 +76,7 @@ class Authenticator(dns_common.DNSAuthenticator):
return _GoogleClient(self.conf('credentials'))
class _GoogleClient(object):
class _GoogleClient:
"""
Encapsulates all communication with the Google Cloud DNS API.
"""

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -30,15 +28,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -401,7 +401,7 @@ class GoogleClientTest(unittest.TestCase):
self.assertRaises(ServerNotFoundError, _GoogleClient.get_project_id)
class DummyResponse(object):
class DummyResponse:
"""
Dummy object to create a fake HTTPResponse (the actual one requires a socket and we only
need the status attribute)

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
@@ -26,15 +24,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -88,7 +88,7 @@ class Authenticator(dns_common.DNSAuthenticator):
dns.tsig.HMAC_MD5))
class _RFC2136Client(object):
class _RFC2136Client:
"""
Encapsulates all communication with the target DNS server.
"""

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -2,15 +2,15 @@
import collections
import logging
import time
from typing import DefaultDict
from typing import Dict
from typing import List
import boto3
from botocore.exceptions import ClientError
from botocore.exceptions import NoCredentialsError
import zope.interface
from acme.magic_typing import DefaultDict
from acme.magic_typing import Dict
from acme.magic_typing import List
from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
@@ -39,7 +39,7 @@ class Authenticator(dns_common.DNSAuthenticator):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.r53 = boto3.client("route53")
self._resource_records = collections.defaultdict(list) # type: DefaultDict[str, List[Dict[str, str]]]
self._resource_records: DefaultDict[str, List[Dict[str, str]]] = collections.defaultdict(list)
def more_info(self): # pylint: disable=missing-function-docstring
return "Solve a DNS01 challenge using AWS Route53"

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -27,15 +25,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,12 +1,10 @@
from distutils.version import LooseVersion
import os
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
@@ -26,15 +24,6 @@ elif 'bdist_wheel' in sys.argv[1:]:
if os.environ.get('SNAP_BUILD'):
install_requires.append('packaging')
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
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',

View File

@@ -1,3 +1,4 @@
# pylint: disable=too-many-lines
"""Nginx Configuration"""
from distutils.version import LooseVersion
import logging
@@ -6,6 +7,12 @@ import socket
import subprocess
import tempfile
import time
from typing import Dict
from typing import List
from typing import Optional
from typing import Set
from typing import Text
from typing import Tuple
import OpenSSL
import pkg_resources
@@ -13,10 +20,6 @@ import zope.interface
from acme import challenges
from acme import crypto_util as acme_crypto_util
from acme.magic_typing import Dict
from acme.magic_typing import List
from acme.magic_typing import Set
from acme.magic_typing import Text
from certbot import crypto_util
from certbot import errors
from certbot import interfaces
@@ -105,18 +108,18 @@ class NginxConfigurator(common.Installer):
self.save_notes = ""
# For creating new vhosts if no names match
self.new_vhost = None
self.new_vhost: Optional[obj.VirtualHost] = None
# List of vhosts configured per wildcard domain on this run.
# used by deploy_cert() and enhance()
self._wildcard_vhosts = {} # type: Dict[str, List[obj.VirtualHost]]
self._wildcard_redirect_vhosts = {} # type: Dict[str, List[obj.VirtualHost]]
self._wildcard_vhosts: Dict[str, List[obj.VirtualHost]] = {}
self._wildcard_redirect_vhosts: Dict[str, List[obj.VirtualHost]] = {}
# Add number of outstanding challenges
self._chall_out = 0
# These will be set in the prepare function
self.parser = None
self.parser: Optional[parser.NginxParser] = None
self.version = version
self.openssl_version = openssl_version
self._enhance_func = {"redirect": self._enable_redirect,
@@ -377,10 +380,13 @@ class NginxConfigurator(common.Installer):
ipv6only_present = True
return (ipv6_active, ipv6only_present)
def _vhost_from_duplicated_default(self, domain, allow_port_mismatch, port):
def _vhost_from_duplicated_default(self, domain: str, allow_port_mismatch: bool, port: str
) -> obj.VirtualHost:
"""if allow_port_mismatch is False, only server blocks with matching ports will be
used as a default server block template.
"""
assert self.parser is not None # prepare should already have been called here
if self.new_vhost is None:
default_vhost = self._get_default_vhost(domain, allow_port_mismatch, port)
self.new_vhost = self.parser.duplicate_vhost(default_vhost,
@@ -509,7 +515,7 @@ class NginxConfigurator(common.Installer):
match['rank'] += NO_SSL_MODIFIER
return sorted(matches, key=lambda x: x['rank'])
def choose_redirect_vhosts(self, target_name, port, create_if_no_match=False):
def choose_redirect_vhosts(self, target_name: str, port: str) -> List[obj.VirtualHost]:
"""Chooses a single virtual host for redirect enhancement.
Chooses the vhost most closely matching target_name that is
@@ -523,9 +529,6 @@ class NginxConfigurator(common.Installer):
:param str target_name: domain name
:param str port: port number
:param bool create_if_no_match: If we should create a new vhost from default
when there is no match found. If we can't choose a default, raise a
MisconfigurationError.
:returns: vhosts associated with name
:rtype: list of :class:`~certbot_nginx._internal.obj.VirtualHost`
@@ -538,32 +541,75 @@ class NginxConfigurator(common.Installer):
else:
matches = self._get_redirect_ranked_matches(target_name, port)
vhosts = [x for x in [self._select_best_name_match(matches)]if x is not None]
if not vhosts and create_if_no_match:
vhosts = [self._vhost_from_duplicated_default(target_name, False, port)]
return vhosts
def _port_matches(self, test_port, matching_port):
def choose_auth_vhosts(self, target_name: str) -> Tuple[List[obj.VirtualHost],
List[obj.VirtualHost]]:
"""Returns a list of HTTP and HTTPS vhosts with a server_name matching target_name.
If no HTTP vhost exists, one will be cloned from the default vhost. If that fails, no HTTP
vhost will be returned.
:param str target_name: non-wildcard domain name
:returns: tuple of HTTP and HTTPS virtualhosts
:rtype: tuple of :class:`~certbot_nginx._internal.obj.VirtualHost`
"""
vhosts = [m['vhost'] for m in self._get_ranked_matches(target_name) if m and 'vhost' in m]
http_vhosts = [vh for vh in vhosts if
self._vhost_listening(vh, str(self.config.http01_port), False)]
https_vhosts = [vh for vh in vhosts if
self._vhost_listening(vh, str(self.config.https_port), True)]
# If no HTTP vhost matches, try create one from the default_server on http01_port.
if not http_vhosts:
try:
http_vhosts = [self._vhost_from_duplicated_default(target_name, False,
str(self.config.http01_port))]
except errors.MisconfigurationError:
http_vhosts = []
return http_vhosts, https_vhosts
def _port_matches(self, test_port: str, matching_port: str) -> bool:
# test_port is a number, matching is a number or "" or None
if matching_port == "" or matching_port is None:
# if no port is specified, Nginx defaults to listening on port 80.
return test_port == self.DEFAULT_LISTEN_PORT
return test_port == matching_port
def _vhost_listening_on_port_no_ssl(self, vhost, port):
found_matching_port = False
if not vhost.addrs:
# if there are no listen directives at all, Nginx defaults to
# listening on port 80.
found_matching_port = (port == self.DEFAULT_LISTEN_PORT)
else:
for addr in vhost.addrs:
if self._port_matches(port, addr.get_port()) and not addr.ssl:
found_matching_port = True
def _vhost_listening(self, vhost: obj.VirtualHost, port: str, ssl: bool) -> bool:
"""Tests whether a vhost has an address listening on a port with SSL enabled or disabled.
if found_matching_port:
# make sure we don't have an 'ssl on' directive
return not self.parser.has_ssl_on_directive(vhost)
return False
:param `obj.VirtualHost` vhost: The vhost whose addresses will be tested
:param port str: The port number as a string that the address should be bound to
:param bool ssl: Whether SSL should be enabled or disabled on the address
:returns: Whether the vhost has an address listening on the port and protocol.
:rtype: bool
"""
assert self.parser is not None # prepare should already have been called here
# if the 'ssl on' directive is present on the vhost, all its addresses have SSL enabled
all_addrs_are_ssl = self.parser.has_ssl_on_directive(vhost)
# if we want ssl vhosts: either 'ssl on' or 'addr.ssl' should be enabled
# if we want plaintext vhosts: neither 'ssl on' nor 'addr.ssl' should be enabled
_ssl_matches = lambda addr: addr.ssl or all_addrs_are_ssl if ssl else \
not addr.ssl and not all_addrs_are_ssl
# if there are no listen directives at all, Nginx defaults to
# listening on port 80.
if not vhost.addrs:
return port == self.DEFAULT_LISTEN_PORT and ssl == all_addrs_are_ssl
return any(self._port_matches(port, addr.get_port()) and _ssl_matches(addr)
for addr in vhost.addrs)
def _vhost_listening_on_port_no_ssl(self, vhost: obj.VirtualHost, port: str) -> bool:
return self._vhost_listening(vhost, port, False)
def _get_redirect_ranked_matches(self, target_name, port):
"""Gets a ranked list of plaintextish port-listening vhosts matching target_name
@@ -595,7 +641,7 @@ class NginxConfigurator(common.Installer):
:rtype: set
"""
all_names = set() # type: Set[str]
all_names: Set[str] = set()
for vhost in self.parser.get_vhosts():
try:
@@ -1176,7 +1222,7 @@ def nginx_restart(nginx_ctl, nginx_conf, sleep_duration):
"""
try:
reload_output = u"" # type: Text
reload_output: Text = u""
with tempfile.TemporaryFile() as out:
proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf, "-s", "reload"],
env=util.env_no_snap_for_external_calls(),

View File

@@ -1,8 +1,7 @@
"""nginx plugin constants."""
import platform
from acme.magic_typing import Any
from acme.magic_typing import Dict
from typing import Any
from typing import Dict
FREEBSD_DARWIN_SERVER_ROOT = "/usr/local/etc/nginx"
LINUX_SERVER_ROOT = "/etc/nginx"
@@ -15,11 +14,11 @@ elif platform.system() in ('NetBSD',):
else:
server_root_tmp = LINUX_SERVER_ROOT
CLI_DEFAULTS = dict(
CLI_DEFAULTS: Dict[str, Any] = dict(
server_root=server_root_tmp,
ctl="nginx",
sleep_seconds=1
) # type: Dict[str, Any]
)
"""CLI defaults."""

View File

@@ -2,9 +2,11 @@
import io
import logging
from typing import List
from typing import Optional
from acme import challenges
from acme.magic_typing import List
from certbot import achallenges
from certbot import errors
from certbot.compat import os
from certbot.plugins import common
@@ -111,7 +113,7 @@ class NginxHttp01(common.ChallengePerformer):
:returns: list of :class:`certbot_nginx._internal.obj.Addr` to apply
:rtype: list
"""
addresses = [] # type: List[obj.Addr]
addresses: List[obj.Addr] = []
default_addr = "%s" % self.configurator.config.http01_port
ipv6_addr = "[::]:{0}".format(
self.configurator.config.http01_port)
@@ -138,13 +140,12 @@ class NginxHttp01(common.ChallengePerformer):
def _get_validation_path(self, achall):
return os.sep + os.path.join(challenges.HTTP01.URI_ROOT_PATH, achall.chall.encode("token"))
def _make_server_block(self, achall):
def _make_server_block(self, achall: achallenges.KeyAuthorizationAnnotatedChallenge) -> List:
"""Creates a server block for a challenge.
:param achall: Annotated HTTP-01 challenge
:type achall:
:class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
:param list addrs: addresses of challenged domain
:class:`list` of type :class:`~nginx.obj.Addr`
:type achall: :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
:returns: server block for the challenge host
:rtype: list
"""
@@ -172,34 +173,35 @@ class NginxHttp01(common.ChallengePerformer):
return location_directive
def _make_or_mod_server_block(self, achall):
"""Modifies a server block to respond to a challenge.
def _make_or_mod_server_block(self, achall: achallenges.KeyAuthorizationAnnotatedChallenge
) -> Optional[List]:
"""Modifies server blocks to respond to a challenge. Returns a new HTTP server block
to add to the configuration if an existing one can't be found.
:param achall: Annotated HTTP-01 challenge
:type achall:
:class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
:type achall: :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
:returns: new server block to be added, if any
:rtype: list
"""
try:
vhosts = self.configurator.choose_redirect_vhosts(achall.domain,
'%i' % self.configurator.config.http01_port, create_if_no_match=True)
except errors.MisconfigurationError:
http_vhosts, https_vhosts = self.configurator.choose_auth_vhosts(achall.domain)
new_vhost: Optional[list] = None
if not http_vhosts:
# Couldn't find either a matching name+port server block
# or a port+default_server block, so create a dummy block
return self._make_server_block(achall)
new_vhost = self._make_server_block(achall)
# len is max 1 because Nginx doesn't authenticate wildcards
# if len were or vhosts None, we would have errored
vhost = vhosts[0]
# Modify any existing server blocks
for vhost in set(http_vhosts + https_vhosts):
location_directive = [self._location_directive_for_achall(achall)]
# Modify existing server block
location_directive = [self._location_directive_for_achall(achall)]
self.configurator.parser.add_server_directives(vhost, location_directive)
self.configurator.parser.add_server_directives(vhost,
location_directive)
rewrite_directive = [['rewrite', ' ', '^(/.well-known/acme-challenge/.*)',
' ', '$1', ' ', 'break']]
self.configurator.parser.add_server_directives(vhost,
rewrite_directive, insert_at_top=True)
rewrite_directive = [['rewrite', ' ', '^(/.well-known/acme-challenge/.*)',
' ', '$1', ' ', 'break']]
self.configurator.parser.add_server_directives(vhost,
rewrite_directive, insert_at_top=True)
return None
return new_vhost

View File

@@ -2,6 +2,8 @@
# Forked from https://github.com/fatiherikli/nginxparser (MIT Licensed)
import copy
import logging
from typing import Any
from typing import IO
from pyparsing import Combine
from pyparsing import Forward
@@ -15,12 +17,11 @@ from pyparsing import restOfLine
from pyparsing import stringEnd
from pyparsing import White
from pyparsing import ZeroOrMore
import six
from acme.magic_typing import IO, Any # pylint: disable=unused-import
logger = logging.getLogger(__name__)
class RawNginxParser(object):
class RawNginxParser:
# pylint: disable=pointless-statement
"""A class that parses nginx configuration with pyparsing."""
@@ -70,7 +71,7 @@ class RawNginxParser(object):
"""Returns the parsed tree as a list."""
return self.parse().asList()
class RawNginxDumper(object):
class RawNginxDumper:
"""A class that dumps nginx configuration from the provided tree."""
def __init__(self, blocks):
self.blocks = blocks
@@ -79,7 +80,7 @@ class RawNginxDumper(object):
"""Iterates the dumped nginx content."""
blocks = blocks or self.blocks
for b0 in blocks:
if isinstance(b0, six.string_types):
if isinstance(b0, str):
yield b0
continue
item = copy.deepcopy(b0)
@@ -96,7 +97,7 @@ class RawNginxDumper(object):
yield '}'
else: # not a block - list of strings
semicolon = ";"
if isinstance(item[0], six.string_types) and item[0].strip() == '#': # comment
if isinstance(item[0], str) and item[0].strip() == '#': # comment
semicolon = ""
yield "".join(item) + semicolon
@@ -105,56 +106,8 @@ class RawNginxDumper(object):
return ''.join(self)
# Shortcut functions to respect Python's serialization interface
# (like pyyaml, picker or json)
spacey = lambda x: (isinstance(x, str) and x.isspace()) or x == ''
def loads(source):
"""Parses from a string.
:param str source: The string to parse
:returns: The parsed tree
:rtype: list
"""
return UnspacedList(RawNginxParser(source).as_list())
def load(_file):
"""Parses from a file.
:param file _file: The file to parse
:returns: The parsed tree
:rtype: list
"""
return loads(_file.read())
def dumps(blocks):
# type: (UnspacedList) -> six.text_type
"""Dump to a Unicode string.
:param UnspacedList block: The parsed tree
:rtype: six.text_type
"""
return six.text_type(RawNginxDumper(blocks.spaced))
def dump(blocks, _file):
# type: (UnspacedList, IO[Any]) -> None
"""Dump to a file.
:param UnspacedList block: The parsed tree
:param IO[Any] _file: The file stream to dump to. It must be opened with
Unicode encoding.
:rtype: None
"""
_file.write(dumps(blocks))
spacey = lambda x: (isinstance(x, six.string_types) and x.isspace()) or x == ''
class UnspacedList(list):
"""Wrap a list [of lists], making any whitespace entries magically invisible"""
@@ -274,3 +227,50 @@ class UnspacedList(list):
idx -= 1
pos += 1
return idx0 + spaces
# Shortcut functions to respect Python's serialization interface
# (like pyyaml, picker or json)
def loads(source):
"""Parses from a string.
:param str source: The string to parse
:returns: The parsed tree
:rtype: list
"""
return UnspacedList(RawNginxParser(source).as_list())
def load(_file):
"""Parses from a file.
:param file _file: The file to parse
:returns: The parsed tree
:rtype: list
"""
return loads(_file.read())
def dumps(blocks: UnspacedList) -> str:
"""Dump to a Unicode string.
:param UnspacedList block: The parsed tree
:rtype: six.text_type
"""
return str(RawNginxDumper(blocks.spaced))
def dump(blocks: UnspacedList, _file: IO[Any]) -> None:
"""Dump to a file.
:param UnspacedList block: The parsed tree
:param IO[Any] _file: The file stream to dump to. It must be opened with
Unicode encoding.
:rtype: None
"""
_file.write(dumps(blocks))

View File

@@ -1,7 +1,6 @@
"""Module contains classes used by the Nginx Configurator."""
import re
import six
from certbot.plugins import common
@@ -144,7 +143,7 @@ class Addr(common.Addr):
return False
class VirtualHost(object):
class VirtualHost:
"""Represents an Nginx Virtualhost.
:ivar str filep: file path of VH
@@ -211,7 +210,7 @@ class VirtualHost(object):
def contains_list(self, test):
"""Determine if raw server block contains test list at top level
"""
for i in six.moves.range(0, len(self.raw) - len(test) + 1):
for i in range(0, len(self.raw) - len(test) + 1):
if self.raw[i:i + len(test)] == test:
return True
return False
@@ -250,7 +249,7 @@ def _find_directive(directives, directive_name, match_content=None):
"""Find a directive of type directive_name in directives. If match_content is given,
Searches for `match_content` in the directive arguments.
"""
if not directives or isinstance(directives, six.string_types):
if not directives or isinstance(directives, str):
return None
# If match_content is None, just match on directive type. Otherwise, match on

View File

@@ -5,15 +5,15 @@ import glob
import io
import logging
import re
from typing import Dict
from typing import List
from typing import Optional
from typing import Set
from typing import Tuple
from typing import Union
import pyparsing
import six
from acme.magic_typing import Dict
from acme.magic_typing import List
from acme.magic_typing import Set
from acme.magic_typing import Tuple
from acme.magic_typing import Union
from certbot import errors
from certbot.compat import os
from certbot_nginx._internal import nginxparser
@@ -22,7 +22,7 @@ from certbot_nginx._internal import obj
logger = logging.getLogger(__name__)
class NginxParser(object):
class NginxParser:
"""Class handles the fine details of parsing the Nginx Configuration.
:ivar str root: Normalized absolute path to the server root
@@ -32,7 +32,7 @@ class NginxParser(object):
"""
def __init__(self, root):
self.parsed = {} # type: Dict[str, Union[List, nginxparser.UnspacedList]]
self.parsed: Dict[str, Union[List, nginxparser.UnspacedList]] = {}
self.root = os.path.abspath(root)
self.config_root = self._find_config_root()
@@ -94,7 +94,7 @@ class NginxParser(object):
"""
servers = self._get_raw_servers()
addr_to_ssl = {} # type: Dict[Tuple[str, str], bool]
addr_to_ssl: Dict[Tuple[str, str], bool] = {}
for filename in servers:
for server, _ in servers[filename]:
# Parse the server block to save addr info
@@ -106,12 +106,11 @@ class NginxParser(object):
addr_to_ssl[addr_tuple] = addr.ssl or addr_to_ssl[addr_tuple]
return addr_to_ssl
def _get_raw_servers(self):
def _get_raw_servers(self) -> Dict:
# pylint: disable=cell-var-from-loop
# type: () -> Dict
"""Get a map of unparsed all server blocks
"""
servers = {} # type: Dict[str, Union[List, nginxparser.UnspacedList]]
servers: Dict[str, Union[List, nginxparser.UnspacedList]] = {}
for filename in self.parsed:
tree = self.parsed[filename]
servers[filename] = []
@@ -362,8 +361,9 @@ class NginxParser(object):
except errors.MisconfigurationError as err:
raise errors.MisconfigurationError("Problem in %s: %s" % (filename, str(err)))
def duplicate_vhost(self, vhost_template, remove_singleton_listen_params=False,
only_directives=None):
def duplicate_vhost(self, vhost_template: obj.VirtualHost,
remove_singleton_listen_params: bool = False,
only_directives: Optional[List] = None) -> obj.VirtualHost:
"""Duplicate the vhost in the configuration files.
:param :class:`~certbot_nginx._internal.obj.VirtualHost` vhost_template: The vhost
@@ -549,7 +549,7 @@ def _is_include_directive(entry):
"""
return (isinstance(entry, list) and
len(entry) == 2 and entry[0] == 'include' and
isinstance(entry[1], six.string_types))
isinstance(entry[1], str))
def _is_ssl_on_directive(entry):
"""Checks if an nginx parsed entry is an 'ssl on' directive.
@@ -654,7 +654,7 @@ def _add_directive(block, directive, insert_at_top):
directive_name = directive[0]
def can_append(loc, dir_name):
""" Can we append this directive to the block? """
return loc is None or (isinstance(dir_name, six.string_types)
return loc is None or (isinstance(dir_name, str)
and dir_name in REPEATABLE_DIRECTIVES)
err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".'
@@ -740,9 +740,9 @@ def _parse_server_raw(server):
:rtype: dict
"""
addrs = set() # type: Set[obj.Addr]
ssl = False # type: bool
names = set() # type: Set[str]
addrs: Set[obj.Addr] = set()
ssl: bool = False
names: Set[str] = set()
apply_ssl_to_all_addrs = False

View File

@@ -3,10 +3,8 @@ raw lists of tokens from pyparsing. """
import abc
import logging
from typing import List
import six
from acme.magic_typing import List
from certbot import errors
logger = logging.getLogger(__name__)
@@ -14,7 +12,7 @@ COMMENT = " managed by Certbot"
COMMENT_BLOCK = ["#", COMMENT]
class Parsable(object):
class Parsable:
""" Abstract base class for "Parsable" objects whose underlying representation
is a tree of lists.
@@ -24,7 +22,7 @@ class Parsable(object):
__metaclass__ = abc.ABCMeta
def __init__(self, parent=None):
self._data = [] # type: List[object]
self._data: List[object] = []
self._tabs = None
self.parent = parent
@@ -152,7 +150,7 @@ class Statements(Parsable):
if not isinstance(raw_list, list):
raise errors.MisconfigurationError("Statements parsing expects a list!")
# If there's a trailing whitespace in the list of statements, keep track of it.
if raw_list and isinstance(raw_list[-1], six.string_types) and raw_list[-1].isspace():
if raw_list and isinstance(raw_list[-1], str) and raw_list[-1].isspace():
self._trailing_whitespace = raw_list[-1]
raw_list = raw_list[:-1]
self._data = [parse_raw(elem, self, add_spaces) for elem in raw_list]
@@ -183,8 +181,8 @@ class Statements(Parsable):
def _space_list(list_):
""" Inserts whitespace between adjacent non-whitespace tokens. """
spaced_statement = [] # type: List[str]
for i in reversed(six.moves.xrange(len(list_))):
spaced_statement: List[str] = []
for i in reversed(range(len(list_))):
spaced_statement.insert(0, list_[i])
if i > 0 and not list_[i].isspace() and not list_[i-1].isspace():
spaced_statement.insert(0, " ")
@@ -206,7 +204,7 @@ class Sentence(Parsable):
:returns: whether this lists is parseable by `Sentence`.
"""
return isinstance(lists, list) and len(lists) > 0 and \
all(isinstance(elem, six.string_types) for elem in lists)
all(isinstance(elem, str) for elem in lists)
def parse(self, raw_list, add_spaces=False):
""" Parses a list of string types into this object.
@@ -214,7 +212,7 @@ class Sentence(Parsable):
if add_spaces:
raw_list = _space_list(raw_list)
if not isinstance(raw_list, list) or \
any(not isinstance(elem, six.string_types) for elem in raw_list):
any(not isinstance(elem, str) for elem in raw_list):
raise errors.MisconfigurationError("Sentence parsing expects a list of string types.")
self._data = raw_list
@@ -272,8 +270,8 @@ class Block(Parsable):
"""
def __init__(self, parent=None):
super(Block, self).__init__(parent)
self.names = None # type: Sentence
self.contents = None # type: Block
self.names: Sentence = None
self.contents: Block = None
@staticmethod
def should_parse(lists):

View File

@@ -1,11 +1,7 @@
from distutils.version import LooseVersion
import sys
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
version = '1.12.0.dev0'
version = '1.14.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -18,15 +14,6 @@ install_requires = [
'zope.interface',
]
setuptools_known_environment_markers = (LooseVersion(setuptools_version) >= LooseVersion('36.2'))
if setuptools_known_environment_markers:
install_requires.append('mock ; python_version < "3.3"')
elif 'bdist_wheel' in sys.argv[1:]:
raise RuntimeError('Error, you are trying to build certbot wheels using an old version '
'of setuptools. Version 36.2+ of setuptools is required.')
elif sys.version_info < (3,3):
install_requires.append('mock')
setup(
name='certbot-nginx',
version=version,

View File

@@ -39,7 +39,7 @@ class NginxConfiguratorTest(util.NginxTest):
def test_prepare(self):
self.assertEqual((1, 6, 2), self.config.version)
self.assertEqual(12, len(self.config.parser.parsed))
self.assertEqual(13, len(self.config.parser.parsed))
@mock.patch("certbot_nginx._internal.configurator.util.exe_exists")
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
@@ -89,7 +89,7 @@ class NginxConfiguratorTest(util.NginxTest):
"155.225.50.69.nephoscale.net", "www.example.org", "another.alias",
"migration.com", "summer.com", "geese.com", "sslon.com",
"globalssl.com", "globalsslsetssl.com", "ipv6.com", "ipv6ssl.com",
"headers.com", "example.net"})
"headers.com", "example.net", "ssl.both.com"})
def test_supported_enhancements(self):
self.assertEqual(['redirect', 'ensure-http-header', 'staple-ocsp'],
@@ -935,7 +935,19 @@ class NginxConfiguratorTest(util.NginxTest):
prefer_ssl=False,
no_ssl_filter_port='80')
# Check that the dialog was called with only port 80 vhosts
self.assertEqual(len(mock_select_vhs.call_args[0][0]), 6)
self.assertEqual(len(mock_select_vhs.call_args[0][0]), 8)
def test_choose_auth_vhosts(self):
"""choose_auth_vhosts correctly selects duplicative and HTTP/HTTPS vhosts"""
http, https = self.config.choose_auth_vhosts('ssl.both.com')
self.assertEqual(len(http), 4)
self.assertEqual(len(https), 2)
self.assertEqual(http[0].names, {'ssl.both.com'})
self.assertEqual(http[1].names, {'ssl.both.com'})
self.assertEqual(http[2].names, {'ssl.both.com'})
self.assertEqual(http[3].names, {'*.both.com'})
self.assertEqual(https[0].names, {'ssl.both.com'})
self.assertEqual(https[1].names, {'*.both.com'})
class InstallSslOptionsConfTest(util.NginxTest):

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