Compare commits

..

117 Commits

Author SHA1 Message Date
Brad Warren
d056c0af8b fix test_revoke_mutual_exclusive_flags 2021-04-07 15:28:50 -07:00
Brad Warren
c91bc1a0b0 fix assertion 2021-04-07 13:48:09 -07:00
Brad Warren
90af4de04a ignore external mock warnings 2021-04-07 11:51:43 -07:00
Brad Warren
891d96ffb6 unpin pytest and update pinnings 2021-04-07 11:44:34 -07:00
ohemorange
c21f277248 Merge pull request #8779 from certbot/candidate-1.14.0
Update files from 1.14.0 release
2021-04-06 16:09:19 -07:00
Brad Warren
04a85742c1 Bump version to 1.15.0 2021-04-06 10:24:35 -07:00
Brad Warren
21be290e24 Add contents to certbot/CHANGELOG.md for next version 2021-04-06 10:24:34 -07:00
Brad Warren
bf40b81b5a Release 1.14.0 2021-04-06 10:24:32 -07:00
Brad Warren
1b6e4028dc Update changelog for 1.14.0 release 2021-04-06 10:17:01 -07:00
Brad Warren
f15d10abc8 Update Dockerfile-dev (#8774)
* switch Dockerfile-dev to Ubuntu Focal

* Make apt noninteractive

* add --no-install-recommends
2021-04-05 16:02:14 -07:00
alexzorin
a12d91aef6 fix various fd leaks (#8747)
* fix various fd leaks

* use context manager for display provider
2021-04-06 00:50:12 +02:00
Adrien Ferrand
c438a397a0 Enable mypy strict mode (#8766)
Built on top of #8748, this PR reenables mypy strict mode and adds the appropriate corrections to pass the types checks.

* Upgrade mypy

* First step for acme

* Cast for the rescue

* Fixing types for certbot

* Fix typing for certbot-nginx

* Finalize type fixes, configure no optional strict check for mypy in tox

* Align requirements

* Isort

* Pylint

* Protocol for python 3.6

* Use Python 3.9 for mypy, make code compatible with Python 3.8<

* Pylint and mypy

* Pragma no cover

* Pythonic NotImplemented constant

* More type definitions

* Add comments

* Simplify typing logic

* Use vararg tuple

* Relax constraints on mypy

* Add more type

* Do not silence error if target is not defined

* Conditionally import Protocol for type checking only

* Clean up imports

* Add comments

* Align python version linting with mypy and coverage

* Just ignore types in an unused module

* Add comments

* Fix lint

* Work in progress

* Finish type control

* Isort

* Fix pylint

* Fix imports

* Fix cli subparser

* Some fixes

* Coverage

* Remove --no-strict-optional (obviously...)

* Update certbot-apache/certbot_apache/_internal/configurator.py

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Update certbot/certbot/_internal/display/completer.py

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Cleanup dns_google

* Improve lock controls and fix subparser

* Use the expected interfaces

* Fix code

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
2021-04-05 15:04:21 -07:00
Adrien Ferrand
0f9f902b6e Use typing-extensions to ensure certbot dev environment is compatible with Python 3.6/3.7 (#8776)
Fixes #8773

I took option 2 from the issue mentionned above (importing `typing-extensions` on dev dependencies) to avoid modifying certbot runtime requirements given that what needs to be added is useful for mypy only.

I did not change the Python version used to execute the linting and mypy on the standard tests, given that the tox `docker_dev` target already checks if the development environment is working for Python < 3.8.
2021-04-05 11:53:57 -07:00
Brad Warren
33f177b361 Upgrade Python to 3.8.9. (#8775)
Over the weekend, Python released new versions of Python 3.8 and Python 3.9 partially in response to the OpenSSL CVEs discussed at https://github.com/certbot/certbot/pull/8741#issuecomment-809644789. You can see this mentioned in their changelog at https://docs.python.org/release/3.8.9/whatsnew/changelog.html#build.

This PR updates the windows installer to use that new release so all of our distribution methods that contain their own copy of OpenSSL are patched for the release tomorrow.

You can see tests passing with this change at https://dev.azure.com/certbot/certbot/_build/results?buildId=3751&view=results. You can see Python 3.8.9 being downloaded instead of an older version at https://dev.azure.com/certbot/certbot/_build/results?buildId=3751&view=logs&j=ad29f110-3cce-5317-4ef2-0a692ae1dee7&t=901eeead-396c-5477-aba2-f402fdcfb885&l=1055.
2021-04-05 11:15:09 -07:00
Brad Warren
69479b7277 use standard errno (#8768)
We were originally using `socket.errno` with a `type: ignore` and a comment suggesting that this attribute needs to be included in the typeshed. This is incorrect.

While it's true that [socket imports errno](43682f1e39/Lib/socket.py (L58)), it's not intended to be part of its API. https://docs.python.org/3/library/socket.html has no mention of it.

Instead, we should be using the standard `errno` module and remove this `type: ignore`.
2021-04-05 10:42:18 -07:00
Brad Warren
2622a700e0 Update a few type ignore comments (#8767)
Some are no longer needed and other's comments are out of date.

For the changes to the acme nonce errors, `Exception` doesn't take kwargs. The error message about this our own classes isn't super helpful:
```
In [2]: BadNonce('nonce', 'error', foo='bar')                                                                                                                                                                                                                                                                               
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-54555658ef99> in <module>
----> 1 BadNonce('nonce', 'error', foo='bar')

TypeError: __init__() got an unexpected keyword argument 'foo'
```
but if you try this on `Exception` which these classes inherit from, you get:
```
In [4]: Exception(foo='bar')                                                                                                                                                                                                                                                                                                
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-028b924f74c5> in <module>
----> 1 Exception(foo='bar')

TypeError: Exception() takes no keyword arguments
```
See https://github.com/python/typeshed/pull/2348 for more info.

* remove outdated ignores

* update locking ignore comment

* don't accept kwargs
2021-04-02 16:19:30 -07:00
Adrien Ferrand
06a53cb7df Upgrade to mypy 0.812 (#8748)
Fixes #8425

This PR upgrades mypy to the latest version available, 0.812.

Given the advanced type inference capabilities provided by this newer version, this PRs also fixes various type inconsistencies that are now detected. Here are the non obvious changes done to fix types:
* typing in mixins has been solved using `Protocol` classes, as recommended by mypy (https://mypy.readthedocs.io/en/latest/more_types.html#mixin-classes, https://mypy.readthedocs.io/en/stable/protocols.html)
* `cast` when we are playing with `Union` types

This PR also disables the strict optional checks that have been enable by default in recent versions of mypy. Once this PR is merged, I will create an issue to study how these checks can be enabled.

`typing.Protocol` is available only since Python 3.8. To keep compatibility with Python 3.6, I try to import the class `Protocol` from `typing`, and fallback to assign `object` to `Protocol` if that fails. This way the code is working with all versions of Python, but the mypy check can be run only with Python 3.8+ because it needs the protocol feature. As a consequence, tox runs mypy under Python 3.8.

Alternatives are:
* importing `typing_extensions`, that proposes backport of newest typing features to Python 3.6, but this implies to add a dependency to Certbot just to run mypy
* redesign the concerned classes to not use mixins, or use them differently, but this implies to modify the code itself even if there is nothing wrong with it and it is just a matter of instructing mypy to understand in which context the mixins can be used
* ignoring type for these classes with `# type: ignore` but we loose the benefit of mypy for them

* Upgrade mypy

* First step for acme

* Cast for the rescue

* Fixing types for certbot

* Fix typing for certbot-nginx

* Finalize type fixes, configure no optional strict check for mypy in tox

* Align requirements

* Isort

* Pylint

* Protocol for python 3.6

* Use Python 3.9 for mypy, make code compatible with Python 3.8<

* Pylint and mypy

* Pragma no cover

* Pythonic NotImplemented constant

* More type definitions

* Add comments

* Simplify typing logic

* Use vararg tuple

* Relax constraints on mypy

* Add more type

* Do not silence error if target is not defined

* Conditionally import Protocol for type checking only

* Clean up imports

* Add comments

* Align python version linting with mypy and coverage

* Just ignore types in an unused module

* Add comments

* Fix lint
2021-04-02 11:54:40 -07:00
Brad Warren
584a1a3ece simplify setup.py (#8760)
I recently noticed that we only support versions of `setuptools` that support environment markers which allows us to simplify our `setup.py` files a bit.
2021-04-02 10:37:48 -07:00
Brad Warren
28fac893f4 add and update pynsist template comments (#8759) 2021-04-02 10:37:40 -07:00
Adrien Ferrand
8a84c88fee Remove wheel hack in windows installer construction script (#8752)
In #8649 we added some code to trick pynsist and make it understand that `abi3` wheels for Windows are forward compatible, meaning that the cryptography wheel tagged `cp36-abi3` is in fact compatible with Python 3.6+, and not only Python 3.6.

Since pynsist 2.7 the tool now understand `abi3` wheels properly, and this trick is not needed anymore.

Please note that despite modifying the pynsist pinning in `dev_constraints.txt`, it will have no effect since pynsist currently escape the pinning system. This is handled in https://github.com/certbot/certbot/pull/8749.
2021-04-02 10:37:19 -07:00
Adrien Ferrand
fea0b4e2e5 Pin pynsist (#8749)
* Pin pynsist

* Update dependencies

* Set windows installer a proper python project

* Optimize usage of the venvs

* Add windows-installer when venv is set up

* Fix call

* Remove env marker
2021-04-01 13:57:03 -07:00
Brad Warren
1ea588d504 increase ARM build timeout (#8757) 2021-03-31 12:42:42 -07:00
Brad Warren
24fd4121cf use snapcraft login (#8756) 2021-03-31 12:41:29 -07:00
ohemorange
8759ccaecb Update issue template to list snap as an installation option (#8754) 2021-03-29 13:56:08 -07: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
Brad Warren
00235d3807 Switch oldest tests to Python 3 (#8590)
Fixes https://github.com/certbot/certbot/issues/8580.

With this PR, it should now be possible to run the oldest tests natively on Linux, at least when using an older version of Python 3, which hasn't been possible in a long time. Unfortunately, this isn't possible on macOS which I opened https://github.com/certbot/certbot/issues/8589 to track.

You can see the full test suite running with these changes at https://dev.azure.com/certbot/certbot/_build/results?buildId=3283&view=results.

I took the version numbers for the packages I updated by searching for the oldest version of the dependency I think we should try and support based on the updated comments at the top of `oldest_constraints.txt`. While kind of annoying, I think it'd be a good idea for the reviewer to double check that I didn't make a mistake with the versions I used here.

To find these versions, I used https://packages.ubuntu.com, https://packages.debian.org, and a CentOS 7 Docker image with EPEL 7 installed. For the latter, not all packages are available in Python 3 yet (which is something Certbot's EPEL package maintainers are working on) and in that case I didn't worry about the system because I think they can/will package the newest version available. If they end up hitting any issues here when trying to package Certbot on Python 3, we can always work with them to fix it.

* remove py27 from oldest name

* update min cryptography version

* remove run_oldest_tests.sh

* upgrade setuptools and pyopenssl

* update cffi, pyparsing, and idna

* expand oldest_constraints comments

* clarify oldest comment

* update min configobj version

* update min parsedatetime version

* quote tox env name

* use Python 3.6 in the oldest tests

* use Python 3.6 for oldest integration tests

* properly pin asn1crypto

* update min six version

* set basepython for a nicer error message

* remove outdated python 2 oldest constraints
2021-01-25 12:59:14 -08:00
Brad Warren
adb7e5e62f remove unused pyicu pinning (#8607) 2021-01-16 07:13:59 +11:00
Miltos
261b5a76d8 Minor fix to logging message (#8605)
* Minor fix to logging message

the `if socket_kwargs` will always evaluate to `true`.

* Update acme/acme/crypto_util.py

Co-authored-by: alexzorin <alex@zor.io>
2021-01-14 20:39:42 +11:00
Aaron Gable
2fca48caaa --preferred-chain: only match root name (#8596)
* --preferred-chain: only match root name

Currently, when certbot is given the `--preferred-chain='Some Name'`
flag, it iterates through all alternate chains offered by the ACME
server until it finds any certificate which has `'Some Name'` as its
Issuer Common Name. Unfortunately, this means that if the desired
alternate chain is a strict subset of any earlier chain (e.g. the
default chain is 'EE <-- Int <-- Root1 <-- Root2', but the desired
chain is 'EE <-- Int <-- Root1'), there is no name which can be
provided by the user which will allow the client to select the desired
chain.

This change makes it so that the `find_chain_with_issuer` logic only
cares about the Issuer Common Name found in the last certificate in
each chain. In the example above, the user would then be able to get
their desired chain by specifying `--preferred-chain='Root1'`: although
that name appears in the default chain, it does not appear in the
highest certificate of that chain.

This change is technically backwards-incompatible. However, the only
advice that has been given to users of certbot (and the only usecase
that we believe has existed so far) involved setting the flag to a
value that is the name of a root, not an intermediate, so we don't
expect any real-world configurations or use-cases to be broken.

Fixes #8577

* Update interfaces.py
2021-01-14 12:12:48 +11:00
Adrien Ferrand
c0917a0302 Use os.path.normcase to have Windows compatible challenge paths on Windows (#8599)
* Use os.path.normcase to have Windows compatible challenge paths on Windows.

* Add integration test and fix lint
2021-01-13 14:38:57 -08:00
alexzorin
13d4a99251 test: certbot-ci crash due to no p521 on boulder (#8602)
* test: certbot-ci crash due to no p521 on boulder

The bugfix in #8598 added an integration test to request a certificate
for an EC P-521 key, which is unsupported when ACME_SERVER=boulder,
failing our nightly integration tests.

* add an integration test for all EC curves
2021-01-12 16:08:32 -08:00
Brad Warren
b9de48e93e Always sign certbot-auto with a yubikey (#8600)
* always sign certbot-auto with the yubikey

* remove tools/offline-sigrequest.sh
2021-01-12 13:45:26 -08:00
Brad Warren
7a02deeeba Modify release script to support yubikey sig (#8574)
Using `tools/offline-sigrequest.sh` is annoying. A while ago I looked into how we could use our yubikeys for our Windows code signing signatures and in the process of doing that learned how to use them for the certbot-auto signature. The certbot-auto signature won't be needed once https://github.com/certbot/certbot/issues/8526 is resolved and we've implemented that plan which will hopefully be in 2-3 months, but despite that, doing this still felt worth it to me.

The script still defaults to using `tools/offline-sign.sh`, but you can set an environment variable to use the yubikey instead. I tested both branches here and it worked.
2021-01-11 15:41:55 -08:00
Daniel Almasi
42f20455cd Fix EC curve name typo in crypto_util (#8598)
* Fix EC curve name typo in crypto_util

Fix typo of secp521r1 in crypto util module.
- secp521r1 is to be supported by certbot, but a typo of "SECP521R1" in the input validation section of the make_key function results in an error being thrown

* Add myself to authors.md 

Add myself to authors.md ^^

* Add test for secp521r1 key generation

Add test for secp521r1 key generation to cli-tests
2021-01-11 13:40:12 -08:00
Antonio Larrosa
434ca1985f Change the SUSE override to use apachectl (#8592)
For some time, SUSE distributions have had both an apachectl
executable and an apache2ctl compat symlink so both could be used
but apachectl is preferred since that's the official upstream name.
This is currently the case in SLE 15 SP2 and openSUSE Leap 15.2
(and every release since SLE 12 SP1)

OTOH, openSUSE Tumbleweed removed the apache2ctl compat symlink
some weeks ago and both SLE/Leap will follow in one of the next
releases so it's better to change certbot to use the official name,
apachectl.
2021-01-08 09:49:21 -08:00
Brad Warren
4a9748ace5 Add matching route53 readme (#8583)
Building on https://github.com/certbot/certbot/pull/8581, our other DNS plugins have a simple `README.rst` file and this PR adds a matching one for the route53 plugin.
2021-01-07 11:30:52 -08:00
sommersoft
fb8cd063eb Automatically Catch Sphinx Errors (#8530)
* clean up some Sphinx warnings

* first attempt at a doc-test pipeline job

* fix formatting

* fix test name

* set env for bash

* try bash vs script

* maybe it didn't like me setting 'PATH'...derp

* drop use of venv

* sphinx-build isn't a py script

* try activating venv

* docs: remove unused html_static tags

* clean up final sphinx build errors for certbot

* clean up final sphinx build errors for acme

* better names for docs pipeline

* fix spelling

* add docs_extras to setup.py

* remove temp doc-testing pipeline; add template to main.yml

* rearrange pipeline execution; run sphinx builds in one job

* add documentation note to compat.os

* add uninstall.rst as a sub-toctree to avoid build error
2021-01-07 20:26:59 +01:00
Brad Warren
e602736bda remove route53 readme (#8581) 2021-01-07 08:08:15 +01:00
Adrien Ferrand
ccde1eef64 Enable Python 3.8 for Certbot on Windows (#8465)
Now that we have a new pipstrap script with recent version of pip, dependencies for Windows can be resolved correctly on Python 3.8.

This PR enables tests on Python 3.8, and package Certbot for Windows on Python 3.8 also. I do not move up to Python 3.9 since some dependencies (`cryptography`, `pynacl`) do not provide wheels for Python 3.9 yet on Windows, which would require a complete C++ build system to compile them.

* Enable windows tests on Python 3.8 and package it on Python 3.8 also.

* Upgrade pynsist, nsis and pywin32, remove old workarounds

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
2021-01-06 16:17:34 -08:00
Brad Warren
c44a5a7701 Fix plugin param type (#8578)
* Fix plugin param type in updater

The command used to do this was:

sed -i 's/\(:type .*plugins:\) `list` of `str`/\1 certbot._internal.plugins.disco.PluginsRegistry/g' certbot/certbot/_internal/updater.py

* fix plugin param type in main.py

The command used to do this was:

sed -i 's/\(:type .*plugins:\) `list` of `str`/\1 plugins_disco.PluginsRegistry/g' certbot/certbot/_internal/main.py
2021-01-06 18:26:01 +11:00
Brad Warren
6e1d042f76 mock out plugin discovery in test_plugins (#8576) 2021-01-06 18:14:43 +11:00
Brad Warren
daf989fc21 skip meta creation to speed up tests (#8575) 2021-01-06 17:47:25 +11:00
ohemorange
5c3fd7d9ee Merge pull request #8573 from certbot/candidate-1.11.0
Update files from 1.11.0 release
2021-01-05 13:25:11 -08:00
Brad Warren
fc6c238bf9 Bump version to 1.12.0 2021-01-05 09:51:11 -08:00
Brad Warren
a49b84d64e Add contents to certbot/CHANGELOG.md for next version 2021-01-05 09:51:10 -08:00
Brad Warren
7567e8d8db Release 1.11.0 2021-01-05 09:51:09 -08:00
Brad Warren
02a5d000cb Update changelog for 1.11.0 release 2021-01-05 09:37:05 -08:00
283 changed files with 3442 additions and 3578 deletions

View File

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

View File

@@ -21,26 +21,24 @@ 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
ACME_SERVER: boulder-v1
linux-boulder-v2-integration-certbot-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-certbot-oldest
ACME_SERVER: boulder-v2
linux-boulder-v1-integration-nginx-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-nginx-oldest
ACME_SERVER: boulder-v1
linux-boulder-v2-integration-nginx-oldest:
PYTHON_VERSION: 3.6
TOXENV: integration-nginx-oldest
ACME_SERVER: boulder-v2
linux-boulder-v1-py27-integration:
PYTHON_VERSION: 2.7
TOXENV: integration
ACME_SERVER: boulder-v1
linux-boulder-v2-py27-integration:
PYTHON_VERSION: 2.7
TOXENV: integration
ACME_SERVER: boulder-v2
linux-boulder-v1-py36-integration:
PYTHON_VERSION: 3.6
TOXENV: integration

View File

@@ -1,55 +1,58 @@
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
# The default timeout of 60 minutes is a little low for compiling
# cryptography on ARM architectures.
timeoutInMinutes: 180
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
@@ -59,7 +62,13 @@ jobs:
versionSpec: 3.8
architecture: x86
addToPath: true
- script: python windows-installer/construct.py
- script: |
python -m venv venv
venv\Scripts\python tools\pipstrap.py
venv\Scripts\python tools\pip_install.py -e windows-installer
displayName: Prepare Windows installer build environment
- script: |
venv\Scripts\construct-windows-installer
displayName: Build Certbot installer
- task: CopyFiles@2
inputs:
@@ -113,105 +122,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

@@ -4,10 +4,10 @@ jobs:
PYTHON_VERSION: 3.9
strategy:
matrix:
macos-py27:
macos-py36:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 2.7
TOXENV: py27
PYTHON_VERSION: 3.6
TOXENV: py36
macos-py39:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.9
@@ -26,14 +26,12 @@ jobs:
TOXENV: integration-certbot
linux-oldest-tests-1:
IMAGE_NAME: ubuntu-18.04
TOXENV: py27-{acme,apache,apache-v2,certbot}-oldest
PYTHON_VERSION: 3.6
TOXENV: '{acme,apache,apache-v2,certbot}-oldest'
linux-oldest-tests-2:
IMAGE_NAME: ubuntu-18.04
TOXENV: py27-{dns,nginx}-oldest
linux-py27:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 2.7
TOXENV: py27
PYTHON_VERSION: 3.6
TOXENV: '{dns,nginx}-oldest'
linux-py36:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
@@ -42,13 +40,13 @@ jobs:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.9
TOXENV: py39-cover
linux-py37-lint:
linux-py39-lint:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.7
PYTHON_VERSION: 3.9
TOXENV: lint
linux-py36-mypy:
linux-py39-mypy:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
PYTHON_VERSION: 3.9
TOXENV: mypy
linux-integration:
IMAGE_NAME: ubuntu-18.04
@@ -63,13 +61,18 @@ jobs:
TOXENV: modification
apacheconftest:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 2.7
PYTHON_VERSION: 3.6
TOXENV: apacheconftest-with-pebble
nginxroundtrip:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 2.7
PYTHON_VERSION: 3.6
TOXENV: nginxroundtrip
pool:
vmImage: $(IMAGE_NAME)
steps:
- template: ../steps/tox-steps.yml
- job: test_sphinx_builds
pool:
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
@@ -55,8 +63,7 @@ stages:
secureFile: snapcraft.cfg
- bash: |
set -e
mkdir -p .snapcraft
ln -s $(snapcraftCfg.secureFilePath) .snapcraft/snapcraft.cfg
snapcraft login --with $(snapcraftCfg.secureFilePath)
for SNAP_FILE in snap/*.snap; do
tools/retry.sh eval snapcraft upload --release=${{ parameters.snapReleaseChannel }} "${SNAP_FILE}"
done

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

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

View File

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

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

@@ -1,6 +1,7 @@
Authors
=======
* [Aaron Gable](https://github.com/aarongable)
* [Aaron Zirbes](https://github.com/aaronzirbes)
* Aaron Zuehlke
* Ada Lovelace
@@ -60,6 +61,7 @@ Authors
* [DanCld](https://github.com/DanCld)
* [Daniel Albers](https://github.com/AID)
* [Daniel Aleksandersen](https://github.com/da2x)
* [Daniel Almasi](https://github.com/almasen)
* [Daniel Convissor](https://github.com/convissor)
* [Daniel "Drex" Drexler](https://github.com/aeturnum)
* [Daniel Huang](https://github.com/dhuang)

View File

@@ -1,5 +1,5 @@
# This Dockerfile builds an image for development.
FROM debian:buster
FROM ubuntu:focal
# Note: this only exposes the port to other docker containers.
EXPOSE 80 443
@@ -8,13 +8,14 @@ WORKDIR /opt/certbot/src
COPY . .
RUN apt-get update && \
apt-get install apache2 git python3-dev python3-venv gcc libaugeas0 \
libssl-dev libffi-dev ca-certificates openssl nginx-light -y && \
DEBIAN_FRONTEND=noninteractive apt-get install apache2 git python3-dev \
python3-venv gcc libaugeas0 libssl-dev libffi-dev ca-certificates \
openssl nginx-light -y --no-install-recommends && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* \
/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

@@ -7,7 +7,7 @@ questions.
## My operating system is (include version):
## I installed Certbot with (certbot-auto, OS package manager, pip, etc):
## I installed Certbot with (snap, OS package manager, pip, certbot-auto, etc):
## I ran this command and it produced this output:

View File

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

View File

@@ -5,18 +5,19 @@ import functools
import hashlib
import logging
import socket
from typing import Type
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 +25,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 +39,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,16 +146,15 @@ 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`
that will be used to generate `response`.
that will be used to generate ``response``.
:param str typ: type of the challenge
"""
typ = NotImplemented
response_cls = NotImplemented
typ: str = NotImplemented
response_cls: Type[KeyAuthorizationChallengeResponse] = NotImplemented
thumbprint_hash_function = (
KeyAuthorizationChallengeResponse.thumbprint_hash_function)

View File

@@ -4,10 +4,16 @@ 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 cast
from typing import Dict
from typing import List
from typing import Set
from typing import Text
from typing import Union
import josepy as jose
import OpenSSL
@@ -15,38 +21,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 +114,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 +250,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 +426,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 +465,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 +539,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 +798,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.
@@ -830,6 +820,7 @@ class BackwardsCompatibleClientV2(object):
def __init__(self, net, key, server):
directory = messages.Directory.from_json(net.get(server).json())
self.acme_version = self._acme_version_from_directory(directory)
self.client: Union[Client, ClientV2]
if self.acme_version == 1:
self.client = Client(directory, key=key, net=net)
else:
@@ -849,16 +840,18 @@ class BackwardsCompatibleClientV2(object):
if check_tos_cb is not None:
check_tos_cb(tos)
if self.acme_version == 1:
regr = self.client.register(regr)
client_v1 = cast(Client, self.client)
regr = client_v1.register(regr)
if regr.terms_of_service is not None:
_assess_tos(regr.terms_of_service)
return self.client.agree_to_tos(regr)
return client_v1.agree_to_tos(regr)
return regr
else:
if "terms_of_service" in self.client.directory.meta:
_assess_tos(self.client.directory.meta.terms_of_service)
client_v2 = cast(ClientV2, self.client)
if "terms_of_service" in client_v2.directory.meta:
_assess_tos(client_v2.directory.meta.terms_of_service)
regr = regr.update(terms_of_service_agreed=True)
return self.client.new_account(regr)
return client_v2.new_account(regr)
def new_order(self, csr_pem):
"""Request a new Order object from the server.
@@ -876,14 +869,15 @@ class BackwardsCompatibleClientV2(object):
"""
if self.acme_version == 1:
client_v1 = cast(Client, self.client)
csr = OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_PEM, csr_pem)
# pylint: disable=protected-access
dnsNames = crypto_util._pyopenssl_cert_or_req_all_names(csr)
authorizations = []
for domain in dnsNames:
authorizations.append(self.client.request_domain_challenges(domain))
authorizations.append(client_v1.request_domain_challenges(domain))
return messages.OrderResource(authorizations=authorizations, csr_pem=csr_pem)
return self.client.new_order(csr_pem)
return cast(ClientV2, self.client).new_order(csr_pem)
def finalize_order(self, orderr, deadline, fetch_alternative_chains=False):
"""Finalize an order and obtain a certificate.
@@ -898,8 +892,9 @@ class BackwardsCompatibleClientV2(object):
"""
if self.acme_version == 1:
client_v1 = cast(Client, self.client)
csr_pem = orderr.csr_pem
certr = self.client.request_issuance(
certr = client_v1.request_issuance(
jose.ComparableX509(
OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_PEM, csr_pem)),
orderr.authorizations)
@@ -907,7 +902,7 @@ class BackwardsCompatibleClientV2(object):
chain = None
while datetime.datetime.now() < deadline:
try:
chain = self.client.fetch_chain(certr)
chain = client_v1.fetch_chain(certr)
break
except errors.Error:
time.sleep(1)
@@ -922,7 +917,8 @@ class BackwardsCompatibleClientV2(object):
chain = crypto_util.dump_pyopenssl_chain(chain).decode()
return orderr.update(fullchain_pem=(cert + chain))
return self.client.finalize_order(orderr, deadline, fetch_alternative_chains)
return cast(ClientV2, self.client).finalize_order(
orderr, deadline, fetch_alternative_chains)
def revoke(self, cert, rsn):
"""Revoke certificate.
@@ -948,10 +944,10 @@ class BackwardsCompatibleClientV2(object):
Always return False for ACMEv1 servers, as it doesn't use External Account Binding."""
if self.acme_version == 1:
return False
return self.client.external_account_required()
return cast(ClientV2, 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 +977,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
@@ -1141,6 +1137,7 @@ class ClientNetwork(object):
# If content is DER, log the base64 of it instead of raw bytes, to keep
# binary data out of the logs.
debug_content: Union[bytes, str]
if response.headers.get("Content-Type") == DER_CONTENT_TYPE:
debug_content = base64.b64encode(response.content)
else:

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
@@ -166,9 +166,9 @@ def probe_sni(name, host, port=443, timeout=300, # pylint: disable=too-many-argu
" from {0}:{1}".format(
source_address[0],
source_address[1]
) if socket_kwargs else ""
) 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

@@ -28,13 +28,8 @@ class NonceError(ClientError):
class BadNonce(NonceError):
"""Bad nonce error."""
def __init__(self, nonce, error, *args, **kwargs):
# MyPy complains here that there is too many arguments for BaseException constructor.
# This is an error fixed in typeshed, see https://github.com/python/mypy/issues/4183
# The fix is included in MyPy>=0.740, but upgrading it would bring dozen of errors due to
# new types definitions. So we ignore the error until the code base is fixed to match
# with MyPy>=0.740 referential.
super(BadNonce, self).__init__(*args, **kwargs) # type: ignore
def __init__(self, nonce, error, *args):
super(BadNonce, self).__init__(*args)
self.nonce = nonce
self.error = error
@@ -49,12 +44,11 @@ class MissingNonce(NonceError):
Replay-Nonce header field in each successful response to a POST it
provides to a client (...)".
:ivar requests.Response response: HTTP Response
:ivar requests.Response ~.response: HTTP Response
"""
def __init__(self, response, *args, **kwargs):
# See comment in BadNonce constructor above for an explanation of type: ignore here.
super(MissingNonce, self).__init__(*args, **kwargs) # type: ignore
def __init__(self, response, *args):
super(MissingNonce, self).__init__(*args)
self.response = response
def __str__(self):

View File

@@ -14,7 +14,9 @@ class Header(jose.Header):
kid = jose.Field('kid', omitempty=True)
url = jose.Field('url', omitempty=True)
@nonce.decoder
# Mypy does not understand the josepy magic happening here, and falsely claims
# that nonce is redefined. Let's ignore the type check here.
@nonce.decoder # type: ignore
def nonce(value): # pylint: disable=no-self-argument,missing-function-docstring
try:
return jose.decode_b64jose(value)

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,11 @@
"""ACME protocol messages."""
from collections.abc import Hashable
import json
from typing import Any
from typing import Dict
from typing import Type
import josepy as jose
import six
from acme import challenges
from acme import errors
@@ -11,13 +14,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 +64,6 @@ def is_acme_error(err):
return False
@six.python_2_unicode_compatible
class Error(jose.JSONObjectWithFields, errors.Error):
"""ACME error.
@@ -95,7 +90,9 @@ class Error(jose.JSONObjectWithFields, errors.Error):
raise ValueError("The supplied code: %s is not a known ACME error"
" code" % code)
typ = ERROR_PREFIX + code
return cls(typ=typ, **kwargs)
# Mypy will not understand that the Error constructor accepts a named argument
# "typ" because of josepy magic. Let's ignore the type check here.
return cls(typ=typ, **kwargs) # type: ignore
@property
def description(self):
@@ -132,7 +129,7 @@ class Error(jose.JSONObjectWithFields, errors.Error):
class _Constant(jose.JSONDeSerializable, Hashable): # type: ignore
"""ACME constant."""
__slots__ = ('name',)
POSSIBLE_NAMES = NotImplemented
POSSIBLE_NAMES: Dict[str, '_Constant'] = NotImplemented
def __init__(self, name):
super(_Constant, self).__init__()
@@ -158,13 +155,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 +171,7 @@ STATUS_DEACTIVATED = Status('deactivated')
class IdentifierType(_Constant):
"""ACME identifier type."""
POSSIBLE_NAMES = {} # type: dict
POSSIBLE_NAMES: Dict[str, 'IdentifierType'] = {}
IDENTIFIER_FQDN = IdentifierType('dns') # IdentifierDNS in Boulder
@@ -195,7 +189,7 @@ class Identifier(jose.JSONObjectWithFields):
class Directory(jose.JSONDeSerializable):
"""Directory."""
_REGISTERED_TYPES = {} # type: dict
_REGISTERED_TYPES: Dict[str, Type[Any]] = {}
class Meta(jose.JSONObjectWithFields):
"""Directory Meta."""
@@ -229,7 +223,7 @@ class Directory(jose.JSONDeSerializable):
return getattr(key, 'resource_type', key)
@classmethod
def register(cls, resource_body_cls):
def register(cls, resource_body_cls: Type[Any]) -> Type[Any]:
"""Register resource."""
resource_type = resource_body_cls.resource_type
assert resource_type not in cls._REGISTERED_TYPES
@@ -275,7 +269,7 @@ class Resource(jose.JSONObjectWithFields):
class ResourceWithURI(Resource):
"""ACME Resource with URI.
:ivar unicode uri: Location of the resource.
:ivar unicode ~.uri: Location of the resource.
"""
uri = jose.Field('uri') # no ChallengeResource.uri
@@ -285,7 +279,7 @@ class ResourceBody(jose.JSONObjectWithFields):
"""ACME Resource Body."""
class ExternalAccountBinding(object):
class ExternalAccountBinding:
"""ACME External Account Binding"""
@classmethod
@@ -539,7 +533,9 @@ class Authorization(ResourceBody):
expires = fields.RFC3339Field('expires', omitempty=True)
wildcard = jose.Field('wildcard', omitempty=True)
@challenges.decoder
# Mypy does not understand the josepy magic happening here, and falsely claims
# that challenge is redefined. Let's ignore the type check here.
@challenges.decoder # type: ignore
def challenges(value): # pylint: disable=no-self-argument,missing-function-docstring
return tuple(ChallengeBody.from_json(chall) for chall in value)
@@ -627,7 +623,7 @@ class Order(ResourceBody):
:ivar str finalize: URL to POST to to request issuance once all
authorizations have "valid" status.
:ivar datetime.datetime expires: When the order expires.
:ivar .Error error: Any error that occurred during finalization, if applicable.
:ivar ~.Error error: Any error that occurred during finalization, if applicable.
"""
identifiers = jose.Field('identifiers', omitempty=True)
status = jose.Field('status', decoder=Status.from_json,
@@ -638,7 +634,9 @@ class Order(ResourceBody):
expires = fields.RFC3339Field('expires', omitempty=True)
error = jose.Field('error', omitempty=True, decoder=Error.from_json)
@identifiers.decoder
# Mypy does not understand the josepy magic happening here, and falsely claims
# that identifiers is redefined. Let's ignore the type check here.
@identifiers.decoder # type: ignore
def identifiers(value): # pylint: disable=no-self-argument,missing-function-docstring
return tuple(Identifier.from_json(identifier) for identifier in value)

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[socketserver.BaseServer] = []
# Must try True first.
# Ubuntu, for example, will fail to bind to IPv4 if we've already bound
@@ -204,8 +203,24 @@ class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
self.simple_http_resources = kwargs.pop("simple_http_resources", set())
self.timeout = kwargs.pop('timeout', 30)
self._timeout = kwargs.pop('timeout', 30)
BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
self.server: HTTP01Server
# In parent class BaseHTTPRequestHandler, 'timeout' is a class-level property but we
# need to define its value during the initialization phase in HTTP01RequestHandler.
# However MyPy does not appreciate that we dynamically shadow a class-level property
# with an instance-level property (eg. self.timeout = ... in __init__()). So to make
# everyone happy, we statically redefine 'timeout' as a method property, and set the
# timeout value in a new internal instance-level property _timeout.
@property
def timeout(self):
"""
The default timeout this server should apply to requests.
:return: timeout to apply
:rtype: int
"""
return self._timeout
def log_message(self, format, *args): # pylint: disable=redefined-builtin
"""Log arbitrary message."""

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

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

View File

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

View File

@@ -1,40 +1,25 @@
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.11.0.dev0'
version = '1.15.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
# load_pem_private/public_key (>=0.6)
# rsa_recover_prime_factors (>=0.8)
'cryptography>=1.2.3',
'cryptography>=2.1.4',
# formerly known as acme.jose:
# 1.1.0+ is required to avoid the warnings described at
# https://github.com/certbot/josepy/issues/13.
'josepy>=1.1.0',
# Connection.set_tlsext_host_name (>=0.13) + matching Xenial requirements (>=0.15.1)
'PyOpenSSL>=0.15.1',
'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',
'six>=1.9.0', # needed for python_2_unicode_compatible
'setuptools>=39.0.1',
]
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',
@@ -54,14 +39,12 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -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

@@ -1,4 +1,5 @@
""" apacheconfig implementation of the ParserNode interfaces """
from typing import Tuple
from certbot_apache._internal import assertions
from certbot_apache._internal import interfaces
@@ -21,7 +22,7 @@ class ApacheParserNode(interfaces.ParserNode):
self.metadata = metadata
self._raw = self.metadata["ac_ast"]
def save(self, msg): # pragma: no cover
def save(self, msg): # pragma: no cover
pass
def find_ancestors(self, name): # pylint: disable=unused-variable
@@ -83,7 +84,7 @@ class ApacheBlockNode(ApacheDirectiveNode):
def __init__(self, **kwargs):
super(ApacheBlockNode, self).__init__(**kwargs)
self.children = ()
self.children: Tuple[ApacheParserNode, ...] = ()
def __eq__(self, other): # pragma: no cover
if isinstance(other, self.__class__):

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,25 @@
"""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 cast
from typing import DefaultDict
from typing import Dict
from typing import List
from typing import Optional
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
@@ -40,6 +37,16 @@ from certbot_apache._internal import dualparser
from certbot_apache._internal import http_01
from certbot_apache._internal import obj
from certbot_apache._internal import parser
from certbot_apache._internal.dualparser import DualBlockNode
from certbot_apache._internal.obj import VirtualHost
from certbot_apache._internal.parser import ApacheParser
try:
import apacheconfig
HAS_APACHECONFIG = True
except ImportError: # pragma: no cover
HAS_APACHECONFIG = False
logger = logging.getLogger(__name__)
@@ -154,9 +161,9 @@ class ApacheConfigurator(common.Installer):
self.options[o] = self.OS_DEFAULTS[o]
# Special cases
self.options["version_cmd"][0] = self.option("ctl")
self.options["restart_cmd"][0] = self.option("ctl")
self.options["conftest_cmd"][0] = self.option("ctl")
cast(List[str], self.options["version_cmd"])[0] = self.option("ctl")
cast(List[str], self.options["restart_cmd"])[0] = self.option("ctl")
cast(List[str], self.options["conftest_cmd"])[0] = self.option("ctl")
@classmethod
def add_parser_arguments(cls, add):
@@ -210,30 +217,30 @@ 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
self.parser_root = None
self.parser: ApacheParser
self.parser_root: Optional[DualBlockNode] = None
self.version = version
self._openssl_version = openssl_version
self.vhosts = None
self.vhosts: List[VirtualHost]
self.options = copy.deepcopy(self.OS_DEFAULTS)
self._enhance_func = {"redirect": self._enable_redirect,
"ensure-http-header": self._set_http_header,
@@ -342,8 +349,9 @@ class ApacheConfigurator(common.Installer):
"augeaspath": self.parser.get_root_augpath(),
"ac_ast": None}
if self.USE_PARSERNODE:
self.parser_root = self.get_parsernode_root(pn_meta)
self.parsed_paths = self.parser_root.parsed_paths()
parser_root = self.get_parsernode_root(pn_meta)
self.parser_root = parser_root
self.parsed_paths = parser_root.parsed_paths()
# Check for errors in parsing files with Augeas
self.parser.check_parsing_errors("httpd.aug")
@@ -405,7 +413,7 @@ class ApacheConfigurator(common.Installer):
super(ApacheConfigurator, self).recovery_routine()
# Reload configuration after these changes take effect if needed
# ie. ApacheParser has been initialized.
if self.parser:
if hasattr(self, "parser"):
# TODO: wrap into non-implementation specific parser interface
self.parser.aug.load()
@@ -832,7 +840,7 @@ class ApacheConfigurator(common.Installer):
:rtype: set
"""
all_names = set() # type: Set[str]
all_names: Set[str] = set()
vhost_macro = []
@@ -996,8 +1004,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.
@@ -1048,6 +1056,9 @@ class ApacheConfigurator(common.Installer):
:rtype: list
"""
if not self.parser_root:
raise errors.Error("This ApacheConfigurator instance is not" # pragma: no cover
" configured to use a node parser.")
vhs = []
vhosts = self.parser_root.find_blocks("VirtualHost", exclude=False)
for vhblock in vhosts:
@@ -2156,7 +2167,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 +2261,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,10 @@
""" Distribution specific override class for CentOS family (RHEL, Fedora) """
import logging
from typing import cast
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
@@ -76,7 +77,7 @@ class CentOSConfigurator(configurator.ApacheConfigurator):
alternative restart cmd used in CentOS.
"""
super(CentOSConfigurator, self)._prepare_options()
self.options["restart_cmd_alt"][0] = self.option("ctl")
cast(List[str], self.options["restart_cmd_alt"])[0] = self.option("ctl")
def get_parser(self):
"""Initializes the ApacheParser"""
@@ -102,9 +103,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)
@@ -118,8 +119,9 @@ class CentOSConfigurator(configurator.ApacheConfigurator):
else:
loadmod_args = path_args
if self.parser.not_modssl_ifmodule(noarg_path): # pylint: disable=no-member
if self.parser.loc["default"] in noarg_path:
centos_parser: CentOSParser = cast(CentOSParser, self.parser)
if centos_parser.not_modssl_ifmodule(noarg_path):
if centos_parser.loc["default"] in noarg_path:
# LoadModule already in the main configuration file
if ("ifmodule/" in noarg_path.lower() or
"ifmodule[1]" in noarg_path.lower()):

View File

@@ -1,4 +1,7 @@
""" Distribution specific override class for Fedora 29+ """
from typing import cast
from typing import List
import zope.interface
from certbot import errors
@@ -69,9 +72,9 @@ class FedoraConfigurator(configurator.ApacheConfigurator):
of Fedora to restart httpd.
"""
super(FedoraConfigurator, self)._prepare_options()
self.options["restart_cmd"][0] = 'apachectl'
self.options["restart_cmd_alt"][0] = 'apachectl'
self.options["conftest_cmd"][0] = 'apachectl'
cast(List[str], self.options["restart_cmd"])[0] = 'apachectl'
cast(List[str], self.options["restart_cmd_alt"])[0] = 'apachectl'
cast(List[str], self.options["conftest_cmd"])[0] = 'apachectl'
class FedoraParser(parser.ApacheParser):

View File

@@ -1,4 +1,7 @@
""" Distribution specific override class for Gentoo Linux """
from typing import cast
from typing import List
import zope.interface
from certbot import interfaces
@@ -36,7 +39,7 @@ class GentooConfigurator(configurator.ApacheConfigurator):
alternative restart cmd used in Gentoo.
"""
super(GentooConfigurator, self)._prepare_options()
self.options["restart_cmd_alt"][0] = self.option("ctl")
cast(List[str], self.options["restart_cmd_alt"])[0] = self.option("ctl")
def get_parser(self):
"""Initializes the ApacheParser"""

View File

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

View File

@@ -3,21 +3,24 @@ import copy
import fnmatch
import logging
import re
import sys
from typing import Dict
from typing import List
from typing import Optional
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
from certbot_apache._internal import constants
try:
from augeas import Augeas
except ImportError: # pragma: no cover
Augeas = None # type: ignore
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...
@@ -42,8 +45,7 @@ class ApacheParser(object):
self.configurator = configurator
# Initialize augeas
self.aug = None
self.init_augeas()
self.aug = init_augeas()
if not self.check_aug_version():
raise errors.NotSupportedError(
@@ -51,9 +53,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, Optional[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)
@@ -86,23 +88,6 @@ class ApacheParser(object):
if self.find_dir("Define", exclude=False):
raise errors.PluginError("Error parsing runtime variables")
def init_augeas(self):
""" Initialize the actual Augeas instance """
try:
import augeas
except ImportError: # pragma: no cover
raise errors.NoInstallationError("Problem in Augeas installation")
self.aug = augeas.Augeas(
# specify a directory to load our preferred lens from
loadpath=constants.AUGEAS_LENS_DIR,
# Do not save backup (we do it ourselves), do not load
# anything by default
flags=(augeas.Augeas.NONE |
augeas.Augeas.NO_MODL_AUTOLOAD |
augeas.Augeas.ENABLE_SPAN))
def check_parsing_errors(self, lens):
"""Verify Augeas can parse all of the lens files.
@@ -266,7 +251,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 +260,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 +538,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 +723,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
@@ -955,3 +937,19 @@ def get_aug_path(file_path):
"""
return "/files%s" % file_path
def init_augeas() -> Augeas:
""" Initialize the actual Augeas instance """
if not Augeas: # pragma: no cover
raise errors.NoInstallationError("Problem in Augeas installation")
return Augeas(
# specify a directory to load our preferred lens from
loadpath=constants.AUGEAS_LENS_DIR,
# Do not save backup (we do it ourselves), do not load
# anything by default
flags=(Augeas.NONE |
Augeas.NO_MODL_AUTOLOAD |
Augeas.ENABLE_SPAN))

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.11.0.dev0'
version = '1.15.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -13,20 +9,11 @@ install_requires = [
'acme>=0.29.0',
'certbot>=1.6.0',
'python-augeas',
'setuptools',
'setuptools>=39.0.1',
'zope.component',
'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',
]
@@ -39,7 +26,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -47,8 +34,6 @@ setup(
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,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

@@ -339,7 +339,7 @@ class ParserInitTest(util.ApacheTest):
shutil.rmtree(self.config_dir)
shutil.rmtree(self.work_dir)
@mock.patch("certbot_apache._internal.parser.ApacheParser.init_augeas")
@mock.patch("certbot_apache._internal.parser.init_augeas")
def test_prepare_no_augeas(self, mock_init_augeas):
from certbot_apache._internal.parser import ApacheParser
mock_init_augeas.side_effect = errors.NoInstallationError

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.10.1"
LE_AUTO_VERSION="1.14.0"
BASENAME=$(basename $0)
USAGE="Usage: $BASENAME [OPTIONS]
A self-updating wrapper script for the Certbot ACME client. When run, updates
@@ -800,10 +800,14 @@ BootstrapMageiaCommon() {
# packages BOOTSTRAP_VERSION is not set.
if [ -f /etc/debian_version ]; then
DEPRECATED_OS=1
NO_SELF_UPGRADE=1
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.
@@ -836,12 +840,7 @@ elif [ -f /etc/redhat-release ]; then
INTERACTIVE_BOOTSTRAP=1
fi
Bootstrap() {
BootstrapMessage "Legacy RedHat-based OSes that will use Python3"
BootstrapRpmPython3Legacy
}
USE_PYTHON_3=1
BOOTSTRAP_VERSION="BootstrapRpmPython3Legacy $BOOTSTRAP_RPM_PYTHON3_LEGACY_VERSION"
# Try now to enable SCL rh-python36 for systems already bootstrapped
# NB: EnablePython36SCL has been defined along with BootstrapRpmPython3Legacy in certbot-auto
@@ -860,43 +859,38 @@ elif [ -f /etc/redhat-release ]; then
fi
if [ "$RPM_USE_PYTHON_3" = 1 ]; then
Bootstrap() {
BootstrapMessage "RedHat-based OSes that will use Python3"
BootstrapRpmPython3
}
USE_PYTHON_3=1
BOOTSTRAP_VERSION="BootstrapRpmPython3 $BOOTSTRAP_RPM_PYTHON3_VERSION"
else
Bootstrap() {
BootstrapMessage "RedHat-based OSes"
BootstrapRpmCommon
}
BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
fi
fi
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
Bootstrap() {
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
}
BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
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
@@ -1125,7 +1119,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
@@ -1493,18 +1489,18 @@ letsencrypt==0.7.0 \
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
certbot==1.10.1 \
--hash=sha256:011ac980fa21b9f29e02c9b8d8b86e8a4bf4670b51b6ad91656e401e9d2d2231 \
--hash=sha256:0d9ee3fc09e0d03b2d1b1f1c4916e61ecfc6904b4216ddef4e6a5ca1424d9cb7
acme==1.10.1 \
--hash=sha256:752d598e54e98ad1e874de53fd50c61044f1b566d6deb790db5676ce9c573546 \
--hash=sha256:fcbb559aedc96b404edf593e78517dcd7291984d5a37036c3fc77f3c5c122fd8
certbot-apache==1.10.1 \
--hash=sha256:f077b4b7f166627ef5e0921fe7cde57700670fc86e9ad9dbdfaf2c573cc0f2fa \
--hash=sha256:97ed637b4c7b03820db6c69aa90145dc989933351d46a3d62baf6b71674f0a10
certbot-nginx==1.10.1 \
--hash=sha256:7c36459021f8a1ec3b6c062e4c4fc866bfaa1dbf26ccd29e043dd6848003be08 \
--hash=sha256:c0bbeccf85f46b728fd95e6bb8c2649d32d3383d7f47ea4b9c312d12bf04d2f0
certbot==1.14.0 \
--hash=sha256:67b4d26ceaea6c7f8325d0d45169e7a165a2cabc7122c84bc971ba068ca19cca \
--hash=sha256:959ea90c6bb8dca38eab9772722cb940972ef6afcd5f15deef08b3c3636841eb
acme==1.14.0 \
--hash=sha256:4f48c41261202f1a389ec2986b2580b58f53e0d5a1ae2463b34318d78b87fc66 \
--hash=sha256:61daccfb0343628cbbca551a7fc4c82482113952c21db3fe0c585b7c98fa1c35
certbot-apache==1.14.0 \
--hash=sha256:b757038db23db707c44630fecb46e99172bd791f0db5a8e623c0842613c4d3d9 \
--hash=sha256:887fe4a21af2de1e5c2c9428bacba6eb7c1219257bc70f1a1d8447c8a321adb0
certbot-nginx==1.14.0 \
--hash=sha256:8916a815437988d6c192df9f035bb7a176eab20eee0956677b335d0698d243fb \
--hash=sha256:cc2a8a0de56d9bb6b2efbda6c80c647dad8db2bb90675cac03ade94bd5fc8597
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
@@ -9,19 +8,20 @@ import shutil
import subprocess
import time
from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1, SECP384R1
from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1
from cryptography.hazmat.primitives.asymmetric.ec import SECP384R1
from cryptography.hazmat.primitives.asymmetric.ec import SECP521R1
from cryptography.x509 import NameOID
import pytest
from certbot_integration_tests.certbot_tests import context as certbot_context
from certbot_integration_tests.certbot_tests.assertions import assert_cert_count_for_lineage
from certbot_integration_tests.certbot_tests.assertions import assert_elliptic_key
from certbot_integration_tests.certbot_tests.assertions import assert_rsa_key
from certbot_integration_tests.certbot_tests.assertions import assert_equals_group_owner
from certbot_integration_tests.certbot_tests.assertions import assert_equals_group_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_equals_world_read_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_hook_execution
from certbot_integration_tests.certbot_tests.assertions import assert_rsa_key
from certbot_integration_tests.certbot_tests.assertions import assert_saved_renew_hook
from certbot_integration_tests.certbot_tests.assertions import assert_world_no_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_world_read_permissions
@@ -148,6 +148,17 @@ def test_certonly(context):
"""Test the certonly verb on certbot."""
context.certbot(['certonly', '--cert-name', 'newname', '-d', context.get_domain('newname')])
assert_cert_count_for_lineage(context.config_dir, 'newname', 1)
def test_certonly_webroot(context):
"""Test the certonly verb with webroot plugin"""
with misc.create_http_server(context.http_01_port) as webroot:
certname = context.get_domain('webroot')
context.certbot(['certonly', '-a', 'webroot', '--webroot-path', webroot, '-d', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 1)
def test_auth_and_install_with_csr(context):
"""Test certificate issuance and install using an existing CSR."""
@@ -476,6 +487,28 @@ def test_default_curve_type(context):
assert_elliptic_key(key1, SECP256R1)
@pytest.mark.parametrize('curve,curve_cls,skip_servers', [
# Curve name, Curve class, ACME servers to skip
('secp256r1', SECP256R1, []),
('secp384r1', SECP384R1, []),
('secp521r1', SECP521R1, ['boulder-v1', 'boulder-v2'])]
)
def test_ecdsa_curves(context, curve, curve_cls, skip_servers):
"""Test issuance for each supported ECDSA curve"""
if context.acme_server in skip_servers:
pytest.skip('ACME server {} does not support ECDSA curve {}'
.format(context.acme_server, curve))
domain = context.get_domain('curve')
context.certbot([
'certonly',
'--key-type', 'ecdsa', '--elliptic-curve', curve,
'--force-renewal', '-d', domain,
])
key = join(context.config_dir, "live", domain, 'privkey.pem')
assert_elliptic_key(key, curve_cls)
def test_renew_with_ec_keys(context):
"""Test proper renew with updated private key complexity."""
certname = context.get_domain('renew')
@@ -597,7 +630,7 @@ def test_revoke_mutual_exclusive_flags(context):
'revoke', '--cert-name', cert,
'--cert-path', join(context.config_dir, 'live', cert, 'fullchain.pem')
])
assert 'Exactly one of --cert-path or --cert-name must be specified' in error.out
assert 'Exactly one of --cert-path or --cert-name must be specified' in error.value.output
def test_revoke_multiple_lineages(context):

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
@@ -17,9 +17,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
@@ -45,7 +44,6 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
src_file = resource_filename('certbot_integration_tests',
'assets/bind-config/rfc2136-credentials-{}.ini.tpl'
.format(label))
contents = None
with open(src_file, 'r') as f:
contents = f.read().format(

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,11 +1,10 @@
#!/usr/bin/env python
"""Module to call certbot in test mode"""
from __future__ import absolute_import
from distutils.version import LooseVersion
import os
import subprocess
import sys
from distutils.version import LooseVersion
import certbot_integration_tests
# pylint: disable=wildcard-import,unused-wildcard-import
@@ -18,7 +17,7 @@ def certbot_test(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
Invoke the certbot executable available in PATH in a test context for the given args.
The test context consists in running certbot in debug mode, with various flags suitable
for tests (eg. no ssl check, customizable ACME challenge ports and config directory ...).
This command captures stdout and returns it to the caller.
This command captures both stdout and stderr and returns it to the caller.
:param list certbot_args: the arguments to pass to the certbot executable
:param str directory_url: URL of the ACME directory server to use
:param int http_01_port: port for the HTTP-01 challenges
@@ -32,7 +31,8 @@ def certbot_test(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
command, env = _prepare_args_env(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
config_dir, workspace, force_renew)
return subprocess.check_output(command, universal_newlines=True, cwd=workspace, env=env)
return subprocess.check_output(command, stderr=subprocess.STDOUT,
universal_newlines=True, cwd=workspace, env=env)
def _prepare_environ(workspace):

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
@@ -10,6 +8,7 @@ import subprocess
import sys
import tempfile
import time
from typing import Optional
from pkg_resources import resource_filename
@@ -21,7 +20,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 +39,7 @@ class DNSServer(object):
self.bind_root = tempfile.mkdtemp()
self.process = None # type: subprocess.Popen
self.process: Optional[subprocess.Popen] = None
self.dns_xdist = {"address": BIND_BIND_ADDRESS[0], "port": BIND_BIND_ADDRESS[1]}
@@ -113,8 +112,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.
@@ -122,6 +120,9 @@ class DNSServer(object):
but otherwise the contents are ignored.
:param int attempts: The number of attempts to make.
"""
if not self.process:
raise ValueError("DNS server has not been started. Please run start() first.")
for _ in range(attempts):
if self.process.poll():
raise ValueError("BIND9 server stopped unexpectedly")

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
@@ -29,10 +29,7 @@ class _ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
request = requests.get(PEBBLE_MANAGEMENT_URL + '/intermediates/0', verify=False)
issuer_cert = x509.load_pem_x509_certificate(request.content, default_backend())
try:
content_len = int(self.headers.getheader('content-length', 0))
except AttributeError:
content_len = int(self.headers.get('Content-Length'))
content_len = int(self.headers.get('Content-Length'))
ocsp_request = ocsp.load_der_ocsp_request(self.rfile.read(content_len))
response = requests.get('{0}/cert-status-by-serial/{1}'.format(

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

@@ -7,6 +7,13 @@ from setuptools import setup
version = '0.32.0.dev0'
# setuptools 36.2+ is needed for support for environment markers
min_setuptools_version='36.2'
# This conditional isn't necessary, but it provides better error messages to
# people who try to install this package with older versions of setuptools.
if LooseVersion(setuptools_version) < LooseVersion(min_setuptools_version):
raise RuntimeError(f'setuptools {min_setuptools_version}+ is required')
install_requires = [
'coverage',
'cryptography',
@@ -14,24 +21,17 @@ 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',
# This dependency needs to be added using environment markers to avoid its
# installation on Linux.
'pywin32>=300 ; sys_platform == "win32"',
'pyyaml',
'requests',
'six'
]
# Add pywin32 on Windows platforms to handle low-level system calls.
# This dependency needs to be added using environment markers to avoid its installation on Linux.
# However environment markers are supported only with setuptools >= 36.2.
# So this dependency is not added for old Linux distributions with old setuptools,
# in order to allow these systems to build certbot from sources.
if LooseVersion(setuptools_version) >= LooseVersion('36.2'):
install_requires.append("pywin32>=224 ; sys_platform == 'win32'")
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.')
setup(
name='certbot-ci',
version=version,
@@ -40,14 +40,12 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -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
@@ -33,7 +33,9 @@ class Proxy(object):
self.args = args
self.http_port = 80
self.https_port = 443
self._configurator = self._all_names = self._test_names = None
self._configurator = None
self._all_names = None
self._test_names = None
def __getattr__(self, name):
"""Wraps the configurator methods"""
@@ -93,5 +95,7 @@ class Proxy(object):
"""Installs cert"""
cert_path, key_path, chain_path = self.copy_certs_and_keys(
cert_path, key_path, chain_path)
if not self._configurator:
raise ValueError("Configurator plugin is not set.")
self._configurator.deploy_cert(
domain, cert_path, key_path, chain_path, fullchain_path)

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.11.0.dev0'
version = '1.15.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')
@@ -38,14 +26,12 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,16 +1,17 @@
"""DNS Authenticator for Cloudflare."""
import logging
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
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
from certbot.plugins.dns_common import CredentialsConfiguration
logger = logging.getLogger(__name__)
@@ -31,7 +32,7 @@ class Authenticator(dns_common.DNSAuthenticator):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.credentials = None
self.credentials: Optional[CredentialsConfiguration] = None
@classmethod
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
@@ -80,12 +81,14 @@ class Authenticator(dns_common.DNSAuthenticator):
self._get_cloudflare_client().del_txt_record(domain, validation_name, validation)
def _get_cloudflare_client(self):
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
if self.credentials.conf('api-token'):
return _CloudflareClient(None, self.credentials.conf('api-token'))
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 +176,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

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -1,18 +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.11.0.dev0'
version = '1.15.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'cloudflare>=1.5.1',
'setuptools',
'setuptools>=39.0.1',
'zope.interface',
]
@@ -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',
@@ -49,7 +38,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,8 +46,6 @@ setup(
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,5 +1,6 @@
"""DNS Authenticator for CloudXNS DNS."""
import logging
from typing import Optional
from lexicon.providers import cloudxns
import zope.interface
@@ -8,6 +9,7 @@ from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
from certbot.plugins import dns_common_lexicon
from certbot.plugins.dns_common import CredentialsConfiguration
logger = logging.getLogger(__name__)
@@ -27,7 +29,7 @@ class Authenticator(dns_common.DNSAuthenticator):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.credentials = None
self.credentials: Optional[CredentialsConfiguration] = None
@classmethod
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
@@ -56,6 +58,8 @@ class Authenticator(dns_common.DNSAuthenticator):
self._get_cloudxns_client().del_txt_record(domain, validation_name, validation)
def _get_cloudxns_client(self):
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
return _CloudXNSLexiconClient(self.credentials.conf('api-key'),
self.credentials.conf('secret-key'),
self.ttl)

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -1,18 +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.11.0.dev0'
version = '1.15.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'setuptools',
'setuptools>=39.0.1',
'zope.interface',
]
@@ -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',
@@ -49,7 +38,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,8 +46,6 @@ setup(
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,5 +1,6 @@
"""DNS Authenticator for DigitalOcean."""
import logging
from typing import Optional
import digitalocean
import zope.interface
@@ -7,6 +8,7 @@ import zope.interface
from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
from certbot.plugins.dns_common import CredentialsConfiguration
logger = logging.getLogger(__name__)
@@ -21,10 +23,11 @@ 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)
self.credentials = None
self.credentials: Optional[CredentialsConfiguration] = None
@classmethod
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
@@ -45,16 +48,19 @@ 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)
def _get_digitalocean_client(self):
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
return _DigitalOceanClient(self.credentials.conf('token'))
class _DigitalOceanClient(object):
class _DigitalOceanClient:
"""
Encapsulates all communication with the DigitalOcean API.
"""
@@ -62,13 +68,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 +97,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 +108,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

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -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.11.0.dev0'
version = '1.15.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'python-digitalocean>=1.11',
'setuptools',
'six',
'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support
'setuptools>=39.0.1',
'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',
@@ -50,7 +38,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -58,8 +46,6 @@ setup(
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -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,5 +1,6 @@
"""DNS Authenticator for DNSimple DNS."""
import logging
from typing import Optional
from lexicon.providers import dnsimple
import zope.interface
@@ -8,6 +9,7 @@ from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
from certbot.plugins import dns_common_lexicon
from certbot.plugins.dns_common import CredentialsConfiguration
logger = logging.getLogger(__name__)
@@ -27,7 +29,7 @@ class Authenticator(dns_common.DNSAuthenticator):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.credentials = None
self.credentials: Optional[CredentialsConfiguration] = None
@classmethod
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
@@ -54,6 +56,8 @@ class Authenticator(dns_common.DNSAuthenticator):
self._get_dnsimple_client().del_txt_record(domain, validation_name, validation)
def _get_dnsimple_client(self):
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
return _DNSimpleLexiconClient(self.credentials.conf('token'), self.ttl)

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -1,17 +1,15 @@
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.11.0.dev0'
version = '1.15.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'setuptools',
'setuptools>=39.0.1',
'zope.interface',
]
@@ -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
@@ -60,7 +49,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -68,8 +57,6 @@ setup(
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,5 +1,6 @@
"""DNS Authenticator for DNS Made Easy DNS."""
import logging
from typing import Optional
from lexicon.providers import dnsmadeeasy
import zope.interface
@@ -8,6 +9,7 @@ from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
from certbot.plugins import dns_common_lexicon
from certbot.plugins.dns_common import CredentialsConfiguration
logger = logging.getLogger(__name__)
@@ -28,7 +30,7 @@ class Authenticator(dns_common.DNSAuthenticator):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.credentials = None
self.credentials: Optional[CredentialsConfiguration] = None
@classmethod
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
@@ -58,6 +60,8 @@ class Authenticator(dns_common.DNSAuthenticator):
self._get_dnsmadeeasy_client().del_txt_record(domain, validation_name, validation)
def _get_dnsmadeeasy_client(self):
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
return _DNSMadeEasyLexiconClient(self.credentials.conf('api-key'),
self.credentials.conf('secret-key'),
self.ttl)

View File

@@ -111,7 +111,7 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------

View File

@@ -1,18 +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.11.0.dev0'
version = '1.15.0.dev0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'setuptools',
'setuptools>=39.0.1',
'zope.interface',
]
@@ -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',
@@ -49,7 +38,7 @@ setup(
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.6',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -57,8 +46,6 @@ setup(
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',

View File

@@ -1,12 +1,15 @@
"""DNS Authenticator for Gehirn Infrastructure Service DNS."""
import logging
from typing import Optional
from lexicon.providers import gehirn
import zope.interface
from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
from certbot.plugins import dns_common_lexicon
from certbot.plugins.dns_common import CredentialsConfiguration
logger = logging.getLogger(__name__)
@@ -27,7 +30,7 @@ class Authenticator(dns_common.DNSAuthenticator):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.credentials = None
self.credentials: Optional[CredentialsConfiguration] = None
@classmethod
def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
@@ -57,6 +60,8 @@ class Authenticator(dns_common.DNSAuthenticator):
self._get_gehirn_client().del_txt_record(domain, validation_name, validation)
def _get_gehirn_client(self):
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
return _GehirnLexiconClient(
self.credentials.conf('api-token'),
self.credentials.conf('api-secret'),

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