Compare commits

...

511 Commits

Author SHA1 Message Date
Adrien Ferrand
656b9c9cd5 Update installer-tests.yml 2020-01-08 08:41:03 +01:00
Adrien Ferrand
192084419c Update installer-tests.yml 2020-01-08 08:32:15 +01:00
Adrien Ferrand
b0c1ef57fd Update installer-tests.yml 2020-01-08 08:27:18 +01:00
Adrien Ferrand
88b65be81c Update installer-tests.yml 2020-01-08 08:26:12 +01:00
Brad Warren
082874e2f5 psvtbl 2020-01-07 11:39:57 -08:00
Brad Warren
295fea3d26 test pwsh version 2020-01-07 11:34:02 -08:00
Adrien Ferrand
fda655370a Update CHANGELOG.md (#7659) 2020-01-02 23:44:16 +01:00
Adrien Ferrand
887d72fd5d Remove POST-as-GET fallback to GET (#6994) 2020-01-02 12:48:55 -08:00
Brad Warren
6d527bcc42 Include header files for compilation. (#7650) 2019-12-19 14:02:24 -08:00
Barbz
6ca80b7ce8 How to uninstall certbot-auto (#7648) 2019-12-19 13:30:13 -08:00
Brad Warren
f520d482fd Remove other 3.8-dev references. (#7646) 2019-12-18 23:00:49 +01:00
Adrien Ferrand
b5a31bec03 Add docker-compose as a requirement of certbot-ci (#7120)
Fixes #7110 

This PR declares docker-compose as a requirement for certbot-ci. This way, a recent version of docker-compose is installed in the standard virtual environment set up by `tools/venv.py` and `tools/venv3.py`, and so is available to pytest integration tests from `tox` or in the virtual environment enabled.

* Add docker-compose as a dev dependency and declares it in certbot-ci requirements

* Update docker-compose 1.25.0
2019-12-18 13:21:54 -08:00
Brad Warren
6ac7aabaf7 Remove warning about dev preview (#7640) 2019-12-18 11:14:58 -08:00
Brad Warren
24fdea5fd8 discourage dns plugins (#7639) 2019-12-18 11:13:57 -08:00
Adrien Ferrand
4a906484ee Execute Windows installer integration tests on several Windows versions (#7641)
This PRs extends the installer tests on Azure Pipeline, in order to run the integration tests on a certbot instance installed with the Windows installer for several Windows versions, corresponding to the scope of supported versions on Certbot:
* Windows Server 2012 R2
* Windows Server 2016
* Windows Server 2019

One can see the result on: https://dev.azure.com/adferrand/certbot/_build/results?buildId=311

* Try specific installer-build step

* Install Python manually

* Add tests on windows 2019
2019-12-16 16:03:39 -08:00
Adrien Ferrand
9e5bca4bbf Lint certbot code on Python 3, and update Pylint to the latest version (#7551)
Part of #7550

This PR makes appropriate corrections to run pylint on Python 3.

Why not keeping the dependencies unchanged and just run pylint on Python 3?
Because the old version of pylint breaks horribly on Python 3 because of unsupported version of astroid.

Why updating pylint + astroid to the latest version ?
Because this version only fixes some internal errors occuring during the lint of Certbot code, and is also ready to run gracefully on Python 3.8.

Why upgrading mypy ?
Because the old version does not support the new version of astroid required to run pylint correctly.

Why not upgrading mypy to its latest version ?
Because this latest version includes a new typshed version, that adds a lot of new type definitions, and brings dozens of new errors on the Certbot codebase. I would like to fix that in a future PR.

That said so, the work has been to find the correct set of new dependency versions, then configure pylint for sane configuration errors in our situation, disable irrelevant lintings errors, then fixing (or ignoring for good reason) the remaining mypy errors.

I also made PyLint and MyPy checks run correctly on Windows.

* Start configuration

* Reconfigure travis

* Suspend a check specific to python 3. Start fixing code.

* Repair call_args

* Fix return + elif lints

* Reconfigure development to run mainly on python3

* Remove incompatible Python 3.4 jobs

* Suspend pylint in some assertions

* Remove pylint in dev

* Take first mypy that supports typed-ast>=1.4.0 to limit the migration path

* Various return + else lint errors

* Find a set of deps that is working with current mypy version

* Update local oldest requirements

* Remove all current pylint errors

* Rebuild letsencrypt-auto

* Update mypy to fix pylint with new astroid version, and fix mypy issues

* Explain type: ignore

* Reconfigure tox, fix none path

* Simplify pinning

* Remove useless directive

* Remove debugging code

* Remove continue

* Update requirements

* Disable unsubscriptable-object check

* Disable one check, enabling two more

* Plug certbot dev version for oldest requirements

* Remove useless disable directives

* Remove useless no-member disable

* Remove no-else-* checks. Use elif in symetric branches.

* Add back assertion

* Add new line

* Remove unused pylint disable

* Remove other pylint disable
2019-12-10 14:12:50 -08:00
Adrien Ferrand
e048da1e38 Reorganize imports (#7616)
* Isort execution

* Fix pylint, adapt coverage

* New isort

* Fix magic_typing lint

* Second round

* Fix pylint

* Third round. Store isort configuration

* Fix latest mistakes

* Other fixes

* Add newline

* Fix lint errors
2019-12-09 15:50:20 -05:00
Brad Warren
34b568f366 Don't list adding type annotations as a PR req. (#7627) 2019-12-04 20:22:10 +01:00
ohemorange
b99bfe8ab7 Merge pull request #7622 from certbot/candidate-1.0.0
Release 1.0
2019-12-04 14:15:49 -05:00
Brad Warren
5da61564d9 Don't list DNS plugins as alpha quality. (#7624)
They should be considered production quality like our other packaged code.
2019-12-03 19:56:16 -08:00
Brad Warren
b45f79d0ab fix bad links in docs (#7623)
This PR fixes the failures at https://travis-ci.com/certbot/website/builds/139193502#L1316.

Once this PR lands, I'll update certbot/website#508 to include this commit.
2019-12-03 11:05:23 -08:00
Brad Warren
3cfa63483d Add full API documentation (#7614)
A lot of Certbot's files don't have API documentation which is fixed by this PR. To do this, from the top level certbot directory I ran:
```
sphinx-apidoc -Me -o docs/api certbot
```
I then merged the resulting `modules.rst` file with `docs/api.rst`.
2019-12-03 09:54:37 -08:00
Brad Warren
27d6f62a96 update external plugin (#7604)
The old plugin at https://github.com/marcan/certbot-external says it's obsolete and points people to https://github.com/EnigmaBridge/certbot-external-auth. The new plugin is also an installer.

I also removed the reference to #2782 about us adding similar functionality since that's been done for a long time. We could reference our manual plugin instead, but I think that devalues their plugin a bit which I don't think is necessary or correct as it has different features.
2019-12-03 09:52:05 -08:00
Brad Warren
e32033f1ec document main (#7610)
I deleted the exceptions because I think it's not feasible to document the possible exceptions raised by all of Certbot.
2019-12-03 09:51:43 -08:00
Brad Warren
d2bad803f3 Bump version to 1.1.0 2019-12-03 09:27:30 -08:00
Brad Warren
5debf7af7e Add contents to certbot/CHANGELOG.md for next version 2019-12-03 09:27:30 -08:00
Brad Warren
6102cc440b Release 1.0.0 2019-12-03 09:27:28 -08:00
Brad Warren
bc80195a58 Update changelog for 1.0.0 release 2019-12-03 09:20:30 -08:00
Felix Schwarz
2008e3cc77 acme/setup.py: comment refers to "PyOpenSSL" not "mock" (#7619) 2019-12-03 01:16:41 +01:00
Adrien Ferrand
4c652b9c82 Upgrade to pywin32>=227 (#7615)
Current version of pywin32 used in certbot (225) does not have wheels available for Python 3.8. Installing certbot for development in this case requires to build from source. On Windows, this implies a Visual Studio C++ environment up and ready, which is absolutely not fun.

Let's upgrade to pywin32 227, that provides these wheels for all Python versions from 3.5 up to current dev status of 3.9.
2019-12-02 13:39:31 -08:00
Brad Warren
84b770b56e Defines the RenewableCert API (#7603)
This is my proposed fix for #7540. I would ideally like this to be included in our 1.0 release.

I came up with this design by adding all attributes used either in our own plugins, 3rd party plugins listed at https://certbot.eff.org/docs/using.html#third-party-plugins, or our public API code.

Despite me thinking that zope is unneeded nowadays, I initially tried to use it to define this interface since we have it and it gives us a way to define expected attributes, but it doesn't work because zope interface objects also have a method called `names` which conflict with the API.

I talked about this with Adrien out of band and did some of my own research and there are some minor benefits with this new approach of using properties:

1. It's more conventional.
2. If you also change the implementation to inherit from the class, Python will error if all properties aren't defined.
3. The PEP 526 style type annotations with mypy seem to (currently) only be used to validate code using the class, not the class implementation itself. You can add a type annotation saying the class needs to have this attribute, never define it, and mypy won't complain.

With this new approach, I had to fix `names` because pylint was complaining that the arguments differed, however, we never used the optional parameter to `names` outside of tests so I just deleted the code altogether.

* fixes #7540

* move to properties
2019-11-27 11:32:00 -08:00
ohemorange
6c1dfe43c7 Refactor tests out of packaged module for apache plugin (#7607)
Part of #7593.

* Refactor tests out of packaged module for apache plugin

* Exclude pycache and .py[cod]

* Change tests path in tox.ini
2019-11-27 09:57:35 -08:00
ohemorange
a8e711d281 Refactor tests out of packaged module for nginx plugin (#7606)
* Refactor tests out of packaged module for nginx plugin

* Exclude pycache and .py[cod]
2019-11-26 17:45:18 -08:00
ohemorange
f36b93267c Exclude pycache and .py[cod] from certbot package (#7608) 2019-11-26 17:45:07 -08:00
ohemorange
d2b65b47f2 Refactor tests out of packaged module for acme plugin (#7600)
* Move acme tests to tests/ directory outside of acme module

* Fix call to messages_test in client_test

* Move test_util.py and testdata/ into tests/

* Update manifest to package tests

* Exclude pycache and .py[cod]
2019-11-26 15:25:41 -08:00
ohemorange
b624172f68 Refactor tests out of packaged module for dns plugins (#7599)
* Refactor tests out of module for certbot-dns-cloudflare

* Refactor tests out of module for certbot-dns-cloudxns

* Refactor tests out of module for certbot-dns-digitalocean

* Refactor tests out of module for certbot-dns-dnsimple

* Refactor tests out of module for certbot-dns-dnsmadeeasy

* Refactor tests out of module for certbot-dns-gehirn

* Refactor tests out of module for certbot-dns-google

* Refactor tests out of module for certbot-dns-linode

* Refactor tests out of module for certbot-dns-luadns

* Refactor tests out of module for certbot-dns-nsone

* Refactor tests out of module for certbot-dns-ovh

* Refactor tests out of module for certbot-dns-rfc2136

* Refactor tests out of module for certbot-dns-sakuracloud

* Refactor tests out of module for certbot-dns-route53

* Move certbot-dns-google testdata/ under tests/

* Use pytest for dns plugins

* Exclude pycache and .py[cod]
2019-11-26 15:25:28 -08:00
ohemorange
6d1472bf8c Implement redirect by default (#7595)
* Change redirect default to yes so that it happens automatically in noninteractive mode

* Update changelog
2019-11-25 18:53:20 -08:00
ohemorange
5c8083851a Fix refactor (#7597)
Clean up some places missed by #7544.

Found this when running test farm tests. They were working as of 5d90544, and I will truly shocked if subsequent changes (all to the windows installer) made them stop working.

* Release script needs to target new CHANGELOG location

* Clean up various other CHANGELOG path references

* Update windows paths for new certbot location

* Add certbot to packages list for windows installer
2019-11-25 18:24:20 -08:00
ohemorange
345bdb46e0 Update pull_request_template.md (#7596)
* Update pull_request_template.md

* Remove line breaks

Github seems to be keeping the line breaks rather than ignoring them, making it be formatted weirdly, so remove them.
2019-11-25 15:42:01 -08:00
ohemorange
e023f889ff Make the contents of the nginx plugin private (#7589)
Part of #5775.

* Create _internal folder certbot-nginx

* Move configurator.py to _internal

* Move constants.py to _internal

* Move display_ops.py to _internal

* Move http_01.py to _internal

* Move nginxparser.py to _internal

* Move obj.py to _internal

* Move parser_obj.py to _internal

* Move parser.py to _internal

* Update location and references for tls_configs

* exclude parser_obj from coverage
2019-11-25 14:30:24 -08:00
ohemorange
4abd81e218 Refactor certbot/ and certbot/tests/ to use the same structure as the other packages (#7544)
Summary of changes in this PR:
- Refactor files involved in the `certbot` module to be of a similar structure to every other package; that is, inside a directory inside the main repo root (see below).
- Make repo root README symlink to `certbot` README.
- Pull tests outside of the distributed module.
- Make `certbot/tests` not be a module so that `certbot` isn't added to Python's path for module discovery.
- Remove `--pyargs` from test calls, and make sure to call tests from repo root since without `--pyargs`, `pytest` takes directory names rather than package names as arguments.
- Replace mentions of `.` with `certbot` when referring to packages to install, usually editably.
- Clean up some unused code around executing tests in a different directory.
- Create public shim around main and make that the entry point.

New directory structure summary:
```
repo root ("certbot", probably, but for clarity all files I mention are relative to here)
├── certbot
│   ├── setup.py
│   ├── certbot
│   │   ├── __init__.py
│   │   ├── achallenges.py
│   │   ├── _internal
│   │   │   ├── __init__.py
│   │   │   ├── account.py
│   │   │   ├── ...
│   │   ├── ...
│   ├── tests
│   │   ├── account_test.py
│   │   ├── display
│   │   │   ├── __init__.py
│   │   │   ├── ...
│   │   ├── ... # note no __init__.py at this level
│   ├── ...
├── acme
│   ├── ...
├── certbot-apache
│   ├── ...
├── ...
```

* refactor certbot/ and certbot/tests/ to use the same structure as the other packages

* git grep -lE "\-e(\s+)\." | xargs sed -i -E "s/\-e(\s+)\./-e certbot/g"

* git grep -lE "\.\[dev\]" | xargs sed -i -E "s/\.\[dev\]/certbot[dev]/g"

* git grep -lE "\.\[dev3\]" | xargs sed -i -E "s/\.\[dev3\]/certbot[dev3]/g"

* Remove replacement of certbot into . in install_and_test.py

* copy license back out to main folder

* remove linter_plugin.py and CONTRIBUTING.md from certbot/MANIFEST.in because these files are not under certbot/

* Move README back into main folder, and make the version inside certbot/ a symlink

* symlink certbot READMEs the other way around

* move testdata into the public api certbot zone

* update source_paths in tox.ini to certbot/certbot to find the right subfolder for tests

* certbot version has been bumped down a directory level

* make certbot tests directory not a package and import sibling as module

* Remove unused script cruft

* change . to certbot in test_sdists

* remove outdated comment referencing a command that doesn't work

* Install instructions should reference an existing file

* update file paths in Dockerfile

* some package named in tox.ini were manually specified, change those to certbot

* new directory format doesn't work easily with pyargs according to http://doc.pytest.org/en/latest/goodpractices.html#tests-as-part-of-application-code

* remove other instance of pyargs

* fix up some references in _release.sh by searching for ' . ' and manual check

* another stray . in tox.ini

* fix paths in tools/_release.sh

* Remove final --pyargs call, and now-unnecessary call to modules instead of local files, since that's fixed by certbot's code being one layer deeper

* Create public shim around main and make that the entry point

* without pyargs, tests cannot be run from an empty directory

* Remove cruft for running certbot directly from main

* Have main shim take real arg

* add docs/api file for main, and fix up main comment

* Update certbot/docs/install.rst

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Fix comments in readthedocs requirements files to refer to current package

* Update .[docs] reference in contributing.rst

* Move plugins tests to certbot tests directory

* add certbot tests to MANIFEST.in so packagers can run python setup.py test

* move examples directory inside certbot/

* Move CHANGELOG into certbot, and create a top-level symlink

* Remove unused sys and logging from main shim

* nginx http01 test no longer relies on certbot plugins common test
2019-11-25 14:28:05 -08:00
ohemorange
d56cd4ef01 Make the contents of the DNS plugins private (#7580)
Part of #5775.

```
modify_item () {
    mkdir certbot-dns-$1/certbot_dns_$1/_internal
    git grep -l "from certbot_dns_$1 import dns_$1" | xargs sed -i "s/from certbot_dns_$1 import dns_$1/from certbot_dns_$1._internal import dns_$1/g"
    git grep -l "certbot_dns_$1\.dns_$1" | xargs sed -i "s/certbot_dns_$1\.dns_$1/certbot_dns_$1._internal.dns_$1/g"
    git checkout -- certbot-dns-$1/certbot_dns_$1/__init__.py
    echo '"""Internal implementation of \`~certbot_dns_$1.dns_$1\` plugin."""' > certbot-dns-$1/certbot_dns_$1/_internal/__init__.py
    mv certbot-dns-$1/certbot_dns_$1/dns_$1.py certbot-dns-$1/certbot_dns_$1/_internal
    git checkout -- CHANGELOG.md
    git status
    git add -A
    git commit -m "Move certbot-dns-$1 to _internal structure"
}
```

Structure now looks like this:
```
certbot-dns-cloudflare/
├── certbot_dns_cloudflare
│   ├── dns_cloudflare_test.py
│   ├── __init__.py
│   └── _internal
│       ├── dns_cloudflare.py
│       └── __init__.py
```

* Move certbot-dns-cloudflare to _internal structure

* Move certbot-dns-cloudxns to _internal structure

* Move certbot-dns-digitalocean to _internal structure

* Move certbot-dns-dnsimple to _internal structure

* Move certbot-dns-dnsmadeeasy to _internal structure

* Move certbot-dns-gehirn to _internal structure

* Move certbot-dns-google to _internal structure

* Move certbot-dns-linode to _internal structure

* Move certbot-dns-luadns to _internal structure

* Move certbot-dns-nsone to _internal structure

* Move certbot-dns-ovh to _internal structure

* Move certbot-dns-rfc2136 to _internal structure

* Move certbot-dns-sakuracloud to _internal structure

* Init file comments need to be comments

* Move certbot-dns-route53 to _internal structure

* Fix comment in route53 init
2019-11-25 10:26:05 -08:00
ohemorange
8139689d4c Make the contents of the apache plugin private (#7579)
Part of #5775.

Tree:
```
certbot-apache/certbot_apache
├── __init__.py
├── _internal
│   ├── apache_util.py
│   ├── augeas_lens
│   │   ├── httpd.aug
│   │   └── README
│   ├── centos-options-ssl-apache.conf
│   ├── configurator.py
│   ├── constants.py
│   ├── display_ops.py
│   ├── entrypoint.py
│   ├── http_01.py
│   ├── __init__.py
│   ├── obj.py
│   ├── options-ssl-apache.conf
│   ├── override_arch.py
│   ├── override_centos.py
│   ├── override_darwin.py
│   ├── override_debian.py
│   ├── override_fedora.py
│   ├── override_gentoo.py
│   ├── override_suse.py
│   └── parser.py
└── tests
    ├── ...
```

* Create _internal folder for certbot_apache

* Move apache_util.py to _internal

* Move display_ops.py to _internal

* Move override_centos.py to _internal

* Move override_gentoo.py to _internal

* Move override_darwin.py to _internal

* Move override_suse.py to _internal

* Move override_debian.py to _internal

* Move override_fedora.py to _internal

* Move override_arch.py to _internal

* Move parser.py to _internal

* Move obj.py to _internal

* Move http_01.py to _internal

* Move entrypoint.py to _internal

* Move constants.py to _internal

* Move configurator.py to _internal

* Move augeas_lens to _internal

* Move options-ssl-apache.conf files to _internal

* move augeas_lens in MANIFEST

* Clean up some stray references to certbot_apache that could use _internal

* Correct imports and lint
2019-11-25 09:44:40 -08:00
ohemorange
a27b1137a5 Remove unused nginx docs (#7576)
Part of #5775. We don't use these docs anywhere, so delete them.

Removes:
- `certbot-nginx/readthedocs.org.requirements.txt`
- `certbot-nginx/docs/` folder
- docs include in `MANIFEST.in`
- docs dependencies in `setup.py`

* Remove unused nginx docs

* Add changelog entry about the removal
2019-11-25 09:18:12 -08:00
Brad Warren
5809aa6a2c remove unused route53 tools (#7586) 2019-11-22 22:24:51 +01:00
ohemorange
d8ca555eed Remove DNS plugin API docs. (#7578)
Replace DNS plugins' API documentation with a note that plugins adhere to certbot's plugin interface.
2019-11-22 12:58:06 -08:00
ohemorange
bd35e71b5c Remove unused certbot-compatibility-test docs (#7577)
Part of #5775. We don't use these docs anywhere, so delete them.

Removes:
- `certbot-compatibility-test/readthedocs.org.requirements.txt`
- `certbot-compatibility-test/docs/` folder
- docs include in `MANIFEST.in`
- docs dependencies in `setup.py`
2019-11-22 12:54:18 -08:00
ohemorange
70e4cb7853 Remove unused apache docs (#7575)
Part of #5775. We don't use these docs anywhere, so delete them.

Removes:
- `certbot-apache/readthedocs.org.requirements.txt`
- `certbot-apache/docs/` folder
- docs include in `MANIFEST.in`
- docs dependencies in `setup.py`
2019-11-22 12:50:01 -08:00
Brad Warren
4f3010ef3f fixes #7553 (#7560) 2019-11-14 14:26:01 -08:00
Brad Warren
2692b862d2 Update pinned dependencies (#7558)
Fixes #7184.

I updated #7358 to track the issue of unpinning all of these dependencies.

* pin back configargparse

* Pin back zope packages.

* update deps

* Add changelog entry.

* run build.py
2019-11-13 13:44:36 -08:00
ohemorange
4d4c83d4d8 Internalize modules called by internal plugins (#7543)
* Move hooks.py to _internal

* Move cli.py to _internal
2019-11-13 11:14:46 -08:00
Brad Warren
57148b7593 Fix shebang in rebuild_deps (#7557)
When you try to run this script, it crashes with:
```
standard_init_linux.go:211: exec user process caused "exec format error"
```
This is caused by the script being written to have the contents:
```
\
#!/bin/sh
set -e
...
```
This fixes the problem by removing the slash and moving the shebang to the first line of the string.
2019-11-13 11:14:26 -08:00
Amjad Mashaal
4a8ede2562 Deprecate certbot register --update-registration (#7556)
Closes #7452.
2019-11-13 10:24:37 -08:00
ohemorange
46d5f7a860 Move configuration.py to _internal (#7542)
Part of #5775. Methodology similar to #7528. Also refactors NGINX test util to use certbot.tests.util.ConfigTestCase.

* refactor nginx tests to no longer rely on certbot.configuration internals

* Move configuration.py to _internal
2019-11-13 10:19:27 -08:00
Adrien Ferrand
595b1b212e [Windows] Avoid letsencrypt.log permissions error during scheduled certbot renew task (#7537)
While coding for #7536, I ran into another issue. It appears that Certbot logs generated during the scheduled task execution have wrong permissions that make them almost unusable: they do not have an owner, and their ACL contains nonsense values (non existant accounts name).

The class `logging.handler.RotatingFileHandler` is responsible for these logs, and become mad when it is in a Python process run under a scheduled task owned by `SYSTEM`. This is precisely our case here.

This PR avoids (but not fix) the issue, by changing the owner of the scheduled task from `SYSTEM` to the `Administrators` group, that appears to work fine.

* Use Administrators group instead of SYSTEM to run the certbot renew task
2019-11-13 10:04:45 -08:00
Adrien Ferrand
75acdeb645 [Windows] Fix certbot renew task failure under NT AUTHORITY\SYSTEM account (#7536)
Turned out that the scheduled task that runs `certbot renew` twice a day, is failing. Without any kind of log of course, otherwise it would not be fun.

It can be revealed by opening a powershell under the `NT AUTHORITY\SYSTEM` account, under which the scheduled task is run. Under theses circumstances, the bug is revealed: Certbot breaks when trying to invoke `certbot.compat.filesystem._get_current_user()`. Indeed the logic there implied to call `win32api.GetUserNameEx(win32api.NameSamCompatible)` and this function does not return always a useful value.

For normal account, it will be typically `DOMAIN_OR_MACHINE_NAME\YOUR_USER_NAME` (e.g. `My Machine\Adrien Ferrand`). But for the account `NT AUTHORITY\SYSTEM`, it will return `MACHINE_NAME\DOMAIN$`, which is a nonsense and makes fail the resolution of the actual SID of the account at the end of `_get_current_user()`.

This PR fixes this behavior by using an explicit construction of the account name that works both for normal users and `SYSTEM`.

* Use a different way to resolve current user account, that works both for normal users and SYSTEM.

* Add a comment to run Certbot under NT AUTHORITY\SYSTEM
2019-11-13 09:43:50 -08:00
Adrien Ferrand
c26d459d0f Remove python2 and certbot-auto references in how to set up a Certbot build environment. (#7549)
Fixes #7548.

This PR udpdates installation instructions to get rid of python2 and certbot-auto in the how-to explaining the Certbot development environment setup.

Instead, Python 3 is used, and appropriate instructions for APT and RPM based distributions are provided.
2019-11-12 13:52:44 -08:00
ohemorange
4792e1ee21 Move constants.py to _internal (#7534)
* Don't call core constants from nginx plugin

* Move constants.py to _internal/

* Move ENHANCEMENTS from now-internal constants to public plugins.enhancements

* Update display.enhancements.ask from its 2015 comment
2019-11-11 15:41:40 -08:00
ohemorange
08c1de34bd Move items in certbot/plugins to _internal (#7533)
* Create and initialize _internal/plugins

* Move plugins/manual.py to _internal/

* Move plugins/disco.py to _internal/

* Move plugins/selection.py to _internal/

* Move plugins/webroot.py to _internal/

* Move plugins/null.py to _internal/

* Move plugins/standalone.py to _internal/

* add missed internalization

* shorten line

* Update outdated init comment
2019-11-11 15:14:18 -08:00
Brad Warren
641b60b8f0 Remove TLS-SNI objects in ACME (#7535)
* fixes #7214

* update changelog

* remove unused import
2019-11-11 23:04:26 +01:00
ohemorange
d290fe464e Move eff.py to _internal (#7530)
* Move eff.py to _internal

* missed a few certbot.effs in tests

* remove sublime autocompletion

* fix messy scripting
2019-11-11 10:20:05 -08:00
ohemorange
e38aa65cae Move items in certbot/display to _internal (#7532)
* Move display/completer.py to _internal/

* Move display/dummy_readline.py to _internal/

* Move display/enhancements.py to _internal/

* Create __init__.py in _internal/display
2019-11-11 10:19:28 -08:00
ohemorange
8fb9e9adde Move log.py to _internal (#7531)
Part of #5775. Methodology similar to #7528, but slightly more manual.
2019-11-11 10:17:16 -08:00
ohemorange
96e02d614b Make uncomplicated modules private (#7528)
* Create _internal package for Certbot's non-public modules

* Move account.py to _internal

* Move auth_handler.py to _internal

* Move cert_manager.py to _internal

* Move client.py to _internal

* Move error_handler.py to _internal

* Move lock.py to _internal

* Move main.py to _internal

* Move notify.py to _internal

* Move ocsp.py to _internal

* Move renewal.py to _internal

* Move reporter.py to _internal

* Move storage.py to _internal

* Move updater.py to _internal

* update apache and nginx oldest requirements

* Keep the lock file as certbot.lock

* nginx oldest tests still need to rely on newer certbot

* python doesn't have good dependency resolution, so specify the transitive dependency

* update required minimum versions in nginx setup.py
2019-11-08 16:19:21 -08:00
Brad Warren
0a48d7bf7e remove get_systemd_os_info (#7526)
Fixes #7500.
2019-11-08 11:11:03 -08:00
Brad Warren
4b488614cf Remove tls sni common (#7527)
* fixes #7478

* add changelog entry
2019-11-08 15:11:09 +01:00
Shell Chen
f4f16605ed dns-rfc2136: use TCP to query SOA records (#7503)
* Use tcp query on dns-rfc2136 plugin

To improve network robust; fixes #7502.

* Update CHANGELOG.md

* Fix dns-rfc2136 test cases

* Add UDP fallback to dns-rfc2136
2019-11-07 18:37:11 +01:00
ohemorange
b84edfd39a Merge pull request #7515 from certbot/candidate-0.40.1
Candidate 0.40.1
2019-11-06 12:01:59 -08:00
ohemorange
88d9a31cf9 Merge branch 'master' into candidate-0.40.1 2019-11-06 11:47:28 -08:00
Amjad Mashaal
1dff022d05 Deprecate config_changes (#7469)
Closes #7454

* Deprecate config_changes

* Error on config_changes

* Fix tests for main.py

* Fix CHANGELOG entry

* Remove remnants of config_changes

* Fix CHANGELOG and add removed functions
2019-11-06 11:29:07 -08:00
Brad Warren
2b4c2a7f55 Match our Travis logic in Azure. (#7514)
In Travis, the full test suite doesn't run on PRs for point release branches, just on commits for them. I think this behavior makes sense because what we actually want to test before a point release is the exact commit we want to release after any squashing/merging has been done. This PR modifies Azure to match this behavior.

After this PR lands, I need to update the tests required to pass on GitHub.
2019-11-06 10:45:17 -08:00
Adrien Ferrand
baf43a2dbc Pin all build dependencies for the Windows installer (#7504)
This PR uses pipstrap to bootstrap the venv used to build Windows installers. This effectively pin all build dependencies, since pynsist is already installed through pip_install.py script.

* Use pipstrap

* Pin also NSIS version
2019-11-06 10:17:53 -08:00
Brad Warren
ebce0adb5a Merge branch 'master' into candidate-0.40.1 2019-11-05 19:49:27 -08:00
Erica Portnoy
61f77c35c0 Bump version to 1.0.0 2019-11-05 18:32:22 -08:00
Erica Portnoy
1b76faada6 Add contents to CHANGELOG.md for next version 2019-11-05 18:32:22 -08:00
Erica Portnoy
b79bcd0bf2 Release 0.40.1 2019-11-05 18:32:20 -08:00
Erica Portnoy
5f6ab47a7b Update changelog for 0.40.1 release 2019-11-05 18:24:52 -08:00
Brad Warren
d87c905c06 Add back Python 3.4 support (#7510) (#7511)
* Revert "Deprecation warnings for Python 3.4 (#7378)"

This reverts commit 6fcdfb0e50.

* Revert "Migrate certbot-auto users on CentOS 6 to Python 3.6 (#7268)"

This reverts commit e19b2e04c7.

* add changelog entry

* keep mona in authors

(cherry picked from commit 9b848b1d65)
2019-11-05 17:11:23 -08:00
Brad Warren
9b848b1d65 Add back Python 3.4 support (#7510)
* Revert "Deprecation warnings for Python 3.4 (#7378)"

This reverts commit 6fcdfb0e50.

* Revert "Migrate certbot-auto users on CentOS 6 to Python 3.6 (#7268)"

This reverts commit e19b2e04c7.

* add changelog entry

* keep mona in authors
2019-11-05 16:45:08 -08:00
Brad Warren
f555e4bf1f Merge pull request #7505 from certbot/candidate-0.40.0
Release 0.40.0
2019-11-05 13:55:40 -08:00
Erica Portnoy
0de2645a8f Bump version to 0.41.0 2019-11-05 12:53:16 -08:00
Erica Portnoy
fcecdfbcc5 Add contents to CHANGELOG.md for next version 2019-11-05 12:53:16 -08:00
Erica Portnoy
73cd5aa81c Release 0.40.0 2019-11-05 12:52:26 -08:00
Erica Portnoy
3d9d212040 Update changelog for 0.40.0 release 2019-11-05 12:35:33 -08:00
Brad Warren
78deca4f60 Don't use --agree-dev-preview in tests. (#7501) 2019-11-05 17:34:46 +01:00
Adrien Ferrand
3c24ff88cc Build Windows installers with pinned dependencies (#7498)
* Consume constraints file

* Independent pywin32 dependency definition in setup.py and construct.py
2019-11-04 14:20:42 -08:00
alexzorin
08d91b456b Use fresh authorizations in dry runs (#7442)
* acme: re-populate uri in deactivate_authorization

* Use fresh authorizations in dry runs

--dry-run now deactivates 'valid' authorizations if it encounters them
when creating a new order.

Resolves #5116.

* remove unused code

* typo in local-oldest-requirements

* better error handling

* certbot-ci: AUTHREUSE to 100 + unskip dry-run test

* improve test coverage for error cases

* restore newline to local-oldest-requirements.txt
2019-11-04 21:23:25 +01:00
Brandon Moore
1c05b9bd07 Dropped deprecated flags from commands (#7482)
This pull request addresses #7451 by removing the deprecated flags.

* Dropped deprecated flags from commands

* Updated changelog for dropped flags and deleted outdated tests

* removed init-script part of apache test
2019-11-04 09:50:57 -08:00
Brad Warren
fffa74edb2 Clarify when the changelog should be modified (#7491) 2019-11-01 16:40:31 -07:00
Brad Warren
8956de6bee Describe distributed Certbot components. (#7493) 2019-11-01 15:05:41 -07:00
Brad Warren
9bc4286a27 Deprecate more code related to TLS-SNI-01 (#7483)
I tried to finish up #7214 by removing the code in acme but we can't really do that until #7478 is resolved which we cannot do until we release 0.40.0.

Since we have to wait, this PR adds deprecation warnings for code that uses the TLS-SNI-01 code or was only used by the long deprecated TLS-SNI-01 code.

I'd like this PR to land before our next release.

* Deprecate more code related to TLS-SNI-01.

* Assert about warning message.
2019-11-01 15:00:22 -07:00
Brad Warren
3e848b8fce Remove changelog entry about unpackaged scripts. (#7490)
We don't package rebuild_dependencies.py so I don't think we need to mention changes to it in our changelog which is primarily read by users and packagers.
2019-11-01 13:59:17 -07:00
Joona Hoikkala
fb1aafb5d2 Use distro library for all OS version detection (#7467)
This pull request ensures that we use distro package in all the distribution version detection. It also replaces the custom systemd /etc/os-release parsing and adds a few version fingerprints to Apache override selection.

Fixes: #7405

* Revert "Try to use platform.linux_distribution() before distro equivalent (#7403)"

This reverts commit ca3077d034.

* Use distro for all os detection code

* Address review comments

* Add changelog entry

* Added tests

* Fix tests to return a consistent os name

* Do not crash on non-linux systems

* Minor fixes to distro compatibility checks

* Make the tests OS independent

* Update certbot/util.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Skip linux specific tests on other platforms

* Test fixes

* Better test state handling

* Lower the coverage target for Windows tests
2019-11-01 10:51:21 -07:00
Brad Warren
f8ff881d23 Don't use acme.test_util outside of acme. (#7484)
`certbot-compatibility-test` is using code in `acme` that I proposed making private and not trivially importable in https://github.com/certbot/certbot/issues/5775.

To fix it, I switched to using Certbot's test utilities which I proposed keeping public to help with writing tests for plugins. When doing this I had to change the name of the key because `rsa1024_key.pem` doesn't exist in Certbot.

I also deleted the keys in `certbot-compatibility-test`'s testdata because because they are unused.
2019-11-01 10:06:32 -07:00
Brad Warren
ef3f8888b5 Don't use dev version of 3.8. (#7485)
Now that Python 3.8 is out, we don't need to use the development version.
2019-11-01 10:06:10 -07:00
Mickaël Schoentgen
a45efcd40d Fix invalid escape sequence \. rebuild_dependencies.py (#7486)
Signed-off-by: Mickaël Schoentgen <contact@tiger-222.fr>
2019-11-01 17:27:18 +01:00
Brad Warren
63d673a3e0 Remove references to TLS-SNI-01 outside of ACME (#7479)
This is a big part of #7214. It removes all references to TLS-SNI-01 outside of acme (and pytest.ini). Those changes will come in a subsequent PR. I thought this one was getting big enough.

* Remove references to TLS-SNI-01 in Apache plugin

* Remove references to TLS-SNI-01 from certbot-nginx

* Remove references to TLS-SNI from Certbot.

* Remove TLS-SNI reference from docs

* add certbot changelog

* Clarify test behavior
2019-10-31 10:17:29 -07:00
Brad Warren
9796128fee Polish changelog (#7476)
I wanted to polish the changelog a bit. Changes made are:

* We don't ship our test farm tests so including info about them in our changelog seems unnecessary.
* I combined and expanded the info about the deprecation of Python 3.4.
2019-10-30 17:07:36 -07:00
Brad Warren
de6b56bec0 Deprecate certbot.plugins.common.TLSSNI01 (#7477)
While working on #7214, I noticed that certbot.plugins.common.TLSSNI01 wasn't printing a deprecation warning and it was still being used in our Apache plugin. This PR fixes that.
2019-10-30 15:19:38 -07:00
James Renken
6f711d9ae8 change random sleep to use fractional seconds (#7473)
If we use fractional instead of whole seconds for the random sleep before renewing, it will reduce bunching of requests to Let's Encrypt's API.
2019-10-30 12:06:30 -07:00
sydneyli
6fcdfb0e50 Deprecation warnings for Python 3.4 (#7378)
Fixes #7367

* Deprecation warnings for Python 3.4 users

* CHANGELOG.md and AUTHORS.md

* double equals typo
2019-10-30 10:57:46 -07:00
Adrien Ferrand
e19b2e04c7 Migrate certbot-auto users on CentOS 6 to Python 3.6 (#7268)
Fixes #7007

Python 3.4 is [EOL](https://www.python.org/dev/peps/pep-0429/), and only Python 3.x version available for CentOS 6 through EPEL is this version, and so is used by `certbot-auto`, the only official way to install Certbot on this platform.

This unpleasant situation becomes a little more uncomfortable, considering that the newest `pip` version (19.2) [just dropped Python 3.4 support](https://github.com/pypa/pip/issues/6685) and will refuse to start on this Python version. We can expect a lot of dependencies to follow this path now.

One direct result of this situation is that a fix to support correctly the ARM platforms requires to upgrade `pip` to 19.2 for `certbot-auto`. So this is not possible right now.

Then, let's upgrade Certbot instances on CentOS 6 to a supported version of Python 3.

This PR proposes a new bootstrap approach for CentOS 6 platform, `BootstrapRpmPython3Legacy`, that will install Python 3.6 from [SCL](https://www.softwarecollections.org) (the latest one available for now on CentOS 6). In term of Python 3 specific bootstrap methods, I take the occasion here to completely separate the bootstrap of CentOS 6 as a legacy system, from the RPM-based newest systems (like Fedora 29+) that are simply dropping support for Python 2.x. This is in prevision of future migration for all systems on Python 3.x, that is a different problematic than supporting old systems.

* Add logic

* Rebuilt letsencrypt-auto

* Fix logic

* Focus on specific packages

* Maintain PATH for further invocations of letsencrypt-auto after bootstrap.

* Various corrections

* Fix farm test for RHEL6

* Working centos6 letsencrypt-auto self tests

* Fix test_sdist for CentOS 6

* Corrections

* Work in progress

* Working configuration

* Fix typo

* Remove EPEL. Add a test.

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Improvements after review

* Improvements

* Add a comment

* Add a test

* Update a test

* Corrections

* Update function return

* Work in progress

* Correct behavior on oracle linux 6.

* Corrections

* Rebuild script

* Add letsencrypt-auto tests for oraclelinux6

* Update tox.ini

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/tests/oraclelinux6_tests.sh

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/tests/oraclelinux6_tests.sh

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Remove specific code for scientific linux

* Change some variables names

* Update letsencrypt-auto-source/tests/oraclelinux6_tests.sh

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Various corrections

* Fix tests

* Add a comment

* Update message

* Fix test message

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update scripts

* More focused assertion

* Add back a test

* Update script

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Check quiet mode

* Add changelog

* Update letsencrypt-auto-source/tests/oraclelinux6_tests.sh

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>
2019-10-30 10:39:45 -07:00
Adrien Ferrand
2dbe47f3a7 Create a release pipeline on Azure for Windows installer (#7441)
This PR creates a pipeline triggered on tag push matching v0.* (eg. v0.40.0).

Once triggered, this pipeline will build the windows installer, and run integration tests on it, like for the pipeline run nightly.

I also add a simple script to extract from CHANGELOG.md file to extract the relevant part to put it in the body of the GitHub release. I believe it makes things nicer.

* Create release pipeline

* Relax condition on tags

* Put beta keyword

* Update job name

* Fix release pipeline
2019-10-30 10:19:10 -07:00
Brad Warren
0f31d9b7ac Remove skip_unless cruft (#7410)
* Remove skip_unless cruft.

* remove unused import
2019-10-24 14:46:55 +02:00
Brad Warren
60673e8a81 Remove AppVeyor. (#7440) 2019-10-24 12:48:00 +02:00
Brad Warren
3132c32c26 Update pluggy pinning. (#7459) 2019-10-23 10:50:18 +02:00
Brad Warren
db46326e95 Run at 4:00AM UTC not 0:04AM UTC. (#7460)
Fixes [cron syntax](https://docs.microsoft.com/en-us/azure/devops/pipelines/build/triggers?view=azure-devops&tabs=yaml#supported-cron-syntax) to get the behavior I had in mind in https://github.com/certbot/certbot/pull/7377#discussion_r331295897.
2019-10-21 14:42:51 -07:00
Brad Warren
44cc8d7a3c Require newer versions of oauth2client (#7458)
Over the weekend, nightly tests on Windows failed for certbot-dns-google: https://dev.azure.com/certbot/web/build.aspx?pcguid=74ef9c03-9faf-405b-9d03-9acf8c43e8d6&builduri=vstfs%3a%2f%2f%2fBuild%2fBuild%2f72

The error occurred inside `oauth2client`'s locking code and the failure seems spurious as it did not reproduce this morning: https://dev.azure.com/certbot/certbot/_build/results?buildId=73

I could not find a relevant changelog entry in `oauth2client` saying they've fixed the problem, but the problematic code no longer exists in `oauth2client>=4.0`. This PR updates our minimum dependency required in an attempt to avoid spurious failures for us in the future. The only downside I am aware of is it'll make it harder for certbot-dns-google to be packaged in Debian Old Stable or Ubuntu 16.04, but I don't expect either of those things to happen anytime soon.

* bump oauth2client dep

* Update dev_constraints.txt.

* Add changelog entry for packagers.
2019-10-21 13:54:17 -07:00
Brad Warren
f8e097a061 Remove warning about rename. (#7453) 2019-10-19 08:09:08 +02:00
Brad Warren
37b3c22dee Run nightly on Azure even if no commits landed. (#7455) 2019-10-19 08:06:37 +02:00
Victor Shih
032178bea0 Clarify possible existence of /etc/letsencrypt/cli.ini (#7449) 2019-10-18 13:36:45 -07:00
alexzorin
118cb3c9b1 cli: allow --dry-run to be combined with --server (#7436)
The value of --server will now be respected, except when it is the
default value, in which case it will be changed to the staging server,
preserving Certbot's existing behavior.
2019-10-10 00:09:25 +02:00
Brad Warren
717afebcff Upload coverage for integration tests (#7433)
* Upload coverage for integration tests.

* Use in not containsValue.
2019-10-09 23:39:49 +02:00
Adrien Ferrand
ec3ec9068c Upgrade to pywin32>=225 and fix unit tests (#7429)
Fixes #7426
2019-10-08 16:17:08 -07:00
Brad Warren
f755cfef48 Add final newlines to files. (#7432)
More conventional and makes it nicer when doing things like running cat to quickly look at the file like I was doing when I noticed this.
2019-10-08 16:16:04 -07:00
Brad Warren
c1f4b86d34 Use shared variable group (#7431)
When setting up Azure Pipelines, I didn't like having to define codecov_token for each pipeline. This works around it by using a shared variable group.

You can see this working successfully at https://dev.azure.com/certbot/certbot/_build/results?buildId=3.

* Use certbot-common.

* update instructions
2019-10-08 16:12:02 -07:00
Adrien Ferrand
fcc398831b Create a new CI for Certbot on Windows using Azure Pipelines (#7377)
This PR defines pipelines that can be run on Azure Pipelines. Currently there are two:
* `.azure-pipelines/main.yml` is the main one, executed on PRs for master, and pushes to master,
* `.azure-pipelines/advanced.yml` add installer testing on top of the main pipeline, and is executed for `test-*` branches, release branches, and nightly run for master.

These two pipelines covers all existing stuff done by AppVeyor currently, and so AppVeyor can be decommissioned once Azure Pipelines is operational.

You can see working pipeline in my fork:
* a PR for `master` (so using main pipeline): https://github.com/adferrand/certbot/pull/65
* a PR for `test-something` (so using advanced pipeline): https://github.com/adferrand/certbot/pull/66
* uploaded coverage from Azure Pipelines: 499aa2cbf2/build

Once this PR is merged, we need to enable Azure Pipelines for Certbot. Instructions are written in `azure-pipelines/INSTALL.md`. This document also references all access rights required to Azure Pipelines onto GitHub to make the CI process work.

Future work for future PRs:
* create a CD pipeline for the releases that will push the installer to GitHub releases
* implement a solution to generate notification on IRC or Mattermost when a nightly build fails

* Define pipelines

* Update locations

* Update nightly

* Use x86

* Update nightly.yml for Azure Pipelines

* Run script

* Use script

* Update install

* Use local installation

* Register warnings

* Fix pywin32 loading

* Clean context

* Enable coverage publication

* Consume codecov token

* Document installation

* Update tool to upload coverage

* Prepare pipeline artifacts

* Update artifact ignore

* Protect against codecov failures

* Add a comment about codecov

* Add a comment on RW access asked by Azure

* Add instructions

* Rename pipeline file

* Update instructions

* Update .azure-pipelines/templates/tests-suite.yml

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update .azure-pipelines/INSTALL.md

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Modified scheduled pipeline

* Add comment

* Remove dynamic version-based installer name
2019-10-08 14:40:17 -07:00
Andrzej Górski
9da07590bd Remove --fast from the test farm tests (#7427) 2019-10-08 21:24:55 +02:00
Brad Warren
0cfedbc5f5 Add test farm tests for Debian 10 (#7421)
Fixes #7225.

I got the AMI ID from https://wiki.debian.org/Cloud/AmazonEC2Image/Buster.

You can see all test farm tests including test_tests.sh passing with these changes at https://travis-ci.com/certbot/certbot/builds/130318446.
2019-10-03 15:08:24 -07:00
Jacob Hoffman-Andrews
3608abb01a Remove unnecessary account ID match check. (#7416)
* Remove unnecessary account ID match check.

Right now the Account object calculates an ID using md5. This is
unnecessary and causes problems on FIPS systems that forbid md5. It's
just as good to pick a random series of bytes for the ID, since the ID
gets read out of renewal/foo.conf.

However, if we switched the algorithm right now, we could wind up
breaking forward compatibility / downgradeability, since older versions
would run into this check.

Removing this check now lays the ground to change the ID-calculation
algorithm in the future.

Related to #1948 and
https://github.com/certbot/certbot/pull/1013#issuecomment-149983479.

* Remove test.

* Remove unused import.
2019-10-02 14:44:25 -07:00
Brad Warren
4739a0616d Merge pull request #7415 from certbot/candidate-0.39.0
Candidate 0.39.0
2019-10-01 13:34:47 -07:00
Erica Portnoy
6e38ad9cce Bump version to 0.40.0 2019-10-01 13:04:10 -07:00
Erica Portnoy
4599aff07f Add contents to CHANGELOG.md for next version 2019-10-01 13:04:10 -07:00
Erica Portnoy
0b605333d9 Release 0.39.0 2019-10-01 13:04:08 -07:00
Erica Portnoy
9c18de993d Update changelog for 0.39.0 release 2019-10-01 12:48:40 -07:00
Brad Warren
e3dbd9ce4a Keep compatibility with IE11 in the Nginx plugin (#7414)
As discussed at https://github.com/mozilla/server-side-tls/issues/263, Mozilla's current intermediate recommendations drop support for some non-EOL'd versions of IE. [Their TLS recommendations were updated to suggest a couple possible workarounds for people who need this support](https://github.com/mozilla/server-side-tls/pull/264) and [April suggested that we make this change in Certbot](https://github.com/mozilla/server-side-tls/issues/263#issuecomment-537085728).

We know `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA` translates to `ECDHE-RSA-AES128-SHA` because [nginx uses the same cipher format as OpenSSL](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers) and the translation is shown in the table at https://github.com/mozilla/server-side-tls/blob/gh-pages/Cipher_Suites.mediawiki.

The risk of regressions making this change is low as we always had this ciphersuite enabled just a few releases ago: https://github.com/certbot/certbot/tree/v0.36.0/certbot-nginx/certbot_nginx

* Keep compatibility with IE11

* update changelog
2019-10-01 10:34:11 -07:00
Brad Warren
c2480b29f7 Add CentOS 8 support to certbot-auto. (#7406)
Fixes #7396.
2019-09-30 09:19:05 -07:00
Brad Warren
6ac8633363 Remove listing for broken icecast plugin. (#7408)
The repo description for the [3rd party Icecast plugin](https://github.com/e00E/lets-encrypt-icecast) says that the plugin isn't currently working and the repository hasn't been updated since 2017. Since it seems broken and unmaintained, let's remove it from the list of third party plugins.

I would happily add it again to the list of third party plugins if people fix and maintain it.
2019-09-27 12:47:12 -07:00
Brad Warren
8a4c2f505f Remove listing for broken heroku plugin (#7409)
The README for the [3rd party heroku plugin](https://github.com/gboudreau/certbot-heroku) says it has been deprecated. Because of this, let's remove it from the list of third party plugins.
2019-09-27 12:46:56 -07:00
Joona Hoikkala
ca3077d034 Try to use platform.linux_distribution() before distro equivalent (#7403)
Try to primarily fall back to using `platform.linux_distribution()` if `/etc/os-release` isn't available. Only use `distro.linux_distribution()` on Python >= 3.8.

* Try to use platform.linux_distribution() before distro equivalent

* Fix tests for py38

* Added changelog entry
2019-09-27 09:50:38 -07:00
Kenichi Maehashi
6c89aa5227 Fix to run with Apache on RHEL 6 (#7401)
This PR fixes a regression in #7337 (0.38.0) that certbot cannot run with Apache on RHEL 6.

In RHEL 6, `distro.linux_distribution()` returns `RedHatEnterpriseServer`.

In RHEL 6:

```py
>>> import distro
>>> distro.linux_distribution()
(u'RedHatEnterpriseServer', u'6.10', u'Santiago')

>>> import platform
>>> platform.linux_distribution()
('Red Hat Enterprise Linux Server', '6.10', 'Santiago')
```

In RHEL 7:

```py
>>> import distro
>>> distro.linux_distribution()
('Red Hat Enterprise Linux Server', '7.6', 'Maipo')

>>> import platform
>>> platform.linux_distribution()
('Red Hat Enterprise Linux Server', '7.6', 'Maipo')
```

* fix to run with Apache on RHEL 6

* fix docs
2019-09-26 13:25:48 -07:00
Brad Warren
8cb57566c0 List support for Python 3.8 (#7392)
Fixes #7368.

When updating the changelog, I replaced the line about running tests on Python 3.8 because I personally think that support for Python 3.8 is the most relevant information for our users/packagers about our changes in this area.

* List support for Python 3.8.

* Update changelog.
2019-09-24 11:38:38 -07:00
ohemorange
18e6c6c2a8 Don't send OCSP requests for expired certificates (#7387)
Fixes #7152.

* don't check ocsp if cert is expired when getting cert information

* don't check ocsp if the cert is expired in ocsp_revoked

* update tests

* update changelog

* move pytz import to the top of the test file
2019-09-23 17:20:11 -07:00
Adrien Ferrand
e402993c34 [Windows] Create a certbot renew scheduled task using the installer (#7371)
This PR implements the item "register a scheduled task for certificate renewal" from the list of requirements described in #7365.

This PR adds required instructions in the NSIS installer for Certbot to create a task, named "Certbot Renew Task" in the Windows Scheduler. This task is run twice a day, to execute the command certbot renew and keep the certificates up-to-date.

Uninstalling Certbot will also remove this scheduled task.

* Implementation

* Corrections

* Update template.nsi

* Improve scripts

* Add a random delay of 12 hours

* Synchronize template against default one in pynsist 2.4

* Clean config of scheduled task

* Install only in AllUsers mode

* Add comments

* Remove the logic of single user install
2019-09-23 12:29:18 -07:00
Brad Warren
754c34c120 Fix Windows sdist. (#7384) 2019-09-20 17:21:07 +02:00
Brad Warren
2883ca839e Use xenial globally (#7380)
As described at https://github.com/certbot/certbot/pull/7372#discussion_r323592366, Travis is transitioning people to Xenial, but it seems this transition still may not be complete as some of our jobs ran on Trusty with all references to `dist` removed as seen at https://travis-ci.com/certbot/certbot/builds/127960999.

This PR sets `dist: xenial` globally and overrides it as needed for the oldest tests.

* Set xenial globally.

* Use trusty in all oldest tests.
2019-09-17 15:24:53 -07:00
ohemorange
fb6aad28bd Get integration tests working on python 3.8 (#7372)
* Get integration tests working on python 3.8

* Run unit tests on py38

* Update coveragercs to use coverage 4.5+ format

* remove line added to tox.ini

* update changelog

* xenial is the new travis default; no need to specify in .travis.yml
2019-09-16 14:14:26 -04:00
Adrien Ferrand
ab76834100 [Windows|Linux] Forbid os.stat and os.fstat (#7325)
Fixes #7212

This PR forbid os.stat and os.fstat, and fix or provide alternatives to avoid its usage in certbot outside of certbot.compat.filesystem.

* Reimplement private key mode propagation

* Remove other os.stat

* Remove last call of os.stat in certbot package

* Forbid stat and fstat

* Implement mode comparison checks

* Add unit tests

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Handle case where multiple ace concerns a given SID in has_min_permissions

* Add a new test scenario

* Add a simple test for has_same_ownership

* Fix name function

* Add a comment explaining an ACE structure

* Move a test in its dedicated class

* Improve a message error

* Calculate has_min_permission result using effective permission rights to be more generic.

* Change an exception message

* Add comments, avoid to skip a test.

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>
2019-09-06 14:30:25 -07:00
Brad Warren
ada2f5c767 Simplify testing of RHEL 8. (#7323) 2019-09-06 08:59:24 +02:00
ohemorange
e4af1f3319 Disable TLS session tickets in Nginx (#7355)
* Find OpenSSL version

* Create and update various config files

* Update logic to use new version constraints

* SSL_OPTIONS_HASHES_NEW and SSL_OPTIONS_HASHES_MEDIUM were just being used for testing, and maintaining them is becoming untenable, so remove them.

* if we don't know the openssl version, we can't turn off session tickets

* add unit test for _get_openssl_version

* add unit tests

* placate lint

* Fix docs and tests and clean up code

* use python correctly

* update changelog

* Lint

* make comment a comment
2019-09-05 13:51:56 -07:00
ohemorange
ab0e382829 Merge pull request #7364 from certbot/candidate-0.38.0
Update files from 0.38.0 release
2019-09-04 18:23:50 -04:00
Adrien Ferrand
ed0b8e4af5 [Windows] Create an installer for Certbot (#7324)
This PR is the first step to create an official distribution channel of Certbot for Windows. It consists essentially in creating a proper Certbot Windows installer.

Usually distributing an application requires, in a way or another, to stabilize the application logic and its dependencies around a given version. On Windows, this usually takes the form of a freezed application, that vendors its dependencies into a single executable.

There are two well-known solutions to create an executable shipping a Python application on Windows: [py2exe](http://www.py2exe.org/) and [pyinstaller](https://www.pyinstaller.org/). However these solutions create self-executable `.EXE` files: you run the `.EXE` file that launches immediately the software.

This is not a end-user solution. Indeed when a Windows user wants to install a piece of software, he expects to find and download an installer. When run the installer would interface with Windows to setup configuration entries in the Registry, update the environment variable, add shortcuts in the Start Menu, and declare a uninstaller entry into the Uninstaller Manager. Quite similarly, this is what you would get from a `.deb` or `.rpm` package.

A solution that builds proper installers is [pynsis](https://pynsist.readthedocs.io/en/latest/). It is a Python project that constructs installers for Python software using [NSIS](https://sourceforge.net/projects/nsis/), the most known free Windows installer builder solution.

This PR uses pynsist to build a Windows installer. The Python script to launch the installer build is `.\windows-installer\construct.py`. Once finished, the installer is located in `.\windows-installer\build\nsis`.

This installer will do the following operations during the installation:
* copy in the install path a full python distribution used exclusively for Certbot
* copy all Python requirements gathered from the `setup.py` of relevant certbot projects
* copy `certbot` and `acme`
* pre-build python binary assets
* register the existence of the application correctly in Windows Registry
* prepare a procedure to uninstall Certbot
* and of course, expose `certbot` executable to the Windows command line, like on Linux, to be able to launch it as any CLI application from Batch or Powershell

This installer support updates: downloading a new version of it and running it on a Windows with existing installation of Certbot will replace it with the new version.

Future capabilities not included in this PR:
* auto-update of Certbot when a new release is available
* online documentation for Windows
* register a scheduled task for certificate renewal
* installer distribution (continuous deployment + distribution channels)
* method to check the downloaded installer is untampered

* Setup config

* Fix shortcut

* Various improvments

* Update windows-installer/construct.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Split into several method

* Change installer name

* Remove DNS plugins for now

* Add a comment about administrator privileges

* Update welcome

* Control python version

* Control bitness

* Update windows-installer/construct.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update windows-installer/construct.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update windows-installer/construct.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>
2019-09-03 16:30:13 -07:00
Brad Warren
8a570b18e9 Bump version to 0.39.0 2019-09-03 12:49:30 -07:00
Brad Warren
deb0168c09 Add contents to CHANGELOG.md for next version 2019-09-03 12:49:29 -07:00
Brad Warren
46a12d0127 Release 0.38.0 2019-09-03 12:49:28 -07:00
Brad Warren
6d4baec955 Update changelog for 0.38.0 release 2019-09-03 12:42:35 -07:00
Brad Warren
4eaa06d58e list py37 support (#7360)
These plugins also support Python 3.7. You can see tests passing at https://travis-ci.com/certbot/certbot/jobs/228820500.
2019-08-28 11:10:13 -07:00
ohemorange
0fe28a6459 Replace platform.linux_distribution with distro.linux_distribution (#7337)
Smallest possible fix for #7106 

* Replace platform.linux_dependencies with distro.linux_dependencies

* run build.py

* Add minimum version of 1.0.1

* Pin back requests package

* Update changelog
2019-08-27 18:31:35 -07:00
Adrien Ferrand
aaeb4582e2 Fix PYTHONPATH in integration tests (#7357)
This PR supersedes #7353.

It fixes the execution of nginx oldest tests when these tests are executed on top of the modifications made in #7337. This execution failure revealed the fact that in some cases, the wrong version of certbot logic was used during integration tests (namely the logic lying in the codebase of the branch built, instead of the logic from the version of certbot declared by certbot-nginx for instance).

I let you appreciate my inline comment for the explanation and the workaround.

Thanks a lot to @bmw who found this python/pytest madness.

You can see the oldest tests succeeding with the logic of #7337 + this PR here: https://travis-ci.com/certbot/certbot/builds/124816254

* Remove certbot root from PYTHONPATH during integration tests

* Add a biiiiig comment.
2019-08-27 16:25:31 -07:00
schoen
fdb0a14812 Merge pull request #7336 from certbot/update-debian-instructions
Update Debian instructions in docs
2019-08-23 13:39:55 -07:00
Adrien Ferrand
0324d1740e Ensure relpath is executed on paths in the same drive (#7335)
On Windows you can have several drives (`C:`, `D:`, ...), that is the roughly (really roughly) equivalent of mount points, since each drive is usually associated to a specific physical partition.

So you can have paths like `C:\one\path`, `D:\another\path`.

In parallel, `os.path.relpath(path, start='.')` calculates the relative path between the given `path` and a `start` path (current directory if not provided). In recent versions of Python, `os.path.relpath` will fail if `path` and `start` are not on the same drive, because a relative path between two paths like `C:\one\path`, `D:\another\path` is not possible.

In saw unit tests failing because of this in two locations. This occurs when the certbot codebase that is tested is on a given drive (like `D:`) while the default temporary directory used by `tempfile` is on another drive (most of the time located in `C:` drive).

This PR fixes that.
2019-08-23 12:53:30 -07:00
Brad Warren
ce325db4e4 address review comments 2019-08-23 12:43:05 -07:00
Brad Warren
74e6736c79 use latest RHEL 7 AMI (#7349) 2019-08-22 09:28:57 -07:00
ohemorange
2ed7608ed3 Merge pull request #7347 from certbot/candidate-0.37.2
Release 0.37.2
2019-08-21 16:21:23 -07:00
ohemorange
eb02acfc4b Merge branch 'master' into candidate-0.37.2 2019-08-21 16:03:51 -07:00
Erica Portnoy
4f19d516d6 Bump version to 0.38.0 2019-08-21 15:23:15 -07:00
Erica Portnoy
3dd918b024 Add contents to CHANGELOG.md for next version 2019-08-21 15:23:15 -07:00
Erica Portnoy
8320018978 Release 0.37.2 2019-08-21 15:23:14 -07:00
Erica Portnoy
c17f2ff6b0 Update changelog for 0.37.2 release 2019-08-21 14:48:40 -07:00
Brad Warren
46a2ef8ba1 Stop turning session tickets off in Nginx (#7344) (#7345)
Related to #7322.

* Stop turning session tickets off in Nginx

* update changelog

(cherry picked from commit 17c1d016c1)
2019-08-21 14:44:09 -07:00
ohemorange
17c1d016c1 Stop turning session tickets off in Nginx (#7344)
Related to #7322.

* Stop turning session tickets off in Nginx

* update changelog
2019-08-21 14:29:10 -07:00
Brad Warren
70ed791709 Update Debian instructions in docs. 2019-08-16 11:42:34 -07:00
Adrien Ferrand
d39f63feca Use travis_retry for farm tests (#7327)
* Use travis_retry in travis builds to retry the farm tests

* travis_retry is a bash function, so it can be called only from current bash

* Update .travis.yml

* Update .travis.yml
2019-08-16 14:55:45 +02:00
Adrien Ferrand
6882f006ac [Windows] Fix closing files descriptors during unit tests (#7326)
* Fix file descriptor cleanup during tests on Windows

* Fix lint

* Remove useless tearDown

* Clean pylint
2019-08-16 11:08:42 +02:00
Adrien Ferrand
9a047a6996 Clean travis config (#7328)
This PR removes some useless capabilities in .travis.yml that are associated to the jobs. This concerns mainly sudo and docker.
2019-08-15 16:41:51 -07:00
Matthias Bilger
a8bd839223 Added DNS plugin for ISPConfig to list (#7332) 2019-08-15 14:43:14 -07:00
tyborr
a1aef4c15c Fix Certbot's Apache plugin doesn't work on Scientific Linux (#7294)
This PR adds OVERRIDE_CLASS in certbot-apache/entrypoint.py for Scientific Linux. Fixes #7248.

* add OVERRIDE_CLASS for Scientific Linux os name

* add entry for Scientific Linux using "scientific" as key

* Update changelog
2019-08-12 12:59:29 -07:00
ohemorange
cb7598b007 Merge pull request #7320 from certbot/merge-cand-0.37.1
Merge cand 0.37.1
2019-08-08 18:01:27 -07:00
Brad Warren
55cf49cebe Merge pull request #7318 from certbot/candidate-0.37.1
Candidate 0.37.1
2019-08-08 17:56:57 -07:00
ohemorange
933f60a3c1 Merge branch 'master' into candidate-0.37.1 2019-08-08 17:48:22 -07:00
Erica Portnoy
44eb048098 Bump version to 0.38.0 2019-08-08 17:01:39 -07:00
Erica Portnoy
794ce57356 Add contents to CHANGELOG.md for next version 2019-08-08 17:01:38 -07:00
Erica Portnoy
48d9715bd5 Release 0.37.1 2019-08-08 17:01:32 -07:00
Erica Portnoy
c5e1be4fd7 Update changelog for 0.37.1 release 2019-08-08 16:39:43 -07:00
Brad Warren
e21401004b Revert disabling TLS session tickets in Apache (#7315) (#7316)
See https://community.letsencrypt.org/t/ssl-error-after-cert-renew/99430.

The first commit of this PR is a simple, clean revert of #7191. Subsequent commits add back pieces of that PR we want to keep.

I also reverted #7299 which landed in a separate PR, but needs to be reverted to keep including the TLS config files in the certbot-apache package when it is built.

I tested this on Ubuntu 18.04 by installing a cert to Apache using Certbot master and then running certbot renew with this branch. I watched the Apache plugin update the configuration file to remove SSLSessionTickets off.

* Revert "Disable TLS session tickets for Apache 2.4.11+ (#7191)"

This reverts commit 9174c631d9.

* Keep hashes with TLS session tickets disabled.

* dont delete changelog entries

* add changelog entry

* Revert "Clean the useless entries in MANIFEST.in (#7299)"

This reverts commit f4d17d9a6b.

(cherry picked from commit 120137eb8d)
2019-08-08 16:36:45 -07:00
Brad Warren
120137eb8d Revert disabling TLS session tickets in Apache (#7315)
See https://community.letsencrypt.org/t/ssl-error-after-cert-renew/99430.

The first commit of this PR is a simple, clean revert of #7191. Subsequent commits add back pieces of that PR we want to keep.

I also reverted #7299 which landed in a separate PR, but needs to be reverted to keep including the TLS config files in the certbot-apache package when it is built.

I tested this on Ubuntu 18.04 by installing a cert to Apache using Certbot master and then running certbot renew with this branch. I watched the Apache plugin update the configuration file to remove SSLSessionTickets off.

* Revert "Disable TLS session tickets for Apache 2.4.11+ (#7191)"

This reverts commit 9174c631d9.

* Keep hashes with TLS session tickets disabled.

* dont delete changelog entries

* add changelog entry

* Revert "Clean the useless entries in MANIFEST.in (#7299)"

This reverts commit f4d17d9a6b.
2019-08-08 16:23:37 -07:00
Matt Nordhoff
2911eda3bd Update link to the Server forum category (#7309)
Let's Encrypt closed it in favor of the Help category.

https://community.letsencrypt.org/t/closing-the-server-category/93016
2019-08-08 11:44:21 -07:00
ohemorange
f1ea37dd71 Merge pull request #7311 from certbot/candidate-0.37.0
Update from 0.37.0 release
2019-08-07 17:44:16 -07:00
Brad Warren
3d3cbc0d16 Don't run tox -e cover. (#7312) 2019-08-08 00:07:37 +02:00
Brad Warren
d978440cb5 Bump version to 0.38.0 2019-08-07 10:35:13 -07:00
Brad Warren
0c04ce3c32 Add contents to CHANGELOG.md for next version 2019-08-07 10:35:13 -07:00
Brad Warren
987ce2c6b2 Release 0.37.0 2019-08-07 10:35:11 -07:00
Brad Warren
dded9290b7 Update changelog for 0.37.0 release 2019-08-07 10:26:34 -07:00
Brad Warren
745ef6e869 Merge pull request #7302 from certbot/rhel8_fix-with-tests
This PR builds off of #7240 to fix #7241.

The code in certbot-auto is unchanged which I +1. Someone else should give it a 2nd review.

For the code in the tests, you can see all tests passing (including test_tests.sh) at  https://travis-ci.com/certbot/certbot/builds/122198270.

I created #7301 to track removing the temporary code in test_leauto_upgrades.sh as suggested at #7282 (comment).

One noteworthy thing here is I did not add the RHEL 8 AMI to the Apache tests due to #7273. This problem is not related to support in certbot-auto though, is an edge case, and I do not personally believe it should block this PR.
2019-08-06 17:02:57 -07:00
Brad Warren
e2844bd0ad Add RHEL8 to test farm targets
* Add RHEL 8 to targets

* Use latest certbot-auto to bootstrap.

* Workaround leauto failures.
2019-08-06 16:39:35 -07:00
Adrien Ferrand
b67fda8832 Fix integration tests on Windows (#7271)
* Fix account_tests

* Fix hook executable test

* Remove the temporary decorator @broken_on_windows

* Fix util_test

* No broken unit test on Windows anymore

* More elegant mock

* Fix context manager

* Fix lint

* Fix mypy

* Adapt coverage

* Corrections

* Fix lint

* Adapt coverage

* Update certbot/tests/compat/filesystem_test.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update util_test.py

* Fix pylint

* Forbid os.access

* Update os_test.py

* Update os.py

* Fix lint

* Update filesystem.py

* Update filesystem.py

* Update filesystem.py

* Update os.py

* Start fixing tests

* Platform independent hooks

* Fix probe fd close

* Add broken_on_windows for integration tests

* Fix a lot of tests

* Use a python hook script, to prepare cross-platform

* New approach to be compliant with Linux and Windows on hook scripts

* New tests fixed

* Test for permissions on Windows

* Permissions comparison for Windows

* No broken tests in certbot core anymore

* Change mode

* Specific config for appveyor

* Use forked pebble for now

* Various fixes

* Assert file permissions for world on private keys

* Clean code

* Fix several things

* Add integration target

* Optimize integration env

* Re-enable all AppVeyor envs

* Use again official pebble

* Update pebble_artifacts.py

* Set PYTEST_ADDOPTS silently

* Update appveyor.yml

* Pin pywin32 for tests, give a minimal requirement for certbot.

* Remove injection of nginx in PATH

* Clean debug code

* Various cleanup, ensure to remove workspace after tests

* Update tox target

* Improve assertions. Control the keyword echoed in hooks

* Fix for virtualenv on Python 3.7.4 for Windows

* Update certbot-ci/certbot_integration_tests/certbot_tests/assertions.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Add conditionally pywin in certbot-ci like in certbot
2019-08-07 00:02:16 +02:00
Michael Watters
d6e6d64848 Update certbot-auto script to work with RHEL 8
/usr/bin/python no longer exists in RHEL 8.  This patch updates
the certbot-auto script to use python3 on nodes running RHEL 8.

Also fixed a bug in the RPM_DIST_VERSION logic which would cause
letsencrypt-auto to fail on servers running CentOS/RHEL 6.
2019-08-06 09:16:14 -04:00
Adrien Ferrand
f4d17d9a6b Clean the useless entries in MANIFEST.in (#7299)
Since #7191, TLS configuration files for Apache have been moved to a dedicated folder tls_configs. Then the entries in MANIFEST.in removed by this PR do not correspond to an existing path, and so are not useful anymore.
2019-08-05 15:57:20 -07:00
Adrien Ferrand
8bcb04af4a Move Nginx TLS configuration files into a specific folder (#7300)
Following discussions in #7298.

This PR moves the three Nginx TLS configuration files into a specific folder, tls_configs, update the MANIFEST to include this folder and its content into the certbot-nginx package, and update tests accordingly.

* Move tls configuration files in a specific folder

* Move new file
2019-08-05 15:45:08 -07:00
ohemorange
14e10f40e5 Follow Mozilla recs for Nginx ssl_protocols, ssl_ciphers, and ssl_prefer_server_ciphers (#7274)
* Follow Mozilla recs for Nginx ssl_protocols, ssl_ciphers, and ssl_prefer_server_ciphers

* Add tests and fix if statement

* Update CHANGELOG.md

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Test that the hashes of all of the current configuration files are in ALL_SSL_OPTIONS_HASHES

* Remove conditioning on OpenSSL version, since Nginx behaves cleanly if its linked OpenSSL doesn't support TLS1.3
2019-08-02 12:25:40 -07:00
Adrien Ferrand
1c7105a940 Create a mock OCSP server for Pebble integration tests (#7281)
* Implement a logic, miss the private key of pebble

* Complete process

* Fix nginx cert path

* Check conditionnally docker

* Update gitignore, fix apacheconftest

* Full object

* Carriage return

* Work in progress

* Move to official v2.1.0 of pebble

* Fix name

* Update acme_server.py

* Link things together with new version of pebble

* Plug the logic to tests

* Update config

* Reinitiate config

* Add OCSP config to pebble

* Working.

* Simplify logic

* Clean code

* Use forked pebble for now

# Conflicts:
#	certbot-ci/certbot_integration_tests/utils/pebble_artifacts.py

* Move full logic of mock at the acme server config

* Continue work

* Finish fixing the date parsing

* Update module name

* Use again official pebble

* Activate mock OCSP server

* Clean code

* Update pebble_artifacts.py

* Remove OCSP stale test

* Add executable permissions

* Clean code

* Update setup.py

* Simplify code

* On-demand import of pebble_ocsp_server

* Revert "Remove OCSP stale test"

This reverts commit 2e4c985b427120cc15526bbcfd15806d02a6f3fc.

# Conflicts:
#	certbot-ci/certbot_integration_tests/utils/misc.py

* Fix for virtualenv on Python 3.7.4 for Windows

* Update acme_server.py
2019-08-02 11:46:12 -07:00
Adrien Ferrand
36b4c312c6 Upgrade virtualenv in dev/tests environments (#7287)
AppVeyor recently upgrade the Python 3.7.x installed in their VM to 3.7.4. However, virtualenv 16.6.1 is broken on that specific version of Python for Windows.

This PR upgrade virtualenv installed for a dev/test environment from 16.6.1 to 16.6.2 in order to fix this issue, and repair the CI jobs execute by AppVeyor on PRs.
2019-08-02 09:47:36 -07:00
Adrien Ferrand
56f609d4f5 Fix unit tests on Windows (#7270)
Fixes #6850

This PR makes the last corrections needed to run all unit tests on Windows:

add a function to check if a hook is executable in a cross-platform compatible way
handle correctly the PATH surgery for Windows during hook execution
handle correctly an account compatibility over both ACMEv1 and ACMEv2
remove (finally!) the @broken_on_windows decorator.

* Fix account_tests

* Fix hook executable test

* Remove the temporary decorator @broken_on_windows

* Fix util_test

* No broken unit test on Windows anymore

* More elegant mock

* Fix context manager

* Adapt coverage

* Corrections

* Adapt coverage

* Forbid os.access
2019-08-01 10:39:46 -07:00
Mikel Kew
2d3f3a042a Update dns-cloudflare docs regarding API Tokens (#7285)
A quick update to the docs to explicitly mention that the Cloudflare Global API Key must me used instead of an API Token.
2019-07-31 10:31:05 +02:00
Brad Warren
bfd4955bad Bump timeout waiting for ACME server to 4 minutes. (#7284)
* Bump timeout to 4 minutes.

* address review comments
2019-07-30 21:28:18 +02:00
Adrien Ferrand
9174c631d9 Disable TLS session tickets for Apache 2.4.11+ (#7191)
* Implement the logic

* Update tests

* Fix lint and changelog

* Update configurator.py

* Move the TLS configs in a dedicated folder. Fix the formalism of their naming and location.

* Improve existing test to check all TLS config have their hash registered in Certbot

* Corrections after review

* Improve a test

* Remove commented useless lines in TLS configs

* Add a nice warning. Because I am nice.

* Fix lint

* Add a test
2019-07-29 22:54:51 +03:00
Adrien Ferrand
81e0b92b43 Refer to ubuntu in install.rst (#6986)
Fixes #5758
2019-07-29 10:27:09 -07:00
Brad Warren
d3da19919f Remove duplicate, failing oldest tests. (#7272)
Nightly tests failed last night at https://travis-ci.com/certbot/certbot/builds/120816454.

The cause was the oldest the version of Ubuntu used in the tests suddenly changed from Trusty to Xenial. You can see Xenial being used in the failing test at  https://travis-ci.com/certbot/certbot/jobs/219873088#L9 and Trusty being used at the last passing test at https://travis-ci.com/certbot/certbot/jobs/218936290#L9. The change in the default doesn't seem to be documented (yet) at https://docs.travis-ci.com/user/reference/overview/.

I started to pin Trusty in these tests, however, I noticed that we are running these same unit tests at e6bf3fe7f8/.travis.yml (L58). These other tests are still succeeding because it appears that including `sudo: required` causes Travis to still default to Trusty.

Deleting these duplicated tests fixes our Travis failures and speeds things up ever so slightly.

* Remove duplicate, failing oldest tests.

* pin trusty
2019-07-26 13:37:16 -07:00
Adrien Ferrand
e6bf3fe7f8 [Windows] Security model for files permissions - STEP 3f (#7233)
* Correct file permissions on TempHandler

* Forbid os.chown and os.geteuid, as theses functions can be harmful to the security model on Windows.

* Implement copy_ownership

* Apply copy_ownership

* Correct webroot tests (and activate another broken test !)

* Correct lint and mypy

* Ensure to apply mode in makedirs

* Apply strict permissions on directories created with tempfile.mkdtemp(), like on Unix.

* Ensure streamHandler has 0600 on Windows

* Reactivate a test on windows

* Pin oldest requirements to current internal libraries (acme and certbot)

* Add dynamically pywin32 in dependencies: always except for certbot-oldest to avoid to break the relevant tests.

* Administrative privileges are always required.

* Correct security implementation (not the logic yet)

* First correction. Allow to manipulate finely file permissions during their generation

* Align to master + fix lint + resolve correctly symbolic links

* Add a test for windows about default paths

* Strenghthen the detection of Linux/Windows to check the standard files layout.

* Fix lint and mypy

* Reflect non usage of cache discovery from dns google plugin to its tests, solving Windows tests on the way

* Apply suggestions from code review

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Add more details in a comment

* Retrigger build.

* Add documentation.

* Fix a test

* Correct RW clear down

* Update util.py

* Remove unused code

* Fix code style

* Adapt certbot coverage threshold on Linux due to Windows specific LOC addition.

* Various optimizations around file owner and file mode

* Fix last error

* Fix copy_ownership_and_apply_mode

* Fix lint

* Correct mypy

* Extract out first part from windows-file-permissions

* Ignore new_compat in coverage for now

* Create test package for compat

* Add unit tests for security module.

* Add pywin32

* Adapt linux coverages to the windows-specific LOCs added

* Clean imports

* Correct import

* Trigger CI

* Reactivate a test

* Create the certbot.compat package. Move logic in certbot.compat.misc

* Clean comment

* Add doc

* Fix lint

* Correct mypy

* Add executable permissions

* Add the delegate certbot.compat.os module, add check coding style to enforce usage of certbot.compat.os instead of standard os

* Load certbot.compat.os instead of os

* Move existing compat test

* Update local oldest requirements

* Import sys

* Fix some mocks

* Update account_test.py

* Update os.py

* Update os.py

* Update local oldest requirements

* Implement the new linter_plugin

* Fix remaining linting errors

* Fix local oldest for nginx

* Remove custom check in favor of pylint plugin

* Remove check coding style

* Update linter_plugin.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Add several comments

* Update the setup.py

* Add documentation

* Update acme dependencies

* Update certbot/compat/os.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot/compat/os.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot/compat/os.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update docs/contributing.rst

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update linter_plugin.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update linter_plugin.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update docs/contributing.rst

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update docs/contributing.rst

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Corrections

* Handle os.path. Simplify checker.

* Add a comment to a reference implementation

* Update changelog

* Fix module registering

* Update docs/contributing.rst

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update docs/contributing.rst

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update docs/contributing.rst

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update config and changelog

* Correction

* Correct os

* Fix merge

* Disable pylint checks

* Normalize imports

* Simplify security

* Corrections

* Reorganize module

* Clean code

* Clean code

* Remove coverage

* No cover

* Implement security.chmod

* Disable a test for now

* Disable hard error for now

* Add a first test. Remove unused import

* Recalibrate coverage

* Modifications for misc

* Correct function call

* Add some types

* Remove newline

* Use os_rename

* Implement security.open

* Revert to windows-files-permissions approach

* Fix lint

* Implement security.mkdir and security.makedirs

* Fix lint

* Clean lint

* Clean lint

* Revert "Clean lint"

This reverts commit 83bf81960ac6bf3f76c286ca065a5ac850c6870b.

* Correct mock

* Conditionally add pywin32 on setuptools versions that support environment markers.

* Fix separator

* Fix separator

* Rename security into filesystem

* Change module security to filesystem

* Move rename into filesystem

* Rename security into filesystem

* Rename security into filesystem

* Rerun CI

* Fix import

* Fix pylint

* Implement copy_ownership_and_apply_mode

* Fix pylint

* Update certbot/compat/os.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Remove default values

* Rewrite a comment.

* Relaunch CI

* Pass as keyword arguments

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Make the private key permissions transfer platform specific

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Rename variable

* Fix comment0

* Add unit test for copy_ownership_and_apply_mode

* Adapt coverage

* Implement new methods.

* Remove the old method

* Reimplement make_or_verify_dir

* Finish migration

* Start to fix tests

* Fix ownership when creating a file with filesystem.open

* Fix security on TempHandler

* Fix validation path permissions

* Fix owner on mkdir

* Use a proper workdir for crypto tests

* Fix pylint

* Adapt coverage

* Update storage_test.py

* Update util_test.py

* Clean code

* Update certbot/compat/filesystem.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Add comment

* Update certbot/compat/filesystem.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Check permissions

* Change test mode

* Add unit test for filesystem.check_* functions

* Update filesystem_test.py

* Better logic for TempHandler

* Adapt coverage
2019-07-26 00:25:36 +02:00
alexzorin
40da709792 docs: s/certbot_tests/certbot_test/ (#7267) 2019-07-25 10:23:28 +02:00
Brad Warren
bf9c681c4f fix backwards logic (#7265) 2019-07-25 10:20:52 +02:00
alexzorin
391f301dd8 acme: Implement authz deactivation (#7254)
Resolves #4945. First PR in order to address #5116.

* acme: Implement authz deactivation

Resolves #4945

* update AUTHORS and CHANGELOG

* typos in mypy annotations

* formatting: missing newline

* improve test_deactivate_authorization

* improve deactivate_authorization

* test: s/STATUS_INVALID/STATUS_DEACTIVATED/

* simplify dict to keyword argument

* acme: add UpdateAuthorization

* acme: use UpdateAuthorization in deactivate_authz

and add mypy annotation

This allows deactivate_authorization to succeed for both ACME v1
and v2 servers.
2019-07-24 18:04:59 -07:00
Brad Warren
06a0dae67f Fix test_symlink_resolution on macOS. (#7263)
This fixes the test failures which can be seen at
https://travis-ci.com/certbot/certbot/builds/120123338.

The problem here is the path returned by tempfile.mkdtemp() contains a symlink.
For instance, one run of the function produced
'/var/folders/3b/zg8fdh5j71x92yyzc1tyllfw0000gp/T/tmp3k9ytfj1' which is a
symlink to
'/private/var/folders/3b/zg8fdh5j71x92yyzc1tyllfw0000gp/T/tmp3k9ytfj1'.

Removing this symlink before testing filesystem.realpath solves the problem.

You can see the macOS tests passing with this change at https://travis-ci.com/certbot/certbot/builds/120250667.
2019-07-23 11:01:29 -07:00
Adrien Ferrand
a35470292e Remove Dockerfiles (#7257) 2019-07-22 13:43:58 +03:00
Brad Warren
47f64c7280 Remove list of packaging efforts. (#7258)
I think this list maybe had value when distros were first starting to package Certbot, but now I don't think it does. What function does this list serve? The instruction generator at https://certbot.eff.org/instructions does a much better job telling users how to use these packages. On the packaging side, I think anyone capable of packaging Certbot at the various distros would be able to search their repositories to see if a Certbot package is available.

Since this list is hard to maintain as links semi-regularly break and keeping it up to date with all distros and all Certbot components is a fair bit of work, let's just remove it.

This PR was motivated by the Travis failures at https://travis-ci.com/certbot/website/builds/119588518 due to GNU Guix changing the layout of their site.
2019-07-19 10:44:17 -07:00
Brad Warren
f7c736da6f Update pexpect to fix Python 3.7 dev venvs. (#7259) 2019-07-18 15:44:01 -07:00
Adrien Ferrand
71ff47daad Implement a consistent realpath function in certbot.compat.filesystem (#7242)
Fixes #7115 

This PR creates a `realpath` method in `filesystem`, whose goal is to replace any call to `os.path.realpath` in Certbot. The reason is that `os.path.realpath` is broken on some versions of Python for Windows. See https://bugs.python.org/issue9949. The function created here works consistently across Linux and Windows.

As for the other forbidden functions in `os` module, our `certbot.compat.os` will raise an exception if its `path.realpath` function is invoked, and using the `os` module from Python is forbidden from the pylint check implemented in our CI.

Every call to `os.path.realpath` is corrected in `certbot` and `certbot-apache` modules.

* Forbid os.path.realpath

* Finish implementation

* Use filesystem.realpath

* Control symlink loops also for Linux

* Add a test for forbidden method

* Import a new object from os.path module

* Use same approach of wrapping than certbot.compat.os

* Correct errors

* Fix dependencies

* Make path module internal
2019-07-18 14:31:39 -07:00
J0WI
41a17f913e Use Buster as base image (#7251) 2019-07-17 13:05:02 -07:00
Po-Chuan Hsieh
750d6a9686 Unify license filename (LICENSE.txt) (#7239)
* Unify license filename (LICENSE.txt)
2019-07-12 22:53:43 +03:00
Adrien Ferrand
c4684f187a Add a test for the default directories on Windows (#7238)
There is a unit test to check that the default directories for Certbot are not diverging, in certbot.tests.cli_test:FlagDefaultTests:test_linux_directories.

But this test is not done on Windows.

This PR fixes that.
2019-07-11 17:49:52 -07:00
Lucid One
82ad736120 Fixes #7220 to allow config to be loaded from <(envsubst < template) (#7221)
* Fixes #7220 to allow config to be loaded from <(envsubst < template)
2019-07-11 14:40:24 -07:00
Brad Warren
ca893bd836 Merge pull request #7236 from certbot/candidate-0.36.0
Release 0.36.0
2019-07-11 14:00:49 -07:00
Erica Portnoy
d1934e36fe Bump version to 0.37.0 2019-07-11 12:31:53 -07:00
Erica Portnoy
15b1d8e5a7 Add contents to CHANGELOG.md for next version 2019-07-11 12:31:53 -07:00
Erica Portnoy
cbd0a37c7a Release 0.36.0 2019-07-11 12:31:51 -07:00
Erica Portnoy
13c44a0595 Update changelog for 0.36.0 release 2019-07-11 12:12:24 -07:00
Brad Warren
89f52ca9f9 Add mypy to contributing checklist. (#7224) 2019-07-10 18:14:12 -07:00
Brad Warren
d0a9695b09 Make PR template a checklist and suggest mypy. (#7223) 2019-07-10 18:14:01 -07:00
Brad Warren
add24d4861 Run tests on apache-parser-v2 (#7231)
We're planning on using the branch apache-parser-v2 allowing us to incrementally work on the new Apache parser and feel comfortable landing temporary test code that we don't really want in master.

The apache-parser-v2 branch is created and locked down, but neither Travis or AppVeyor are configured to run tests on it. See #7230. This PR fixes that problem.

This could probably just land in the apache-parser-v2 branch, but why unnecessarily deviate the branch from master? It doesn't hurt anything there. Once it lands, I'll get this added to the apache-parser-v2 branch too.

* Run tests on apache-parser-v2.

* add comment

* Don't run full test suite on apache-parser-v2.
2019-07-10 16:30:06 -07:00
Adrien Ferrand
74292a10f5 [Windows] Security model for files permissions - STEP 3e (#7182)
This PR implements the filesystem.copy_ownership_and_apply_mode method from #6497.

This method is used in two places in Certbot, replacing os.chown, to copy the owner and group owner from a file to another one, and apply to the latter the given POSIX mode.

* Implement copy_ownership_and_apply_mode

* Update certbot/compat/os.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Remove default values

* Rewrite a comment.

* Relaunch CI

* Pass as keyword arguments

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Make the private key permissions transfer platform specific

* Update certbot/compat/filesystem.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Rename variable

* Fix comment0

* Add unit test for copy_ownership_and_apply_mode

* Adapt coverage

* Execute unconditionally chmod with copy_ownership_and_apply_mode. Improve doc.
2019-07-10 16:26:30 -07:00
Brad Warren
74bf9ef46a Remove test symlink. (#7232) 2019-07-10 23:48:34 +02:00
Adrien Ferrand
2ac99fefe0 [Windows|Linux] Launch integration tests on Pebble without Docker (#7157)
This PR is a part of the actions necessary to make Certbot-CI work on Windows, in order to execute the integration tests on this platform.

Following #7156, this PR changes how the integration tests are setup against Pebble to not need Docker anymore.

As a reminder, one can check #7156 and letsencrypt/pebble#240 to see the rationale about why using Docker is a problem to run the integration tests on Windows.

Basically, this PR executes directly Pebble using its executable, since it is build using Go, and Go produces self-contained executable that can run without any installation on Linux and on Windows. During the integration tests setup, Certbot-CI will get the Pebble (and Challtestsrv) executables for the defined target version on the GitHub releases. The binaries are persisted on the filesystem, so it is not needed to download them again on the second integration tests execution. Nonetheless, we are talking about 20MB of executables.

Since the setup needs to hold a state, I also took this occasion to refactor the acme_server, in order to use on object oriented approach and improve the readability/maintainability.

Once this PR and #7156 are merged, Docker will not be needed anymore for the main integration tests usecase, that is to use Pebble.

* Complete process

* Fix nginx cert path

* Check conditionnally docker

* Update gitignore, fix apacheconftest

* Full object

* Carriage return

* Move to official v2.1.0 of pebble

* Fix name

* Update acme_server.py

* Relaunch CI

* Update certbot-ci/certbot_integration_tests/utils/acme_server.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/acme_server.py

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update docstring

* Update documentation

* Configure a stdout to ACMEServer

* Map all process through defined stdout

* Remove unused variable

* Handle using signals

* Use failsafe entering context

* Remove failsafe rmtree, that is not needed anymore
2019-07-10 14:29:57 -07:00
Brad Warren
43f58ca803 Document pytest packaging problems. (#7226)
This is probably unlikely to come up again, but this documents that people should run our tests using setuptools rather than calling something like pytest directly. See https://opensource.eff.org/eff-open-source/pl/wdrky4uyzjguppgch3r7t7qjmc for more info.
2019-07-09 15:07:33 -07:00
Brad Warren
17f2cabbbf Replace broken link with archive link. (#7222) 2019-07-08 10:27:25 -07:00
Adrien Ferrand
7d61e9ea56 [Windows] Security model for files permissions - STEP 3d (#6968)
* Implement security.mkdir and security.makedirs

* Fix lint

* Correct mock

* Rename security into filesystem

* Update apache and nginx plugins requirements

* Update certbot/plugins/webroot.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Reenable pylint here

* Move code

* Reimplement mkdir

* Control errors on eexist, remove superfluous chmod for makedirs

* Add proper skip for windows only tests

* Fix lint

* Fix mypy

* Clean code

* Adapt coverage threshold on Linux with addition of LOC specific to Windows

* Add forbiden functions to tests

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Simplify code

* Sync _get_current_user with part3c

* Use the simpliest implementation

* Remove exist_ok, simplify code.

* Simplify inline comment

* Update filesystem_test.py

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/plugins/webroot.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/plugins/webroot.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Add a test to check we set back os.mkdir correctly after filesystem.makedirs is called.

* Fix lint, adapt coverage
2019-07-03 16:20:43 -07:00
Brad Warren
20b595bc9e Simplify and deprecate viewing config changes (#7198)
* Remove apache and nginx from config_changes help

* Deprecate certbot_config changes.

* Document config_changes deprecation.

* Remove view_config_changes as IInstaller method.

* Remove view_config_changes from plugins.

* Add view_config_changes warnings.

* simplify test_config_changes_deprecation
2019-07-02 17:20:12 -07:00
Adrien Ferrand
88876b9901 [Windows] Security model for files permissions - STEP 3c (#6967)
* Implement security.open

* Clean lint

* Rename security into filesystem

* Update certbot/compat/filesystem.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/util.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/lock.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/lock.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Simplify and make more clear comment on os.open.

* Secure implementation preventing race conditions

* Revert "Secure implementation preventing race conditions"

This reverts commit dbb85492195122020ca0b4a685ddb4836fdc6d12.

* Simplify the logic on Windows.

* Implement os.open to prevent race conditions

* Add unit tests

* Handle os.O_CREAT and os.O_EXCL directly from the Windows APIs

* Improve comments

* Use CREATE_ALWAYS

* Adapt coverage threshold to new Windows specific LOCs.

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/compat/os.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/compat/filesystem.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Add some comments

* Fix pylint

* Improve docstring

* Added test cases

* Improve docstring

* Update certbot/lock.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Update certbot/lock.py

Co-Authored-By: ohemorange <ebportnoy@gmail.com>

* Fix lint

* Adapt coverage

* Adapt coverage
2019-07-02 16:21:24 -07:00
Brad Warren
448d159223 Install Python3 only dev tools with tools/venv3.py (#7215)
These packages can be useful and I found that they aren't being installed in our Python 3 development environment. Let's fix that.
2019-07-02 13:45:57 -07:00
Brad Warren
3e872627d8 Pin/upgrade virtualenv in our tests (#7211)
* Update virtualenv to the latest version.

* Use venv from pip and pin more packages.

* Pin codecov.

* update appveyor config

* Write the path separator backwards.

* s/pip_install.py install/pip_install.py

* Prefix tools\\pip_install.py with python exe.

* Upgrade py to fix AppVeyor failures.

* add back comment

* Update virtualenv with CERTBOT_NO_PIN.

* Pass -U to upgrade tox and deps.

* Upgrade virtualenv.
2019-07-02 20:02:00 +03:00
Brad Warren
76b7eb0628 Document certbot-auto's code freeze. (#7207)
Inspired by #7194, this PR adds a note to our documentation that we're not accepting most changes to certbot-auto.
2019-06-28 15:53:56 -07:00
dkp
4fc30f2ecb Replace Some Platform-Specific Line Separation (#7203)
os.linesep isn't supposed to be used when writing to files opened in
text mode, where '\n' is escaped to the platform-specific ASCII
sequence.  For example, on Windows, os.linesep is '\r\n' and in text
mode is escaped to ASCII sequence CR CR LF rather than just CR LF.
This is also true for the default logger and IDisplay notifications.

Replacing os.linesep with '\n' ensures the right sequence is escaped.

Resolves: 6899
2019-06-28 13:06:52 -07:00
sydneyli
1c75b6dacd Update Nginx conf file to match Mozilla's security recommendations (#7163)
Fixes #7089
2019-06-28 12:16:51 -07:00
Joona Hoikkala
c08a4dec2d Refactor augeas_configurator.py functionality to configurator.py and parser.py accordingly. (#7181)
This pull request moves the functionality within `AugeasConfigurator` that previously existed as a parent class of `ApacheConfigurator` to `ApacheConfigurator` and `ApacheParser` accordingly.

Most of the methods were moved as-is, and one (`recovery_routine()`) was completely removed. Few of the methods had to be split between the configurator and parser, good example of this is `save()`.

The Augeas object now lives completely within the `ApacheParser`.

* Remove augeasconfigurator

* Fix references

* Adjust tests accordingly

* Simplify test

* Address review comments

* Address review comments

* Move test_recovery_routine_reload
2019-06-28 08:39:13 -07:00
Brad Warren
4fc0ef0fbe certbot-plugin-gandi is not an installer. (#7201)
This [plugin](https://github.com/obynio/certbot-plugin-gandi) is an authenticator but not an installer. It's a DNS authenticator plugin.
2019-06-27 15:17:45 -07:00
Brad Warren
26a1eddd89 Remove plesk from the list of 3rd party plugins. (#7200)
Our link for the Plesk plugin goes to https://github.com/plesk/letsencrypt-plesk which refers you to https://ext.plesk.com/packages/f6847e61-33a7-4104-8dc9-d26a0183a8dd-letsencrypt and in their changelog for 2.0.0 it says "Replaced Python-based certbot with PHP-based client".
2019-06-27 15:17:31 -07:00
Brad Warren
1c6210ee00 Fix certbot config_changes (#7197)
* Remove for_logging parameter.

* Remove broken/unused --num parameter.

* update changelog
2019-06-26 17:46:51 -07:00
Brad Warren
a27f3ebd4f s/for for/for (#7196) 2019-06-26 17:24:04 -07:00
Brad Warren
a778b50403 Run le_auto_xenial on every PR. (#7195)
https://github.com/certbot/certbot/pull/7190/files removed our only le_auto_* tests on PRs. This PR fixes that by running le_auto_xenial on every PR which also includes running modification-check.py like we used to for Trusty.
2019-06-26 14:54:08 -07:00
Brad Warren
f2ab6a338c Remove files for old Docker image. (#7188) 2019-06-26 11:54:02 +02:00
Hunter
0d5bad6c8c dns-cloudflare: update URL for obtaining API keys (#7052)
Updated the ACCOUNT_URL in the Cloudflare-DNS plugin.
This uses the new "dash.cloudflare.com" scheme and future-proofs this URL for an upcoming change to Cloudflare API keys (this is not public yet, so no other changes related to this).
2019-06-25 17:53:31 -07:00
Brad Warren
dc0cfa21c9 Drop support for Ubuntu Trusty (#7190)
* Remove references and tests for Ubuntu Trusty.

* Mention dropped support in changelog.
2019-06-25 14:04:25 -07:00
Brad Warren
a37a4486cf Add Debian ARM AMI. (#7189)
Inspired by the number of ARM users we have (and because I want to rip out the only 32 bit test we have which without this PR would remove all tests we have on non-x86_64 architectures), this test adds an ARM image to the test farm tests. The image ID was taken from https://wiki.debian.org/Cloud/AmazonEC2Image/Stretch, you can see tests passing at https://travis-ci.com/certbot/certbot/builds/116857897, and I ran test_tests.sh locally and it passed.
2019-06-25 14:03:45 -07:00
Brad Warren
776e939a4c Drop support for quay.io. (#7187)
In this spirt of cleaning up some low hanging cruft, this fixes #4343.

There are no (recent) release tags on quay.io and the builds are just following master. See https://quay.io/repository/letsencrypt/letsencrypt?tab=tags.

Once this lands, I can disable the automated builds on quay.io and we can delete Dockerfile-old and tools/docker-warning.sh.
2019-06-25 11:05:28 -07:00
Brad Warren
69cf64079c Mention dropped support in changelog. 2019-06-25 10:18:39 -07:00
Brad Warren
9962cf0b8e Upgrade compatibility tests to stretch. (#7185)
Inspired by #7180, there's no reason for these tests to be running on old stable. This upgrades them to the latest stable version of Debian.

You can see tests passing with these changes at https://travis-ci.com/certbot/certbot/builds/116844923.
2019-06-25 10:13:57 -07:00
Brad Warren
4c95b687ae Remove references and tests for Ubuntu Trusty. 2019-06-25 10:10:14 -07:00
Brad Warren
a3bbdd52e7 Improve issue closing behavior. (#7178) 2019-06-24 16:39:45 -07:00
Siilike
2e3c1d7c77 Add reference to the Standalone DNS Authenticator (#7137)
Updated documentation to add a reference to the Standalone DNS Authenticator, https://github.com/siilike/certbot-dns-standalone
2019-06-24 12:47:50 -07:00
Adrien Ferrand
249af5c4cd Fix integration tests with Pebble v2.1.0 + (#7175)
Since Pebble v2.1.0, new controls have been added on ACME specs compliance on Pebble with strict mode enabled. These controls are described here: letsencrypt/pebble@3a2ce1c

Currently Certbot is not compliant enough to pass these new controls. One part of the work to do is described here: #7171

As a consequence, our CI is currently broken, both on PR builds and nightly builds.

This PR disables the strict mode during integration tests, fixing temporarily our CI. This will give us some time to fix theses deviations, and add back the strict mode in a future PR once it is merged.

* Remove -strict mode on Pebble for now.

* Refer to relevant Certbot PR

* Clean code
2019-06-24 12:03:24 -07:00
Adrien Ferrand
9a60f6df78 Fix codecov quality gate since flags have been removed (#7173)
Because some users were complaining about staled workflow when flags (https://docs.codecov.io/docs/flags) are enabled, Codecov decided to remove them when calculating the coverage on branches until they improved this functionality.

See: https://docs.codecov.io/docs/flags#section-flags-in-the-codecov-ui

The flags are still taken into account on PR builds, but not on based branch.

This is a problem for us, because we use the flags to compare specifically the coverage of a PR against its base branch for Windows on one side, and Linux on the other side. Without flags taken into account on the base branch, the CI fails because the coverage on Windows is too low.

As a temporary fix until the situation is clarified on Codecov side, this PR replaces the validation condition, that was a comparison against the base branch, to a fixed coverage registered in the local .codecov.yml file in Certbot repository.

This way, the coverage on PR builds, that takes into account the flags, is validated against an appropriate value.

This is a temporary solution, that will require an explicit update of .codecov.yml in the mean time if the coverage significantly increases, or decreases on some developments. But until the situation is fixed, this will allow to have a functional quality gate.
2019-06-21 12:00:03 -07:00
Adrien Ferrand
e9bcaaa576 [Windows] Security model for files permissions - STEP 3a (#6964)
This PR implements the filesystem.chmod method from #6497.

* Implement filesystem.chmod

* Conditionally add pywin32 on setuptools versions that support environment markers.

* Update apache plugin requirements

* Use a try/except import approach similar to lock

* Add comments about well-known SIDs

* Add main command

* Call filesystem.chmod in tests, remove one test

* Add test for os module

* Update environment marker

* Ensure we are not building wheels using an old version of setuptools

* Added a link to list of NTFS rights

* Simplify sid comparison

* Enable coverage

* Sometimes, double-quote is the solution

* Add entrypoint

* Add unit tests to filesystem

* Resolve recursively the link, add doc

* Move imports to the top of the file

* Remove string conversion of the ACL, fix setup

* Ensure admins have all permissions

* Simplify dacl comparison

* Conditionally raise for windows temporary workaround

* Add a test to check filesystem.chown is protected against symlink loops
2019-06-20 10:52:43 -07:00
Brad Warren
5078b58de9 Upgrade to the latest macOS image (#7167)
This fixes the test failures we saw last night at https://travis-ci.com/certbot/certbot/builds/116073070.

The problem is that the Homebrew installation included in the Travis image is outdated and when it tries to install packages, it fails. You can see this at https://travis-ci.com/certbot/certbot/jobs/209185570#L83. There is a thread in Travis' community froum about this at https://travis-ci.community/t/xcode-8-3-homebrew-outdated-error/3798.

To fix this, we could either upgrade Hombrew which can be a slow process according to both Travis and the original poster of the issue or we could upgrade to a newer version of macOS. I chose the latter to avoid the speed problems and picked the latest version available.

You can see tests passing with these changes at https://travis-ci.com/certbot/certbot/builds/116186095.
2019-06-19 14:09:30 -07:00
schoen
03cf5d15a6 Merge pull request #6894 from suqld/fix-google-dns-private-zones
Detect private DNS zones in Google and skip them
2019-06-19 13:21:22 -07:00
David Drobner
8efe3fb19a RFC8555 Messages (#7131)
Add new error types and descriptions from RFC 8555 to acme (#7116)
2019-06-18 17:29:53 -07:00
Brad Warren
9863c2d18e Update Ubuntu 18.04 AMI to fix blocking on input (#7166) 2019-06-18 12:07:45 -07:00
timwsuqld
6172821d90 Merge branch 'master' into fix-google-dns-private-zones 2019-06-18 14:04:21 +10:00
Brad Warren
dde16df778 Fixes #3400. (#7162)
The person who wrote this code no longer works on Certbot and regardless of
what the intended behavior was, let's document the actual behavior.
2019-06-17 15:56:06 -07:00
Adrien Ferrand
1df778859b [Windows|Linux] Use builtin Python proxy capabilities for Certbot-CI (#7156)
This PR is a part of the actions necessary to make Certbot-CI work on Windows, in order to execute the integration tests on this platform.

I initially used the fully-fledged HTTP proxy [Traefik](https://docs.traefik.io/) to distribute HTTP challenges among several pytest nodes, and so parallelize the integration tests. Traefik for this purpose is overkill. We just want to redirect the ACME server to a pytest node depending on the `Host` header, and we use here a production-grade HTTP proxy for that.

However it was not a problem on Linux, as soon as you can have Docker, because this instance is deployed through it.

But this becomes a problem for Windows, where Docker is not available everywhere, very compelling on its setup, and limited by the implemented network drivers. See my comments here https://github.com/letsencrypt/pebble/pull/240 for more details.

Hopefully Python ships with everything needed to implement a simple HTTP proxy, with strictly what we need for the parallelization of integration tests.

This PR implements this kind of HTTP proxy, and remove the coupling to Traefik.

This PR has been tested successfully with integration tests on Pebble under Linux for Python 2.x and Python 3.x, and the proxy alone has been also tested successfully on Windows (no integration tests can be run for now on this platform).

* Create a python proxy

* Refactor proxy config

* Working logic

* Resolve from the path

* Give proxy process to the ACMEServer context manager
2019-06-14 16:28:14 -07:00
Brad Warren
20ca47dec6 Bump stale threshold to 1 year. (#7149)
While I expect stale bot will close out 150 - 250 issues, that'll still leave us with 400+ open issues. My concern is that with a threshold of 6 months, most of these 400 issues will be in the same state 6 months from now and stale bot will annoy people by asking them if their issue is still valid too frequently.

Doubling the stale threshold to 1 year should mitigate this problem a bit I think.
2019-06-14 15:51:15 -07:00
sydneyli
6c53f5d8ed Turn off session tickets for versions of Nginx that support it (#7092)
* Turn off session tickets for versions of Nginx that support it

In line with Mozilla's security recommendations.

* Changelog.

* Set version before installing config files

* lint: remove unused import

* windows testfix

* another windows testfix?

* Testing path of updating src file with old nginx

* Fix windows, and make config update tests fail if update doesn't happen
2019-06-14 13:44:50 -07:00
Brad Warren
add90cef32 Tell people they can add their name to AUTHORS.md. (#7155) 2019-06-14 00:38:39 +02:00
Adrien Ferrand
1b54c74621 Remove the remaining integration tests bash scripts (#7153)
Since #7073 for Certbot and letsencrypt/boulder@3918714 for Boulder have landed, the bash scripts that remained after certbot-ci are not useful anymore outside of Certbot.

Only remaining place is the apacheconftest-with-pebble tox target, which leverages pebble-fetch.py script to expose a running ACME server to the apache-conf-test script.

This PR refactor apacheconftest-with-pebble to use certbot-ci instead. Finally, this PR remove the remaining integration tests bash scripts, that are _common.sh, boulder-fetch.py and pebble-fetch.py.

* Disconnect common and boulder-fetch

* Prepare reconnection of apacheconftest to new pebble deployment logic

* Finish the configuration for apacheconftest

* Add executable flag to python script

* Fix shebang

* Delete pebble-fetch.sh
2019-06-13 14:09:09 -07:00
Adrien Ferrand
e60651057e Add a branch in acme_server to properly clean the boulder workspace (#7154)
Currently integration tests against Boulder fail during nightly tests. See https://travis-ci.com/certbot/certbot/builds/115373954.

This is due to a failure to cleanup the workspace associated to the Boulder docker started during the integration tests. Indeed this docker compile several artifacts whose owner is root, and permissions are 0744. These files are persisted in the workspace folder attached to the Docker.

Since tox is run as a non-root user (but this user still have access to the Docker daemon), everything works fine until the end of the test suite, when all resources are cleaned up. At this point, pytest fires a PermissionError when failing to delete these artifacts, return with a non-zero exit code, and so fail the build.

Since this situation could happen outside of the CI, I made appropriate corrections to allow the integration tests to be run as a non-root user, instead of changing Travis to execute tests as root user.

The correction is to add a step to the cleanup process: the deletion of these artifacts through an ad-hoc docker instance.
2019-06-13 13:27:06 -07:00
Adrien Ferrand
e394889864 Add executable scripts to start certbot and acme server in certbot-ci (#7073)
During review of #6989, we saw that some of our test bash scripts were still used in the Boulder project in particular. It is about `tests/integration/_common.sh` in particular, to expose the `certbot_test` bash function,  that is an appropriate way to execute a local version of certbot in test mode: define a custom server, remove several checks, full log and so on.

This PR is an attempt to assert this goal: exposing a new `certbot_test` executable for test purpose. More generally, this PR is about giving well suited scripts to quickly make manual tests against certbot without launching the full automated pytest suite.

The idea here is to leverage the existing logic in certbot-ci, and expose it as executable scripts. This is done thanks to the `console_scripts` entry of setuptools entrypoint feature, that install scripts in the `PATH`, when `pip install` is invoked, that delegate to specific functions in the installed packages.

Two scripts are defined this way:
* `certbot_test`: it executes certbot in test mode in a very similar way than the original `certbot_test` in `_common.sh`, by delegating to `certbot_integration_tests.utils.certbot_call:main`. By default this execution will target a pebble directory url started locally. The url, and also http-01/tls-alpn-01 challenge ports can be configured using ad-hoc environment variables. All arguments passed to `certbot_test` are transferred to the underlying certbot command.
* `acme_server`: it set up a fully running instance of an ACME server, ready for tests (in particular, all FQDN resolves to localhost in order to target a locally running `certbot_test` command) by delegating to `certbot_integration_tests.utils.acme_server:main`. The choice of the ACME server is given by the first parameter passed to `acme_server`, it can be `pebble`, `boulder-v1` or `boulder-v2`. The command keeps running on foreground, displaying the logs of the ACME server on stdout/stderr. The server is shut down and resources cleaned upon entering CTRL+C.

This two commands can be run also through the underlying python modules, that are executable.

Finally, a typical workflow on certbot side to run manual tests would be:
```
cd certbot
tools/venv.py
source venv/bin/activate
acme_server pebble &
certbot_test certonly --standalone -d test.example.com
```

On boulder side it could be:
```
# Follow certbot dev environment setup instructions, then ...
cd boulder
docker-compose run --use-aliases -e FAKE_DNS=172.17.0.1 --service-ports boulder ./start.py
SERVER=http://localhost:4001/directory certbot_test certonly --standalone -d test.example.com
```

* Configure certbot-ci to expose a certbot_test console script calling certbot in test mode against a local pebble instance

* Add a command to start pebble/boulder

* Use explicit start

* Add execution permission to acme_server

* Add a docstring to certbot_test function

* Change executable name

* Increase sleep to 3600s

* Implement a context manager to handle the acme server

* Add certbot_test workspace in .gitignore

* Add documentation

* Remove one function in context, split logic of certbot_test towards capturing non capturing

* Use an explicit an properly configured ACMEServer as handler.

* Add doc. Put constants.
2019-06-12 17:19:23 -07:00
Adrien Ferrand
d75908c645 [Windows] Security model for files permissions - STEP 3b (#6965)
* Modifications for misc

* Add some types

* Use os_rename

* Move rename into filesystem

* Use our os package

* Rename filesystem.rename to filesystem.replace

* Disable globally function redefined lint in os module
2019-06-11 17:08:48 -07:00
Adrien Ferrand
72e5d89e95 Move nginx_compat to nightly (#7001)
With the various optimizations already done and upcoming (certbot-ci), the time execution of integration tests have significantly decreased, allowing potentially a complete execution of a Travis PR job to be done within 5min30s.

However, one job is significantly longer that the other ones after this migration: this is nginx_compat, that takes more that 11min to finish. I tried to split the nginx_compat in terms of tested configuration and of tests to execute (auth, install, enhance). Both are not satisfactory:

splitting by configuration may work, but add a significant complexity in the tests
splitting by tests type is supported almost out-of-the-box, but fails to make two fast tests (see https://travis-ci.org/adferrand/certbot/builds/525892885?utm_source=github_status&utm_medium=notification for instance)
Since these tests are designed to check corner cases on the nginx parser, this is mostly useless to execute them on each PR, as the nginx parser is rarely updated.

After some discussion with @bmw, I think that we can just move the nginx_compat from the PR tests to the nightly tests. This PR does that.
2019-06-11 14:54:36 -07:00
Brad Warren
0c5f526f8b Remove the Postfix plugin (#7097)
* Remove the postfix plugin.

* Remove references to postfix plugin in code.

* Remove reference to postfix plugin in docs.
2019-06-11 23:41:25 +02:00
Brad Warren
5385375571 Remove list of modified packages from changelog. (#7146) 2019-06-11 14:02:54 -07:00
Brad Warren
7b4201fbdb Merge pull request #7147 from certbot/candidate-0.35.1
Release 0.35.1
2019-06-11 12:39:09 -07:00
Erica Portnoy
8106f74dc0 Merge branch 'master' into candidate-0.35.1 2019-06-11 12:21:17 -07:00
Erica Portnoy
3bceae4a89 Bump version to 0.36.0 2019-06-10 15:25:16 -07:00
Erica Portnoy
f18143b117 Add contents to CHANGELOG.md for next version 2019-06-10 15:25:15 -07:00
Erica Portnoy
0cc56677e2 Release 0.35.1 2019-06-10 15:25:09 -07:00
Erica Portnoy
6334d065cf Update changelog for 0.35.1 release 2019-06-10 15:02:09 -07:00
Brad Warren
c3edc25fb7 Fix dns rfc2136 (#7142) (#7143)
* Revert "Add an option to dns_rfc2136 plugin to specify an authorative base domain. (#7029)"

This reverts commit 5ab6a597b0.

* Update changelog.

(cherry picked from commit 23b52ca1c8)
2019-06-10 14:12:59 -07:00
Brad Warren
23b52ca1c8 Fix dns rfc2136 (#7142)
* Revert "Add an option to dns_rfc2136 plugin to specify an authorative base domain. (#7029)"

This reverts commit 5ab6a597b0.

* Update changelog.
2019-06-10 13:56:57 -07:00
Brad Warren
02cf051e45 List Certbot package given #7127. (#7132) 2019-06-07 14:35:08 -07:00
Brad Warren
4d034122c6 Ask for updates, the issue isn't stale. (#7133)
This PR attempts to improve the behavior our "stale" bot by asking for updates instead of telling people that their issue is stale.
2019-06-07 14:34:40 -07:00
Brad Warren
391f742df7 List Certbot package given #7127. 2019-06-07 13:56:38 -07:00
Rob Stradling
5c663d4d97 Update the 'manage your account' help to be more generic. (#7127)
Resolves #7121.

* Update the 'manage your account' help to be more generic.

* Add changelog entry about #7127.
2019-06-07 13:03:35 -07:00
Brad Warren
89d907b182 Improve Apache error message when run with insufficient privileges (#7129)
* fixes #6369

* Add changelog entry.

* Improve error message again.
2019-06-07 19:57:21 +02:00
Brad Warren
d0f1a3e205 Merge pull request #7124 from certbot/candidate-0.35.0
Candidate 0.35.0
2019-06-05 14:51:17 -07:00
Erica Portnoy
f3b73c4d2a Bump version to 0.36.0 2019-06-05 14:00:54 -07:00
Erica Portnoy
f25a9b2004 Add contents to CHANGELOG.md for next version 2019-06-05 14:00:54 -07:00
Erica Portnoy
3568070c73 Release 0.35.0 2019-06-05 14:00:46 -07:00
Erica Portnoy
8e92577cb0 Update changelog for 0.35.0 release 2019-06-05 13:39:05 -07:00
Brad Warren
459ba89aef Add changelog entry about #7054. (#7122)
* Add changelog entry about #7054.

* Fix typo noticed by cpu

Co-Authored-By: Daniel McCarney <daniel@binaryparadox.net>
2019-06-04 14:17:49 -07:00
Adrien Ferrand
bfd1ce97ef Add Adrien Ferrand to the authors list (#7119) 2019-06-04 11:37:54 -07:00
Thue
419ad7df1e Fix typo cerbot->certbot. (#7118) 2019-06-04 14:46:40 +02:00
Adrien Ferrand
889aeb31df Validate OCSP responses in case an explicit responder is designated (#7054)
* Validate OCSP response for responders that are not the certificate's issuer.

* Improve OCSP tests using a issuer/responder pair for OCSP responses

* Clean code

* Update ocsp_test.py

* Add various comments

* Add several cases of ocsp responder. More factories for the resilience tests.

* Update ocsp_test.py
2019-06-03 22:55:26 +03:00
Brad Warren
09b7d2f461 Configure the stale bot (#7108)
* Configure the stale bot.

* Add top level comment.

* except assignees

* Give warning about closing issues.
2019-06-03 10:25:23 -07:00
Brad Warren
18797dca79 Remove scripts that are never run. (#7111)
* Remove scripts that are never run.

* Update example in multitester.py docstring.
2019-06-03 10:20:20 +03:00
Brad Warren
31e81e7ae0 Add explanation of the purpose of test_tests.sh. (#7112)
This is one of the two action items from the conversation at https://opensource.eff.org/eff-open-source/pl/rno49hd6q7ba7dr18ph11njc6o.

Just to make sure I didn't make a typo, I ran this script with these changes and the tests still pass.
2019-05-31 18:09:17 -07:00
Brad Warren
4b06eeae64 Update Fedora AMI (#7102)
Fixes #6955.

This updates the Fedora version used in our test farm tests to Fedora 30. The AMI ID comes from https://alt.fedoraproject.org/cloud/ where it is listed as their standard HVM AMI for the region we use us-east-1 (US East (N. Virginia)).

Unfortunately, there were a lot of small changes required for this. The big reason for this is on Fedora, there isn't a Python 2 executable installed. In fact, there's not even an executable named python. It's just python3. Rather than installing another Python in each test, I wrote a script that the test scripts can share to figure out the different paths and names that should be used in their script. (This isn't used in test_sdists.sh because the logic is a little different.)

Other changes here worth flagging are:

I changed the name of the variable RUN_PYTHON3_TESTS in test_leauto_upgrades.sh to RUN_RHEL6_TESTS. The tests that are run when this variable is set test the upgrade from Python 2 to Python 3 on RHEL 6. I think this new name is much better now that we also have Fedora running Python 3.
I made tools/simple_http_server.py work on Python 3.
You can see tests passing with these changes at https://travis-ci.com/certbot/certbot/builds/113821476. I also ran test_tests.sh and they passed.

* Update to Fedora 30 in test farm tests.

Fedora 28 is likely to reach its EOL soon.

* Add set_python_envvars.sh.

* Fix test_apache2.sh on python3 only distros.

* Fix test_leauto_upgrades.sh on python3 systems.

* Fix certonly_standalone tests with python3 only

* Fix test_sdists.sh on python3 only distros.

* Make simple_http_server.py work on Python 3.

* add comments
2019-05-31 18:08:52 -07:00
Felix Lechner
641aba68b1 Ignore editor backups when running hooks. (#7109)
* Ignore editor backups when running hooks.

When processing hooks, certbot also runs editor backups even though
such files are outdated, clearly warranted correction and may quite
possibly be defective.

That behavior could lead to unexpected breakage, and perhaps even pose
security risks---for example, if a previous script was careless with
file permissions. As an aggravating factor, the backup runs after the
corrected version and could unintentionally override a fix the user
thought was properly implemented.

This commit causes editor backup files ending in tilde (~) to be
excluded when running hooks.

Additional information can be found here:

https://github.com/certbot/certbot/issues/7107
https://community.letsencrypt.org/t/editor-backup-files-executed-as-renewal-hooks/94750

* Add unit test for hook scripts with filenames ending in tilde.

* Provide changelog entry for not running hook scripts ending in tilde.

* Add Felix Lechner to the list of contributors.
2019-05-30 15:02:15 -07:00
Adrien Ferrand
926c8c198c Remove dependency on acme in certbot-ci (#7055)
Following discussion in #6947 (comment), I have second thoughts about relying on acme in certbot-ci.

Indeed, I think it is a good design to not rely in tests on the code you are testing. Obviously in unit tests it is very difficult, since most of the time the unit that is tested needs input generated by other part of the code. However it is not really a problem in a unit test, as its purpose is to make assertions about a specific portion of the code, not the others parts.

In the scope of integration tests, the software tested is treated as a black box. In this case, having some parts of the test logic that use in fact part of the code in the black box, increase the risk that some assertions compared two results coming from the same flawed logic from the tested software.

Since using acme in certbot-ci is only saving few lines of code, I think it does not worth the risk and the added complexity to declare acme as a dependency. I prefer to duplicate these lines and keep certbot-ci free of any dependency coming from the certbot project.
2019-05-30 07:09:09 -07:00
Pete Cooper
4c299be965 Update docs/cli-help.txt -- typo and formatting (#7105)
* Update docs/cli-help.txt -- yypo and formatting

'areusing' -> 'are using'

* Update cli.py -- formatting

See https://github.com/certbot/certbot/pull/7105

Addresses https://github.com/certbot/certbot/pull/7105#issuecomment-497079342
2019-05-29 14:16:16 -07:00
Brad Warren
561534b754 Move IRC notifications to #certbot-devel. (#7098)
* Move IRC notifications to #certbot-devel.

* Don't use notice.
2019-05-29 09:54:26 +03:00
Adrien Ferrand
7d35f95293 Avoid to delete both webroot_map and webroot_path (#7095)
* Always restore webroot_path in renewal config.

* Add unit tests to ensure correct behavior

* Add changelog

* Add certbot as modified package
2019-05-28 15:16:12 -07:00
Brad Warren
d2a2b88090 Update Ubuntu AMI to 19.04. (#7099) 2019-05-28 23:36:10 +02:00
Brad Warren
bf818036eb Revert "Fix unpinned dependencies tests towards botocore and urllib3 (#7081)" (#7101)
This reverts commit 51a7e7cd19.
2019-05-25 00:20:54 +02:00
Brad Warren
8b684e9b95 Enable test farm tests in Travis (#7072)
You can see the full test suite running at https://travis-ci.com/certbot/certbot/builds/112291892.

A few noteworthy things:

--fast is included because without, the tests would sometimes reach Travis' 50 minute timeout even with 1 test script per Travis build.
The only script that is run at release time which is not being run here is https://github.com/certbot/certbot/blob/master/tests/letstest/scripts/test_tests.sh because that script runs tests on the packages installed by certbot-auto which won't be updated until midway through a release.
We check TRAVIS_PULL_REQUEST and error out if it is not false for simplicity which should be fine because these tests are never run on PRs. The reason it's more complex to run test farm tests on PRs is the test farm tests need a named branch to pull from and Travis effectively merges the PR into the target branch before running tests complicating this.
I don't think this should block this PRs, but the one final change we may want to make to the current setup is #7071.

* Add encrypted private key.

* Add test farm tests to tox and travis.

* Change magic profile name.

* Further split test farm tests.

* Build local branch.

* more depth
2019-05-17 16:30:20 -07:00
Adrien Ferrand
51a7e7cd19 Fix unpinned dependencies tests towards botocore and urllib3 (#7081)
* Limit transitive dependency on urllib3 to <1.25 to fulfill requirements in certbot-dns-route53 throught botocore dependency.
2019-05-17 14:50:29 -07:00
Brad Warren
d1753e46f9 Allow magic profile name none for configuring test farm tests. (#7069)
* Allow magic profile name none.

* Naming is hard.
2019-05-17 20:40:20 +02:00
Brad Warren
16834a0d78 Stop sharing state between processes in test farm tests (#7057)
* Set LOGDIR at top of script.

* Set sentinel at top of script.

* Don't use EC2 global to block on instance start.

* Remove global boto3 state.

* Pass in boulder_url.

* Create main function.

* Add link to reload docs.
2019-05-17 20:36:58 +02:00
Brad Warren
11c3e7107c Exit with a nonzero status when tests fail. (#7065) 2019-05-17 19:44:51 +02:00
Brad Warren
6bcd0415e0 Fix race condition adding tags to instance. (#7080) 2019-05-17 11:27:40 +02:00
Brad Warren
7a6545b747 Regularly print output in the test farm tests (#7079)
* Occasionally print output in test farm tests.

* Flush output.
2019-05-17 11:21:57 +02:00
Brad Warren
f4bbaadd18 Fix cleanup on failure. (#7070)
* Fix cleanup on failure.

* Incrementally build instances list.
2019-05-17 11:17:27 +02:00
Brad Warren
f8614e7c04 Fix centos6 test_sdists (#7068)
* Use Python 3 when appropriate.

* fix venv path
2019-05-17 11:03:00 +02:00
Brad Warren
def9af9f5e Update known good apache2 targets. (#7067) 2019-05-17 10:49:06 +02:00
Brad Warren
26d01537cb Add test farm tests requirements file (#7061)
* Add requirements.txt.

* update readme
2019-05-17 10:41:11 +02:00
Andreas Vogler
7cfbeaeac8 Added certbot-dns-rfc2136 to list of changed modules in CHANGELOG (#7074)
* Add an option to dns_rfc2136 plugin to explicitly specify an authorative base domain.

* Updated CHANGELOG mentioning added base domain option

* Made the comment on the new option more clear on auto-detection

* Updated comment on how the authorative base domain is determined

* Added certbot-dns-rfc2136 to list of changed modules in CHANGELOG
2019-05-16 13:06:29 +02:00
Andreas Vogler
5ab6a597b0 Add an option to dns_rfc2136 plugin to specify an authorative base domain. (#7029)
* Add an option to dns_rfc2136 plugin to explicitly specify an authorative base domain.

* Updated CHANGELOG mentioning added base domain option

* Made the comment on the new option more clear on auto-detection

* Updated comment on how the authorative base domain is determined
2019-05-16 10:40:17 +02:00
Adrien Ferrand
9a7f774706 [Unix] Create a framework for certbot integration tests: PART 5-FINAL (#6989)
* Connect certbot-ci to travis. Remove old bash files.

* Configure test-everything

* Protect against import error

* Remove unused ignore

* Better handling of urllib3

* Correct path

* Remove a warning

* Correct call

* Protect atexit register execution

* Update docs/contributing.rst

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update docs/contributing.rst

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Add again some bash scripts to avoid breaking to much retro-compatiblity on third party scripts

* Move boulder-v1 and boulder-v2 in nightly tests

* Separate oldest unit tests and oldest integration tests

* Remove try/except

* Test integration included in toxenv

* Add a wait to avoid a transient issue on OCSP status in oldest tests

* Clean travis.yml, split other tests

* Remove useless config

* Update .travis.yml

Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>

* Update tox.ini

* Update tox.ini

* Remove pytest-sugar

* Remove empty pytest.ini, tests are working without it
2019-05-14 13:56:32 -07:00
Brad Warren
2abe39d8a2 Add legalese around MM instance. (#7064) 2019-05-14 13:28:23 -07:00
Adrien Ferrand
3888bc8f2a Revert " Add FreeBSD specific paths (#6702)" (#7056)
Revert #6702

After some discussions, we realized that changing the path for FreeBSD users, if it corresponds to the path used when Certbot is installed using ports, will break for users that installed it through certbot-auto.

Indeed in this case, the path used was the one for Linux. After #6702, Certbot would not find anymore the existing config path by default.

It would require, to be integrated, a proper documentation and a migration path. For now, it is preferable to revert it.

This reverts commit 7fe82cf1ac.
2019-05-13 13:55:22 -07:00
Po-Chuan Hsieh
7fe82cf1ac Add FreeBSD specific paths (#6702)
* Add support for FreeBSD specific paths

Reference:	https://svnweb.freebsd.org/ports/head/security/py-certbot/files/patch-certbot_compat.py

* Add CHANGELOG.md entry

* Fix linting error

Pointed out by:	@adferrand
2019-05-11 08:13:37 +02:00
Brad Warren
d391fb8876 Merge pull request #7044 from certbot/candidate-0.34.2
Candidate 0.34.2
2019-05-07 15:03:03 -07:00
ohemorange
60bf8edc79 Merge branch 'master' into candidate-0.34.2 2019-05-07 14:51:17 -07:00
Erica Portnoy
ccedde088d Bump version to 0.35.0 2019-05-07 12:52:34 -07:00
Erica Portnoy
c3a395e7c5 Add contents to CHANGELOG.md for next version 2019-05-07 12:52:34 -07:00
Erica Portnoy
0e95cd8cde Release 0.34.2 2019-05-07 12:52:28 -07:00
Erica Portnoy
7683636684 Update changelog for 0.34.2 release 2019-05-07 12:17:33 -07:00
Brad Warren
8ff24f60a8 0.34.x check_permissions.py filesystem root (#7038)
* Fix check permissions logic (#7034)

Fixes #7031 

I use the same approach than in `CreateVenv()` and `CompareVersions()`: a new bash function `CheckPathPermissions()` is declared an execute a python script passed to the interpreter through stdin.

This allows:
* to not require the temp_dir that holds a temporary script to be executed
* to reduce at the bare minimum the change to make on the order of bash command to execute (including when the temp_dir is created)

* Fix check permissions logic in certbot-auto by making a temp dir useless

* Update CHANGELOG.md

(cherry picked from commit 71b1b8c2d9)

* Fixup changelog.
2019-05-06 16:54:33 -07:00
Brad Warren
a754a90940 Fix test_leauto_upgrades.sh on CentOS 6. (#7037) 2019-05-06 16:50:03 -07:00
ohemorange
f56fad59c9 Merge pull request #7036 from certbot/candidate-0.34.1-2
Candidate 0.34.1-2
2019-05-06 16:24:47 -07:00
Brad Warren
b86f553586 Merge branch 'master' into candidate-0.34.1-2 2019-05-06 15:56:22 -07:00
Adrien Ferrand
71b1b8c2d9 Fix check permissions logic (#7034)
Fixes #7031 

I use the same approach than in `CreateVenv()` and `CompareVersions()`: a new bash function `CheckPathPermissions()` is declared an execute a python script passed to the interpreter through stdin.

This allows:
* to not require the temp_dir that holds a temporary script to be executed
* to reduce at the bare minimum the change to make on the order of bash command to execute (including when the temp_dir is created)

* Fix check permissions logic in certbot-auto by making a temp dir useless

* Update CHANGELOG.md
2019-05-06 15:49:47 -07:00
Brad Warren
0c96cf6560 Merge pull request #7033 from certbot/0.34.1.release
Release 0.34.1
2019-05-06 15:26:28 -07:00
Erica Portnoy
0baefcae32 Bump version to 0.35.0 2019-05-06 13:28:23 -07:00
Erica Portnoy
115ed0e10b Add contents to CHANGELOG.md for next version 2019-05-06 13:28:23 -07:00
Erica Portnoy
2b4d6e23d5 Release 0.34.1 2019-05-06 13:28:15 -07:00
Erica Portnoy
e5cdc2738d Update changelog for 0.34.1 release 2019-05-06 13:12:42 -07:00
Brad Warren
3410b9332c Update changelog for 0.34.1. (#7021) (#7023)
(cherry picked from commit 4bf6eb2091)
2019-05-02 15:28:27 -07:00
Adrien Ferrand
6a970f74d0 Try another approach (#7022)
In #7019, a solution has been integrated to fix oldest tests execution in the corner cases described in #7014.

However this solution was not very satisfactory, as it consists in making a --force-reinstall for all requirements on each oldest tests (apache, certbot, acme, each dns plugin ...). As a consequence, the overall execution time of these tests increased from 5 min to 10 min.

In this PR I propose a more elegant solution: instead of reinstalling all dependencies, we force reinstall only the requirements themselves describe in the relevant oldest-requirements.txt files. This way only the packages that are potentially ignored by pip because they exists locally (acme, certbot, ...) are reinstalled.

The result is the same than in #7019 (we are sure that all packages are really installed by pip), but the very limited number of force reinstalled package here make the impact on execution time negligible.

As a consequence, I revert back also the tox environments to execute all oldest tests together.

A successful execution of oldest tests using this PR material in the context of a point release can be seen here: https://travis-ci.org/adferrand/certbot/builds/527513101
2019-05-02 15:17:11 -07:00
Brad Warren
4bf6eb2091 Update changelog for 0.34.1. (#7021) 2019-05-02 14:52:36 -07:00
Brad Warren
e50d47d25c Merge pull request #7020 from certbot/prep-0.34.1
Prep 0.34.1
2019-05-02 14:46:42 -07:00
Adrien Ferrand
0ab2bb21fa Fix oldest tests when local dependencies are used (#7019)
Fixes #7014.

Using a --force-reinstall (only for oldest tests), dependencies are properly reinstalled. Since this action significantly increases the execution time of oldest tests, I split them into two parts to allow their parallel execution by Travis.

We will need to find a better way to solve this in the future.

An example of successful execution of oldest tests in the situation of a point release can be found here: https://travis-ci.org/adferrand/certbot/builds/527475532

* Fix for oldest requirements

* Split oldest tests

* Update a comment

(cherry picked from commit b19d4801c9)
2019-05-02 14:32:20 -07:00
Adrien Ferrand
b19d4801c9 Fix oldest tests when local dependencies are used (#7019)
Fixes #7014.

Using a --force-reinstall (only for oldest tests), dependencies are properly reinstalled. Since this action significantly increases the execution time of oldest tests, I split them into two parts to allow their parallel execution by Travis.

We will need to find a better way to solve this in the future.

An example of successful execution of oldest tests in the situation of a point release can be found here: https://travis-ci.org/adferrand/certbot/builds/527475532

* Fix for oldest requirements

* Split oldest tests

* Update a comment
2019-05-02 14:32:02 -07:00
Brad Warren
57be329058 Bump initial version to 0.33.1. (#7017)
We made this change locally yesterday while preparing the release.

I tested this change on all AMIs currently in the test farm as well as Fedora 29 and this test passed on all instances.

(cherry picked from commit 862577fffc)
2019-05-02 14:30:04 -07:00
Brad Warren
698e520044 Stop certbot-auto from printing blank lines (#7016)
Fixes #7012.

Apparently, the previous test we had here doesn't catch the case when certbot-auto prints blank lines. (I don't yet understand why so if someone does, please let me know!)

Regardless, I fixed up the test and verified it fails with the version of letsencrypt-auto in master and then fixed letsencrypt-auto so the test passes.

I ran test farm tests on the changes here and they passed on all instances.

* correct test

* fixes #7012

(cherry picked from commit e15e848474)
2019-05-02 14:29:43 -07:00
Brad Warren
e15e848474 Stop certbot-auto from printing blank lines (#7016)
Fixes #7012.

Apparently, the previous test we had here doesn't catch the case when certbot-auto prints blank lines. (I don't yet understand why so if someone does, please let me know!)

Regardless, I fixed up the test and verified it fails with the version of letsencrypt-auto in master and then fixed letsencrypt-auto so the test passes.

I ran test farm tests on the changes here and they passed on all instances.

* correct test

* fixes #7012
2019-05-02 11:36:47 -07:00
Brad Warren
862577fffc Bump initial version to 0.33.1. (#7017)
We made this change locally yesterday while preparing the release.

I tested this change on all AMIs currently in the test farm as well as Fedora 29 and this test passed on all instances.
2019-05-02 11:32:49 -07:00
Josh Soref
82f64126d9 Grammar (#7013)
* spelling: these

* grammar: either-or

* spelling: e.g.
2019-05-02 18:46:59 +02:00
Brad Warren
60e734c969 Merge pull request #7010 from certbot/candidate-0.34.0
Candidate 0.34.0
2019-05-01 15:51:25 -07:00
Erica Portnoy
7711da9fc2 Bump version to 0.35.0 2019-05-01 14:07:30 -07:00
Erica Portnoy
9734be6922 Add contents to CHANGELOG.md for next version 2019-05-01 14:07:30 -07:00
Erica Portnoy
7d28480844 Release 0.34.0 2019-05-01 14:07:25 -07:00
Erica Portnoy
6ba242bc3d Update changelog for 0.34.0 release 2019-05-01 13:24:21 -07:00
Erica Portnoy
2ef1c512b4 Remove unused Changelog sections 2019-05-01 13:21:32 -07:00
schoen
5b76de48de Merge pull request #7009 from rigrassm/dns-rfc2136-config-changes
Make tsig algorithm configuration option in the certbot_dns_rfc2136 config file case insensitive
2019-04-30 19:15:16 -07:00
Ricky Grassmuck
5f5f44dd97 Merge branch 'master' into dns-rfc2136-config-changes 2019-04-30 20:43:07 -05:00
Ricky Grassmuck
40481e0fdb Update CHANGELOG.md
Signed-off-by: Ricky Grassmuck <rigrassm@gmail.com>
2019-04-30 20:33:05 -05:00
Adrien Ferrand
de88e7d777 Implements specific overrides for Fedora 29+ in Apache plugin (#6988)
* Start to plug specific logic for Fedora >= 29

* Invert the logic

* Implement specifics for Fedora 29

* Fix config

* Add documentation

* Fix parser, fix tests

* Fix import

* Fix lint

* Use LooseVersion to be fail safe on versions comparison

* Remove conditional restart on fedora override

* Use parent logic

* Update certbot-apache/certbot_apache/tests/fedora_test.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Simplify restart test

* Update certbot-apache/certbot_apache/override_fedora.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Correct test assertion

* Fix pylint errors

* Revert to a direct call to systemctl
2019-05-01 03:21:10 +03:00
Adrien Ferrand
b0d960f102 Send a POST-as-GET request to query registration in ACME v2 (#6993)
* Send a post-as-get request to query registration

* Add changelog

* Add comments. Add again a line.

* Prepare code for future PR about post-as-get
2019-04-30 15:37:23 -07:00
ohemorange
3900e56b52 Update Debian Jessie AMI to continue being able to use apt (#7003)
Fixes #6907.
2019-04-30 13:16:47 -07:00
ohemorange
f0f5bb4fc0 Update test farm version of boulder to current master (#7002)
Recent changes are no longer compatible with the old version of boulder used in the test farm tests. This PR updates the version of boulder used, and runs it with the new way of running boulder.

A new ami was created and is used here that uses Ubuntu 18.04, so that docker-compose can be installed more properly.

Removed commented-out section about rabbitmq that was already deprecated.

Switched to using the public DNS resolver 8.8.8.8 for the tests because the way to find the correct local resolver changed.
2019-04-30 13:13:37 -07:00
Brad Warren
dcf89c9396 Update Lexicon dependency in dnsimple (#7008)
* Add CERTBOT_OLDEST conditional to setup.py.

* Unset CERTBOT_OLDEST in release script.

* import os
2019-04-30 20:59:05 +02:00
Brad Warren
d1330efe41 Print warning when certbot-auto has insecure permissions. (#6995)
This PR attempts to better inform people about the problem identified at https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/.

I was hesitant to add the flag --no-permissions-check, however, if there's some obscure distro out there (or custom user setup) that has a strange users and groups, I didn't want us to either:

Have to put out a bug fix release
Refuse to fix the problem and let them deal with warnings on every run

* add check_permissions.py

* Update letsencrypt-auto.template.

* build letsencrypt-auto

* Add test_permissions_warnings to auto_test

* Allow uid/gid < 1000.

* Add --no-permissions-check to Certbot.

* Add --no-permissions-check to certbot-auto.

* Add test farm test that letsencrypt-auto is quiet.

As a bonus, this new test will catch problems like the one that the caused
0.33.1 point release.

* Update CHANGELOG about permissions check.

* Update permissions comment.

* Fix symlink handling.

* Use a better default in auto_test.py.
2019-04-30 10:45:03 -07:00
Brad Warren
b41a992545 Use archive.org instead of ietf.org directly. (#7004)
Fixes the failing website builds at https://travis-ci.com/certbot/website/builds/110049706.
2019-04-30 09:42:58 -07:00
ohemorange
1aa111f941 Fix typo and add instructions for changing a single dependency (#6978)
* Fix typo and add instructions for changing a single dependency

* Only mention installing hashin
2019-04-30 06:59:45 +02:00
Ricky Grassmuck
a1dc63a0a2 Allow algorithm in certbot_dns_rfc2136's config to be case insensitive
Update dns_rfc2136_test to use a mixed-case test value in the valid algorithm test.
2019-04-28 21:45:27 -05:00
ohemorange
c99079fb0a Warn install users that future versions of certbot will automatically redirect (#6976)
First step of #6960.

* Warn install users that future versions of certbot will automatically redirect

* Only warn when the user declines or auto-declines redirect

* Unit tests

* Update changelog
2019-04-26 12:43:09 -07:00
Tim White
352218510a Update changelog 2019-04-26 09:34:17 +10:00
Tim White
463d089407 Detect private DNS zones in Google and skip them until we get to a public zone 2019-04-26 09:31:39 +10:00
Trinopoty Biswas
333ea90d1b Added support for linode version 4 tokens (#6588)
* certbot-dns-linode : Added support for linode version 4 tokens

* certbot-dns-linode : Added credentials ini option to override automatic api version detection

* certbot-dns-linode : Added clearer messages and documentation based on review

* certbot-dns-linode : Added check for empty 'linode_version' config instead of missing

* certbot-dns-linode : Fix rebase on master

* certbot-dns-linode : Updated local-oldest-requirements.txt

* Updated CHANGELOG to indicate Linode v4 API key support
2019-04-24 22:41:42 +02:00
schoen
fb83a1ac09 Merge pull request #6963 from certbot/coc
Adding links to EFF's Public Projects Code of Conduct
2019-04-24 12:17:22 -07:00
Adrien Ferrand
9dd2990e59 Remove keyAuthorization fallback dump in challenges response (#6975)
Fixes #6974.

This PR removes the fallback that consists in retrying to send the keyAuthorization field during a challenge request in case of malformed request.

* Remove keyAuthorization fallback dump in challenges response

* Correct import

* Add changelog entry
2019-04-23 15:10:15 -07:00
Adrien Ferrand
618e0562a0 [Unix] Create a framework for certbot integration tests: PART 4 (#6958)
This PR is the part 4 to implement #6541. It adds the integration tests for the nginx certbot plugin, and corresponds to the certbot-ci translation of certbot-nginx/tests/boulder-integration.sh that is executed for each PR.

As with certbot core tests, tests are written in Python, and executed by pytest, against a dynamic Boulder/Pebble instance setup. Tests are parallelized, of course, and a specific IntegrationTestsContext class, extended the one from certbot core tests, is crafter for these specific tests: its main goal is to setup a specific nginx instance for the current test.

On top of that, I use the test parametrization feature of Pytest, to drastically reduce the size of the actual code: indeed, the 6 tests from the original bash script share the same logic. So using a parametrization, one unique test is written, that is then executed 6 times against 6 different sets of parameters.

Note that the module integration_tests.nginx_tests.nginx_config do the same, but in Python, than certbot-nginx/tests/boulder-integration.conf.sh. The latter will be removed in a future PR, with all other bash scripts.

* Add nginx tests

* Distribute the other_port

* Load a pre-generated key/cert for nginx config

* Correct preload, remove a test, simplify a variable

* Integrate assertion directly in the test function

* Check process is not terminated

* Add spaces in the nginx config

* Add comments

* Use indirection

* Allow external cert

* Add coverage threshold for certbot-nginx
2019-04-23 13:29:48 -07:00
ohemorange
2812f054a3 Update urllib3 to 1.24.2 (#6977)
* Update urllib3 to 1.24.2

* Run build.py

* Update changelog
2019-04-22 15:23:26 -07:00
Brad Warren
a817e4f0ec There's no need to use certbot-auto here. (#6970)
I came across this when looking through our docs for other references to certbot-auto.

For the README changes, I deleted a bunch of duplicated and outdated instructions in favor of pointing people to https://certbot.eff.org.
2019-04-22 09:14:20 -07:00
Adrien Ferrand
a58ad22002 [Unix] Create a framework for certbot integration tests: PART 3e (#6951)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering checks on L430-447 in tests/certbot-boulder-integration.sh. Previous lines are covered with existing tests, or by #6946, #6947, #6948, #6949.

* Add tests

* Change param

* Increase coverage min to 64%

* Disable OCSP Must-Staple test for Pebble
2019-04-17 15:24:39 -07:00
Jeremy Gillula
f5d0d4241f Added a CODE_OF_CONDUCT.md file so Github doesn't complain 2019-04-17 11:36:26 -07:00
Joona Hoikkala
0ee1002edc Clarify certbot-auto installation instructions (#6969) 2019-04-17 10:44:50 -07:00
Jeremy Gillula
7e5dcaa383 Adding the EFF Public Projects Code of Conduct to the contributing guide 2019-04-16 16:28:32 -07:00
Jeremy Gillula
24eb299a9b Added a link to the EFF Public Projects Code of Conduct to the readme. 2019-04-16 16:27:22 -07:00
Adrien Ferrand
410e74c4a1 [Unix] Create a framework for certbot integration tests: PART 3g (#6953)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering checks on L531 to the end on tests/certbot-boulder-integration.sh. Previous lines are covered with existing tests, or by #6946, #6947, #6948, #6949, #6951, #6952.

* Add tests

* Add load resource

* Separate OCSP in two tests

* Copy new asset

* Load the asset

* Add coverage limit
2019-04-15 17:39:38 -07:00
Adrien Ferrand
298b1db36b [Unix] Create a framework for certbot integration tests: PART 3f (#6952)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering checks on L448-530 in tests/certbot-boulder-integration.sh. Previous lines are covered with existing tests, or by #6946, #6947, #6948, #6949, #6951.

* Add tests

* Normalize paths

* Fix merge error in git
2019-04-15 16:42:06 -07:00
Adrien Ferrand
6bdc6435eb [Unix] Create a framework for certbot integration tests: PART 3d (#6949)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering checks on L397-429 in tests/certbot-boulder-integration.sh. Previous lines are covered with existing tests, or by #6946, #6947 and #6948.

* Add tests

* Change a variable name

* Fix merge errors from git
2019-04-15 16:18:24 -07:00
Adrien Ferrand
b73c551f14 [Unix] Create a framework for certbot integration tests: PART 3c (#6948)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering about renew, on L283-396 in tests/certbot-boulder-integration.sh (by including existing test_renew_files_permissions and test_renew_with_hook_scripts). Previous lines are covered with existing tests, or by #6946 and #6947.

* Add tests

* Correct assertion about world permission
2019-04-15 15:09:57 -07:00
Adrien Ferrand
471f8aecc0 [Unix] Create a framework for certbot integration tests: PART 3b (#6947)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering on L268-282 in tests/certbot-boulder-integration.sh. Previous lines are covered with existing tests, or by #6946.

* Add tests

* Fix CSR generation

* Add dependency
2019-04-15 15:04:22 -07:00
Adrien Ferrand
3f0dc7c81c [Unix] Create a framework for certbot integration tests: PART 3a (#6946)
Following #6821, this PR continues to convert certbot integration tests into certbot-ci.

This PR add tests covering on L185-222 in tests/certbot-boulder-integration.sh.

* Add tests

* Correct some assertions
2019-04-15 14:59:45 -07:00
Brad Warren
d7610c1ae7 Update Fedora AMI (#6956)
* Update Fedora AMI to Fedora 28.

* Update initial version in test_leauto_upgrades.
2019-04-12 23:44:43 +02:00
Brad Warren
de84688844 Remove slash from path. (#6957) 2019-04-12 23:08:45 +02:00
Adrien Ferrand
d5de24d9fc [Windows] Security model for files permissions - STEP 2 (#6895)
This PR is the second part of #6497 to ease the integration, following the new plan propose by @bmw here: #6497 (comment)

This PR creates the module certbot.compat.os, that delegates everything to os, and that will be the safeguard against problematic methods of the standard module. On top of that, a quality check wrapper is called in the lint tox environment. This wrapper calls pylint and ensures that standard os module is no used directly in the certbot codebase.

Finally local oldest requirements are updated to ensure that tests will take the new logic when running.

* Add executable permissions

* Add the delegate certbot.compat.os module, add check coding style to enforce usage of certbot.compat.os instead of standard os

* Load certbot.compat.os instead of os

* Move existing compat test

* Update local oldest requirements

* Import sys

* Update account_test.py

* Update os.py

* Update os.py

* Update local oldest requirements

* Implement the new linter_plugin

* Fix local oldest for nginx

* Remove check coding style

* Update linter_plugin.py

* Add several comments

* Update the setup.py

* Add documentation

* Update acme dependencies

* Update certbot/compat/os.py

* Update docs/contributing.rst

* Update linter_plugin.py

* Handle os.path. Simplify checker.

* Add a comment to a reference implementation

* Update changelog

* Fix module registering

* Update docs/contributing.rst

* Update config and changelog
2019-04-12 13:32:51 -07:00
Brad Warren
9c54f3dec8 Add back used sys import. (#6954) 2019-04-12 21:33:17 +02:00
Joona Hoikkala
3a2e9ff1fa Try to restart httpd on Fedora if config check fails (#6941)
This PR adds a step to Apache plugin config_test when run on Fedora. Because Fedora now creates self signed certificate and related key material upon first startup of httpd. This was causing issues for users who run certbot-auto or install certbot (and mod_ssl) and run Certbot directly after.

Fixes: #6828

* Try to restart httpd on Fedora if config check fails

* Update CHANGELOG.md
2019-04-12 09:40:51 -07:00
Adrien Ferrand
2b1c77c1ca [Unix] Create a framework for certbot integration tests: PART 2 (#6821)
* Second part: integration tests for certbot core

* Specific coverages

* Add comments

* Improve names

* Suspend fail-under until complete coverage

* Implement a minimal functional example

* Update certbot-ci/certbot_integration_tests/certbot_tests/conftest.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/certbot_tests/context.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/certbot_tests/context.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Fist set of corrections after review

* Fix test and test deploy hook flag

* Improve an assertion, remove conftest

* Add a test to cover all assertions. Remove the CSR logic for now

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Some corrections

* Add the http-01 test to complete coverage

* Add a comment.

* Make single requirements

* Update certbot-ci/certbot_integration_tests/certbot_tests/context.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Revert "Some corrections"

This reverts commit 6f20a060e5cd1913c94eebd4e4b67714a245a4ac.

# Conflicts:
#	certbot-ci/certbot_integration_tests/certbot_tests/context.py
#	certbot-ci/certbot_integration_tests/certbot_tests/test_main.py

* Clean join

* Update certbot-ci/certbot_integration_tests/certbot_tests/context.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/certbot_tests/context.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Change assertion name

* Rewrite http auth hook as real python scripts

* Correct output in some OS

* Try a direct execution

* Fix shebang

* Correct a script

* Update certbot config

* Call explicitly with python, to be cross platform compatible

* Avoid infinite loops. Improve documentation.

* Fix syntax
2019-04-11 18:07:36 -07:00
Brad Warren
d5ea9f4486 Add reminder to local-oldest-requirements.txt. (#6943) 2019-04-11 23:16:25 +02:00
Brad Warren
b0285438cc Move venv symlink check out of leauto_upgrades. (#6830)
* Move venv symlink check out of leauto_upgrades.

* Add back double venv check.
2019-04-10 18:24:32 -07:00
Brad Warren
3381bc6616 Add --disable-pip-version-check to pip calls. (#6938) 2019-04-09 22:39:41 +02:00
Adrien Ferrand
278cc8feef Disable default aggregated report. Reactivate auto-validation of reports against base branch. (#6939)
Following #6934, this PR finalize two things, as explained in #6934:

disable the default aggregated report
validate linux and windows reports against the PR base branch
2019-04-09 12:47:53 -07:00
Adrien Ferrand
fb5974b8c3 Improve codecov report integration to CI in Certbot (#6934)
So, we observed lately several inconsistencies in how Codecov behave toward the CI pipeline for PRs in Certbot. One example is #6888. The most annoying thing is that the build of PR is **temporary** marked as failed, until all coverage are run.

The correction on the latter is done in two PRs. This is the first part.

TL;DR

This PR separates the Codecov report in two: one for coverage executed on Windows, one for Linux. This is the correct way to do regarding our current CI pipeline. Actions are required by a GitHub administrator of Certbot once this PR is merged.

Complete explanation

So the failure stated in the introduction is essentially due to several things interacting together:
* AppVeyor generates a coverage report for Windows, that have a coverage value a little lower than on Linux (96%)
* Travis generates a coverage report for Linux. Its coverage is higher, and slowly decrease as more specific Windows code is added to Certbot, that cannot be tested on Travis
* Since AppVeyor saw its capacity increasing, it finishes its coverage job before the one from Travis
* Certbot GitHub repo is configured to require the coverage pipeline to succeed (in whatever that means) to success the overall PR build

So here the suite of events:
1) PR is issued. GitHub expect three pipeline to succeed: AppVeyor CI, Travis CI and Codecov (displayed in the PR page)
2) Codecov receive first the report of AppVeyor coverage. It is 96%. It is a failure for now, because coverage in master (AppVeyor+Travis) is 98.6%.
3) GitHub is reported of the failure on Codecov, so fail the PR build
4) Codecov receive then the report of Travis coverage. It is 98%. It merges it with the report from AppVeyor, leading to the 98.6%. The failure becomes a success.
5) GitHub is reported of the success on Codecov, so, nevermind, the PR build is a success finally!

So we have a CI flow that change its mind. Great. This is because of 2) and 4), and we could expect that Codecov should handle that. This is not the case: it is somewhat misleading, because Codecov adverts a lot about its capability to merge reports, including from different CI. But it is about the final state, not about the transient state, while reports are progressively received.

Two things to things that a transient state is existing, with a result that can change:
* first, from Codecov doc itself, explaining that reports should not be trusted during the CI pipeline execution: https://docs.codecov.io/docs/ci-service-relationship#section-checking-ci-status
* second, is an example of transient state of `cryptography` project, this is advert by Codecov to be a reference of the implementation:
![image](https://user-images.githubusercontent.com/9728851/55796456-5b1c8480-5aca-11e9-9628-41b83fba1bde.png)

As you can see above, build state of `cryptography` is failing after the first report is received, and until all coverage reports from Travis are received.

So, what can we do about it? Thing is, we are aggregating coverage from very two unrelated sources (two different OS systems), and Codecov has something for that. This is flags: https://docs.codecov.io/docs/flags

Flags allow to flag coverage material depending on any logic you apply to the command that uploaded the coverage report (eg. `codecov -F a_flag`). Then, several logics can be applied on it, for instance having in Codecov UI the capability to filter the coverage other a flag, having status of build for each flag and ... having a report for a specific flag.

So:
1) I modified Travis and AppVeyor to send their report under a specific flag: `linux` or `windows`
2) I created a project specific `.codecov.yml` configuration in Certbot repository, to instruct Codecov to push two separate reports on GitHub build: one for Linux, one for Windows. Each report can be validated against its specific coverage from the `master` branch (more on this just after)

With all of this, now the GitHub is succeeding, because each coverage is validated independently.

I think it is the good approach, because it solves the specific issue here, and because it reflects the logic behind: merging coverage from different OS architectures does not make much sense. It would be a long-term problem, because as I said at the beginning, coverages will slowly decrease as more platform specific code is added in Certbot.

Now, it is not finished. Two things need to be done: an administrator action, and a second PR

Administrator action

Certbot GitHub as a a branch protection rule (Settings > Branches > Branch protection rules). It needs to be changed.

Indeed this rule is expecting the full coverage report (named `codecov/project`) to be valid on a PR. It needs to be changed to expect two coverage reports: `codecov/project/linux` and `codecov/project/windows`. The `codecov/project` needs to be removed.

This can be done once this PR is merged, and the specific coverage reports have been generated on master.

Second PR

Once this PR is merged and administrative actions have been done. I will make a new PR modifying `.codecov.yml` with two things:
* disable the faulty full coverage report, that is not required anymore by GitHub branch protection rules
* modify the `linux` and `windows` reports to validate against the relevant coverage calculated from `master` (indeed, in this PR it is a fixed ratio rule, since the coverage to compare on master is the full coverage one, significantly higher)

* Tag reports

* Set per-project codecov configuration
2019-04-09 11:43:26 -07:00
Brad Warren
12ab59e1fc Merge pull request #6932 from adferrand/pylint-squash
Update Pylint to 1.9.4 (squashed PR)
2019-04-09 10:47:19 -07:00
Brad Warren
6249cd0237 Use VIRTUALENV_NO_DOWNLOAD in tools/venv.py. (#6931) 2019-04-09 16:10:19 +02:00
Adrien Ferrand
04152c21b5 Update to Pylint 1.9.4 and corrections 2019-04-09 09:22:19 +02:00
Brad Warren
c77159a30c Update the lexicon version used in tests/Docker. (#6929)
This will resolve problems with certbot-dns-dnsimple in Docker.
2019-04-08 12:51:52 -07:00
kaduk
9c312a3882 Fix typo in comment ("upstreqm") (#6926)
Spell "upstream" correctly.
2019-04-07 22:20:03 +02:00
Brad Warren
944d0e05c8 Use venv over virtualenv in venv3 (#6922)
Fixes #6861.

_venv_common.py is no longer executable. The reason for this is the venv creation logic is now different between Python 2 and Python 3. We could add code that branches on the Python version running the script, but I personally think that's unnecessary.

--setuptools and --no-site-packages is no longer passed to virtualenv either. These flags were made noops in virtualenv 1.10 and 1.7 respectively, but all of CentOS 6, 7, Debian 8+, and Ubuntu 14.04+ have new enough versions of virtualenv where these flags are no longer necessary. They are not even accepted as flags to Python 3's venv module.

Use of VENV_ARGS from test_sdists.sh was also removed because that environment variable hasn't done anything in a while.

I ran test farm tests on test_apache2.sh and test_sdists.sh with these changes and they passed.

* Fixes #6861.

* _venv_common is no longer executable.
2019-04-05 15:01:09 -07:00
Brad Warren
157d1ea0d8 Don't run pip tools/venv.py (#6923)
It won't work. Instead, follow the instructions at the top of this document to set up a virtual environment and activate it.
2019-04-05 13:42:30 -07:00
Brad Warren
aec29c2f1d Remove amazon linux test farm targets. (#6822) 2019-04-05 13:39:39 -07:00
Brad Warren
7d58e67fd6 Move fixing oldest reqs to avoid merge conflicts. (#6921)
When releasing 0.33.1 and resolving merge conflicts between the candidate-0.33.1 branch and master, I had merge conflicts in the local-oldest-requirements.txt files. This is because the point release branch does not contain modifications to these files that landed in master because it happens later in the release script in the commit bumping version numbers which is not included in the point release branch.

I think having to resolve these merge conflicts is unnecessary and even a slight problem because it means that the "oldest" tests on the point release branch may still be using the latest version of certain components when they actually should be using an older version.

I fixed this by moving this code earlier in the script so the local-oldest-requirements.txt files are updated at the same time as the setup.py files.
2019-04-05 13:38:37 -07:00
Adrien Ferrand
b7caa3b3a1 Merge pull request #6919 from certbot/candidate-0.33.1
Candidate 0.33.1
2019-04-05 21:11:00 +02:00
Brad Warren
6d32dd8792 Merge branch 'master' into candidate-0.33.1 2019-04-05 11:58:05 -07:00
Brad Warren
f2b071f8f4 Don't search for plugins once for each config item (#6917) 2019-04-05 08:54:43 +02:00
Brad Warren
e63ceb8dd2 Bump version to 0.34.0 2019-04-04 15:24:45 -07:00
Brad Warren
ae9c57d68c Add contents to CHANGELOG.md for next version 2019-04-04 15:24:44 -07:00
Brad Warren
c32b57607f Release 0.33.1 2019-04-04 15:24:43 -07:00
Brad Warren
45869f8315 Update changelog for 0.33.1 release 2019-04-04 15:02:08 -07:00
Brad Warren
6590875a1a mattermost > irc (#6916) 2019-04-04 13:30:38 -07:00
Brad Warren
7c7715743c Prepare for the 0.33.1 release. (#6915)
The changelog should still say <version> - master because it will be fixed up automatically by the release script at https://github.com/certbot/certbot/blob/master/tools/_release.sh#L69.

* Protect certbot-auto against non numerical version release in some RPM distributions (#6913)

Fixes #6912

Bash evaluate all condition in a predicate statement, eg. `"$SOMEVAR" = "test" -a "$ANOTHERVAR" = "test2"`, even if it is not necessary, for instance if the first condition is false in the example here.

As a consequence, on non-Fedora distributions, an evaluation of the distribution version could be done on non numeric value, eg. `"6.7" -eq "29"`, making certbot-auto failing in this case.

This PR fixes that, by evaluating the version on RPM distributions only if we are on Fedora. Otherwise, version will be "0".

(cherry picked from commit c2d9ea1f61)

* Update changelog about #6912 fix. (#6914)

(cherry picked from commit 30eafba997)

* cleanup changelog
2019-04-04 11:38:30 -07:00
Brad Warren
30eafba997 Update changelog about #6912 fix. (#6914) 2019-04-04 11:08:07 -07:00
Adrien Ferrand
c2d9ea1f61 Protect certbot-auto against non numerical version release in some RPM distributions (#6913)
Fixes #6912

Bash evaluate all condition in a predicate statement, eg. `"$SOMEVAR" = "test" -a "$ANOTHERVAR" = "test2"`, even if it is not necessary, for instance if the first condition is false in the example here.

As a consequence, on non-Fedora distributions, an evaluation of the distribution version could be done on non numeric value, eg. `"6.7" -eq "29"`, making certbot-auto failing in this case.

This PR fixes that, by evaluating the version on RPM distributions only if we are on Fedora. Otherwise, version will be "0".
2019-04-04 10:46:46 -07:00
Brad Warren
2cf216122b Correct changelog to mention acme changes. (#6909) 2019-04-04 00:17:25 +02:00
Brad Warren
4de4b17216 Fix typo in changelog. (#6910) 2019-04-04 00:16:43 +02:00
Brad Warren
15763a3793 Merge pull request #6908 from certbot/candidate-0.33.0
Candidate 0.33.0
2019-04-03 14:25:34 -07:00
Erica Portnoy
7b7f7b25fb Bump version to 0.34.0 2019-04-03 13:08:11 -07:00
Erica Portnoy
69bb3eac2c Add contents to CHANGELOG.md for next version 2019-04-03 13:08:10 -07:00
Erica Portnoy
58c21aa484 Release 0.33.0 2019-04-03 13:08:02 -07:00
Erica Portnoy
1bbfc669ab Update changelog for 0.33.0 release 2019-04-03 11:53:40 -07:00
Adrien Ferrand
3830c0f900 Reinsert fix for #5456 (#6904)
Dependencies generated by the script introduced with #6839 were not including anymore the fix about enum34 for CentOS 6.

This PR reinserts this fix, and updates the script overrides to ensure that this fix will stay in next dependencies generation.

* Add the environment marker back. Ensure that it will stay by adding an override to dependencies generator.

* Add comments, for future fix

* Update letsencrypt-auto-source/rebuild_dependencies.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update comment
2019-04-02 16:49:38 -07:00
Adrien Ferrand
4515a52d3f Merge branch 'master' into pylint
# Conflicts:
#	acme/acme/client.py
#	acme/acme/crypto_util.py
#	acme/acme/standalone.py
#	certbot-apache/certbot_apache/configurator.py
#	certbot-apache/certbot_apache/parser.py
#	certbot-apache/certbot_apache/tests/tls_sni_01_test.py
#	certbot-apache/certbot_apache/tests/util.py
#	certbot-apache/certbot_apache/tls_sni_01.py
#	certbot-nginx/certbot_nginx/configurator.py
#	certbot-nginx/certbot_nginx/parser.py
#	certbot-nginx/certbot_nginx/tests/util.py
#	certbot/account.py
#	certbot/cert_manager.py
#	certbot/cli.py
#	certbot/configuration.py
#	certbot/main.py
#	certbot/ocsp.py
#	certbot/plugins/dns_common_lexicon.py
#	certbot/plugins/standalone.py
#	certbot/plugins/util.py
#	certbot/plugins/webroot.py
#	certbot/tests/auth_handler_test.py
#	certbot/tests/cert_manager_test.py
#	certbot/tests/display/util_test.py
#	certbot/tests/main_test.py
#	certbot/tests/util.py
#	certbot/util.py
#	tox.ini
2019-04-02 22:32:01 +02:00
Joona Hoikkala
fd6702b869 Fix CentOS 6 installer issue (#6784)
In CentOS 6 default httpd configuration, the `LoadModule ssl_module ...` is handled in `conf.d/ssl.conf`. As the `VirtualHost` configuration files in `conf.d/` are loaded in alphabetical order, this means that all files that have `<IfModule mod_ssl.c>` and are loaded before `ssl.conf` are effectively ignored. This PR moves the `LoadModule ssl_module` to the main `httpd.conf` while leaving a conditional `LoadModule` directive in `ssl.conf`.

Features
 - Reads the module configuration from `ssl.conf` in case some modifications to paths have been made by the user.
 - Falls back to default paths if the directive doesn't exist.
 - Moves the `LoadModule` directive in `ssl.conf` inside `<IfModule !mod_ssl.c>` to avoid printing warning messages of duplicate module loads.
 - Adds `LoadModule ssl_module` inside of `<IfModule !mod_ssl.c>` to the top of the main `httpd.conf`.
 - Ensures that these modifications are not made multiple times.

Fixes: #6606

* Fix CentOS6 installer issue

* Changelog entry

* Address review comments

* Do not enable mod_ssl if multiple different values were found

* Add test comment

* Address rest of the review comments

* Address review comments

* Better ifmodule argument checking

* Test fixes

* Make linter happy

* Raise an exception when differing LoadModule ssl_module statements are found

* If IfModule !mod_ssl.c with LoadModule ssl_module already exists in Augeas path, do not create new LoadModule directive

* Do not use deprecated assertion functions

* Address review comments

* Kick tests

* Revert "Kick tests"

This reverts commit 967bb574c2.

* Address review comments

* Add pydoc return value to create_ifmod
2019-04-02 09:26:58 -07:00
schoen
1daa3ca076 Merge pull request #6898 from aditj/aditj-patch-1
Changed the text of -h to add details regarding unregister
2019-04-01 20:07:55 -07:00
Adrien Ferrand
232e0ea50f Rely on universal newline mode on python 3 for windows (#6866) 2019-04-01 09:50:08 -07:00
aditj
63c8f2e34d Changed the text of -h to add details regarding unregister and all 2019-03-31 00:01:11 +05:30
Adrien Ferrand
ea568d4dc2 [Windows] Fix ErrorHandler tests, by disabling signal error handling (#6868)
This PR is a part of the effort to remove the last broken unit tests in certbot codebase for Windows, as described in #6850.

It solves the problems associated to ErrorHandler in Windows (enlighted by tests errors) by ... wipping out the problem: no signal is handled by ErrorHandler on Windows. See the relevant inline comment in certbot.error_handler for explanation and sources.
2019-03-28 16:50:42 -07:00
Adrien Ferrand
6ce6c67932 [Windows] Security model for files permissions - STEP 1 (#6893)
This PR is the first part of #6497 to ease the integration, following the new plan propose by @bmw here: #6497 (comment)

This step 1 refactor existing certbot.compat module into certbot.compat.misc, without any logic changed. Package certbot.compat will host the new modules that constitute the security model for Windows.

* Create the certbot.compat package. Move logic in certbot.compat.misc

* Add doc

* Fix lint

* Correct mypy

* Update client.py
2019-03-28 15:51:48 -07:00
Brad Warren
b0fb570c1c Bump min nginx requirements to tested versions. (#6891) 2019-03-27 22:38:28 +01:00
Brad Warren
8b8fc5ae54 Fix acme race condition (#6892)
* Fix acme race condition.

* Assert process has executed.
2019-03-27 21:27:38 +01:00
Brad Warren
414c70aa6c Bump the min Certbot version for nginx plugin. (#6890)
* Bump the min Certbot version for nginx plugin.

* s/certbot/./g
2019-03-27 21:07:42 +01:00
Joona Hoikkala
b30a5e5b73 Add a test to ensure test coverage regardless of the vhost order (#6873)
Add a new test to make sure that we are covering all the branches of get_virtual_hosts() regardless of the order that Augeas returns the found VirtualHost paths.

Fixes: #6813

* Add a test to ensure test coverage regardless of the order of returned vhosts

* Use deepcopy instead, and increase coverage requirement back to 100%
2019-03-27 10:10:52 -07:00
Brad Warren
491d6c8f45 Revert "Configure jessie repos in LTS mode during Docker build (#6887)" (#6889)
This reverts commit a27bd28b39.
2019-03-27 07:27:06 +01:00
Adrien Ferrand
a03e7b95d3 Deprecate all tls-sni related objects in acme module (#6859)
This PR is a part of the tls-sni-01 removal plan described in #6849.

As `acme` is a library, we need to put some efforts to make a decent deprecation path before totally removing tls-sni in it. While initialization of `acme.challenges.TLSSNI01` was already creating deprecation warning, not all cases were covered.

For instance, and innocent call like this ...
```python
if not isinstance(challenge, acme.challenges.TLSSNI01):
    print('I am not using this TLS-SNI deprecated stuff, what could possibly go wrong?')
```
... would break if we suddenly remove all objects related to this challenge.

So, I use the _Deprecator Warning Machine, Let's Pacify this Technical Debt_ (Guido ®), to make `acme.challenges` and `acme.standalone` patch themselves, and display a deprecation warning on stderr for any access to the tls-sni challenge objects.

No dev should be able to avoid the deprecation warning. I set the deprecation warning in the idea to remove the code on `0.34.0`, but the exact deprecation window is open to discussion of course.

* Modules challenges and standalone patch themselves to generated deprecation warning when tls-sni related objects are accessed.

* Correct unit tests

* Correct lint

* Update challenges_test.py

* Correct lint

* Fix an error during tests

* Update coverage

* Use multiprocessing for coverage

* Add coverage

* Update test_util.py

* Factor the logic about global deprecation warning when accessing TLS-SNI-01 attributes

* Fix coverage

* Add comment for cryptography example.

* Use warnings.

* Add a changelog

* Fix deprecation during tests

* Reload

* Update acme/acme/__init__.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update CHANGELOG.md

* Pick a random free port.
2019-03-26 18:26:38 -07:00
Adrien Ferrand
821bec6997 Remove tls-sni related flags in cli. Add a deprecation warning instead. (#6853)
This PR is a part of the tls-sni-01 removal plan described in #6849.

This PR removes --tls-sni-01-port, --tls-sni-01-address and tls-sni-01/tls-sni options from --preferred-challenges. They are replace by deprecation warning, indicating that these options will be removed soon.

This deprecation, instead of complete removal, is done to avoid certbot instances to hard fail if some automated scripts still use these flags for some users.

Once this PR lands, we can remove completely theses flags in one or two release.

* Remove tls-sni related flags in cli. Add a deprecation warning instead.

* Adapt tests to cli and renewal towards tls-sni flags deprecation

* Add https_port option. Make tls_sni_01_port show a deprecation warning, but silently modify https_port if set

* Migrate last items

* Fix lint

* Update certbot/cli.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Ensure to remove all occurences of tls-sni-01

* Remove unused parameter

* Revert modifications on cli-help.txt

* Use logger.warning instead of sys.stderr

* Update the logger warning message

* Remove standalone_supported_challenges option.

* Fix order of preferred-challenges

* Remove supported_challenges property

* Fix some tests

* Fix lint

* Fix tests

* Add a changelog

* Clean code, fix test

* Update CI

* Reload

* No hard date for tls-sni removal

* Remove useless cast to list

* Update certbot/tests/renewal_test.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Add entry to the changelog

* Add entry to the changelog
2019-03-26 17:46:32 -07:00
Adrien Ferrand
a27bd28b39 Configure jessie repos in LTS mode during Docker build (#6887)
Currently, `tox -e le_auto_jessie` job fails. It breaks in particular the cron pipeline that test everything each night.

The failure occurs while setting up the Jessie Docker container to run the tests for certbot-auto, when `apt-get update` is invoked, with this error:
```
W: Failed to fetch http://deb.debian.org/debian/dists/jessie-updates/main/binary-amd64/Packages  404  Not Found
```

Indeed, if there are `stretch-updates`, `buster-updates` and so on in the repository, there is no `jessie-updates`. I do not know exactly the logic of Debian here, but as `*-updates` folders store stable updates, a distribution moving to LTS support like Jessie has no stable updates anymore. I suppose `jessie-updates have been decommissioned recently, and the official Docker has not been updated yet to use the LTS configuration for repositories.

This PR does that live in the Dockerfile, using official instructions from https://wiki.debian.org/LTS/Using, and fixes this specific job.

An example of a successful job with this modification can be found here: https://travis-ci.com/certbot/certbot/jobs/187864341
2019-03-26 11:35:43 -07:00
Brad Warren
50607eb0ff Document dropped tls-sni-01 support in plugins. (#6884) 2019-03-25 15:57:53 -07:00
Adrien Ferrand
97d269ceb5 Raise explicitly an error (#6883)
Explicit is better than implicit

When calling raise without an argument, Python will raise the last error occured from the caller except block. This makes my PyCharm very sad however. So this PR makes the function handling the error raising explicitly the error received as an argument.
2019-03-25 15:06:13 -07:00
Adrien Ferrand
20ed165699 Remove unused code in apache (#6882)
To fix one of the two uncovered lines in certbot-apache, given in #6880. Instead of adding a test to just increase the coverage, this fixes the uncovered line by removing the unused code.
2019-03-25 13:48:36 -07:00
Adrien Ferrand
4d2dfab4dd Simplify a branching that is not totally covered. (#6881) 2019-03-25 21:22:56 +01:00
Adrien Ferrand
537bffbc23 [Windows] Fix some unit tests (#6865)
This PR is a part of the effort to remove the last broken unit tests in certbot codebase for Windows, as described in #6850.

This PR fixes various unit tests on Windows, whose resolution was only to modify some logic in the tests, or minor changes in certbot codecase impacting Windows only (like handling correctly paths with DOS-style).

* Correct several tests

* Skip test definitively

* Test to be reactivated with #6497

* Mock log system to avoid errors due to multiple calls to main in main_test

* Simplify mock

* Update cli_test.py

* One test to be repaired when windows file permissions PR is merged
2019-03-25 12:56:28 -07:00
Adrien Ferrand
c1d2efec4e Construct the sanitized, pinned and hashed requirements file for certbot-auto (#6839)
* Setup an independant create_venv piece for certbot-auto

* Debug

* First implementation

* Some corrections, disable python 3

* Continue work

* Add hashin

* Polish CLI

* Fix logic

* Add executable permissions

* Assynchronous process

* Correction

* Add comments

* More controls

* Correct image name

* Fix image

* Test with 2

* Test timeout

* Remove parallelization for now. To much bugs.

* Add comments

* Correct installation

* Correct keys map view usage

* Improve filtering

* Correction

* Improve filtering, again

* Remove dependency on python 3

* Remove necessity to run from certbot root

* Add constraints. Clean code.

* Pure constraints

* More involved base test

* Update certbot-auto with calculated dependencies

* Update header

* Rebuild UI

* Correction

* Remove debug info

* Ensure docker exit when process finish

* Another try to stop docker

* Clean stdout/stderr

* Fix python-augeas

* Catch stderr

* Update dependencies with new constraints

* Update certbot-auto

* Corrections after review.

* Clean endline

* Silent execution

* Filter editable installation of local certbot packages, strict check on package names
2019-03-25 18:52:59 +01:00
Adrien Ferrand
d9880721b3 Remove tls sni in nginx plugin (#6857)
* Remove tls-sni from nginx config

* Add a dedicated configuration to define what is the HTTPS port for this certbot instance.

* Correct some tests

* Reestablish default vhost creation

* Clean tls references for nginx integration tests

* Associate https_port only to tests and nginx
2019-03-18 10:22:19 -07:00
Adrien Ferrand
b447b0a8e9 Remove tls sni in apache plugin (#6858)
* Add a dedicated configuration to define what is the HTTPS port for this certbot instance.

* Remove tls-sni in apache plugin

* Update constants.py

* Update interfaces.py

* Remove option

* Simplify a test
2019-03-15 16:39:43 -07:00
Adrien Ferrand
e909b0852c Remove tls-sni challenge in manual plugin (#6855)
* Remove tls-sni challenge in manual

* Remove unused logic
2019-03-14 17:56:56 -07:00
Adrien Ferrand
c2f2aa5ee0 Remove tls-sni in compatibility tests (#6854)
* Reconfigure compatibility tests to use http challenge

* Correct simple test

* Add a fake DNS resolution for HTTP simple_verify

* Debug

* More subtle approach: we monkey patch urllib3 to fake a dns resolution to the target IP, allowing every host header to be preserved.

* Private package

* Relaxed permissions on certbot temp working dir

* Move the fake DNS logic in compatibility test, to avoid degrading the acme coverage

* Fix lint

* Update certbot-compatibility-test/certbot_compatibility_test/configurators/common.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>
2019-03-15 01:07:49 +01:00
Adrien Ferrand
5e64349a4a Remove tls-sni challenge in standalone plugin (#6856) 2019-03-14 16:30:17 -07:00
Brad Warren
8386d08de2 Use Python3 to run tools/venv3.py. (#6860) 2019-03-14 19:07:19 +01:00
Adrien Ferrand
acc918eee7 Remove tls-sni integration tests (#6852)
This PR is a part of the tls-sni-01 removal plan described in #6849.

This PR removes the tls-sni-01 challenge tests during the integration tests. The approach I used here is not to remove completely the existing test code, but simply editing it to use a http-01 challenge. Indeed:
* the current integration tests are strongly coupled, and would require more modifications that it is worth, because ...
* the certbot-ci project, that has already no tls-sni tests, will soon replace completely the current integration tests code.
2019-03-13 15:42:07 -07:00
Adrien Ferrand
cf29e89366 Move coverage computation during certbot integration tests at the end of the script (#6842)
Currently coverage invocation during integration tests on certbot core is misplaced, just before the OCSP statuses tests.

This PR move back the coverage invocation at the end of the script.
2019-03-11 16:16:48 -07:00
Brad Warren
d34beb4149 This is now at the top level of their site (#6846) 2019-03-11 16:14:34 -07:00
Brad Warren
a7f2f24426 Mention OCSP UTC fix in changelog. (#6845) 2019-03-11 16:14:20 -07:00
Seth Schoen
e20adedb94 This is now at the top level of their site 2019-03-11 15:50:27 -07:00
Adrien Ferrand
81d9b5250e Clean stderr in case of /etc/os-release does not exist (#6835) 2019-03-11 15:42:32 -07:00
schoen
674ba896eb Merge pull request #6817 from obynio/master
Replace deprecated Gandi plugin link
2019-03-11 15:41:45 -07:00
Adrien Ferrand
cac4be7046 Calculate timedelta with thisUpdate/nextUpdate in UTC (#6838)
Fixes #6836.

OCSP responses contains a thisUpdate and nextUpdate that allow to calculate its validity. Certbot currently uses datetime.now() to get the current time when OCSP check is done through cryptography. But datetime.now() expresses the date in the machine local time, and comparison operators on datetime do not take into account the offset between two datetime objects expressed in difference timezones.

As a consequence, a given thisUpdate may be seen as a future date depending on the local time, failing the OCSP check process.

The error is not critical for certbot, as it will just make some valid OCSP responses giving an EXPIRED status been ignored.

This PR fixes this comparison by taking the current time in UTC using datetime.utctime().
2019-03-11 15:27:33 -07:00
Brad Warren
45229eebdf Drop expected Apache coverage to workaround #6813. (#6826)
* Drop expected Apache coverage to workaround #6813.

* add comment
2019-03-07 20:57:08 +01:00
Adrien Ferrand
34393f9bf4 Correct certbot-auto for Fedora 29+ (#6812)
Fixes #6698

Fedora maintainers engaged a deprecation path for Python 2.x with Fedora 29. As a first step, python2-virtualenv does not install the virtualenv binary anymore, in favor of python3-virtualenv, and so the installation of Python 3 virtual environments by default.

However, certbot-auto installs python2-virtualenv for all recent RPM distributions, and relies of the execution of virtualenv, and this is failing the process.

Since the plan in the future is to remove Python 2.x from Fedora, this PR follows this logic to fix certbot-auto: started to Fedora 29, certbot-auto will install and execute certbot on Python 3. This implies to detect that we are on Fedora 29+, install python3-virtualenv that will install also Python 3 dependencies and virtualenv binary, then instruct the process to use Python 3. This is in fact similar to EOL distributions shipping with Python 2.6, and for which Python 3.4 from EPEL is installed and used.

Older versions of Fedora continue to use Python 2.x, and their process is untouched. Four scenarios are covered here:

fresh Fedora 28: old process is used, nothing changes
fresh Fedora 29: new process is used, Python 3 is installed, certbot runs on it
update Fedora 29 from 28, already installed certbot-auto without rebootstrapping required: existing venv continue to be used, certbot runs on it
update Fedora 29 from 28, already installed certbot-auto with rebootstrapping required: new process is used, installing python3-virtualenv, python3-devel and python3-rpm-macros, Python 3 is installed, certbot runs on it

* Add a step to handle python3 on fedora29

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update letsencrypt-auto-source/letsencrypt-auto.template

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update rpm_python3.sh

* Rebuild certbot-auto

* Empty commit to relaunch CI pipeline

* Add changelog

* Update CHANGELOG.md

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update CHANGELOG.md
2019-03-07 10:05:20 -08:00
Adrien Ferrand
f378536ffa Do not run the full CI pipeline on master (#6811)
* Configure appveyor

* Renaming in travis yml

* Update .travis.yml

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update .travis.yml

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>
2019-03-06 14:49:43 -08:00
Brad Warren
e593674930 Merge pull request #6820 from certbot/candidate-0.32.0
Release 0.32.0
2019-03-06 13:58:02 -08:00
Erica Portnoy
8dda6cc68f Bump version to 0.33.0 2019-03-06 12:47:29 -08:00
Erica Portnoy
0343f365ab Add contents to CHANGELOG.md for next version 2019-03-06 12:47:28 -08:00
Erica Portnoy
0492855166 Release 0.32.0 2019-03-06 12:47:27 -08:00
Erica Portnoy
a276523c09 Update changelog for 0.32.0 release 2019-03-06 12:18:08 -08:00
Erica Portnoy
be6df7de04 Remove Fixed section from changelog 2019-03-06 12:16:13 -08:00
Adrien Ferrand
670e9d89b7 Fix faulty test (#6816) 2019-03-05 18:30:33 -08:00
Brad Warren
198c447e77 Move the OCSP change to the right section. (#6818)
Looks like this got added to our changelog for the released 0.31.0 instead of the upcoming release. We want this change for the release tomorrow.
2019-03-05 14:01:08 -08:00
Yohann Leon
6a0f3248a8 Replace deprecated Gandi plugin link 2019-03-05 22:27:07 +01:00
Brad Warren
0c6d83dc39 Merge pull request #6786 from certbot/pin-deps_continued
This continues from the work of @sydneyli in PR #6671
I didn't do much here. Basically added support for reading data from sys.stdin to both tools/merge_requirements.py and tools/strip_hashes.py as well as support for reading files from paths passed as cli parameters to strip_hashes.py.

Reading the filepaths was not strictly required, but I thought would be a good thing to do in order to keep the tooling usage options consistent.

Fixes #6581

* Generate constraints file to pin deps in Docker images

Dockerfiles pin versions using constraints file

Pulling out strip_hashes and add --no-deps flag

* Add stdin option for merge_requirements

Add stdin and file path support to strip_hashes

* Address review comments
2019-03-04 09:02:53 -08:00
Joona Hoikkala
d8a3fa3904 Address review comments 2019-03-04 15:52:38 +02:00
Adrien Ferrand
6ff101dcbb Cover case of OpenSUSE Leap 15+ in certbot-auto (#6794)
Fixes #6228.

Since OpenSUSE Leap 15, python-virtualenv became a source package, breaking certbot-auto bootstrap on this version. Then python2-virtualenv must be used to create Python 2.x virtual environments.

This PR makes certbot-auto compatible to prior and after Leap 15, by testing the existence of python-virtualenv on current OpenSUSE system, and then use appropriate packages.

* Cover case of OpenSUSE Leap 15+ in certbot-auto

* Revert increment on bootstrap for OpenSUSE

* Fix configuration for Leap15+

* Add comment about explicit installation of python2-setuptools

* Update letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update letsencrypt-auto
2019-03-01 18:07:07 -08:00
Adrien Ferrand
bf1f83f47b Revert "Disable build for commits pushed on master (#6804)" (#6807)
By removing all the builds on push to master, done in #6804, we also removing the coveralls reports that are necessary to calculate the effect of a PR on code coverage, that is part of the quality gate process.

This PR is reverting #6804, and another implementation preserving the coveralls reports will be done soon.

This reverts commit a468a3b255.
2019-03-01 17:16:22 -08:00
Adrien Ferrand
3ed3787bd8 Implement Retry-After, and refactor authorization polling (#6766)
Fixes #5789

This PR is about allowing Certbot to respect the Retry-After HTTP header that an ACME CA server can return to a client POSTing to a challenge, to instruct him and retry the request later.

However, this feature was not easily implementable in the current code of certbot.auth_handler, because the code became really hard to read. In fact, @bmw was thinking that the code was really deceiving, and a lot of supposed functionalities declared in the comments were in fact not implemented or not functional.

So I took the time to understand what was going on, and effectively, most of the code is in fact not usable or not used. Then I did a refactoring against the bare ACME spec about what to do to prepare challenges, instruct the ACME CA server to perform them, then polling regularly the authorization resources until they are decided (valid or invalid).

And of course this implementation takes care of Retry-After ^^

I added a lot of comments in the new implementation, to explain what is going on for a future developer. The workflow I used is relying on the relationships between authorizations and challenges states as described in section 7.1.6 of the ACME spec draft: https://datatracker.ietf.org/doc/draft-ietf-acme-acme/

* Clean auth_handler a bit, and implement retry-after.

* Remove a debug logger

* Correct tests

* Fix mypy and lint. Setup max retries and default retry after accordingly.

* Ease a comparison in tests

* Update documentation

* Add tests

* Adapt windows coverage threshold to the global LOC reduction

* Update certbot/auth_handler.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Corrections under review

* Correction under review

* Update certbot/auth_handler.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Corrections under review

* Update auth_handler_test.py

* Reimplementing user readable report for failed authorizations

* Fixes two tests

* Fix another test + lint + mypy

* Update auth_handler.py

* Update auth_handler_test.py

* Fix tests

* Update certbot/auth_handler.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Raise directly the exception on polling timout

* Improve interface documentation

* Move the wait on top of the loop, to be used initially or after a new loop iteration. Do not wait for negative values.

* Always display the report about failed authorizations.

* Clarify an exception.

* Return, instead of break

* Use setdefault

* Remove useless assertion

* Adapt tests

* Improve a test about retry after value.

* Update certbot/auth_handler.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Add a complete test on best_effort

* Add entry to the changelog

* Gather all failed authzrs to be reported in one unique report in case of best_effort

* Build complete warn/report/raise process about failed authzrs
2019-03-01 17:03:33 -08:00
Brad Warren
c7f8f15e9b Fix spurious test_relevant_values* failures (#6799)
* Mock set_by_cli in _test_relevant_values_common.

* Empty commit

* Revert "Mock set_by_cli in _test_relevant_values_common."

This reverts commit 9dfec8dfa9.

* mock less

* Use plugin_common code instead of reimplementing.

* Revert 2nd implementation.

* Simplify certbot.storage.relevant_values() tests.

In addition to cleaning up the code a bit, it also removes the problems we've
seen in these tests with the global state used in cli.py.
2019-03-02 01:04:26 +01:00
Adrien Ferrand
a468a3b255 Disable build for commits pushed on master (#6804)
Fixes #6746.

Every commit on master is always the result of a merged PR, that has been tested by Travis. So retesting the merge commit on master is superfluous. This PR uses build conditions to avoid to launch a build for a commit push on master.

I also added the equivalent logic for AppVeyor. Builds cannot received conditions, so it needs to be done on init using Exit-AppVeyorBuild. This command does not fail the build, it finishes it prematurely with success.

* Disable build for commit pushed on master (PR are still tested of course)

* Equivalent exclusion code for AppVeyor
2019-03-01 14:21:07 -08:00
Adrien Ferrand
7161e792e8 Fix the Nginx configuration during integration tests (#6801)
If you execute `tests/lock_test.py` or `tox -e integration` on a fairly recent machine, you will get the following error during tests executing against a live Nginx instance:
```
no "ssl_certificate" is defined in server listening on SSL port while SSL handshaking, client: x.x.x.x, server: y:y:y:y:z
```

Indeed, having no defined ssl certificate for a ssl port would inevitably lead to an error during the handshake SSL process between a client and this mis-configured nginx instance.

However it was not a problem one year before, because the handshake was not occurring in practice: the test just need to have a nginx started, and then immediately proceed to modify the configuration with a correct SSL setup. And nginx was able to start with a mis-configuration on SSL. 

But then this fix has been done: https://trac.nginx.org/nginx/ticket/178

Basically with this, validation of the configuration is done during nginx startup, that will refuse to start with invalid configuration on SSL. Consequently, all related tests are failing with a sufficiently up-to-date nginx. For now, it is not seen on Travis because Ubuntu Trusty is used, with an old Nginx.

The PR fixes that, by generating on the fly self-signed certificates in the two impacted tests, and pushing the right parameters in the Nginx configuration.

* Fix nginx configuration with self-signed certificates generated on the fly

* Fix lint/mypy

* Fix old cryptography

* Unattended openssl

* Update lock_test.py
2019-03-01 13:54:09 -08:00
Adrien Ferrand
841f8efd0a [Unix] Create a framework for certbot integration tests: PART 1 (#6578)
* First part

* Several optimizations about the docker env setup

* Documentation

* Various corrections and documentation. Add acme and certbot explicitly as dependencies of certbot-ci.

* Correct a variable misinterpreted as a pytest hook

* Correct strict parsing option on pebble

* Refactor acme setup to be executed from pytest hooks.

* Pass TRAVIS env variable to trigger specific xdist logic

* Retrigger build.

* Work in progress

* Config operational

* Propagate to xdist

* Corrections on acme and misc

* Correct subnet for pebble

* Remove gobetween, as tls-sni challenges are not tested anymore.

* Improve pebble setup. Reduce LOC.

* Update acme.py

* Optimize acme ca setup, with less temporary assets

* Silent setup

* Clean code

* Remove unused workspace

* Use default network driver

* Remove bridge

* Update package documentation

* Remove rerun capability for integration tests, not needed.

* Add documentation

* Variable for all ports and subnets used by the stack

* Update certbot-ci/certbot_integration_tests/conftest.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/acme.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update tox.ini

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/misc.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/acme.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/utils/acme.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot-ci/certbot_integration_tests/conftest.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Rename to acme_server

* Add comment

* Refactor in a unique context fixture

* Remove the need of CERTBOT_ACME_XDIST environment variable

* Remove nonstrict/strict options in pebble

* Clean dependencies

* Clean tox

* Change function name

* Add comment about coveragerc specificities

* Change a comment.

* Update setup.py

* Update conftest.py

* Use the production-ready docker-compose.yml file for Pebble

* New style class

* Tune pebble to have a stable test environment

* Pin a dependency
2019-03-01 13:18:06 -08:00
Adrien Ferrand
efc8d49806 Disable rerun feature of Travis (#6800)
The rerun capability in a test campaign can be a nice feature. It can also a bad design.

It is right that with high level tests, like performance or end-to-end tests, a given test runtime can depend on an external component, outside the scope of the developers, that would spuriously fail. In theses situations, having the capacity to rerun several time a test can be a great benefit. Indeed, as these tests are inherently flaky, the rerun greatly reduces their failure because of external reasons, reducing also the Pierre et le Loup effect (from a well-known french book for children): because tests constantly fail for no reason, you stop to listen to them and to not see when they fail for a real reason.

However this not apply to unit tests, or operations about code quality. For theses executions, the flakiness should not exist: a unit test is supposed to have no external dependency. A rerun approach would just hide a situation that is not desirable for this kind of tests

Also effectively I see that our tests are usually not flaky: so the only effect of the rerun is to give the failure state about a test three times slower. If a test becomes flaky, this should be fixed, and so be visible immediately in our CI.

For these reasons, I remove the travis_retry in the script section of .travis-ci.yml, to call directly tox and let the pipeline fails on the first error.

* Disable rerun feature of Travis

* Update .travis.yml

* Remove completely the retry logic
2019-02-28 12:49:36 -08:00
Adrien Ferrand
5e849e03f6 [Windows] Fix pipstrap (#6775)
* Fix pipstrap on windows

* Pipstrap pin setuptools version, so explicit install it is not needed anymore.

* Rebuild letsencrypt-auto source

* Use sys.executable in pipstrap to allow straightforward execution in a venv by choosing the python interpreter from this venv.

* Update letsencrypt-auto

* Simulate test-everything

* Revert "Simulate test-everything"

This reverts commit b62c4d719a6e741cb11126c7490097a79c68cf4d.

* Clean pipstrap code
2019-02-28 20:35:58 +01:00
schoen
a809c3697d Warn sysadmins about privilege escalation risk (#6795) 2019-02-27 16:32:57 -08:00
Adrien Ferrand
9c405a3cd1 Fix cryptography OCSP support (#6751)
* Reenabling OCSP cryptography support

* Refactor the validation logic of OCSP response to match the OpenSSL one

* Prepare runtime for OCSP response test

* Move unrelated test to another relevant place

* Reimplement OCSP status checks in integration tests

* Clean script

* Protect OCSP check against connection errors

* Update tests/certbot-boulder-integration.sh

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Cleaning

* Add a specific script for letsencrypt-auto install+help

* Remove inconsistent assertion

* Add executable permissions

* Remove unused variable

* Move testdata

* Corrected cleanup code

* Empty commit
2019-02-28 00:16:52 +01:00
Adrien Ferrand
339d034d6a Remove keyAuthorization field from the challenge response JWS token (#6758)
Fixes #6755.

POSTing the `keyAuthorization` in a JWS token when answering an ACME challenge, has been deprecated for some time now. Indeed, this is superfluous as the request is already authentified by the JWS signature.

Boulder still accepts to see this field in the JWS token, and ignore it. Pebble in non strict mode also. But Pebble in strict mode refuses the request, to prepare complete removal of this field in ACME v2.

Certbot still sends the `keyAuthorization` field. This PR removes it, and makes Certbot compliant with current ACME v2 protocol, and so Pebble in strict mode.

See also [letsencrypt/pebble#192](https://github.com/letsencrypt/pebble/issues/192) for implementation details server side.

* New implementation, with a fallback.

* Add deprecation on changelog

* Update acme/acme/client.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Fix an instance parameter

* Update changelog, extend coverage

* Update comment

* Add unit tests on keyAuthorization dump

* Update acme/acme/client.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Restrict the magic of setting a variable in immutable object in one place. Make a soon to be removed method private.
2019-02-27 09:21:47 -08:00
schoen
f5b23361bd Merge pull request #6791 from certbot/no-manual-tests
Remove display.py.
2019-02-25 15:38:30 -08:00
Julie R
401045be89 Add acme library usage example (http-01) (#5494)
* Add acme library usage example

Create, edit and deactivate account.
Setup and perform http-01 challenge.
Issue, renew and revoke certificate.

* Adapt example to ACME-v2 and exclude data persistence

The code to persist/load data would length this example and distract from what is actually important.

* Fix domain names and e-mail addresses

* Remove unnecessary license header

This usage example is under the license for the acme package.

* Remove logging information

The code will be mostly read by developers, so simplify the logging info into comments.

* Revert abstraction of simple methods

All methods that are used only once in this example were expanded into the main code in order to make the process more explicit.

* Fix missing URL suffix

* Improve aesthetics and reorganize workflow

Also make words capitalization consistent and improve comments.
No complaints from pep8.
2019-02-22 18:02:43 -08:00
Brad Warren
f105aedc92 Remove display.py. 2019-02-22 16:55:50 -08:00
Brad Warren
31b4b8e57c Log the execution of manual hooks (#6788)
* Move logging to execute and fix tests.

* update changelog
2019-02-22 16:42:01 +02:00
Adrien Ferrand
b10ceb7d90 Fix test sdists with atexit handlers (#6769)
So merging the study from @bmw and me, here is what happened.

Each invocation of `certbot.logger.post_arg_parse_setup` create a file handler on `letsencrypt.log`. This function also set an atexit handler invoking `logger.shutdown()`, that have the effect to close all logger file handler not already closed at this point. This method is supposed to be called when a python process is close to exit, because it makes all logger unable to write new logs on any handler.

Before #6667 and this PR, for tests, the atexit handle would be triggered only at the end of the pytest process. It means that each test that launches `certbot.logger.post_arg_parse_setup` add a new file handler. These tests were typically connecting the file handler on a `letsencrypt.log` located in a temporary directory, and this directory and content was wipped out at each test tearDown. As a consequence, the file handles, not cleared from the logger, were accumulating in the logger, with all of them connected to a deleted file log, except the last one that was just created by the current test. Considering the number of tests concerned, there were ~300 file handler at the end of pytest execution.

One can see that, on prior #6667, by calling `print(logger.getLogger().handlers` on the `tearDown` of these tests, and see the array growing at each test execution.

Even if this represent a memory leak, this situation was not really a problem on Linux: because a file can be deleted before it is closed, it was only meaning that a given invocation of `logger.debug` for instance, during the tests, was written in 300 log files. The overhead is negligeable. On Windows however, the file handlers were failing because you cannot delete a file before it is closed.

It was one of the reason for #6667, that added a call to `logging.shutdown()` at each test tearDown, with the consequence to close all file handlers. At this point, Linux is not happy anymore. Any call to `logger.warn` will generate an error for each closed file handler. As a file handler is added for each test, the number of errors grows on each test, following an arithmetical suite divergence.

On `test_sdists.py`, that is using the bare setuptools test suite without output capturing, we can see the damages. The total output takes 216000 lines, and 23000 errors are generated. A decent machine can support this load, but a not a small AWS instance, that is crashing during the execution. Even with pytest, the captured output and the memory leak become so large that segfaults are generated.

On the current PR, the problem is solved, by resetting the file handlers array on the logging system on each test tearDown. So each fileHandler is properly closed, and removed from the stack. They do not participate anymore in the logging system, and can be garbage collected. Then we stay on always one file handler opened at any time, and tests can succeed on AWS instances.

For the record, here is all the places where the logging system is called and fail if there is still file handlers closed but not cleaned (extracted from the original huge output before correction):

```
Logged from file account.py, line 116
Logged from file account.py, line 178
Logged from file client.py, line 166
Logged from file client.py, line 295
Logged from file client.py, line 415
Logged from file client.py, line 422
Logged from file client.py, line 480
Logged from file client.py, line 503
Logged from file client.py, line 540
Logged from file client.py, line 601
Logged from file client.py, line 622
Logged from file client.py, line 750
Logged from file cli.py, line 220
Logged from file cli.py, line 226
Logged from file crypto_util.py, line 101
Logged from file crypto_util.py, line 127
Logged from file crypto_util.py, line 147
Logged from file crypto_util.py, line 261
Logged from file crypto_util.py, line 283
Logged from file crypto_util.py, line 307
Logged from file crypto_util.py, line 336
Logged from file disco.py, line 116
Logged from file disco.py, line 124
Logged from file disco.py, line 134
Logged from file disco.py, line 138
Logged from file disco.py, line 141
Logged from file dns_common_lexicon.py, line 45
Logged from file dns_common_lexicon.py, line 61
Logged from file dns_common_lexicon.py, line 67
Logged from file dns_common.py, line 316
Logged from file dns_common.py, line 64
Logged from file eff.py, line 60
Logged from file eff.py, line 73
Logged from file error_handler.py, line 105
Logged from file error_handler.py, line 110
Logged from file error_handler.py, line 87
Logged from file hooks.py, line 248
Logged from file main.py, line 1071
Logged from file main.py, line 1075
Logged from file main.py, line 1189
Logged from file ops.py, line 122
Logged from file ops.py, line 325
Logged from file ops.py, line 338
Logged from file reporter.py, line 55
Logged from file selection.py, line 110
Logged from file selection.py, line 118
Logged from file selection.py, line 123
Logged from file selection.py, line 176
Logged from file selection.py, line 231
Logged from file selection.py, line 310
Logged from file selection.py, line 66
Logged from file standalone.py, line 101
Logged from file standalone.py, line 88
Logged from file standalone.py, line 97
Logged from file standalone.py, line 98
Logged from file storage.py, line 52
Logged from file storage.py, line 59
Logged from file storage.py, line 75
Logged from file util.py, line 56
Logged from file webroot.py, line 165
Logged from file webroot.py, line 186
Logged from file webroot.py, line 187
Logged from file webroot.py, line 204
Logged from file webroot.py, line 223
Logged from file webroot.py, line 234
Logged from file webroot.py, line 235
Logged from file webroot.py, line 237
Logged from file webroot.py, line 91
```

* Reapply #6667

* Make setuptools delegates tests execution to pytest, like in acme module.

* Clean handlers at each tearDown to avoid memory leaks.

* Update changelog
2019-02-21 16:55:08 -08:00
Adrien Ferrand
eb5c4eca87 [Windows] Working unit tests for certbot-nginx (#6782)
This PR fixes certbot-nginx and relevant tests to make them succeed on Windows.

Next step will be to enable integration tests through certbot-ci in a future PR.

* Fix tests and incompabilities in certbot-nginx for Windows

* Fix lint, fix oldest local dependencies
2019-02-20 16:20:16 -08:00
ohemorange
eef4c47633 Add failure message if test farm tests do not run the correct number of tests. (#6771)
Fixes #6748.
2019-02-20 15:20:44 -08:00
Joona Hoikkala
8bda10541a Add stdin option for merge_requirements
Add stdin and file path support to strip_hashes
2019-02-20 21:34:05 +02:00
sydneyli
7c731599a0 Generate constraints file to pin deps in Docker images
Dockerfiles pin versions using constraints file

Pulling out strip_hashes and add --no-deps flag
2019-02-20 16:59:55 +02:00
sblondon
bda840b3ee add version parameter when the help message is displayed (#6780) 2019-02-20 01:08:20 +01:00
Adrien Ferrand
209a0c4d2c [Windows] Refactor lock_and_call using queues (#6778)
* Refactor lock_and_call using queues

* Update util.py

* Replace queue by event

* Add comments

* Update certbot/tests/util.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Update certbot/tests/util.py

Co-Authored-By: adferrand <adferrand@users.noreply.github.com>

* Add control on timeout
2019-02-19 15:15:06 -08:00
Adrien Ferrand
0489ca5888 [Windows] Fixes lock_and_call test method (#6772)
The method `lock_and_call`, in `certbot.tests.util` is designed to acquire a lock on a foreign process, then execute a callable in the current process. This is done to closely reproduce the lock mechanism involved between two certbot instances that are running in parallel.

This method uses the `multiprocessing` module. But its implementation in `lock_and_call` is broken for Windows: the two processes fail to communicate, leading to a deadlock.

In fact, `multiprocessing` module is using the fork mechanism on Linux, and the spawn mechanism on Windows, leading to behavior inconsistencies between the two platforms.

As this method is for tests, and not for production code, I did not try to make two implementations "by the book", one suitable for Windows, the other for Linux, like for the `certbot.lock` module.

Instead, I use a `subprocess` approach with a trigger file allowing to coordinate the current process and the subprocess. With this, `lock_and_call` is running from the same code both on Linux and Windows.

Relevant tests in the `certbot.tests.lock_test` test module are now enabled for Windows.

* Implement new lock_and_call method

* Reactivate tests for Windows
2019-02-15 18:51:22 -08:00
Adrien Ferrand
e40d929e80 [Windows|Unix] New platform independent locking mechanism - the revenge (#6663)
First PR about this issue, #6440, involved to much refactoring to ensure a correct behavior on Linux and installer plugins.

This PR proposes a new implementation of a lock mechanism for Linux and Windows, without implying a refactoring. It takes strictly the existing behavior for Linux, and add the appropriate logic for Windows. The `lock` module formalizes two independant mechanism dedicated to each platform, to improve maintainability.

Tests related to locking are re-activated for Windows, or definitively skipped because of irrelevancy for this platform. 6 more tests are enabled overall.

* Reimplement lock file on basic level

* Remove unused code

* Re-activate some tests

* Update doc

* Reactivate tests relevant to locks in Windows. Correct a test that was not testing what is supposed to test.

* Clean compat.

* Move close sooner in Windows lock implementation

* Add strong mypy types

* Use os.name

* Refactor lock mechanism logic

* Enable more tests

* Update lock.py

* Update lock_test.py
2019-02-14 16:55:27 -08:00
Brad Warren
583d40f5cf Pin pytest in test_sdists.sh. (#6764)
* pin pytest in test_sdists.sh.

* Use pip_install.py in test_tests.sh.
2019-02-14 15:26:44 -08:00
Adrien Ferrand
acc0b1e773 Fix the pebble fetch script (#6765)
This PR updates and fixes `pebble-fetch.sh` considering latest improvements done on Pebble, to start a working instance.

* Fix the pebble fetch script

* Update pebble-fetch.sh

* Update tox.ini
2019-02-14 10:43:27 -08:00
Joona Hoikkala
cff8769db7 Apache: respect CERTBOT_DOCS environment variable (#6598)
Apache plugin will now use command line default values from `ApacheConfingurator.OS_DEFAULTS` instead of respective distribution override when `CERTBOT_DOCS=1` environment variable is present.

Fixes: #6234

* Apache: respect CERTBOT_DOCS environment variable

* Move the tests to apache plugin
2019-02-13 08:37:01 -08:00
Brad Warren
f10f98fec5 More carefully check for certbot --version output. (#6762) 2019-02-12 16:54:04 -08:00
Adrien Ferrand
a0a8292ff2 Correct the Content-Type used in the POST-as-GET request to retrieve a cert (#6757) 2019-02-12 15:36:27 -08:00
Brad Warren
66c9767623 Fix #6501 (#6761) 2019-02-12 23:59:34 +01:00
Brad Warren
ec4c03fa6d Merge pull request #6754 from certbot/candidate-0.31.0
Release 0.31.0
2019-02-07 15:50:39 -08:00
Brad Warren
381d097895 Bump version to 0.32.0 2019-02-07 13:27:13 -08:00
Brad Warren
917dc16b30 Add contents to CHANGELOG.md for next version 2019-02-07 13:27:12 -08:00
Brad Warren
75499277be Release 0.31.0 2019-02-07 13:27:10 -08:00
Brad Warren
ee3c14cbab Update changelog for 0.31.0 release 2019-02-07 13:20:30 -08:00
Brad Warren
432e18d943 Revert "Call atexit handlers before test tearDown to remove errors on Windows (#6667)" (#6752)
This reverts commit ca25d1b66a.
2019-02-07 21:40:45 +01:00
J0WI
67828562a0 Upgrade to Alpine 3.9 (#6743)
Alpine 3.9 comes with OpenSSL 1.1.1.
2019-02-07 09:06:04 -08:00
James Payne
5300d7d71f Fix Pylint upgrade issues
* Remove unsupported pylint disable options
    * star-args removed in Pylint 1.4.3
    * abstract-class-little-used removed in Pylint 1.4.3

* Fixes new lint errors

* Copy dummy-variable-rgx expression to new ignored-argument-names expression to ignore unused funtion arguments

* Notable changes
    * Refactor to satisfy Pylint no-else-return warning
    * Fix Pylint inconsistent-return-statements warning
    * Refactor to satisfy consider-iterating-dictionary
    * Remove methods with only super call to satisfy useless-super-delegation
    * Refactor too-many-nested-statements where possible
    * Suppress type checked errors where member is dynamically added (notably derived from josepy.JSONObjectWithFields)
    * Remove None default of func parameter for ExitHandler and ErrorHandler

Resolves #5973
2018-05-16 20:37:39 +00:00
cclauss
24974b07ba Safer to pylint on Python 3 2018-05-16 19:28:51 +00:00
967 changed files with 19562 additions and 17489 deletions

119
.azure-pipelines/INSTALL.md Normal file
View File

@@ -0,0 +1,119 @@
# Configuring Azure Pipelines with Certbot
Let's begin. All pipelines are defined in `.azure-pipelines`. Currently there are two:
* `.azure-pipelines/main.yml` is the main one, executed on PRs for master, and pushes to master,
* `.azure-pipelines/advanced.yml` add installer testing on top of the main pipeline, and is executed for `test-*` branches, release branches, and nightly run for master.
Several templates are defined in `.azure-pipelines/templates`. These YAML files aggregate common jobs configuration that can be reused in several pipelines.
Unlike Travis, where CodeCov is working without any action required, CodeCov supports Azure Pipelines
using the coverage-bash utility (not python-coverage for now) only if you provide the Codecov repo token
using the `CODECOV_TOKEN` environment variable. So `CODECOV_TOKEN` needs to be set as a secured
environment variable to allow the main pipeline to publish coverage reports to CodeCov.
This INSTALL.md file explains how to configure Azure Pipelines with Certbot in order to execute the CI/CD logic defined in `.azure-pipelines` folder with it.
During this installation step, warnings describing user access and legal comitments will be displayed like this:
```
!!! ACCESS REQUIRED !!!
```
This document suppose that the Azure DevOps organization is named _certbot_, and the Azure DevOps project is also _certbot_.
## Useful links
* https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema
* https://www.azuredevopslabs.com/labs/azuredevops/github-integration/
* https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/python?view=azure-devops
## Prerequisites
### Having a GitHub account
Use your GitHub user for a normal GitHub account, or a user that has administrative rights to the GitHub organization if relevant.
### Having an Azure DevOps account
- Go to https://dev.azure.com/, click "Start free with GitHub"
- Login to GitHub
```
!!! ACCESS REQUIRED !!!
Personal user data (email + profile info, in read-only)
```
- Microsoft will create a Live account using the email referenced for the GitHub account. This account is also linked to GitHub account (meaning you can log it using GitHub authentication)
- Proceed with account registration (birth date, country), add details about name and email contact
```
!!! ACCESS REQUIRED !!!
Microsoft proposes to send commercial links to this mail
Azure DevOps terms of service need to be accepted
```
_Logged to Azure DevOps, account is ready._
### Installing Azure Pipelines to GitHub
- On GitHub, go to Marketplace
- Select Azure Pipeline, and "Set up a plan"
- Select Free, then "Install it for free"
- Click "Complete order and begin installation"
```
!!! ACCESS !!!
Azure Pipeline needs RW on code, RO on metadata, RW on checks, commit statuses, deployments, issues, pull requests.
RW access here is required to allow update of the pipelines YAML files from Azure DevOps interface, and to
update the status of builds and PRs on GitHub side when Azure Pipelines are triggered.
Note however that no admin access is defined here: this means that Azure Pipelines cannot do anything with
protected branches, like master, and cannot modify the security context around this on GitHub.
Access can be defined for all or only selected repositories, which is nice.
```
- Redirected to Azure DevOps, select the account created in _Having an Azure DevOps account_ section.
- Select the organization, and click "Create a new project" (let's name it the same than the targetted github repo)
- The Visibility is public, to profit from 10 parallel jobs
```
!!! ACCESS !!!
Azure Pipelines needs access to the GitHub account (in term of beeing able to check it is valid), and the Resources shared between the GitHub account and Azure Pipelines.
```
_Done. We can move to pipelines configuration._
## Import an existing pipelines from `.azure-pipelines` folder
- On Azure DevOps, go to your organization (eg. _certbot_) then your project (eg. _certbot_)
- Click "Pipelines" tab
- Click "New pipeline"
- Where is your code?: select "__Use the classic editor__"
__Warning: Do not choose the GitHub option in Where is your code? section. Indeed, this option will trigger an OAuth
grant permissions from Azure Pipelines to GitHub in order to setup a GitHub OAuth Application. The permissions asked
then are way too large (admin level on almost everything), while the classic approach does not add any more
permissions, and works perfectly well.__
- Select GitHub in "Select your repository section", choose certbot/certbot in Repository, master in default branch.
- Click on YAML option for "Select a template"
- Choose a name for the pipeline (eg. test-pipeline), and browse to the actual pipeline YAML definition in the
"YAML file path" input (eg. `.azure-pipelines/test-pipeline.yml`)
- Click "Save & queue", choose the master branch to build the first pipeline, and click "Save and run" button.
_Done. Pipeline is operational. Repeat to add more pipelines from existing YAML files in `.azure-pipelines`._
## Add a secret variable to a pipeline (like `CODECOV_TOKEN`)
__NB: Following steps suppose that you already setup the YAML pipeline file to
consume the secret variable that these steps will create as an environment variable.
For a variable named `CODECOV_TOKEN` consuming the variable `codecov_token`,
in the YAML file this setup would take the form of the following:
```
steps:
- script: ./do_something_that_consumes_CODECOV_TOKEN # Eg. `codecov -F windows`
env:
CODECOV_TOKEN: $(codecov_token)
```
To set up a variable that is shared between pipelines, follow the instructions
at
https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups.
When adding variables to a group, don't forget to tick "Keep this value secret"
if it shouldn't be shared publcily.

View File

@@ -0,0 +1,19 @@
# Advanced pipeline for isolated checks and release purpose
trigger:
- test-*
- '*.x'
pr:
- test-*
# This pipeline is also nightly run on master
schedules:
- cron: "0 4 * * *"
displayName: Nightly build
branches:
include:
- master
always: true
jobs:
# Any addition here should be reflected in the release pipeline.
# It is advised to declare all jobs here as templates to improve maintainability.
- template: templates/installer-tests.yml

12
.azure-pipelines/main.yml Normal file
View File

@@ -0,0 +1,12 @@
trigger:
# apache-parser-v2 is a temporary branch for doing work related to
# rewriting the parser in the Apache plugin.
- apache-parser-v2
- master
pr:
- apache-parser-v2
- master
- '*.x'
jobs:
- template: templates/tests-suite.yml

View File

@@ -0,0 +1,13 @@
# Release pipeline to build and deploy Certbot for Windows for GitHub release tags
trigger:
tags:
include:
- v*
pr: none
jobs:
# Any addition here should be reflected in the advanced pipeline.
# It is advised to declare all jobs here as templates to improve maintainability.
- template: templates/tests-suite.yml
- template: templates/installer-tests.yml
- template: templates/changelog.yml

View File

@@ -0,0 +1,14 @@
jobs:
- job: changelog
pool:
vmImage: vs2017-win2016
steps:
- bash: |
CERTBOT_VERSION="$(cd certbot && python -c "import certbot; print(certbot.__version__)" && cd ~-)"
"${BUILD_REPOSITORY_LOCALPATH}\tools\extract_changelog.py" "${CERTBOT_VERSION}" >> "${BUILD_ARTIFACTSTAGINGDIRECTORY}/release_notes.md"
displayName: Prepare changelog
- task: PublishPipelineArtifact@1
inputs:
path: $(Build.ArtifactStagingDirectory)
artifact: changelog
displayName: Publish changelog

View File

@@ -0,0 +1,15 @@
jobs:
- job: installer_run
strategy:
matrix:
win2019:
imageName: windows-2019
win2016:
imageName: vs2017-win2016
win2012r2:
imageName: vs2015-win2012r2
pool:
vmImage: $(imageName)
steps:
- script: wusa /uninstall /kb:3134758 /quiet /norestart & exit 0
- script: powershell -Command "$PSVersionTable.PSVersion"

View File

@@ -0,0 +1,38 @@
jobs:
- job: test
pool:
vmImage: vs2017-win2016
strategy:
matrix:
py35:
PYTHON_VERSION: 3.5
TOXENV: py35
py37-cover:
PYTHON_VERSION: 3.7
TOXENV: py37-cover
integration-certbot:
PYTHON_VERSION: 3.7
TOXENV: integration-certbot
PYTEST_ADDOPTS: --numprocesses 4
variables:
- group: certbot-common
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: $(PYTHON_VERSION)
addToPath: true
- script: python tools/pip_install.py -U tox coverage
displayName: Install dependencies
- script: python -m tox
displayName: Run tox
# We do not require codecov report upload to succeed. So to avoid to break the pipeline if
# something goes wrong, each command is suffixed with a command that hides any non zero exit
# codes and echoes an informative message instead.
- bash: |
curl -s https://codecov.io/bash -o codecov-bash || echo "Failed to download codecov-bash"
chmod +x codecov-bash || echo "Failed to apply execute permissions on codecov-bash"
./codecov-bash -F windows || echo "Codecov did not collect coverage reports"
condition: in(variables['TOXENV'], 'py37-cover', 'integration-certbot')
env:
CODECOV_TOKEN: $(codecov_token)
displayName: Publish coverage

18
.codecov.yml Normal file
View File

@@ -0,0 +1,18 @@
coverage:
status:
project:
default: off
linux:
flags: linux
# Fixed target instead of auto set by #7173, can
# be removed when flags in Codecov are added back.
target: 97.4
threshold: 0.1
base: auto
windows:
flags: windows
# Fixed target instead of auto set by #7173, can
# be removed when flags in Codecov are added back.
target: 97.4
threshold: 0.1
base: auto

View File

@@ -1,2 +1,5 @@
[run]
omit = */setup.py
[report]
omit = */setup.py

35
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
# Configuration for https://github.com/marketplace/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 365
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
# When changing this value, be sure to also update markComment below.
daysUntilClose: 30
# Ignore issues with an assignee (defaults to false)
exemptAssignees: true
# Label to use when marking as stale
staleLabel: needs-update
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
We've made a lot of changes to Certbot since this issue was opened. If you
still have this issue with an up-to-date version of Certbot, can you please
add a comment letting us know? This helps us to better see what issues are
still affecting our users. If there is no activity in the next 30 days, this
issue will be automatically closed.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been closed due to lack of activity, but if you think it
should be reopened, please open a new issue with a link to this one and we'll
take a look.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1
# Don't mark pull requests as stale.
only: issues

5
.gitignore vendored
View File

@@ -44,3 +44,8 @@ tests/letstest/venv/
# docker files
.docker
# certbot tests
.certbot_test_workspace
**/assets/pebble*
**/assets/challtestsrv*

7
.isort.cfg Normal file
View File

@@ -0,0 +1,7 @@
[settings]
skip_glob=venv*
skip=letsencrypt-auto-source
force_sort_within_sections=True
force_single_line=True
order_by_type=False
line_length=400

View File

@@ -24,6 +24,11 @@ persistent=yes
# usually to register additional checkers.
load-plugins=linter_plugin
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=pywintypes,win32api,win32file,win32security
[MESSAGES CONTROL]
@@ -41,10 +46,14 @@ load-plugins=linter_plugin
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=fixme,locally-disabled,locally-enabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes,cyclic-import,duplicate-code
# abstract-class-not-used cannot be disabled locally (at least in
# pylint 1.4.1), same for abstract-class-little-used
# CERTBOT COMMENT
# 1) Once certbot codebase is claimed to be compatible exclusively with Python 3,
# the useless-object-inheritance check can be enabled again, and code fixed accordingly.
# 2) Check unsubscriptable-object tends to create a lot of false positives. Let's disable it.
# See https://github.com/PyCQA/pylint/issues/1498.
# 3) Same as point 2 for no-value-for-parameter.
# See https://github.com/PyCQA/pylint/issues/2820.
disable=fixme,locally-disabled,locally-enabled,bad-continuation,no-self-use,invalid-name,cyclic-import,duplicate-code,design,import-outside-toplevel,useless-object-inheritance,unsubscriptable-object,no-value-for-parameter,no-else-return,no-else-raise,no-else-break,no-else-continue
[REPORTS]
@@ -251,7 +260,7 @@ ignored-modules=pkg_resources,confargparse,argparse,six.moves,six.moves.urllib
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject
ignored-classes=Field,Header,JWS,closing
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
@@ -297,40 +306,6 @@ valid-classmethod-first-arg=cls
valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
# Maximum number of arguments for function / method
max-args=6
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=12
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to

View File

@@ -1,11 +1,16 @@
language: python
dist: xenial
cache:
directories:
- $HOME/.cache/pip
before_script:
- 'if [ $TRAVIS_OS_NAME = osx ] ; then ulimit -n 1024 ; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ulimit -n 1024 ; fi'
# On Travis, the fastest parallelization for integration tests has proved to be 4.
- 'if [[ "$TOXENV" == *"integration"* ]]; then export PYTEST_ADDOPTS="--numprocesses 4"; fi'
# Use Travis retry feature for farm tests since they are flaky
- 'if [[ "$TOXENV" == "travis-test-farm"* ]]; then export TRAVIS_RETRY=travis_retry; fi'
- export TOX_TESTENV_PASSENV=TRAVIS
# Only build pushes to the master branch, PRs, and branches beginning with
@@ -14,171 +19,27 @@ before_script:
# is a cap of on the number of simultaneous runs.
branches:
only:
# apache-parser-v2 is a temporary branch for doing work related to
# rewriting the parser in the Apache plugin.
- apache-parser-v2
- master
- /^\d+\.\d+\.x$/
- /^test-.*$/
# Jobs for the main test suite are always executed (including on PRs) except for pushes on master.
not-on-master: &not-on-master
if: NOT (type = push AND branch = master)
# Jobs for the extended test suite are executed for cron jobs and pushes to
# non-development branches. See the explanation for apache-parser-v2 above.
extended-test-suite: &extended-test-suite
if: type = cron OR (type = push AND branch NOT IN (apache-parser-v2, master))
matrix:
include:
# These environments are always executed
- python: "2.7"
env: BOULDER_INTEGRATION=v1 INTEGRATION_TEST=all TOXENV=py27_install
sudo: required
services: docker
- python: "2.7"
env: BOULDER_INTEGRATION=v2 INTEGRATION_TEST=all TOXENV=py27_install
sudo: required
services: docker
# This job is always executed, including on master
- python: "2.7"
env: TOXENV=py27-cover FYI="py27 tests + code coverage"
- sudo: required
env: TOXENV=nginx_compat
services: docker
before_install:
addons:
- python: "2.7"
env: TOXENV=lint
- python: "3.4"
env: TOXENV=mypy
- python: "3.5"
env: TOXENV=mypy
- python: "2.7"
env: TOXENV='py27-{acme,apache,certbot,dns,nginx,postfix}-oldest'
sudo: required
services: docker
- python: "3.4"
env: TOXENV=py34
sudo: required
services: docker
- python: "3.7"
dist: xenial
env: TOXENV=py37
sudo: required
services: docker
- sudo: required
env: TOXENV=apache_compat
services: docker
before_install:
addons:
- sudo: required
env: TOXENV=le_auto_trusty
services: docker
before_install:
addons:
- python: "2.7"
env: TOXENV=apacheconftest-with-pebble
sudo: required
services: docker
- python: "2.7"
env: TOXENV=nginxroundtrip
# These environments are executed on cron events and commits to tested
# branches other than master. Which branches are tested is controlled by
# the "branches" section earlier in this file.
- python: "3.7"
dist: xenial
env: TOXENV=py37 CERTBOT_NO_PIN=1
if: type = cron OR (type = push AND branch != master)
- python: "2.7"
env: BOULDER_INTEGRATION=v1 INTEGRATION_TEST=certbot TOXENV=py27-certbot-oldest
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "2.7"
env: BOULDER_INTEGRATION=v2 INTEGRATION_TEST=certbot TOXENV=py27-certbot-oldest
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "2.7"
env: BOULDER_INTEGRATION=v1 INTEGRATION_TEST=nginx TOXENV=py27-nginx-oldest
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "2.7"
env: BOULDER_INTEGRATION=v2 INTEGRATION_TEST=nginx TOXENV=py27-nginx-oldest
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.4"
env: TOXENV=py34 BOULDER_INTEGRATION=v1
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.4"
env: TOXENV=py34 BOULDER_INTEGRATION=v2
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.5"
env: TOXENV=py35 BOULDER_INTEGRATION=v1
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.5"
env: TOXENV=py35 BOULDER_INTEGRATION=v2
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.6"
env: TOXENV=py36 BOULDER_INTEGRATION=v1
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.6"
env: TOXENV=py36 BOULDER_INTEGRATION=v2
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.7"
dist: xenial
env: TOXENV=py37 BOULDER_INTEGRATION=v1
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- python: "3.7"
dist: xenial
env: TOXENV=py37 BOULDER_INTEGRATION=v2
sudo: required
services: docker
if: type = cron OR (type = push AND branch != master)
- sudo: required
env: TOXENV=le_auto_xenial
services: docker
if: type = cron OR (type = push AND branch != master)
- sudo: required
env: TOXENV=le_auto_jessie
services: docker
if: type = cron OR (type = push AND branch != master)
- sudo: required
env: TOXENV=le_auto_centos6
services: docker
if: type = cron OR (type = push AND branch != master)
- sudo: required
env: TOXENV=docker_dev
services: docker
addons:
apt:
packages: # don't install nginx and apache
- libaugeas0
if: type = cron OR (type = push AND branch != master)
- language: generic
env: TOXENV=py27
os: osx
addons:
homebrew:
packages:
- augeas
- python2
if: type = cron OR (type = push AND branch != master)
- language: generic
env: TOXENV=py3
os: osx
addons:
homebrew:
packages:
- augeas
- python3
if: type = cron OR (type = push AND branch != master)
# container-based infrastructure
sudo: false
@@ -187,7 +48,6 @@ addons:
apt:
packages: # Keep in sync with letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh and Boulder.
- python-dev
- python-virtualenv
- gcc
- libaugeas0
- libssl-dev
@@ -197,19 +57,19 @@ addons:
- nginx-light
- openssl
install: "travis_retry $(command -v pip || command -v pip3) install codecov tox"
script:
- travis_retry tox
- '[ -z "${BOULDER_INTEGRATION+x}" ] || (travis_retry tests/boulder-fetch.sh && tests/tox-boulder-integration.sh)'
# tools/pip_install.py is used to pin packages to a known working version
# except in tests where the environment variable CERTBOT_NO_PIN is set.
# virtualenv is listed here explicitly to make sure it is upgraded when
# CERTBOT_NO_PIN is set to work around failures we've seen when using an older
# version of virtualenv.
install: 'tools/pip_install.py -U codecov tox virtualenv'
# Most of the time TRAVIS_RETRY is an empty string, and has no effect on the
# script command. It is set only to `travis_retry` during farm tests, in
# order to trigger the Travis retry feature, and compensate the inherent
# flakiness of these specific tests.
script: '$TRAVIS_RETRY tox'
after_success: '[ "$TOXENV" == "py27-cover" ] && codecov'
after_success: '[ "$TOXENV" == "py27-cover" ] && codecov -F linux'
notifications:
email: false
irc:
channels:
- secure: "SGWZl3ownKx9xKVV2VnGt7DqkTmutJ89oJV9tjKhSs84kLijU6EYdPnllqISpfHMTxXflNZuxtGo0wTDYHXBuZL47w1O32W6nzuXdra5zC+i4sYQwYULUsyfOv9gJX8zWAULiK0Z3r0oho45U+FR5ZN6TPCidi8/eGU+EEPwaAw="
on_cancel: never
on_success: never
on_failure: always
use_notice: true

View File

@@ -5,6 +5,7 @@ Authors
* Aaron Zuehlke
* Ada Lovelace
* [Adam Woodbeck](https://github.com/awoodbeck)
* [Adrien Ferrand](https://github.com/adferrand)
* [Aidin Gharibnavaz](https://github.com/aidin36)
* [AJ ONeal](https://github.com/coolaj86)
* [Alcaro](https://github.com/Alcaro)
@@ -14,8 +15,10 @@ Authors
* [Alex Gaynor](https://github.com/alex)
* [Alex Halderman](https://github.com/jhalderm)
* [Alex Jordan](https://github.com/strugee)
* [Alex Zorin](https://github.com/alexzorin)
* [Amjad Mashaal](https://github.com/TheNavigat)
* [Andrew Murray](https://github.com/radarhere)
* [Andrzej Górski](https://github.com/andrzej3393)
* [Anselm Levskaya](https://github.com/levskaya)
* [Antoine Jacoutot](https://github.com/ajacoutot)
* [asaph](https://github.com/asaph)
@@ -75,6 +78,7 @@ Authors
* [Fabian](https://github.com/faerbit)
* [Faidon Liambotis](https://github.com/paravoid)
* [Fan Jiang](https://github.com/tcz001)
* [Felix Lechner](https://github.com/lechner)
* [Felix Schwarz](https://github.com/FelixSchwarz)
* [Felix Yan](https://github.com/felixonmars)
* [Filip Ochnik](https://github.com/filipochnik)
@@ -124,6 +128,7 @@ Authors
* [Joubin Jabbari](https://github.com/joubin)
* [Juho Juopperi](https://github.com/jkjuopperi)
* [Kane York](https://github.com/riking)
* [Kenichi Maehashi](https://github.com/kmaehashi)
* [Kenneth Skovhede](https://github.com/kenkendk)
* [Kevin Burke](https://github.com/kevinburke)
* [Kevin London](https://github.com/kevinlondon)
@@ -159,8 +164,10 @@ Authors
* [Michael Schumacher](https://github.com/schumaml)
* [Michael Strache](https://github.com/Jarodiv)
* [Michael Sverdlin](https://github.com/sveder)
* [Michael Watters](https://github.com/blackknight36)
* [Michal Moravec](https://github.com/https://github.com/Majkl578)
* [Michal Papis](https://github.com/mpapis)
* [Mickaël Schoentgen](https://github.com/BoboTiG)
* [Minn Soe](https://github.com/MinnSoe)
* [Min RK](https://github.com/minrk)
* [Miquel Ruiz](https://github.com/miquelruiz)
@@ -224,6 +231,7 @@ Authors
* [Stavros Korokithakis](https://github.com/skorokithakis)
* [Stefan Weil](https://github.com/stweil)
* [Steve Desmond](https://github.com/stevedesmond-ca)
* [sydneyli](https://github.com/sydneyli)
* [Tan Jay Jun](https://github.com/jayjun)
* [Tapple Gao](https://github.com/tapple)
* [Telepenin Nikolay](https://github.com/telepenin)

File diff suppressed because it is too large Load Diff

1
CHANGELOG.md Symbolic link
View File

@@ -0,0 +1 @@
certbot/CHANGELOG.md

1
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1 @@
This project is governed by [EFF's Public Projects Code of Conduct](https://www.eff.org/pages/eppcode).

View File

@@ -33,3 +33,5 @@ started. In particular, we recommend you read these sections
- [Finding issues to work on](https://certbot.eff.org/docs/contributing.html#find-issues-to-work-on)
- [Coding style](https://certbot.eff.org/docs/contributing.html#coding-style)
- [Submitting a pull request](https://certbot.eff.org/docs/contributing.html#submitting-a-pull-request)
- [EFF's Public Projects Code of Conduct](https://www.eff.org/pages/eppcode)

View File

@@ -1,29 +0,0 @@
FROM python:2-alpine3.7
ENTRYPOINT [ "certbot" ]
EXPOSE 80 443
VOLUME /etc/letsencrypt /var/lib/letsencrypt
WORKDIR /opt/certbot
COPY CHANGELOG.md README.rst setup.py src/
COPY letsencrypt-auto-source/pieces/dependency-requirements.txt .
COPY acme src/acme
COPY certbot src/certbot
RUN apk add --no-cache --virtual .certbot-deps \
libffi \
libssl1.0 \
openssl \
ca-certificates \
binutils
RUN apk add --no-cache --virtual .build-deps \
gcc \
linux-headers \
openssl-dev \
musl-dev \
libffi-dev \
&& pip install -r /opt/certbot/dependency-requirements.txt \
&& pip install --no-cache-dir \
--editable /opt/certbot/src/acme \
--editable /opt/certbot/src \
&& apk del .build-deps

View File

@@ -1,21 +1,20 @@
# This Dockerfile builds an image for development.
FROM ubuntu:xenial
FROM debian:buster
# Note: this only exposes the port to other docker containers.
EXPOSE 80 443
WORKDIR /opt/certbot/src
# TODO: Install Apache/Nginx for plugin development.
COPY . .
RUN apt-get update && \
apt-get install apache2 git nginx-light -y && \
letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \
apt-get install apache2 git python3-dev python3-venv gcc libaugeas0 \
libssl-dev libffi-dev ca-certificates openssl nginx-light -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* \
/tmp/* \
/var/tmp/*
RUN VENV_NAME="../venv" python tools/venv.py
RUN VENV_NAME="../venv3" python3 tools/venv3.py
ENV PATH /opt/certbot/venv/bin:$PATH
ENV PATH /opt/certbot/venv3/bin:$PATH

View File

@@ -1,75 +0,0 @@
# https://github.com/letsencrypt/letsencrypt/pull/431#issuecomment-103659297
# it is more likely developers will already have ubuntu:trusty rather
# than e.g. debian:jessie and image size differences are negligible
FROM ubuntu:trusty
MAINTAINER Jakub Warmuz <jakub@warmuz.org>
MAINTAINER William Budington <bill@eff.org>
# Note: this only exposes the port to other docker containers. You
# still have to bind to 443@host at runtime, as per the ACME spec.
EXPOSE 443
# TODO: make sure --config-dir and --work-dir cannot be changed
# through the CLI (certbot-docker wrapper that uses standalone
# authenticator and text mode only?)
VOLUME /etc/letsencrypt /var/lib/letsencrypt
WORKDIR /opt/certbot
# no need to mkdir anything:
# https://docs.docker.com/reference/builder/#copy
# If <dest> doesn't exist, it is created along with all missing
# directories in its path.
ENV DEBIAN_FRONTEND=noninteractive
COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto
RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* \
/tmp/* \
/var/tmp/*
# the above is not likely to change, so by putting it further up the
# Dockerfile we make sure we cache as much as possible
COPY setup.py README.rst CHANGELOG.md MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/certbot/src/
# all above files are necessary for setup.py and venv setup, however,
# package source code directory has to be copied separately to a
# subdirectory...
# https://docs.docker.com/reference/builder/#copy: "If <src> is a
# directory, the entire contents of the directory are copied,
# including filesystem metadata. Note: The directory itself is not
# copied, just its contents." Order again matters, three files are far
# more likely to be cached than the whole project directory
COPY certbot /opt/certbot/src/certbot/
COPY acme /opt/certbot/src/acme/
COPY certbot-apache /opt/certbot/src/certbot-apache/
COPY certbot-nginx /opt/certbot/src/certbot-nginx/
RUN VIRTUALENV_NO_DOWNLOAD=1 virtualenv --no-site-packages -p python2 /opt/certbot/venv
# PATH is set now so pipstrap upgrades the correct (v)env
ENV PATH /opt/certbot/venv/bin:$PATH
RUN /opt/certbot/venv/bin/python /opt/certbot/src/pipstrap.py && \
/opt/certbot/venv/bin/pip install \
-e /opt/certbot/src/acme \
-e /opt/certbot/src \
-e /opt/certbot/src/certbot-apache \
-e /opt/certbot/src/certbot-nginx
# 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);
# this might also help in debugging: you can "docker run --entrypoint
# bash" and investigate, apply patches, etc.
# set up certbot/letsencrypt wrapper to warn people about Dockerfile changes
COPY tools/docker-warning.sh /opt/certbot/bin/certbot
RUN ln -s /opt/certbot/bin/certbot /opt/certbot/bin/letsencrypt
ENV PATH /opt/certbot/bin:$PATH
ENTRYPOINT [ "certbot" ]

View File

@@ -1,157 +0,0 @@
.. This file contains a series of comments that are used to include sections of this README in other files. Do not modify these comments unless you know what you are doing. tag:intro-begin
Certbot is part of EFFs effort to encrypt the entire Internet. Secure communication over the Web relies on HTTPS, which requires the use of a digital certificate that lets browsers verify the identity of web servers (e.g., is that really google.com?). Web servers obtain their certificates from trusted third parties called certificate authorities (CAs). Certbot is an easy-to-use client that fetches a certificate from Lets Encrypt—an open certificate authority launched by the EFF, Mozilla, and others—and deploys it to a web server.
Anyone who has gone through the trouble of setting up a secure website knows what a hassle getting and maintaining a certificate is. Certbot and Lets Encrypt can automate away the pain and let you turn on and manage HTTPS with simple commands. Using Certbot and Let's Encrypt is free, so theres no need to arrange payment.
How you use Certbot depends on the configuration of your web server. The best way to get started is to use our `interactive guide <https://certbot.eff.org>`_. It generates instructions based on your configuration settings. In most cases, youll need `root or administrator access <https://certbot.eff.org/faq/#does-certbot-require-root-administrator-privileges>`_ to your web server to run Certbot.
Certbot is meant to be run directly on your web server, not on your personal computer. If youre using a hosted service and dont have direct access to your web server, you might not be able to use Certbot. Check with your hosting provider for documentation about uploading certificates or using certificates issued by Lets Encrypt.
Certbot is a fully-featured, extensible client for the Let's
Encrypt CA (or any other CA that speaks the `ACME
<https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md>`_
protocol) that can automate the tasks of obtaining certificates and
configuring webservers to use them. This client runs on Unix-based operating
systems.
To see the changes made to Certbot between versions please refer to our
`changelog <https://github.com/certbot/certbot/blob/master/CHANGELOG.md>`_.
Until May 2016, Certbot was named simply ``letsencrypt`` or ``letsencrypt-auto``,
depending on install method. Instructions on the Internet, and some pieces of the
software, may still refer to this older name.
Contributing
------------
If you'd like to contribute to this project please read `Developer Guide
<https://certbot.eff.org/docs/contributing.html>`_.
.. _installation:
Installation
------------
The easiest way to install Certbot is by visiting `certbot.eff.org`_, where you can
find the correct installation instructions for many web server and OS combinations.
For more information, see `Get Certbot <https://certbot.eff.org/docs/install.html>`_.
.. _certbot.eff.org: https://certbot.eff.org/
How to run the client
---------------------
In many cases, you can just run ``certbot-auto`` or ``certbot``, and the
client will guide you through the process of obtaining and installing certs
interactively.
For full command line help, you can type::
./certbot-auto --help all
You can also tell it exactly what you want it to do from the command line.
For instance, if you want to obtain a cert for ``example.com``,
``www.example.com``, and ``other.example.net``, using the Apache plugin to both
obtain and install the certs, you could do this::
./certbot-auto --apache -d example.com -d www.example.com -d other.example.net
(The first time you run the command, it will make an account, and ask for an
email and agreement to the Let's Encrypt Subscriber Agreement; you can
automate those with ``--email`` and ``--agree-tos``)
If you want to use a webserver that doesn't have full plugin support yet, you
can still use "standalone" or "webroot" plugins to obtain a certificate::
./certbot-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net
Understanding the client in more depth
--------------------------------------
To understand what the client is doing in detail, it's important to
understand the way it uses plugins. Please see the `explanation of
plugins <https://certbot.eff.org/docs/using.html#plugins>`_ in
the User Guide.
Links
=====
.. Do not modify this comment unless you know what you're doing. tag:links-begin
Documentation: https://certbot.eff.org/docs
Software project: https://github.com/certbot/certbot
Notes for developers: https://certbot.eff.org/docs/contributing.html
Main Website: https://certbot.eff.org
Let's Encrypt Website: https://letsencrypt.org
Community: https://community.letsencrypt.org
ACME spec: http://ietf-wg-acme.github.io/acme/
ACME working area in github: https://github.com/ietf-wg-acme/acme
|build-status| |coverage| |docs| |container|
.. |build-status| image:: https://travis-ci.com/certbot/certbot.svg?branch=master
:target: https://travis-ci.com/certbot/certbot
:alt: Travis CI status
.. |coverage| image:: https://codecov.io/gh/certbot/certbot/branch/master/graph/badge.svg
:target: https://codecov.io/gh/certbot/certbot
:alt: Coverage status
.. |docs| image:: https://readthedocs.org/projects/letsencrypt/badge/
:target: https://readthedocs.org/projects/letsencrypt/
:alt: Documentation status
.. |container| image:: https://quay.io/repository/letsencrypt/letsencrypt/status
:target: https://quay.io/repository/letsencrypt/letsencrypt
:alt: Docker Repository on Quay.io
.. Do not modify this comment unless you know what you're doing. tag:links-end
System Requirements
===================
See https://certbot.eff.org/docs/install.html#system-requirements.
.. Do not modify this comment unless you know what you're doing. tag:intro-end
.. Do not modify this comment unless you know what you're doing. tag:features-begin
Current Features
=====================
* Supports multiple web servers:
- apache/2.x
- nginx/0.8.48+
- webroot (adds files to webroot directories in order to prove control of
domains and obtain certs)
- standalone (runs its own simple webserver to prove you control a domain)
- other server software via `third party plugins <https://certbot.eff.org/docs/using.html#third-party-plugins>`_
* The private key is generated locally on your system.
* Can talk to the Let's Encrypt CA or optionally to other ACME
compliant services.
* Can get domain-validated (DV) certificates.
* Can revoke certificates.
* Adjustable RSA key bit-length (2048 (default), 4096, ...).
* Can optionally install a http -> https redirect, so your site effectively
runs https only (Apache only)
* Fully automated.
* Configuration changes are logged and can be reverted.
* Supports an interactive text UI, or can be driven entirely from the
command line.
* Free and Open Source Software, made with Python.
.. Do not modify this comment unless you know what you're doing. tag:features-end
For extensive documentation on using and contributing to Certbot, go to https://certbot.eff.org/docs. If you would like to contribute to the project or run the latest code from git, you should read our `developer guide <https://certbot.eff.org/docs/contributing.html>`_.

1
README.rst Symbolic link
View File

@@ -0,0 +1 @@
certbot/README.rst

View File

@@ -3,4 +3,6 @@ include README.rst
include pytest.ini
recursive-include docs *
recursive-include examples *
recursive-include acme/testdata *
recursive-include tests *
global-exclude __pycache__
global-exclude *.py[cod]

View File

@@ -6,13 +6,13 @@ 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.
#
# It is based on
# https://github.com/requests/requests/blob/1278ecdf71a312dc2268f3bfc0aabfab3c006dcf/requests/packages.py
import josepy as jose
for mod in list(sys.modules):

View File

@@ -3,27 +3,19 @@ import abc
import functools
import hashlib
import logging
import socket
import warnings
from cryptography.hazmat.primitives import hashes # type: ignore
import josepy as jose
import OpenSSL
import requests
import six
from acme import errors
from acme import crypto_util
from acme import fields
logger = logging.getLogger(__name__)
# pylint: disable=too-few-public-methods
class Challenge(jose.TypedJSONObjectWithFields):
# _fields_to_partial_json | pylint: disable=abstract-method
# _fields_to_partial_json
"""ACME challenge."""
TYPES = {} # type: dict
@@ -37,7 +29,7 @@ class Challenge(jose.TypedJSONObjectWithFields):
class ChallengeResponse(jose.TypedJSONObjectWithFields):
# _fields_to_partial_json | pylint: disable=abstract-method
# _fields_to_partial_json
"""ACME challenge response."""
TYPES = {} # type: dict
resource_type = 'challenge'
@@ -62,8 +54,7 @@ class UnrecognizedChallenge(Challenge):
object.__setattr__(self, "jobj", jobj)
def to_partial_json(self):
# pylint: disable=no-member
return self.jobj
return self.jobj # pylint: disable=no-member
@classmethod
def from_json(cls, jobj):
@@ -96,6 +87,7 @@ class _TokenChallenge(Challenge):
"""
# TODO: check that path combined with uri does not go above
# URI_ROOT_PATH!
# pylint: disable=unsupported-membership-test
return b'..' not in self.token and b'/' not in self.token
@@ -120,7 +112,7 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
:rtype: bool
"""
parts = self.key_authorization.split('.') # pylint: disable=no-member
parts = self.key_authorization.split('.')
if len(parts) != 2:
logger.debug("Key authorization (%r) is not well formed",
self.key_authorization)
@@ -140,10 +132,14 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
return True
def to_partial_json(self):
jobj = super(KeyAuthorizationChallengeResponse, self).to_partial_json()
jobj.pop('keyAuthorization', None)
return jobj
@six.add_metaclass(abc.ABCMeta)
class KeyAuthorizationChallenge(_TokenChallenge):
# pylint: disable=abstract-class-little-used,too-many-ancestors
"""Challenge based on Key Authorization.
:param response_cls: Subclass of `KeyAuthorizationChallengeResponse`
@@ -175,7 +171,7 @@ class KeyAuthorizationChallenge(_TokenChallenge):
:rtype: KeyAuthorizationChallengeResponse
"""
return self.response_cls(
return self.response_cls( # pylint: disable=not-callable
key_authorization=self.key_authorization(account_key))
@abc.abstractmethod
@@ -212,7 +208,7 @@ class DNS01Response(KeyAuthorizationChallengeResponse):
"""ACME dns-01 challenge response."""
typ = "dns-01"
def simple_verify(self, chall, domain, account_public_key):
def simple_verify(self, chall, domain, account_public_key): # pylint: disable=unused-argument
"""Simple verify.
This method no longer checks DNS records and is a simple wrapper
@@ -228,14 +224,13 @@ class DNS01Response(KeyAuthorizationChallengeResponse):
:rtype: bool
"""
# pylint: disable=unused-argument
verified = self.verify(chall, account_public_key)
if not verified:
logger.debug("Verification of key authorization in response failed")
return verified
@Challenge.register # pylint: disable=too-many-ancestors
@Challenge.register
class DNS01(KeyAuthorizationChallenge):
"""ACME dns-01 challenge."""
response_cls = DNS01Response
@@ -325,7 +320,7 @@ class HTTP01Response(KeyAuthorizationChallengeResponse):
return True
@Challenge.register # pylint: disable=too-many-ancestors
@Challenge.register
class HTTP01(KeyAuthorizationChallenge):
"""ACME http-01 challenge."""
response_cls = HTTP01Response
@@ -365,154 +360,6 @@ class HTTP01(KeyAuthorizationChallenge):
return self.key_authorization(account_key)
@ChallengeResponse.register
class TLSSNI01Response(KeyAuthorizationChallengeResponse):
"""ACME tls-sni-01 challenge response."""
typ = "tls-sni-01"
DOMAIN_SUFFIX = b".acme.invalid"
"""Domain name suffix."""
PORT = 443
"""Verification port as defined by the protocol.
You can override it (e.g. for testing) by passing ``port`` to
`simple_verify`.
"""
@property
def z(self): # pylint: disable=invalid-name
"""``z`` value used for verification.
:rtype bytes:
"""
return hashlib.sha256(
self.key_authorization.encode("utf-8")).hexdigest().lower().encode()
@property
def z_domain(self):
"""Domain name used for verification, generated from `z`.
:rtype bytes:
"""
return self.z[:32] + b'.' + self.z[32:] + self.DOMAIN_SUFFIX
def gen_cert(self, key=None, bits=2048):
"""Generate tls-sni-01 certificate.
:param OpenSSL.crypto.PKey key: Optional private key used in
certificate generation. If not provided (``None``), then
fresh key will be generated.
:param int bits: Number of bits for newly generated key.
:rtype: `tuple` of `OpenSSL.crypto.X509` and `OpenSSL.crypto.PKey`
"""
if key is None:
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, bits)
return crypto_util.gen_ss_cert(key, [
# z_domain is too big to fit into CN, hence first dummy domain
'dummy', self.z_domain.decode()], force_san=True), key
def probe_cert(self, domain, **kwargs):
"""Probe tls-sni-01 challenge certificate.
:param unicode domain:
"""
# TODO: domain is not necessary if host is provided
if "host" not in kwargs:
host = socket.gethostbyname(domain)
logger.debug('%s resolved to %s', domain, host)
kwargs["host"] = host
kwargs.setdefault("port", self.PORT)
kwargs["name"] = self.z_domain
# TODO: try different methods?
# pylint: disable=protected-access
return crypto_util.probe_sni(**kwargs)
def verify_cert(self, cert):
"""Verify tls-sni-01 challenge certificate.
:param OpensSSL.crypto.X509 cert: Challenge certificate.
:returns: Whether the certificate was successfully verified.
:rtype: bool
"""
# pylint: disable=protected-access
sans = crypto_util._pyopenssl_cert_or_req_san(cert)
logger.debug('Certificate %s. SANs: %s', cert.digest('sha256'), sans)
return self.z_domain.decode() in sans
def simple_verify(self, chall, domain, account_public_key,
cert=None, **kwargs):
"""Simple verify.
Verify ``validation`` using ``account_public_key``, optionally
probe tls-sni-01 certificate and check using `verify_cert`.
:param .challenges.TLSSNI01 chall: Corresponding challenge.
:param str domain: Domain name being validated.
:param JWK account_public_key:
:param OpenSSL.crypto.X509 cert: Optional certificate. If not
provided (``None``) certificate will be retrieved using
`probe_cert`.
:param int port: Port used to probe the certificate.
:returns: ``True`` iff client's control of the domain has been
verified.
:rtype: bool
"""
if not self.verify(chall, account_public_key):
logger.debug("Verification of key authorization in response failed")
return False
if cert is None:
try:
cert = self.probe_cert(domain=domain, **kwargs)
except errors.Error as error:
logger.debug(str(error), exc_info=True)
return False
return self.verify_cert(cert)
@Challenge.register # pylint: disable=too-many-ancestors
class TLSSNI01(KeyAuthorizationChallenge):
"""ACME tls-sni-01 challenge."""
response_cls = TLSSNI01Response
typ = response_cls.typ
# boulder#962, ietf-wg-acme#22
#n = jose.Field("n", encoder=int, decoder=int)
def __init__(self, *args, **kwargs):
warnings.warn("TLS-SNI-01 is deprecated, and will stop working soon.",
DeprecationWarning, stacklevel=2)
super(TLSSNI01, self).__init__(*args, **kwargs)
def validation(self, account_key, **kwargs):
"""Generate validation.
:param JWK account_key:
:param OpenSSL.crypto.PKey cert_key: Optional private key used
in certificate generation. If not provided (``None``), then
fresh key will be generated.
:rtype: `tuple` of `OpenSSL.crypto.X509` and `OpenSSL.crypto.PKey`
"""
return self.response(account_key).gen_cert(key=kwargs.get('cert_key'))
@ChallengeResponse.register
class TLSALPN01Response(KeyAuthorizationChallengeResponse):
"""ACME TLS-ALPN-01 challenge response.
@@ -524,7 +371,7 @@ class TLSALPN01Response(KeyAuthorizationChallengeResponse):
typ = "tls-alpn-01"
@Challenge.register # pylint: disable=too-many-ancestors
@Challenge.register
class TLSALPN01(KeyAuthorizationChallenge):
"""ACME tls-alpn-01 challenge.
@@ -540,7 +387,7 @@ class TLSALPN01(KeyAuthorizationChallenge):
raise NotImplementedError()
@Challenge.register # pylint: disable=too-many-ancestors
@Challenge.register
class DNS(_TokenChallenge):
"""ACME "dns" challenge."""
typ = "dns"

View File

@@ -5,25 +5,26 @@ import datetime
from email.utils import parsedate_tz
import heapq
import logging
import re
import sys
import time
import six
from six.moves import http_client # pylint: disable=import-error
import josepy as jose
import OpenSSL
import re
from requests_toolbelt.adapters.source import SourceAddressAdapter
import requests
from requests.adapters import HTTPAdapter
import sys
from requests_toolbelt.adapters.source import SourceAddressAdapter
import six
from six.moves import http_client # pylint: disable=import-error
from acme import crypto_util
from acme import errors
from acme import jws
from acme import messages
# pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Dict, List, Set, Text
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Text # pylint: disable=unused-import, no-name-in-module
logger = logging.getLogger(__name__)
@@ -33,7 +34,6 @@ logger = logging.getLogger(__name__)
# https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
if sys.version_info < (2, 7, 9): # pragma: no cover
try:
# pylint: disable=no-member
requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3() # type: ignore
except AttributeError:
import urllib3.contrib.pyopenssl # pylint: disable=import-error
@@ -44,7 +44,7 @@ DEFAULT_NETWORK_TIMEOUT = 45
DER_CONTENT_TYPE = 'application/pkix-cert'
class ClientBase(object): # pylint: disable=too-many-instance-attributes
class ClientBase(object):
"""ACME client base object.
:ivar messages.Directory directory:
@@ -123,14 +123,21 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
"""
return self.update_registration(regr, update={'status': 'deactivated'})
def query_registration(self, regr):
"""Query server about registration.
def deactivate_authorization(self, authzr):
# type: (messages.AuthorizationResource) -> messages.AuthorizationResource
"""Deactivate authorization.
:param messages.RegistrationResource: Existing Registration
Resource.
:param messages.AuthorizationResource authzr: The Authorization resource
to be deactivated.
:returns: The Authorization resource that was deactivated.
:rtype: `.AuthorizationResource`
"""
return self._send_recv_regr(regr, messages.UpdateRegistration())
body = messages.UpdateAuthorization(status='deactivated')
response = self._post(authzr.uri, body)
return self._authzr_from_response(response,
authzr.body.identifier, authzr.uri)
def _authzr_from_response(self, response, identifier=None, uri=None):
authzr = messages.AuthorizationResource(
@@ -247,7 +254,6 @@ class Client(ClientBase):
URI from which the resource will be downloaded.
"""
# pylint: disable=too-many-arguments
self.key = key
if net is None:
net = ClientNetwork(key, alg=alg, verify_ssl=verify_ssl)
@@ -273,9 +279,17 @@ class Client(ClientBase):
assert response.status_code == http_client.CREATED
# "Instance of 'Field' has no key/contact member" bug:
# pylint: disable=no-member
return self._regr_from_response(response)
def query_registration(self, regr):
"""Query server about registration.
:param messages.RegistrationResource: Existing Registration
Resource.
"""
return self._send_recv_regr(regr, messages.UpdateRegistration())
def agree_to_tos(self, regr):
"""Agree to the terms-of-service.
@@ -419,7 +433,6 @@ class Client(ClientBase):
was marked by the CA as invalid
"""
# pylint: disable=too-many-locals
assert max_attempts > 0
attempts = collections.defaultdict(int) # type: Dict[messages.AuthorizationResource, int]
exhausted = set()
@@ -450,7 +463,6 @@ class Client(ClientBase):
updated[authzr] = updated_authzr
attempts[authzr] += 1
# pylint: disable=no-member
if updated_authzr.body.status not in (
messages.STATUS_VALID, messages.STATUS_INVALID):
if attempts[authzr] < max_attempts:
@@ -591,7 +603,6 @@ class ClientV2(ClientBase):
if response.status_code == 200 and 'Location' in response.headers:
raise errors.ConflictError(response.headers.get('Location'))
# "Instance of 'Field' has no key/contact member" bug:
# pylint: disable=no-member
regr = self._regr_from_response(response)
self.net.account = regr
return regr
@@ -603,10 +614,13 @@ class ClientV2(ClientBase):
Resource.
"""
self.net.account = regr
updated_regr = super(ClientV2, self).query_registration(regr)
self.net.account = updated_regr
return updated_regr
self.net.account = regr # See certbot/certbot#6258
# ACME v2 requires to use a POST-as-GET request (POST an empty JWS) here.
# This is done by passing None instead of an empty UpdateRegistration to _post().
response = self._post(regr.uri, None)
self.net.account = self._regr_from_response(response, uri=regr.uri,
terms_of_service=regr.terms_of_service)
return self.net.account
def update_registration(self, regr, update=None):
"""Update registration.
@@ -652,7 +666,7 @@ class ClientV2(ClientBase):
response = self._post(self.directory['newOrder'], order)
body = messages.Order.from_json(response.json())
authorizations = []
for url in body.authorizations:
for url in body.authorizations: # pylint: disable=not-an-iterable
authorizations.append(self._authzr_from_response(self._post_as_get(url), uri=url))
return messages.OrderResource(
body=body,
@@ -712,9 +726,9 @@ class ClientV2(ClientBase):
for authzr in responses:
if authzr.body.status != messages.STATUS_VALID:
for chall in authzr.body.challenges:
if chall.error != None:
if chall.error is not None:
failed.append(authzr)
if len(failed) > 0:
if failed:
raise errors.ValidationError(failed)
return orderr.update(authorizations=responses)
@@ -739,8 +753,7 @@ class ClientV2(ClientBase):
if body.error is not None:
raise errors.IssuanceError(body.error)
if body.certificate is not None:
certificate_response = self._post_as_get(body.certificate,
content_type=DER_CONTENT_TYPE).text
certificate_response = self._post_as_get(body.certificate).text
return orderr.update(body=body, fullchain_pem=certificate_response)
raise errors.TimeoutError()
@@ -759,36 +772,17 @@ class ClientV2(ClientBase):
def external_account_required(self):
"""Checks if ACME server requires External Account Binding authentication."""
if hasattr(self.directory, 'meta') and self.directory.meta.external_account_required:
return True
else:
return False
return hasattr(self.directory, 'meta') and self.directory.meta.external_account_required
def _post_as_get(self, *args, **kwargs):
"""
Send GET request using the POST-as-GET protocol if needed.
The request will be first issued using POST-as-GET for ACME v2. If the ACME CA servers do
not support this yet and return an error, request will be retried using GET.
For ACME v1, only GET request will be tried, as POST-as-GET is not supported.
Send GET request using the POST-as-GET protocol.
:param args:
:param kwargs:
:return:
"""
if self.acme_version >= 2:
# We add an empty payload for POST-as-GET requests
new_args = args[:1] + (None,) + args[1:]
try:
return self._post(*new_args, **kwargs) # pylint: disable=star-args
except messages.Error as error:
if error.code == 'malformed':
logger.debug('Error during a POST-as-GET request, '
'your ACME CA may not support it:\n%s', error)
logger.debug('Retrying request with GET.')
else: # pragma: no cover
raise
# If POST-as-GET is not supported yet, we use a GET instead.
return self.net.get(*args, **kwargs)
new_args = args[:1] + (None,) + args[1:]
return self._post(*new_args, **kwargs)
class BackwardsCompatibleClientV2(object):
@@ -866,8 +860,7 @@ class BackwardsCompatibleClientV2(object):
for domain in dnsNames:
authorizations.append(self.client.request_domain_challenges(domain))
return messages.OrderResource(authorizations=authorizations, csr_pem=csr_pem)
else:
return self.client.new_order(csr_pem)
return self.client.new_order(csr_pem)
def finalize_order(self, orderr, deadline):
"""Finalize an order and obtain a certificate.
@@ -904,8 +897,7 @@ class BackwardsCompatibleClientV2(object):
chain = crypto_util.dump_pyopenssl_chain(chain).decode()
return orderr.update(fullchain_pem=(cert + chain))
else:
return self.client.finalize_order(orderr, deadline)
return self.client.finalize_order(orderr, deadline)
def revoke(self, cert, rsn):
"""Revoke certificate.
@@ -923,8 +915,7 @@ class BackwardsCompatibleClientV2(object):
def _acme_version_from_directory(self, directory):
if hasattr(directory, 'newNonce'):
return 2
else:
return 1
return 1
def external_account_required(self):
"""Checks if the server requires an external account for ACMEv2 servers.
@@ -932,11 +923,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
else:
return self.client.external_account_required()
return self.client.external_account_required()
class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
class ClientNetwork(object):
"""Wrapper around requests that signs POSTs for authentication.
Also adds user agent, and handles Content-Type.
@@ -962,7 +952,6 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
def __init__(self, key, account=None, alg=jose.RS256, verify_ssl=True,
user_agent='acme-python', timeout=DEFAULT_NETWORK_TIMEOUT,
source_address=None):
# pylint: disable=too-many-arguments
self.key = key
self.account = account
self.alg = alg
@@ -1011,7 +1000,6 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
if self.account is not None:
kwargs["kid"] = self.account["uri"]
kwargs["key"] = self.key
# pylint: disable=star-args
return jws.JWS.sign(jobj, **kwargs).json_dumps(indent=2)
@classmethod
@@ -1071,7 +1059,6 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
return response
def _send_request(self, method, url, *args, **kwargs):
# pylint: disable=too-many-locals
"""Send HTTP request.
Makes sure that `verify_ssl` is respected. Logs request and
@@ -1118,10 +1105,9 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
err_regex = r".*host='(\S*)'.*Max retries exceeded with url\: (\/\w*).*(\[Errno \d+\])([A-Za-z ]*)"
m = re.match(err_regex, str(e))
if m is None:
raise # pragma: no cover
else:
host, path, _err_no, err_msg = m.groups()
raise ValueError("Requesting {0}{1}:{2}".format(host, path, err_msg))
raise # pragma: no cover
host, path, _err_no, err_msg = m.groups()
raise ValueError("Requesting {0}{1}:{2}".format(host, path, err_msg))
# If content is DER, log the base64 of it instead of raw bytes, to keep
# binary data out of the logs.
@@ -1187,15 +1173,11 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
if error.code == 'badNonce':
logger.debug('Retrying request after error:\n%s', error)
return self._post_once(*args, **kwargs)
else:
raise
raise
def _post_once(self, url, obj, content_type=JOSE_CONTENT_TYPE,
acme_version=1, **kwargs):
try:
new_nonce_url = kwargs.pop('new_nonce_url')
except KeyError:
new_nonce_url = None
new_nonce_url = kwargs.pop('new_nonce_url', None)
data = self._wrap_in_jws(obj, self._get_nonce(url, new_nonce_url), url, acme_version)
kwargs.setdefault('headers', {'Content-Type': content_type})
response = self._send_request('POST', url, data=data, **kwargs)

View File

@@ -6,32 +6,29 @@ import os
import re
import socket
from OpenSSL import crypto
from OpenSSL import SSL # type: ignore # https://github.com/python/typeshed/issues/2052
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
# pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Callable, Union, Tuple, Optional
# pylint: enable=unused-import, no-name-in-module
from acme.magic_typing import Callable # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Optional # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Tuple # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Union # pylint: disable=unused-import, no-name-in-module
logger = logging.getLogger(__name__)
# TLSSNI01 certificate serving and probing is not affected by SSL
# vulnerabilities: prober needs to check certificate for expected
# contents anyway. Working SNI is the only thing that's necessary for
# the challenge and thus scoping down SSL/TLS method (version) would
# cause interoperability issues: TLSv1_METHOD is only compatible with
# Default SSL method selected here is the most compatible, while secure
# SSL method: TLSv1_METHOD is only compatible with
# TLSv1_METHOD, while SSLv23_METHOD is compatible with all other
# methods, including TLSv2_METHOD (read more at
# https://www.openssl.org/docs/ssl/SSLv23_method.html). _serve_sni
# should be changed to use "set_options" to disable SSLv2 and SSLv3,
# in case it's used for things other than probing/serving!
_DEFAULT_TLSSNI01_SSL_METHOD = SSL.SSLv23_METHOD # type: ignore
_DEFAULT_SSL_METHOD = SSL.SSLv23_METHOD # type: ignore
class SSLSocket(object): # pylint: disable=too-few-public-methods
class SSLSocket(object):
"""SSL wrapper for sockets.
:ivar socket sock: Original wrapped socket.
@@ -40,7 +37,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
:ivar method: See `OpenSSL.SSL.Context` for allowed values.
"""
def __init__(self, sock, certs, method=_DEFAULT_TLSSNI01_SSL_METHOD):
def __init__(self, sock, certs, method=_DEFAULT_SSL_METHOD):
self.sock = sock
self.certs = certs
self.method = method
@@ -77,7 +74,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
class FakeConnection(object):
"""Fake OpenSSL.SSL.Connection."""
# pylint: disable=too-few-public-methods,missing-docstring
# pylint: disable=missing-docstring
def __init__(self, connection):
self._wrapped = connection
@@ -112,7 +109,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
def probe_sni(name, host, port=443, timeout=300,
method=_DEFAULT_TLSSNI01_SSL_METHOD, source_address=('', 0)):
method=_DEFAULT_SSL_METHOD, source_address=('', 0)):
"""Probe SNI server for SSL certificate.
:param bytes name: Byte string to send as the server name in the
@@ -137,7 +134,6 @@ def probe_sni(name, host, port=443, timeout=300,
socket_kwargs = {'source_address': source_address}
try:
# pylint: disable=star-args
logger.debug(
"Attempting to connect to %s:%d%s.", host, port,
" from {0}:{1}".format(
@@ -198,8 +194,7 @@ def _pyopenssl_cert_or_req_all_names(loaded_cert_or_req):
if common_name is None:
return sans
else:
return [common_name] + [d for d in sans if d != common_name]
return [common_name] + [d for d in sans if d != common_name]
def _pyopenssl_cert_or_req_san(cert_or_req):
"""Get Subject Alternative Names from certificate or CSR using pyOpenSSL.

View File

@@ -29,7 +29,12 @@ class NonceError(ClientError):
class BadNonce(NonceError):
"""Bad nonce error."""
def __init__(self, nonce, error, *args, **kwargs):
super(BadNonce, self).__init__(*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
self.nonce = nonce
self.error = error
@@ -48,7 +53,8 @@ class MissingNonce(NonceError):
"""
def __init__(self, response, *args, **kwargs):
super(MissingNonce, self).__init__(*args, **kwargs)
# See comment in BadNonce constructor above for an explanation of type: ignore here.
super(MissingNonce, self).__init__(*args, **kwargs) # type: ignore
self.response = response
def __str__(self):
@@ -83,6 +89,7 @@ class PollError(ClientError):
return '{0}(exhausted={1!r}, updated={2!r})'.format(
self.__class__.__name__, self.exhausted, self.updated)
class ValidationError(Error):
"""Error for authorization failures. Contains a list of authorization
resources, each of which is invalid and should have an error field.
@@ -91,9 +98,11 @@ class ValidationError(Error):
self.failed_authzrs = failed_authzrs
super(ValidationError, self).__init__()
class TimeoutError(Error):
class TimeoutError(Error): # pylint: disable=redefined-builtin
"""Error for when polling an authorization or an order times out."""
class IssuanceError(Error):
"""Error sent by the server after requesting issuance of a certificate."""
@@ -105,6 +114,7 @@ class IssuanceError(Error):
self.error = error
super(IssuanceError, self).__init__()
class ConflictError(ClientError):
"""Error for when the server returns a 409 (Conflict) HTTP status.

View File

@@ -4,7 +4,6 @@ import logging
import josepy as jose
import pyrfc3339
logger = logging.getLogger(__name__)

View File

@@ -40,10 +40,10 @@ class Signature(jose.Signature):
class JWS(jose.JWS):
"""ACME-specific JWS. Includes none, url, and kid in protected header."""
signature_cls = Signature
__slots__ = jose.JWS._orig_slots # pylint: disable=no-member
__slots__ = jose.JWS._orig_slots
@classmethod
# pylint: disable=arguments-differ,too-many-arguments
# pylint: disable=arguments-differ
def sign(cls, payload, key, alg, nonce, url=None, kid=None):
# Per ACME spec, jwk and kid are mutually exclusive, so only include a
# jwk field if kid is not provided.

View File

@@ -1,6 +1,7 @@
"""Shim class to not have to depend on typing module in prod."""
import sys
class TypingClass(object):
"""Ignore import errors by getting anything"""
def __getattr__(self, name):

View File

@@ -1,37 +1,55 @@
"""ACME protocol messages."""
import six
import json
try:
from collections.abc import Hashable # pylint: disable=no-name-in-module
except ImportError:
from collections import Hashable
import josepy as jose
import six
from acme import challenges
from acme import errors
from acme import fields
from acme import util
from acme import jws
from acme import util
try:
from collections.abc import Hashable # pylint: disable=no-name-in-module
except ImportError: # pragma: no cover
from collections import Hashable
OLD_ERROR_PREFIX = "urn:acme:error:"
ERROR_PREFIX = "urn:ietf:params:acme:error:"
ERROR_CODES = {
'accountDoesNotExist': 'The request specified an account that does not exist',
'alreadyRevoked': 'The request specified a certificate to be revoked that has' \
' already been revoked',
'badCSR': 'The CSR is unacceptable (e.g., due to a short key)',
'badNonce': 'The client sent an unacceptable anti-replay nonce',
'badPublicKey': 'The JWS was signed by a public key the server does not support',
'badRevocationReason': 'The revocation reason provided is not allowed by the server',
'badSignatureAlgorithm': 'The JWS was signed with an algorithm the server does not support',
'caa': 'Certification Authority Authorization (CAA) records forbid the CA from issuing' \
' a certificate',
'compound': 'Specific error conditions are indicated in the "subproblems" array',
'connection': ('The server could not connect to the client to verify the'
' domain'),
'dns': 'There was a problem with a DNS query during identifier validation',
'dnssec': 'The server could not validate a DNSSEC signed domain',
'incorrectResponse': 'Response recieved didn\'t match the challenge\'s requirements',
# deprecate invalidEmail
'invalidEmail': 'The provided email for a registration was invalid',
'invalidContact': 'The provided contact URI was invalid',
'malformed': 'The request message was malformed',
'rejectedIdentifier': 'The server will not issue certificates for the identifier',
'orderNotReady': 'The request attempted to finalize an order that is not ready to be finalized',
'rateLimited': 'There were too many requests of a given type',
'serverInternal': 'The server experienced an internal error',
'tls': 'The server experienced a TLS error during domain verification',
'unauthorized': 'The client lacks sufficient authorization',
'unsupportedContact': 'A contact URL for an account used an unsupported protocol scheme',
'unknownHost': 'The server could not resolve a domain name',
'unsupportedIdentifier': 'An identifier is of an unsupported type',
'externalAccountRequired': 'The server requires external account binding',
}
@@ -46,8 +64,7 @@ def is_acme_error(err):
"""Check if argument is an ACME error."""
if isinstance(err, Error) and (err.typ is not None):
return (ERROR_PREFIX in err.typ) or (OLD_ERROR_PREFIX in err.typ)
else:
return False
return False
@six.python_2_unicode_compatible
@@ -102,6 +119,7 @@ class Error(jose.JSONObjectWithFields, errors.Error):
code = str(self.typ).split(':')[-1]
if code in ERROR_CODES:
return code
return None
def __str__(self):
return b' :: '.join(
@@ -116,18 +134,19 @@ class _Constant(jose.JSONDeSerializable, Hashable): # type: ignore
POSSIBLE_NAMES = NotImplemented
def __init__(self, name):
self.POSSIBLE_NAMES[name] = self
super(_Constant, self).__init__()
self.POSSIBLE_NAMES[name] = self # pylint: disable=unsupported-assignment-operation
self.name = name
def to_partial_json(self):
return self.name
@classmethod
def from_json(cls, value):
if value not in cls.POSSIBLE_NAMES:
def from_json(cls, jobj):
if jobj not in cls.POSSIBLE_NAMES: # pylint: disable=unsupported-membership-test
raise jose.DeserializationError(
'{0} not recognized'.format(cls.__name__))
return cls.POSSIBLE_NAMES[value]
return cls.POSSIBLE_NAMES[jobj]
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, self.name)
@@ -152,6 +171,7 @@ STATUS_VALID = Status('valid')
STATUS_INVALID = Status('invalid')
STATUS_REVOKED = Status('revoked')
STATUS_READY = Status('ready')
STATUS_DEACTIVATED = Status('deactivated')
class IdentifierType(_Constant):
@@ -186,7 +206,6 @@ class Directory(jose.JSONDeSerializable):
def __init__(self, **kwargs):
kwargs = dict((self._internal_name(k), v) for k, v in kwargs.items())
# pylint: disable=star-args
super(Directory.Meta, self).__init__(**kwargs)
@property
@@ -322,7 +341,7 @@ class Registration(ResourceBody):
def _filter_contact(self, prefix):
return tuple(
detail[len(prefix):] for detail in self.contact
detail[len(prefix):] for detail in self.contact # pylint: disable=not-an-iterable
if detail.startswith(prefix))
@property
@@ -394,7 +413,6 @@ class ChallengeBody(ResourceBody):
def __init__(self, **kwargs):
kwargs = dict((self._internal_name(k), v) for k, v in kwargs.items())
# pylint: disable=star-args
super(ChallengeBody, self).__init__(**kwargs)
def encode(self, name):
@@ -457,7 +475,7 @@ class Authorization(ResourceBody):
:ivar datetime.datetime expires:
"""
identifier = jose.Field('identifier', decoder=Identifier.from_json)
identifier = jose.Field('identifier', decoder=Identifier.from_json, omitempty=True)
challenges = jose.Field('challenges', omitempty=True)
combinations = jose.Field('combinations', omitempty=True)
@@ -477,7 +495,7 @@ class Authorization(ResourceBody):
def resolved_combinations(self):
"""Combinations with challenges instead of indices."""
return tuple(tuple(self.challenges[idx] for idx in combo)
for combo in self.combinations)
for combo in self.combinations) # pylint: disable=not-an-iterable
@Directory.register
@@ -487,6 +505,12 @@ class NewAuthorization(Authorization):
resource = fields.Resource(resource_type)
class UpdateAuthorization(Authorization):
"""Update authorization."""
resource_type = 'authz'
resource = fields.Resource(resource_type)
class AuthorizationResource(ResourceWithURI):
"""Authorization Resource.

View File

@@ -1,28 +1,22 @@
"""Support for standalone client challenge solvers. """
import argparse
import collections
import functools
import logging
import os
import socket
import sys
import threading
from six.moves import BaseHTTPServer # type: ignore # pylint: disable=import-error
from six.moves import http_client # pylint: disable=import-error
from six.moves import socketserver # type: ignore # pylint: disable=import-error
import OpenSSL
from acme import challenges
from acme import crypto_util
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
logger = logging.getLogger(__name__)
# six.moves.* | pylint: disable=no-member,attribute-defined-outside-init
# pylint: disable=too-few-public-methods,no-init
# pylint: disable=no-init
class TLSServer(socketserver.TCPServer):
@@ -37,7 +31,7 @@ class TLSServer(socketserver.TCPServer):
self.certs = kwargs.pop("certs", {})
self.method = kwargs.pop(
# pylint: disable=protected-access
"method", crypto_util._DEFAULT_TLSSNI01_SSL_METHOD)
"method", crypto_util._DEFAULT_SSL_METHOD)
self.allow_reuse_address = kwargs.pop("allow_reuse_address", True)
socketserver.TCPServer.__init__(self, *args, **kwargs)
@@ -50,7 +44,7 @@ class TLSServer(socketserver.TCPServer):
return socketserver.TCPServer.server_bind(self)
class ACMEServerMixin: # pylint: disable=old-style-class
class ACMEServerMixin:
"""ACME server common settings mixin."""
# TODO: c.f. #858
server_version = "ACME client standalone challenge solver"
@@ -82,7 +76,7 @@ class BaseDualNetworkedServers(object):
kwargs["ipv6"] = ip_version
new_address = (server_address[0],) + (port,) + server_address[2:]
new_args = (new_address,) + remaining_args
server = ServerClass(*new_args, **kwargs) # pylint: disable=star-args
server = ServerClass(*new_args, **kwargs)
logger.debug(
"Successfully bound to %s:%s using %s", new_address[0],
new_address[1], "IPv6" if ip_version else "IPv4")
@@ -90,8 +84,8 @@ class BaseDualNetworkedServers(object):
if self.servers:
# Already bound using IPv6.
logger.debug(
"Certbot wasn't able to bind to %s:%s using %s, this " +
"is often expected due to the dual stack nature of " +
"Certbot wasn't able to bind to %s:%s using %s, this "
"is often expected due to the dual stack nature of "
"IPv6 socket implementations.",
new_address[0], new_address[1],
"IPv6" if ip_version else "IPv4")
@@ -104,14 +98,13 @@ class BaseDualNetworkedServers(object):
# If two servers are set up and port 0 was passed in, ensure we always
# bind to the same port for both servers.
port = server.socket.getsockname()[1]
if len(self.servers) == 0:
if not self.servers:
raise socket.error("Could not bind to IPv4 or IPv6.")
def serve_forever(self):
"""Wraps socketserver.TCPServer.serve_forever"""
for server in self.servers:
thread = threading.Thread(
# pylint: disable=no-member
target=server.serve_forever)
thread.start()
self.threads.append(thread)
@@ -131,35 +124,6 @@ class BaseDualNetworkedServers(object):
self.threads = []
class TLSSNI01Server(TLSServer, ACMEServerMixin):
"""TLSSNI01 Server."""
def __init__(self, server_address, certs, ipv6=False):
TLSServer.__init__(
self, server_address, BaseRequestHandlerWithLogging, certs=certs, ipv6=ipv6)
class TLSSNI01DualNetworkedServers(BaseDualNetworkedServers):
"""TLSSNI01Server Wrapper. Tries everything for both. Failures for one don't
affect the other."""
def __init__(self, *args, **kwargs):
BaseDualNetworkedServers.__init__(self, TLSSNI01Server, *args, **kwargs)
class BaseRequestHandlerWithLogging(socketserver.BaseRequestHandler):
"""BaseRequestHandler with logging."""
def log_message(self, format, *args): # pylint: disable=redefined-builtin
"""Log arbitrary message."""
logger.debug("%s - - %s", self.client_address[0], format % args)
def handle(self):
"""Handle request."""
self.log_message("Incoming request")
socketserver.BaseRequestHandler.handle(self)
class HTTPServer(BaseHTTPServer.HTTPServer):
"""Generic HTTP Server."""
@@ -262,39 +226,3 @@ class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""
return functools.partial(
cls, simple_http_resources=simple_http_resources)
def simple_tls_sni_01_server(cli_args, forever=True):
"""Run simple standalone TLSSNI01 server."""
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
parser.add_argument(
"-p", "--port", default=0, help="Port to serve at. By default "
"picks random free port.")
args = parser.parse_args(cli_args[1:])
certs = {}
_, hosts, _ = next(os.walk('.')) # type: ignore # https://github.com/python/mypy/issues/465
for host in hosts:
with open(os.path.join(host, "cert.pem")) as cert_file:
cert_contents = cert_file.read()
with open(os.path.join(host, "key.pem")) as key_file:
key_contents = key_file.read()
certs[host.encode()] = (
OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key_contents),
OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert_contents))
server = TLSSNI01Server(('', int(args.port)), certs=certs)
logger.info("Serving at https://%s:%s...", *server.socket.getsockname()[:2])
if forever: # pragma: no cover
server.serve_forever()
else:
server.handle_request()
if __name__ == "__main__":
sys.exit(simple_tls_sni_01_server(sys.argv)) # pragma: no cover

View File

@@ -12,10 +12,9 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import shlex
import sys
here = os.path.abspath(os.path.dirname(__file__))

View File

@@ -0,0 +1,241 @@
"""Example ACME-V2 API for HTTP-01 challenge.
Brief:
This a complete usage example of the python-acme API.
Limitations of this example:
- Works for only one Domain name
- Performs only HTTP-01 challenge
- Uses ACME-v2
Workflow:
(Account creation)
- Create account key
- Register account and accept TOS
(Certificate actions)
- Select HTTP-01 within offered challenges by the CA server
- Set up http challenge resource
- Set up standalone web server
- Create domain private key and CSR
- Issue certificate
- Renew certificate
- Revoke certificate
(Account update actions)
- Change contact information
- Deactivate Account
"""
from contextlib import contextmanager
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
import josepy as jose
import OpenSSL
from acme import challenges
from acme import client
from acme import crypto_util
from acme import errors
from acme import messages
from acme import standalone
# Constants:
# This is the staging point for ACME-V2 within Let's Encrypt.
DIRECTORY_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory'
USER_AGENT = 'python-acme-example'
# Account key size
ACC_KEY_BITS = 2048
# Certificate private key size
CERT_PKEY_BITS = 2048
# Domain name for the certificate.
DOMAIN = 'client.example.com'
# If you are running Boulder locally, it is possible to configure any port
# number to execute the challenge, but real CA servers will always use port
# 80, as described in the ACME specification.
PORT = 80
# Useful methods and classes:
def new_csr_comp(domain_name, pkey_pem=None):
"""Create certificate signing request."""
if pkey_pem is None:
# Create private key.
pkey = OpenSSL.crypto.PKey()
pkey.generate_key(OpenSSL.crypto.TYPE_RSA, CERT_PKEY_BITS)
pkey_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM,
pkey)
csr_pem = crypto_util.make_csr(pkey_pem, [domain_name])
return pkey_pem, csr_pem
def select_http01_chall(orderr):
"""Extract authorization resource from within order resource."""
# Authorization Resource: authz.
# This object holds the offered challenges by the server and their status.
authz_list = orderr.authorizations
for authz in authz_list:
# Choosing challenge.
# authz.body.challenges is a set of ChallengeBody objects.
for i in authz.body.challenges:
# Find the supported challenge.
if isinstance(i.chall, challenges.HTTP01):
return i
raise Exception('HTTP-01 challenge was not offered by the CA server.')
@contextmanager
def challenge_server(http_01_resources):
"""Manage standalone server set up and shutdown."""
# Setting up a fake server that binds at PORT and any address.
address = ('', PORT)
try:
servers = standalone.HTTP01DualNetworkedServers(address,
http_01_resources)
# Start client standalone web server.
servers.serve_forever()
yield servers
finally:
# Shutdown client web server and unbind from PORT
servers.shutdown_and_server_close()
def perform_http01(client_acme, challb, orderr):
"""Set up standalone webserver and perform HTTP-01 challenge."""
response, validation = challb.response_and_validation(client_acme.net.key)
resource = standalone.HTTP01RequestHandler.HTTP01Resource(
chall=challb.chall, response=response, validation=validation)
with challenge_server({resource}):
# Let the CA server know that we are ready for the challenge.
client_acme.answer_challenge(challb, response)
# Wait for challenge status and then issue a certificate.
# It is possible to set a deadline time.
finalized_orderr = client_acme.poll_and_finalize(orderr)
return finalized_orderr.fullchain_pem
# Main examples:
def example_http():
"""This example executes the whole process of fulfilling a HTTP-01
challenge for one specific domain.
The workflow consists of:
(Account creation)
- Create account key
- Register account and accept TOS
(Certificate actions)
- Select HTTP-01 within offered challenges by the CA server
- Set up http challenge resource
- Set up standalone web server
- Create domain private key and CSR
- Issue certificate
- Renew certificate
- Revoke certificate
(Account update actions)
- Change contact information
- Deactivate Account
"""
# Create account key
acc_key = jose.JWKRSA(
key=rsa.generate_private_key(public_exponent=65537,
key_size=ACC_KEY_BITS,
backend=default_backend()))
# Register account and accept TOS
net = client.ClientNetwork(acc_key, user_agent=USER_AGENT)
directory = messages.Directory.from_json(net.get(DIRECTORY_URL).json())
client_acme = client.ClientV2(directory, net=net)
# Terms of Service URL is in client_acme.directory.meta.terms_of_service
# Registration Resource: regr
# Creates account with contact information.
email = ('fake@example.com')
regr = client_acme.new_account(
messages.NewRegistration.from_data(
email=email, terms_of_service_agreed=True))
# Create domain private key and CSR
pkey_pem, csr_pem = new_csr_comp(DOMAIN)
# Issue certificate
orderr = client_acme.new_order(csr_pem)
# Select HTTP-01 within offered challenges by the CA server
challb = select_http01_chall(orderr)
# The certificate is ready to be used in the variable "fullchain_pem".
fullchain_pem = perform_http01(client_acme, challb, orderr)
# Renew certificate
_, csr_pem = new_csr_comp(DOMAIN, pkey_pem)
orderr = client_acme.new_order(csr_pem)
challb = select_http01_chall(orderr)
# Performing challenge
fullchain_pem = perform_http01(client_acme, challb, orderr)
# Revoke certificate
fullchain_com = jose.ComparableX509(
OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, fullchain_pem))
try:
client_acme.revoke(fullchain_com, 0) # revocation reason = 0
except errors.ConflictError:
# Certificate already revoked.
pass
# Query registration status.
client_acme.net.account = regr
try:
regr = client_acme.query_registration(regr)
except errors.Error as err:
if err.typ == messages.OLD_ERROR_PREFIX + 'unauthorized' \
or err.typ == messages.ERROR_PREFIX + 'unauthorized':
# Status is deactivated.
pass
raise
# Change contact information
email = 'newfake@example.com'
regr = client_acme.update_registration(
regr.update(
body=regr.body.update(
contact=('mailto:' + email,)
)
)
)
# Deactivate account/registration
regr = client_acme.deactivate_registration(regr)
if __name__ == "__main__":
example_http()

View File

@@ -1,10 +1,10 @@
# readthedocs.org gives no way to change the install command to "pip
# install -e .[docs]" (that would in turn install documentation
# install -e acme[docs]" (that would in turn install documentation
# dependencies), but it allows to specify a requirements.txt file at
# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259)
# Although ReadTheDocs certainly doesn't need to install the project
# in --editable mode (-e), just "pip install .[docs]" does not work as
# expected and "pip install -e .[docs]" must be used instead
# in --editable mode (-e), just "pip install acme[docs]" does not work as
# expected and "pip install -e acme[docs]" must be used instead
-e acme[docs]

View File

@@ -1,9 +1,10 @@
from setuptools import setup
from setuptools import find_packages
from setuptools.command.test import test as TestCommand
import sys
version = '0.31.0.dev0'
from setuptools import find_packages
from setuptools import setup
from setuptools.command.test import test as TestCommand
version = '1.1.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
@@ -11,9 +12,11 @@ install_requires = [
# rsa_recover_prime_factors (>=0.8)
'cryptography>=1.2.3',
# formerly known as acme.jose:
'josepy>=1.0.0',
# Connection.set_tlsext_host_name (>=0.13)
# 1.1.0+ is required to avoid the warnings described at
# https://github.com/certbot/josepy/issues/13.
'josepy>=1.1.0',
'mock',
# Connection.set_tlsext_host_name (>=0.13)
'PyOpenSSL>=0.13.1',
'pyrfc3339',
'pytz',
@@ -34,6 +37,7 @@ docs_extras = [
'sphinx_rtd_theme',
]
class PyTest(TestCommand):
user_options = []
@@ -48,6 +52,7 @@ class PyTest(TestCommand):
errno = pytest.main(shlex.split(self.pytest_args))
sys.exit(errno)
setup(
name='acme',
version=version,
@@ -69,6 +74,7 @@ setup(
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
],
@@ -80,7 +86,7 @@ setup(
'dev': dev_extras,
'docs': docs_extras,
},
tests_require=["pytest"],
test_suite='acme',
tests_require=["pytest"],
cmdclass={"test": PyTest},
)

View File

@@ -1,16 +1,12 @@
"""Tests for acme.challenges."""
import unittest
import warnings
import josepy as jose
import mock
import OpenSSL
import requests
from six.moves.urllib import parse as urllib_parse
from six.moves.urllib import parse as urllib_parse # pylint: disable=import-error
from acme import errors
from acme import test_util
import test_util
CERT = test_util.load_comparable_cert('cert.pem')
KEY = jose.JWKRSA(key=test_util.load_rsa_private_key('rsa512_key.pem'))
@@ -22,7 +18,6 @@ class ChallengeTest(unittest.TestCase):
from acme.challenges import Challenge
from acme.challenges import UnrecognizedChallenge
chall = UnrecognizedChallenge({"type": "foo"})
# pylint: disable=no-member
self.assertEqual(chall, Challenge.from_json(chall.jobj))
@@ -78,7 +73,6 @@ class KeyAuthorizationChallengeResponseTest(unittest.TestCase):
class DNS01ResponseTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.challenges import DNS01Response
@@ -94,7 +88,8 @@ class DNS01ResponseTest(unittest.TestCase):
self.response = self.chall.response(KEY)
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
self.assertEqual({k: v for k, v in self.jmsg.items() if k != 'keyAuthorization'},
self.msg.to_partial_json())
def test_from_json(self):
from acme.challenges import DNS01Response
@@ -149,7 +144,6 @@ class DNS01Test(unittest.TestCase):
class HTTP01ResponseTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.challenges import HTTP01Response
@@ -165,7 +159,8 @@ class HTTP01ResponseTest(unittest.TestCase):
self.response = self.chall.response(KEY)
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
self.assertEqual({k: v for k, v in self.jmsg.items() if k != 'keyAuthorization'},
self.msg.to_partial_json())
def test_from_json(self):
from acme.challenges import HTTP01Response
@@ -258,152 +253,7 @@ class HTTP01Test(unittest.TestCase):
self.msg.update(token=b'..').good_token)
class TLSSNI01ResponseTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.challenges import TLSSNI01
self.chall = TLSSNI01(
token=jose.b64decode(b'a82d5ff8ef740d12881f6d3c2277ab2e'))
self.response = self.chall.response(KEY)
self.jmsg = {
'resource': 'challenge',
'type': 'tls-sni-01',
'keyAuthorization': self.response.key_authorization,
}
# pylint: disable=invalid-name
label1 = b'dc38d9c3fa1a4fdcc3a5501f2d38583f'
label2 = b'b7793728f084394f2a1afd459556bb5c'
self.z = label1 + label2
self.z_domain = label1 + b'.' + label2 + b'.acme.invalid'
self.domain = 'foo.com'
def test_z_and_domain(self):
self.assertEqual(self.z, self.response.z)
self.assertEqual(self.z_domain, self.response.z_domain)
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.response.to_partial_json())
def test_from_json(self):
from acme.challenges import TLSSNI01Response
self.assertEqual(self.response, TLSSNI01Response.from_json(self.jmsg))
def test_from_json_hashable(self):
from acme.challenges import TLSSNI01Response
hash(TLSSNI01Response.from_json(self.jmsg))
@mock.patch('acme.challenges.socket.gethostbyname')
@mock.patch('acme.challenges.crypto_util.probe_sni')
def test_probe_cert(self, mock_probe_sni, mock_gethostbyname):
mock_gethostbyname.return_value = '127.0.0.1'
self.response.probe_cert('foo.com')
mock_gethostbyname.assert_called_once_with('foo.com')
mock_probe_sni.assert_called_once_with(
host='127.0.0.1', port=self.response.PORT,
name=self.z_domain)
self.response.probe_cert('foo.com', host='8.8.8.8')
mock_probe_sni.assert_called_with(
host='8.8.8.8', port=mock.ANY, name=mock.ANY)
self.response.probe_cert('foo.com', port=1234)
mock_probe_sni.assert_called_with(
host=mock.ANY, port=1234, name=mock.ANY)
self.response.probe_cert('foo.com', bar='baz')
mock_probe_sni.assert_called_with(
host=mock.ANY, port=mock.ANY, name=mock.ANY, bar='baz')
self.response.probe_cert('foo.com', name=b'xxx')
mock_probe_sni.assert_called_with(
host=mock.ANY, port=mock.ANY,
name=self.z_domain)
def test_gen_verify_cert(self):
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
cert, key2 = self.response.gen_cert(key1)
self.assertEqual(key1, key2)
self.assertTrue(self.response.verify_cert(cert))
def test_gen_verify_cert_gen_key(self):
cert, key = self.response.gen_cert()
self.assertTrue(isinstance(key, OpenSSL.crypto.PKey))
self.assertTrue(self.response.verify_cert(cert))
def test_verify_bad_cert(self):
self.assertFalse(self.response.verify_cert(
test_util.load_cert('cert.pem')))
def test_simple_verify_bad_key_authorization(self):
key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem'))
self.response.simple_verify(self.chall, "local", key2.public_key())
@mock.patch('acme.challenges.TLSSNI01Response.verify_cert', autospec=True)
def test_simple_verify(self, mock_verify_cert):
mock_verify_cert.return_value = mock.sentinel.verification
self.assertEqual(
mock.sentinel.verification, self.response.simple_verify(
self.chall, self.domain, KEY.public_key(),
cert=mock.sentinel.cert))
mock_verify_cert.assert_called_once_with(
self.response, mock.sentinel.cert)
@mock.patch('acme.challenges.TLSSNI01Response.probe_cert')
def test_simple_verify_false_on_probe_error(self, mock_probe_cert):
mock_probe_cert.side_effect = errors.Error
self.assertFalse(self.response.simple_verify(
self.chall, self.domain, KEY.public_key()))
class TLSSNI01Test(unittest.TestCase):
def setUp(self):
self.jmsg = {
'type': 'tls-sni-01',
'token': 'a82d5ff8ef740d12881f6d3c2277ab2e',
}
def _msg(self):
from acme.challenges import TLSSNI01
with warnings.catch_warnings(record=True) as warn:
warnings.simplefilter("always")
msg = TLSSNI01(
token=jose.b64decode('a82d5ff8ef740d12881f6d3c2277ab2e'))
assert warn is not None # using a raw assert for mypy
self.assertTrue(len(warn) == 1)
self.assertTrue(issubclass(warn[-1].category, DeprecationWarning))
self.assertTrue('deprecated' in str(warn[-1].message))
return msg
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self._msg().to_partial_json())
def test_from_json(self):
from acme.challenges import TLSSNI01
self.assertEqual(self._msg(), TLSSNI01.from_json(self.jmsg))
def test_from_json_hashable(self):
from acme.challenges import TLSSNI01
hash(TLSSNI01.from_json(self.jmsg))
def test_from_json_invalid_token_length(self):
from acme.challenges import TLSSNI01
self.jmsg['token'] = jose.encode_b64jose(b'abcd')
self.assertRaises(
jose.DeserializationError, TLSSNI01.from_json, self.jmsg)
@mock.patch('acme.challenges.TLSSNI01Response.gen_cert')
def test_validation(self, mock_gen_cert):
mock_gen_cert.return_value = ('cert', 'key')
self.assertEqual(('cert', 'key'), self._msg().validation(
KEY, cert_key=mock.sentinel.cert_key))
mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key)
class TLSALPN01ResponseTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.challenges import TLSALPN01Response
@@ -419,7 +269,8 @@ class TLSALPN01ResponseTest(unittest.TestCase):
self.response = self.chall.response(KEY)
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
self.assertEqual({k: v for k, v in self.jmsg.items() if k != 'keyAuthorization'},
self.msg.to_partial_json())
def test_from_json(self):
from acme.challenges import TLSALPN01Response

View File

@@ -5,21 +5,19 @@ import datetime
import json
import unittest
from six.moves import http_client # pylint: disable=import-error
import josepy as jose
import mock
import OpenSSL
import requests
from six.moves import http_client # pylint: disable=import-error
from acme import challenges
from acme import errors
from acme import jws as acme_jws
from acme import messages
from acme import messages_test
from acme import test_util
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
import messages_test
import test_util
CERT_DER = test_util.load_vector('cert.der')
CERT_SAN_PEM = test_util.load_vector('cert-san.pem')
@@ -63,8 +61,8 @@ 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
self.new_reg = messages.NewRegistration(**the_arg) # pylint: disable=star-args
the_arg = dict(reg) # type: Dict
self.new_reg = messages.NewRegistration(**the_arg)
self.regr = messages.RegistrationResource(
body=reg, uri='https://www.letsencrypt-demo.org/acme/reg/1')
@@ -318,7 +316,6 @@ class BackwardsCompatibleClientV2Test(ClientTestBase):
class ClientTest(ClientTestBase):
"""Tests for acme.client.Client."""
# pylint: disable=too-many-instance-attributes,too-many-public-methods
def setUp(self):
super(ClientTest, self).setUp()
@@ -358,7 +355,6 @@ class ClientTest(ClientTestBase):
def test_register(self):
# "Instance of 'Field' has no to_json/update member" bug:
# pylint: disable=no-member
self.response.status_code = http_client.CREATED
self.response.json.return_value = self.regr.body.to_json()
self.response.headers['Location'] = self.regr.uri
@@ -371,7 +367,6 @@ class ClientTest(ClientTestBase):
def test_update_registration(self):
# "Instance of 'Field' has no to_json/update member" bug:
# pylint: disable=no-member
self.response.headers['Location'] = self.regr.uri
self.response.json.return_value = self.regr.body.to_json()
self.assertEqual(self.regr, self.client.update_registration(self.regr))
@@ -639,6 +634,14 @@ class ClientTest(ClientTestBase):
errors.PollError, self.client.poll_and_request_issuance,
csr, authzrs, mintime=mintime, max_attempts=2)
def test_deactivate_authorization(self):
authzb = self.authzr.body.update(status=messages.STATUS_DEACTIVATED)
self.response.json.return_value = authzb.to_json()
authzr = self.client.deactivate_authorization(self.authzr)
self.assertEqual(authzb, authzr.body)
self.assertEqual(self.client.net.post.call_count, 1)
self.assertTrue(self.authzr.uri in self.net.post.call_args_list[0][0])
def test_check_cert(self):
self.response.headers['Location'] = self.certr.uri
self.response.content = CERT_DER
@@ -844,7 +847,6 @@ class ClientV2Test(ClientTestBase):
def test_update_registration(self):
# "Instance of 'Field' has no to_json/update member" bug:
# pylint: disable=no-member
self.response.headers['Location'] = self.regr.uri
self.response.json.return_value = self.regr.body.to_json()
self.assertEqual(self.regr, self.client.update_registration(self.regr))
@@ -883,19 +885,6 @@ class ClientV2Test(ClientTestBase):
new_nonce_url='https://www.letsencrypt-demo.org/acme/new-nonce')
self.client.net.get.assert_not_called()
class FakeError(messages.Error): # pylint: disable=too-many-ancestors
"""Fake error to reproduce a malformed request ACME error"""
def __init__(self): # pylint: disable=super-init-not-called
pass
@property
def code(self):
return 'malformed'
self.client.net.post.side_effect = FakeError()
self.client.poll(self.authzr2) # pylint: disable=protected-access
self.client.net.get.assert_called_once_with(self.authzr2.uri)
class MockJSONDeSerializable(jose.JSONDeSerializable):
# pylint: disable=missing-docstring
@@ -906,13 +895,12 @@ class MockJSONDeSerializable(jose.JSONDeSerializable):
return {'foo': self.value}
@classmethod
def from_json(cls, value):
def from_json(cls, jobj):
pass # pragma: no cover
class ClientNetworkTest(unittest.TestCase):
"""Tests for acme.client.ClientNetwork."""
# pylint: disable=too-many-public-methods
def setUp(self):
self.verify_ssl = mock.MagicMock()
@@ -962,8 +950,8 @@ class ClientNetworkTest(unittest.TestCase):
def test_check_response_not_ok_jobj_error(self):
self.response.ok = False
self.response.json.return_value = messages.Error(
detail='foo', typ='serverInternal', title='some title').to_json()
self.response.json.return_value = messages.Error.with_code(
'serverInternal', detail='foo', title='some title').to_json()
# pylint: disable=protected-access
self.assertRaises(
messages.Error, self.net._check_response, self.response)
@@ -988,7 +976,7 @@ class ClientNetworkTest(unittest.TestCase):
self.response.json.side_effect = ValueError
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access,no-value-for-parameter
# pylint: disable=protected-access
self.assertEqual(
self.response, self.net._check_response(self.response))
@@ -1002,7 +990,7 @@ class ClientNetworkTest(unittest.TestCase):
self.response.json.return_value = {}
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access,no-value-for-parameter
# pylint: disable=protected-access
self.assertEqual(
self.response, self.net._check_response(self.response))
@@ -1118,7 +1106,6 @@ class ClientNetworkTest(unittest.TestCase):
class ClientNetworkWithMockedResponseTest(unittest.TestCase):
"""Tests for acme.client.ClientNetwork which mock out response."""
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.client import ClientNetwork
@@ -1128,8 +1115,8 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.response.headers = {}
self.response.links = {}
self.response.checked = False
self.acmev1_nonce_response = mock.MagicMock(ok=False,
status_code=http_client.METHOD_NOT_ALLOWED)
self.acmev1_nonce_response = mock.MagicMock(
ok=False, status_code=http_client.METHOD_NOT_ALLOWED)
self.acmev1_nonce_response.headers = {}
self.obj = mock.MagicMock()
self.wrapped_obj = mock.MagicMock()

View File

@@ -5,15 +5,14 @@ import threading
import time
import unittest
import six
from six.moves import socketserver #type: ignore # pylint: disable=import-error
import josepy as jose
import OpenSSL
import six
from six.moves import socketserver # type: ignore # pylint: disable=import-error
from acme import errors
from acme import test_util
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
import test_util
class SSLSocketAndProbeSNITest(unittest.TestCase):
@@ -30,7 +29,6 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
class _TestServer(socketserver.TCPServer):
# pylint: disable=too-few-public-methods
# six.moves.* | pylint: disable=attribute-defined-outside-init,no-init
def server_bind(self): # pylint: disable=missing-docstring
@@ -40,7 +38,6 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
self.server = _TestServer(('', 0), socketserver.BaseRequestHandler)
self.port = self.server.socket.getsockname()[1]
self.server_thread = threading.Thread(
# pylint: disable=no-member
target=self.server.handle_request)
def tearDown(self):
@@ -67,7 +64,7 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
def test_probe_connection_error(self):
# pylint has a hard time with six
self.server.server_close() # pylint: disable=no-member
self.server.server_close()
original_timeout = socket.getdefaulttimeout()
try:
socket.setdefaulttimeout(1)

View File

@@ -2,6 +2,7 @@
import importlib
import unittest
class JoseTest(unittest.TestCase):
"""Tests for acme.jose shim."""
@@ -20,11 +21,10 @@ class JoseTest(unittest.TestCase):
# We use the imports below with eval, but pylint doesn't
# understand that.
# pylint: disable=eval-used,unused-variable
import acme
import josepy
acme_jose_mod = eval(acme_jose_path)
josepy_mod = eval(josepy_path)
import acme # pylint: disable=unused-import
import josepy # pylint: disable=unused-import
acme_jose_mod = eval(acme_jose_path) # pylint: disable=eval-used
josepy_mod = eval(josepy_path) # pylint: disable=eval-used
self.assertIs(acme_jose_mod, josepy_mod)
self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute))

View File

@@ -3,8 +3,7 @@ import unittest
import josepy as jose
from acme import test_util
import test_util
KEY = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem'))

View File

@@ -5,9 +5,8 @@ import josepy as jose
import mock
from acme import challenges
from acme import test_util
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
import test_util
CERT = test_util.load_comparable_cert('cert.der')
CSR = test_util.load_comparable_csr('csr.der')
@@ -19,8 +18,7 @@ class ErrorTest(unittest.TestCase):
def setUp(self):
from acme.messages import Error, ERROR_PREFIX
self.error = Error(
detail='foo', typ=ERROR_PREFIX + 'malformed', title='title')
self.error = Error.with_code('malformed', detail='foo', title='title')
self.jobj = {
'detail': 'foo',
'title': 'some title',
@@ -28,7 +26,6 @@ class ErrorTest(unittest.TestCase):
}
self.error_custom = Error(typ='custom', detail='bar')
self.empty_error = Error()
self.jobj_custom = {'type': 'custom', 'detail': 'bar'}
def test_default_typ(self):
from acme.messages import Error
@@ -43,8 +40,7 @@ class ErrorTest(unittest.TestCase):
hash(Error.from_json(self.error.to_json()))
def test_description(self):
self.assertEqual(
'The request message was malformed', self.error.description)
self.assertEqual('The request message was malformed', self.error.description)
self.assertTrue(self.error_custom.description is None)
def test_code(self):
@@ -54,17 +50,17 @@ class ErrorTest(unittest.TestCase):
self.assertEqual(None, Error().code)
def test_is_acme_error(self):
from acme.messages import is_acme_error
from acme.messages import is_acme_error, Error
self.assertTrue(is_acme_error(self.error))
self.assertFalse(is_acme_error(self.error_custom))
self.assertFalse(is_acme_error(Error()))
self.assertFalse(is_acme_error(self.empty_error))
self.assertFalse(is_acme_error("must pet all the {dogs|rabbits}"))
def test_unicode_error(self):
from acme.messages import Error, ERROR_PREFIX, is_acme_error
arabic_error = Error(
detail=u'\u0639\u062f\u0627\u0644\u0629', typ=ERROR_PREFIX + 'malformed',
title='title')
from acme.messages import Error, is_acme_error
arabic_error = Error.with_code(
'malformed', detail=u'\u0639\u062f\u0627\u0644\u0629', title='title')
self.assertTrue(is_acme_error(arabic_error))
def test_with_code(self):
@@ -305,8 +301,7 @@ class ChallengeBodyTest(unittest.TestCase):
from acme.messages import Error
from acme.messages import STATUS_INVALID
self.status = STATUS_INVALID
error = Error(typ='urn:ietf:params:acme:error:serverInternal',
detail='Unable to communicate with DNS server')
error = Error.with_code('serverInternal', detail='Unable to communicate with DNS server')
self.challb = ChallengeBody(
uri='http://challb', chall=self.chall, status=self.status,
error=error)

View File

@@ -1,23 +1,17 @@
"""Tests for acme.standalone."""
import os
import shutil
import socket
import threading
import tempfile
import unittest
from six.moves import http_client # pylint: disable=import-error
from six.moves import queue # pylint: disable=import-error
from six.moves import socketserver # type: ignore # pylint: disable=import-error
import josepy as jose
import mock
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
from acme import test_util
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
import test_util
class TLSServerTest(unittest.TestCase):
@@ -28,41 +22,14 @@ class TLSServerTest(unittest.TestCase):
from acme.standalone import TLSServer
server = TLSServer(
('', 0), socketserver.BaseRequestHandler, bind_and_activate=True)
server.server_close() # pylint: disable=no-member
server.server_close()
def test_ipv6(self):
if socket.has_ipv6:
from acme.standalone import TLSServer
server = TLSServer(
('', 0), socketserver.BaseRequestHandler, bind_and_activate=True, ipv6=True)
server.server_close() # pylint: disable=no-member
class TLSSNI01ServerTest(unittest.TestCase):
"""Test for acme.standalone.TLSSNI01Server."""
def setUp(self):
self.certs = {b'localhost': (
test_util.load_pyopenssl_private_key('rsa2048_key.pem'),
test_util.load_cert('rsa2048_cert.pem'),
)}
from acme.standalone import TLSSNI01Server
self.server = TLSSNI01Server(('localhost', 0), certs=self.certs)
# pylint: disable=no-member
self.thread = threading.Thread(target=self.server.serve_forever)
self.thread.start()
def tearDown(self):
self.server.shutdown() # pylint: disable=no-member
self.thread.join()
def test_it(self):
host, port = self.server.socket.getsockname()[:2]
cert = crypto_util.probe_sni(
b'localhost', host=host, port=port, timeout=1)
self.assertEqual(jose.ComparableX509(cert),
jose.ComparableX509(self.certs[b'localhost'][1]))
server.server_close()
class HTTP01ServerTest(unittest.TestCase):
@@ -77,13 +44,12 @@ class HTTP01ServerTest(unittest.TestCase):
from acme.standalone import HTTP01Server
self.server = HTTP01Server(('', 0), resources=self.resources)
# pylint: disable=no-member
self.port = self.server.socket.getsockname()[1]
self.thread = threading.Thread(target=self.server.serve_forever)
self.thread.start()
def tearDown(self):
self.server.shutdown() # pylint: disable=no-member
self.server.shutdown()
self.thread.join()
def test_index(self):
@@ -136,7 +102,6 @@ class BaseDualNetworkedServersTest(unittest.TestCase):
# NB: On Windows, socket.IPPROTO_IPV6 constant may be missing.
# We use the corresponding value (41) instead.
level = getattr(socket, "IPPROTO_IPV6", 41)
# pylint: disable=no-member
self.socket.setsockopt(level, socket.IPV6_V6ONLY, 1)
try:
self.server_bind()
@@ -170,33 +135,6 @@ class BaseDualNetworkedServersTest(unittest.TestCase):
prev_port = port
class TLSSNI01DualNetworkedServersTest(unittest.TestCase):
"""Test for acme.standalone.TLSSNI01DualNetworkedServers."""
def setUp(self):
self.certs = {b'localhost': (
test_util.load_pyopenssl_private_key('rsa2048_key.pem'),
test_util.load_cert('rsa2048_cert.pem'),
)}
from acme.standalone import TLSSNI01DualNetworkedServers
self.servers = TLSSNI01DualNetworkedServers(('localhost', 0), certs=self.certs)
self.servers.serve_forever()
def tearDown(self):
self.servers.shutdown_and_server_close()
def test_connect(self):
socknames = self.servers.getsocknames()
# connect to all addresses
for sockname in socknames:
host, port = sockname[:2]
cert = crypto_util.probe_sni(
b'localhost', host=host, port=port, timeout=1)
self.assertEqual(jose.ComparableX509(cert),
jose.ComparableX509(self.certs[b'localhost'][1]))
class HTTP01DualNetworkedServersTest(unittest.TestCase):
"""Tests for acme.standalone.HTTP01DualNetworkedServers."""
@@ -209,7 +147,6 @@ class HTTP01DualNetworkedServersTest(unittest.TestCase):
from acme.standalone import HTTP01DualNetworkedServers
self.servers = HTTP01DualNetworkedServers(('', 0), resources=self.resources)
# pylint: disable=no-member
self.port = self.servers.getsocknames()[0][1]
self.servers.serve_forever()
@@ -248,51 +185,5 @@ class HTTP01DualNetworkedServersTest(unittest.TestCase):
self.assertFalse(self._test_http01(add=False))
@test_util.broken_on_windows
class TestSimpleTLSSNI01Server(unittest.TestCase):
"""Tests for acme.standalone.simple_tls_sni_01_server."""
def setUp(self):
# mirror ../examples/standalone
self.test_cwd = tempfile.mkdtemp()
localhost_dir = os.path.join(self.test_cwd, 'localhost')
os.makedirs(localhost_dir)
shutil.copy(test_util.vector_path('rsa2048_cert.pem'),
os.path.join(localhost_dir, 'cert.pem'))
shutil.copy(test_util.vector_path('rsa2048_key.pem'),
os.path.join(localhost_dir, 'key.pem'))
from acme.standalone import simple_tls_sni_01_server
self.thread = threading.Thread(
target=simple_tls_sni_01_server, kwargs={
'cli_args': ('filename',),
'forever': False,
},
)
self.old_cwd = os.getcwd()
os.chdir(self.test_cwd)
def tearDown(self):
os.chdir(self.old_cwd)
self.thread.join()
shutil.rmtree(self.test_cwd)
@mock.patch('acme.standalone.logger')
def test_it(self, mock_logger):
# Use a Queue because mock objects aren't thread safe.
q = queue.Queue() # type: queue.Queue[int]
# Add port number to the queue.
mock_logger.info.side_effect = lambda *args: q.put(args[-1])
self.thread.start()
# After the timeout, an exception is raised if the queue is empty.
port = q.get(timeout=5)
cert = crypto_util.probe_sni(b'localhost', b'0.0.0.0', port)
self.assertEqual(jose.ComparableX509(cert),
test_util.load_comparable_cert(
'rsa2048_cert.pem'))
if __name__ == "__main__":
unittest.main() # pragma: no cover

View File

@@ -4,20 +4,12 @@
"""
import os
import sys
import pkg_resources
import unittest
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import josepy as jose
from OpenSSL import crypto
def vector_path(*names):
"""Path to a test vector."""
return pkg_resources.resource_filename(
__name__, os.path.join('testdata', *names))
import pkg_resources
def load_vector(*names):
@@ -33,8 +25,7 @@ def _guess_loader(filename, loader_pem, loader_der):
return loader_pem
elif ext.lower() == '.der':
return loader_der
else: # pragma: no cover
raise ValueError("Loader could not be recognized based on extension")
raise ValueError("Loader could not be recognized based on extension") # pragma: no cover
def load_cert(*names):
@@ -74,32 +65,3 @@ def load_pyopenssl_private_key(*names):
loader = _guess_loader(
names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
return crypto.load_privatekey(loader, load_vector(*names))
def skip_unless(condition, reason): # pragma: no cover
"""Skip tests unless a condition holds.
This implements the basic functionality of unittest.skipUnless
which is only available on Python 2.7+.
:param bool condition: If ``False``, the test will be skipped
:param str reason: the reason for skipping the test
:rtype: callable
:returns: decorator that hides tests unless condition is ``True``
"""
if hasattr(unittest, "skipUnless"):
return unittest.skipUnless(condition, reason)
elif condition:
return lambda cls: cls
else:
return lambda cls: None
def broken_on_windows(function):
"""Decorator to skip temporarily a broken test on Windows."""
reason = 'Test is broken and ignored on windows but should be fixed.'
return unittest.skipIf(
sys.platform == 'win32'
and os.environ.get('SKIP_BROKEN_TESTS_ON_WINDOWS', 'true') == 'true',
reason)(function)

View File

@@ -1,32 +0,0 @@
image: Visual Studio 2015
environment:
matrix:
- TOXENV: py35
- TOXENV: py37-cover
branches:
only:
- master
- /^\d+\.\d+\.x$/ # Version branches like X.X.X
- /^test-.*$/
install:
# Use Python 3.7 by default
- "SET PATH=C:\\Python37;C:\\Python37\\Scripts;%PATH%"
# Check env
- "python --version"
# Upgrade pip to avoid warnings
- "python -m pip install --upgrade pip"
# Ready to install tox and coverage
- "pip install tox codecov"
build: off
test_script:
- set TOX_TESTENV_PASSENV=APPVEYOR
# Test env is set by TOXENV env variable
- tox
on_success:
- if exist .coverage codecov

View File

@@ -1,7 +1,8 @@
include LICENSE.txt
include README.rst
recursive-include docs *
recursive-include certbot_apache/tests/testdata *
include certbot_apache/centos-options-ssl-apache.conf
include certbot_apache/options-ssl-apache.conf
recursive-include certbot_apache/augeas_lens *.aug
recursive-include tests *
include certbot_apache/_internal/centos-options-ssl-apache.conf
include certbot_apache/_internal/options-ssl-apache.conf
recursive-include certbot_apache/_internal/augeas_lens *.aug
global-exclude __pycache__
global-exclude *.py[cod]

View File

@@ -0,0 +1 @@
"""Certbot Apache plugin."""

View File

@@ -1,8 +1,9 @@
""" Utility functions for certbot-apache plugin """
import binascii
import os
from certbot import util
from certbot.compat import os
def get_mod_deps(mod_name):
"""Get known module dependencies.

View File

@@ -1,40 +1,39 @@
"""Apache Configuration based off of Augeas Configurator."""
"""Apache Configurator."""
# pylint: disable=too-many-lines
from collections import defaultdict
import copy
import fnmatch
import logging
import os
import pkg_resources
import re
import six
import socket
import time
import pkg_resources
import six
import zope.component
import zope.interface
from acme import challenges
from acme.magic_typing import Any, DefaultDict, Dict, List, Set, Union # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import DefaultDict # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Union # pylint: disable=unused-import, no-name-in-module
from certbot import errors
from certbot import interfaces
from certbot import util
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge # pylint: disable=unused-import
from certbot.compat import filesystem
from certbot.compat import os
from certbot.plugins import common
from certbot.plugins.util import path_surgery
from certbot.plugins.enhancements import AutoHSTSEnhancement
from certbot_apache import apache_util
from certbot_apache import augeas_configurator
from certbot_apache import constants
from certbot_apache import display_ops
from certbot_apache import http_01
from certbot_apache import obj
from certbot_apache import parser
from certbot_apache import tls_sni_01
from collections import defaultdict
from certbot.plugins.util import path_surgery
from certbot_apache._internal import apache_util
from certbot_apache._internal import constants
from certbot_apache._internal import display_ops
from certbot_apache._internal import http_01
from certbot_apache._internal import obj
from certbot_apache._internal import parser
logger = logging.getLogger(__name__)
@@ -70,28 +69,29 @@ logger = logging.getLogger(__name__)
@zope.interface.implementer(interfaces.IAuthenticator, interfaces.IInstaller)
@zope.interface.provider(interfaces.IPluginFactory)
class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# pylint: disable=too-many-instance-attributes,too-many-public-methods
class ApacheConfigurator(common.Installer):
"""Apache configurator.
State of Configurator: This code has been been tested and built for Ubuntu
14.04 Apache 2.4 and it works for Ubuntu 12.04 Apache 2.2
:ivar config: Configuration.
:type config: :class:`~certbot.interfaces.IConfig`
:ivar parser: Handles low level parsing
:type parser: :class:`~certbot_apache.parser`
:type parser: :class:`~certbot_apache._internal.parser`
:ivar tup version: version of Apache
:ivar list vhosts: All vhosts found in the configuration
(:class:`list` of :class:`~certbot_apache.obj.VirtualHost`)
(:class:`list` of :class:`~certbot_apache._internal.obj.VirtualHost`)
:ivar dict assoc: Mapping between domains and vhosts
"""
description = "Apache Web Server plugin"
if os.environ.get("CERTBOT_DOCS") == "1":
description += ( # pragma: no cover
" (Please note that the default values of the Apache plugin options"
" change depending on the operating system Certbot is run on.)"
)
OS_DEFAULTS = dict(
server_root="/etc/apache2",
@@ -109,7 +109,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
handle_sites=False,
challenge_location="/etc/apache2",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "options-ssl-apache.conf")
"certbot_apache", os.path.join("_internal", "options-ssl-apache.conf"))
)
def option(self, key):
@@ -141,31 +141,37 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# When adding, modifying or deleting command line arguments, be sure to
# include the changes in the list used in method _prepare_options() to
# ensure consistent behavior.
add("enmod", default=cls.OS_DEFAULTS["enmod"],
# Respect CERTBOT_DOCS environment variable and use default values from
# base class regardless of the underlying distribution (overrides).
if os.environ.get("CERTBOT_DOCS") == "1":
DEFAULTS = ApacheConfigurator.OS_DEFAULTS
else:
# cls.OS_DEFAULTS can be distribution specific, see override classes
DEFAULTS = cls.OS_DEFAULTS
add("enmod", default=DEFAULTS["enmod"],
help="Path to the Apache 'a2enmod' binary")
add("dismod", default=cls.OS_DEFAULTS["dismod"],
add("dismod", default=DEFAULTS["dismod"],
help="Path to the Apache 'a2dismod' binary")
add("le-vhost-ext", default=cls.OS_DEFAULTS["le_vhost_ext"],
add("le-vhost-ext", default=DEFAULTS["le_vhost_ext"],
help="SSL vhost configuration extension")
add("server-root", default=cls.OS_DEFAULTS["server_root"],
add("server-root", default=DEFAULTS["server_root"],
help="Apache server root directory")
add("vhost-root", default=None,
help="Apache server VirtualHost configuration root")
add("logs-root", default=cls.OS_DEFAULTS["logs_root"],
add("logs-root", default=DEFAULTS["logs_root"],
help="Apache server logs directory")
add("challenge-location",
default=cls.OS_DEFAULTS["challenge_location"],
default=DEFAULTS["challenge_location"],
help="Directory path for challenge configuration")
add("handle-modules", default=cls.OS_DEFAULTS["handle_modules"],
add("handle-modules", default=DEFAULTS["handle_modules"],
help="Let installer handle enabling required modules for you " +
"(Only Ubuntu/Debian currently)")
add("handle-sites", default=cls.OS_DEFAULTS["handle_sites"],
add("handle-sites", default=DEFAULTS["handle_sites"],
help="Let installer handle enabling sites for you " +
"(Only Ubuntu/Debian currently)")
add("ctl", default=cls.OS_DEFAULTS["ctl"],
add("ctl", default=DEFAULTS["ctl"],
help="Full path to Apache control script")
util.add_deprecated_argument(
add, argument_name="init-script", nargs=1)
def __init__(self, *args, **kwargs):
"""Initialize an Apache Configurator.
@@ -188,6 +194,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self._enhanced_vhosts = defaultdict(set) # type: DefaultDict[str, Set[obj.VirtualHost]]
# Temporary state for AutoHSTS enhancement
self._autohsts = {} # type: Dict[str, Dict[str, Union[int, float]]]
# Reverter save notes
self.save_notes = ""
# These will be set in the prepare function
self._prepared = False
@@ -202,15 +210,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
@property
def mod_ssl_conf(self):
"""Full absolute path to SSL configuration file."""
return os.path.join(self.config.config_dir,
constants.MOD_SSL_CONF_DEST)
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
@property
def updated_mod_ssl_conf_digest(self):
"""Full absolute path to digest of updated SSL configuration file."""
return os.path.join(self.config.config_dir, constants.UPDATED_MOD_SSL_CONF_DIGEST)
def prepare(self):
"""Prepare the authenticator/installer.
@@ -220,12 +226,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:raises .errors.PluginError: If there is any other error
"""
# Perform the actual Augeas initialization to be able to react
try:
self.init_augeas()
except ImportError:
raise errors.NoInstallationError("Problem in Augeas installation")
self._prepare_options()
# Verify Apache is installed
@@ -241,18 +241,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
'.'.join(str(i) for i in self.version))
if self.version < (2, 2):
raise errors.NotSupportedError(
"Apache Version %s not supported.", str(self.version))
if not self._check_aug_version():
raise errors.NotSupportedError(
"Apache plugin support requires libaugeas0 and augeas-lenses "
"version 1.2.0 or higher, please make sure you have you have "
"those installed.")
"Apache Version {0} not supported.".format(str(self.version)))
# Recover from previous crash before Augeas initialization to have the
# correct parse tree from the get go.
self.recovery_routine()
# Perform the actual Augeas initialization to be able to react
self.parser = self.get_parser()
# Check for errors in parsing files with Augeas
self.check_parsing_errors("httpd.aug")
self.parser.check_parsing_errors("httpd.aug")
# Get all of the available vhosts
self.vhosts = self.get_virtual_hosts()
@@ -266,9 +264,72 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
except (OSError, errors.LockError):
logger.debug("Encountered error:", exc_info=True)
raise errors.PluginError(
"Unable to lock %s", self.option("server_root"))
"Unable to create a lock file in {0}. Are you running"
" Certbot with sufficient privileges to modify your"
" Apache configuration?".format(self.option("server_root")))
self._prepared = True
def save(self, title=None, temporary=False):
"""Saves all changes to the configuration files.
This function first checks for save errors, if none are found,
all configuration changes made will be saved. According to the
function parameters. If an exception is raised, a new checkpoint
was not created.
:param str title: The title of the save. If a title is given, the
configuration will be saved as a new checkpoint and put in a
timestamped directory.
:param bool temporary: Indicates whether the changes made will
be quickly reversed in the future (ie. challenges)
"""
save_files = self.parser.unsaved_files()
if save_files:
self.add_to_checkpoint(save_files,
self.save_notes, temporary=temporary)
# Handle the parser specific tasks
self.parser.save(save_files)
if title and not temporary:
self.finalize_checkpoint(title)
def recovery_routine(self):
"""Revert all previously modified files.
Reverts all modified files that have not been saved as a checkpoint
:raises .errors.PluginError: If unable to recover the configuration
"""
super(ApacheConfigurator, self).recovery_routine()
# Reload configuration after these changes take effect if needed
# ie. ApacheParser has been initialized.
if self.parser:
# TODO: wrap into non-implementation specific parser interface
self.parser.aug.load()
def revert_challenge_config(self):
"""Used to cleanup challenge configurations.
:raises .errors.PluginError: If unable to revert the challenge config.
"""
self.revert_temporary_config()
self.parser.aug.load()
def rollback_checkpoints(self, rollback=1):
"""Rollback saved checkpoints.
:param int rollback: Number of checkpoints to revert
:raises .errors.PluginError: If there is a problem with the input or
the function is unable to correctly revert the configuration
"""
super(ApacheConfigurator, self).rollback_checkpoints(rollback)
self.parser.aug.load()
def _verify_exe_availability(self, exe):
"""Checks availability of Apache executable"""
if not util.exe_exists(exe):
@@ -276,26 +337,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
raise errors.NoInstallationError(
'Cannot find Apache executable {0}'.format(exe))
def _check_aug_version(self):
""" Checks that we have recent enough version of libaugeas.
If augeas version is recent enough, it will support case insensitive
regexp matching"""
self.aug.set("/test/path/testing/arg", "aRgUMeNT")
try:
matches = self.aug.match(
"/test//*[self::arg=~regexp('argument', 'i')]")
except RuntimeError:
self.aug.remove("/test/path")
return False
self.aug.remove("/test/path")
return matches
def get_parser(self):
"""Initializes the ApacheParser"""
# If user provided vhost_root value in command line, use it
return parser.ApacheParser(
self.aug, self.option("server_root"), self.conf("vhost-root"),
self.option("server_root"), self.conf("vhost-root"),
self.version, configurator=self)
def _wildcard_domain(self, domain):
@@ -344,7 +390,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
counterpart, should one get created
:returns: List of VirtualHosts or None
:rtype: `list` of :class:`~certbot_apache.obj.VirtualHost`
:rtype: `list` of :class:`~certbot_apache._internal.obj.VirtualHost`
"""
if self._wildcard_domain(domain):
@@ -382,7 +428,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
if len(name.split(".")) == len(domain.split(".")):
return fnmatch.fnmatch(name, domain)
return None
def _choose_vhosts_wildcard(self, domain, create_ssl=True):
"""Prompts user to choose vhosts to install a wildcard certificate for"""
@@ -403,7 +449,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
filtered_vhosts[name] = vhost
# Only unique VHost objects
dialog_input = set([vhost for vhost in filtered_vhosts.values()])
dialog_input = set(filtered_vhosts.values())
# Ask the user which of names to enable, expect list of names back
dialog_output = display_ops.select_vhost_multiple(list(dialog_input))
@@ -428,7 +474,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self._wildcard_vhosts[domain] = return_vhosts
return return_vhosts
def _deploy_cert(self, vhost, cert_path, key_path, chain_path, fullchain_path):
"""
Helper function for deploy_cert() that handles the actual deployment
@@ -436,8 +481,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
domain originally passed for deploy_cert(). This is especially true
with wildcard certificates
"""
# This is done first so that ssl module is enabled and cert_path,
# cert_key... can all be parsed appropriately
self.prepare_server_https("443")
@@ -477,8 +520,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# install SSLCertificateFile, SSLCertificateKeyFile,
# and SSLCertificateChainFile directives
set_cert_path = cert_path
self.aug.set(path["cert_path"][-1], cert_path)
self.aug.set(path["cert_key"][-1], key_path)
self.parser.aug.set(path["cert_path"][-1], cert_path)
self.parser.aug.set(path["cert_key"][-1], key_path)
if chain_path is not None:
self.parser.add_dir(vhost.path,
"SSLCertificateChainFile", chain_path)
@@ -490,8 +533,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
raise errors.PluginError("Please provide the --fullchain-path "
"option pointing to your full chain file")
set_cert_path = fullchain_path
self.aug.set(path["cert_path"][-1], fullchain_path)
self.aug.set(path["cert_key"][-1], key_path)
self.parser.aug.set(path["cert_path"][-1], fullchain_path)
self.parser.aug.set(path["cert_key"][-1], key_path)
# Enable the new vhost if needed
if not vhost.enabled:
@@ -522,7 +565,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
counterpart, should one get created
:returns: vhost associated with name
:rtype: :class:`~certbot_apache.obj.VirtualHost`
:rtype: :class:`~certbot_apache._internal.obj.VirtualHost`
:raises .errors.PluginError: If no vhost is available or chosen
@@ -557,9 +600,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"in the Apache config.",
target_name)
raise errors.PluginError("No vhost selected")
elif temp:
if temp:
return vhost
elif not vhost.ssl:
if not vhost.ssl:
addrs = self._get_proposed_addrs(vhost, "443")
# TODO: Conflicts is too conservative
if not any(vhost.enabled and vhost.conflicts(addrs) for
@@ -625,7 +668,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:param str target_name: domain handled by the desired vhost
:param vhosts: vhosts to consider
:type vhosts: `collections.Iterable` of :class:`~certbot_apache.obj.VirtualHost`
:type vhosts: `collections.Iterable` of :class:`~certbot_apache._internal.obj.VirtualHost`
:param bool filter_defaults: whether a vhost with a _default_
addr is acceptable
@@ -709,7 +752,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
if name:
all_names.add(name)
if len(vhost_macro) > 0:
if vhost_macro:
zope.component.getUtility(interfaces.IDisplay).notification(
"Apache mod_macro seems to be in use in file(s):\n{0}"
"\n\nUnfortunately mod_macro is not yet supported".format(
@@ -767,7 +810,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Helper function for get_virtual_hosts().
:param host: In progress vhost whose names will be added
:type host: :class:`~certbot_apache.obj.VirtualHost`
:type host: :class:`~certbot_apache._internal.obj.VirtualHost`
"""
@@ -786,12 +829,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:param str path: Augeas path to virtual host
:returns: newly created vhost
:rtype: :class:`~certbot_apache.obj.VirtualHost`
:rtype: :class:`~certbot_apache._internal.obj.VirtualHost`
"""
addrs = set()
try:
args = self.aug.match(path + "/arg")
args = self.parser.aug.match(path + "/arg")
except RuntimeError:
logger.warning("Encountered a problem while parsing file: %s, skipping", path)
return None
@@ -809,7 +852,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
is_ssl = True
filename = apache_util.get_file_path(
self.aug.get("/augeas/files%s/path" % apache_util.get_file_path(path)))
self.parser.aug.get("/augeas/files%s/path" % apache_util.get_file_path(path)))
if filename is None:
return None
@@ -827,7 +870,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
def get_virtual_hosts(self):
"""Returns list of virtual hosts found in the Apache configuration.
:returns: List of :class:`~certbot_apache.obj.VirtualHost`
:returns: List of :class:`~certbot_apache._internal.obj.VirtualHost`
objects found in configuration
:rtype: list
@@ -839,7 +882,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Make a list of parser paths because the parser_paths
# dictionary may be modified during the loop.
for vhost_path in list(self.parser.parser_paths):
paths = self.aug.match(
paths = self.parser.aug.match(
("/files%s//*[label()=~regexp('%s')]" %
(vhost_path, parser.case_i("VirtualHost"))))
paths = [path for path in paths if
@@ -849,7 +892,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
if not new_vhost:
continue
internal_path = apache_util.get_internal_aug_path(new_vhost.path)
realpath = os.path.realpath(new_vhost.filep)
realpath = filesystem.realpath(new_vhost.filep)
if realpath not in file_paths:
file_paths[realpath] = new_vhost.filep
internal_paths[realpath].add(internal_path)
@@ -884,7 +927,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
now NameVirtualHosts. If version is earlier than 2.4, check if addr
has a NameVirtualHost directive in the Apache config
:param certbot_apache.obj.Addr target_addr: vhost address
:param certbot_apache._internal.obj.Addr target_addr: vhost address
:returns: Success
:rtype: bool
@@ -902,19 +945,18 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Adds NameVirtualHost directive for given address.
:param addr: Address that will be added as NameVirtualHost directive
:type addr: :class:`~certbot_apache.obj.Addr`
:type addr: :class:`~certbot_apache._internal.obj.Addr`
"""
loc = parser.get_aug_path(self.parser.loc["name"])
if addr.get_port() == "443":
path = self.parser.add_dir_to_ifmodssl(
self.parser.add_dir_to_ifmodssl(
loc, "NameVirtualHost", [str(addr)])
else:
path = self.parser.add_dir(loc, "NameVirtualHost", [str(addr)])
self.parser.add_dir(loc, "NameVirtualHost", [str(addr)])
msg = ("Setting %s to be NameBasedVirtualHost\n"
"\tDirective added to %s\n" % (addr, path))
msg = "Setting {0} to be NameBasedVirtualHost\n".format(addr)
logger.debug(msg)
self.save_notes += msg
@@ -1055,6 +1097,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Ugly but takes care of protocol def, eg: 1.1.1.1:443 https
if listen.split(":")[-1].split(" ")[0] == port:
return True
return None
def prepare_https_modules(self, temp):
"""Helper method for prepare_server_https, taking care of enabling
@@ -1070,24 +1113,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
if "ssl_module" not in self.parser.modules:
self.enable_mod("ssl", temp=temp)
def make_addrs_sni_ready(self, addrs):
"""Checks to see if the server is ready for SNI challenges.
:param addrs: Addresses to check SNI compatibility
:type addrs: :class:`~certbot_apache.obj.Addr`
"""
# Version 2.4 and later are automatically SNI ready.
if self.version >= (2, 4):
return
for addr in addrs:
if not self.is_name_vhost(addr):
logger.debug("Setting VirtualHost at %s to be a name "
"based virtual host", addr)
self.add_name_vhost(addr)
def make_vhost_ssl(self, nonssl_vhost): # pylint: disable=too-many-locals
def make_vhost_ssl(self, nonssl_vhost):
"""Makes an ssl_vhost version of a nonssl_vhost.
Duplicates vhost and adds default ssl options
@@ -1097,10 +1123,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
.. note:: This function saves the configuration
:param nonssl_vhost: Valid VH that doesn't have SSLEngine on
:type nonssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
:type nonssl_vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:returns: SSL vhost
:rtype: :class:`~certbot_apache.obj.VirtualHost`
:rtype: :class:`~certbot_apache._internal.obj.VirtualHost`
:raises .errors.PluginError: If more than one virtual host is in
the file or if plugin is unable to write/read vhost files.
@@ -1109,16 +1135,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
avail_fp = nonssl_vhost.filep
ssl_fp = self._get_ssl_vhost_path(avail_fp)
orig_matches = self.aug.match("/files%s//* [label()=~regexp('%s')]" %
orig_matches = self.parser.aug.match("/files%s//* [label()=~regexp('%s')]" %
(self._escape(ssl_fp),
parser.case_i("VirtualHost")))
self._copy_create_ssl_vhost_skeleton(nonssl_vhost, ssl_fp)
# Reload augeas to take into account the new vhost
self.aug.load()
self.parser.aug.load()
# Get Vhost augeas path for new vhost
new_matches = self.aug.match("/files%s//* [label()=~regexp('%s')]" %
new_matches = self.parser.aug.match("/files%s//* [label()=~regexp('%s')]" %
(self._escape(ssl_fp),
parser.case_i("VirtualHost")))
@@ -1129,7 +1155,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Make Augeas aware of the new vhost
self.parser.parse_file(ssl_fp)
# Try to search again
new_matches = self.aug.match(
new_matches = self.parser.aug.match(
"/files%s//* [label()=~regexp('%s')]" %
(self._escape(ssl_fp),
parser.case_i("VirtualHost")))
@@ -1191,16 +1217,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
if self.conf("vhost-root") and os.path.exists(self.conf("vhost-root")):
fp = os.path.join(os.path.realpath(self.option("vhost_root")),
fp = os.path.join(filesystem.realpath(self.option("vhost_root")),
os.path.basename(non_ssl_vh_fp))
else:
# Use non-ssl filepath
fp = os.path.realpath(non_ssl_vh_fp)
fp = filesystem.realpath(non_ssl_vh_fp)
if fp.endswith(".conf"):
return fp[:-(len(".conf"))] + self.option("le_vhost_ext")
else:
return fp + self.option("le_vhost_ext")
return fp + self.option("le_vhost_ext")
def _sift_rewrite_rule(self, line):
"""Decides whether a line should be copied to a SSL vhost.
@@ -1280,8 +1305,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"vhost for your HTTPS site located at {1} because they have "
"the potential to create redirection loops.".format(
vhost.filep, ssl_fp), reporter.MEDIUM_PRIORITY)
self.aug.set("/augeas/files%s/mtime" % (self._escape(ssl_fp)), "0")
self.aug.set("/augeas/files%s/mtime" % (self._escape(vhost.filep)), "0")
self.parser.aug.set("/augeas/files%s/mtime" % (self._escape(ssl_fp)), "0")
self.parser.aug.set("/augeas/files%s/mtime" % (self._escape(vhost.filep)), "0")
def _sift_rewrite_rules(self, contents):
""" Helper function for _copy_create_ssl_vhost_skeleton to prepare the
@@ -1339,12 +1364,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
result.append(comment)
sift = True
result.append('\n'.join(
['# ' + l for l in chunk]))
continue
result.append('\n'.join(['# ' + l for l in chunk]))
else:
result.append('\n'.join(chunk))
continue
return result, sift
def _get_vhost_block(self, vhost):
@@ -1356,7 +1378,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
try:
span_val = self.aug.span(vhost.path)
span_val = self.parser.aug.span(vhost.path)
except ValueError:
logger.critical("Error while reading the VirtualHost %s from "
"file %s", vhost.name, vhost.filep, exc_info=True)
@@ -1391,13 +1413,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
def _update_ssl_vhosts_addrs(self, vh_path):
ssl_addrs = set()
ssl_addr_p = self.aug.match(vh_path + "/arg")
ssl_addr_p = self.parser.aug.match(vh_path + "/arg")
for addr in ssl_addr_p:
old_addr = obj.Addr.fromstring(
str(self.parser.get_arg(addr)))
ssl_addr = old_addr.get_addr_obj("443")
self.aug.set(addr, str(ssl_addr))
self.parser.aug.set(addr, str(ssl_addr))
ssl_addrs.add(ssl_addr)
return ssl_addrs
@@ -1416,15 +1438,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
vh_path, False)) > 1:
directive_path = self.parser.find_dir(directive, None,
vh_path, False)
self.aug.remove(re.sub(r"/\w*$", "", directive_path[0]))
self.parser.aug.remove(re.sub(r"/\w*$", "", directive_path[0]))
def _remove_directives(self, vh_path, directives):
for directive in directives:
while len(self.parser.find_dir(directive, None,
vh_path, False)) > 0:
while self.parser.find_dir(directive, None, vh_path, False):
directive_path = self.parser.find_dir(directive, None,
vh_path, False)
self.aug.remove(re.sub(r"/\w*$", "", directive_path[0]))
self.parser.aug.remove(re.sub(r"/\w*$", "", directive_path[0]))
def _add_dummy_ssl_directives(self, vh_path):
self.parser.add_dir(vh_path, "SSLCertificateFile",
@@ -1463,7 +1484,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
matches = self.parser.find_dir(
"ServerAlias", start=vh_path, exclude=False)
aliases = (self.aug.get(match) for match in matches)
aliases = (self.parser.aug.get(match) for match in matches)
return self.domain_in_names(aliases, target_name)
def _add_name_vhost_if_necessary(self, vhost):
@@ -1473,7 +1494,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
https://httpd.apache.org/docs/2.2/mod/core.html#namevirtualhost
:param vhost: New virtual host that was recently created.
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
"""
need_to_save = False
@@ -1508,7 +1529,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:param str id_str: Id string for matching
:returns: The matched VirtualHost or None
:rtype: :class:`~certbot_apache.obj.VirtualHost` or None
:rtype: :class:`~certbot_apache._internal.obj.VirtualHost` or None
:raises .errors.PluginError: If no VirtualHost is found
"""
@@ -1525,7 +1546,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
used for keeping track of VirtualHost directive over time.
:param vhost: Virtual host to add the id
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:returns: The unique ID or None
:rtype: str or None
@@ -1547,7 +1568,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
If ID already exists, returns that instead.
:param vhost: Virtual host to add or find the id
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:returns: The unique ID for vhost
:rtype: str or None
@@ -1585,9 +1606,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:param str domain: domain to enhance
:param str enhancement: enhancement type defined in
:const:`~certbot.constants.ENHANCEMENTS`
:const:`~certbot.plugins.enhancements.ENHANCEMENTS`
:param options: options for the enhancement
See :const:`~certbot.constants.ENHANCEMENTS`
See :const:`~certbot.plugins.enhancements.ENHANCEMENTS`
documentation for appropriate parameter.
:raises .errors.PluginError: If Enhancement is not supported, or if
@@ -1625,7 +1646,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Increase the AutoHSTS max-age value
:param vhost: Virtual host object to modify
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:param str id_str: The unique ID string of VirtualHost
@@ -1646,7 +1667,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
if header_path:
pat = '(?:[ "]|^)(strict-transport-security)(?:[ "]|$)'
for match in header_path:
if re.search(pat, self.aug.get(match).lower()):
if re.search(pat, self.parser.aug.get(match).lower()):
hsts_dirpath = match
if not hsts_dirpath:
err_msg = ("Certbot was unable to find the existing HSTS header "
@@ -1660,7 +1681,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Our match statement was for string strict-transport-security, but
# we need to update the value instead. The next index is for the value
hsts_dirpath = hsts_dirpath.replace("arg[3]", "arg[4]")
self.aug.set(hsts_dirpath, hsts_maxage)
self.parser.aug.set(hsts_dirpath, hsts_maxage)
note_msg = ("Increasing HSTS max-age value to {0} for VirtualHost "
"in {1}\n".format(nextstep_value, vhost.filep))
logger.debug(note_msg)
@@ -1709,13 +1730,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
.. note:: This function saves the configuration
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
:type ssl_vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:param unused_options: Not currently used
:type unused_options: Not Available
:returns: Success, general_vhost (HTTP vhost)
:rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`)
:rtype: (bool, :class:`~certbot_apache._internal.obj.VirtualHost`)
"""
min_apache_ver = (2, 3, 3)
@@ -1742,7 +1763,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# We'll simply delete the directive, so that we'll have a
# consistent OCSP cache path.
if stapling_cache_aug_path:
self.aug.remove(
self.parser.aug.remove(
re.sub(r"/\w*$", "", stapling_cache_aug_path[0]))
self.parser.add_dir_to_ifmodssl(ssl_vhost_aug_path,
@@ -1765,14 +1786,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
.. note:: This function saves the configuration
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
:type ssl_vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:param header_substring: string that uniquely identifies a header.
e.g: Strict-Transport-Security, Upgrade-Insecure-Requests.
:type str
:returns: Success, general_vhost (HTTP vhost)
:rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`)
:rtype: (bool, :class:`~certbot_apache._internal.obj.VirtualHost`)
:raises .errors.PluginError: If no viable HTTP host can be created or
set with header header_substring.
@@ -1800,7 +1821,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
contains the string header_substring.
:param ssl_vhost: vhost to check
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:param header_substring: string that uniquely identifies a header.
e.g: Strict-Transport-Security, Upgrade-Insecure-Requests.
@@ -1819,7 +1840,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# "Existing Header directive for virtualhost"
pat = '(?:[ "]|^)(%s)(?:[ "]|$)' % (header_substring.lower())
for match in header_path:
if re.search(pat, self.aug.get(match).lower()):
if re.search(pat, self.parser.aug.get(match).lower()):
raise errors.PluginEnhancementAlreadyPresent(
"Existing %s header" % (header_substring))
@@ -1837,7 +1858,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
.. note:: This function saves the configuration
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
:type ssl_vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:param unused_options: Not currently used
:type unused_options: Not Available
@@ -1912,7 +1933,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self.parser.add_dir(vhost.path, "RewriteRule",
constants.REWRITE_HTTPS_ARGS)
def _verify_no_certbot_redirect(self, vhost):
"""Checks to see if a redirect was already installed by certbot.
@@ -1923,7 +1943,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
delete certbot's old rewrite rules and set the new one instead.
:param vhost: vhost to check
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:raises errors.PluginEnhancementAlreadyPresent: When the exact
certbot redirection WriteRule exists in virtual host.
@@ -1947,11 +1967,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
constants.REWRITE_HTTPS_ARGS_WITH_END]
for dir_path, args_paths in rewrite_args_dict.items():
arg_vals = [self.aug.get(x) for x in args_paths]
arg_vals = [self.parser.aug.get(x) for x in args_paths]
# Search for past redirection rule, delete it, set the new one
if arg_vals in constants.OLD_REWRITE_HTTPS_ARGS:
self.aug.remove(dir_path)
self.parser.aug.remove(dir_path)
self._set_https_redirection_rewrite_rule(vhost)
self.save()
raise errors.PluginEnhancementAlreadyPresent(
@@ -1965,7 +1985,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Checks if there exists a RewriteRule directive in vhost
:param vhost: vhost to check
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:returns: True if a RewriteRule directive exists.
:rtype: bool
@@ -1979,7 +1999,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Checks if a RewriteEngine directive is on
:param vhost: vhost to check
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
"""
rewrite_engine_path_list = self.parser.find_dir("RewriteEngine", "on",
@@ -1996,10 +2016,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Creates an http_vhost specifically to redirect for the ssl_vhost.
:param ssl_vhost: ssl vhost
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
:type ssl_vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:returns: tuple of the form
(`success`, :class:`~certbot_apache.obj.VirtualHost`)
(`success`, :class:`~certbot_apache._internal.obj.VirtualHost`)
:rtype: tuple
"""
@@ -2007,7 +2027,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
redirect_filepath = self._write_out_redirect(ssl_vhost, text)
self.aug.load()
self.parser.aug.load()
# Make a new vhost data structure and add it to the lists
new_vhost = self._create_vhost(parser.get_aug_path(self._escape(redirect_filepath)))
self.vhosts.append(new_vhost)
@@ -2125,7 +2145,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
of this method where available.
:param vhost: vhost to enable
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:raises .errors.NotSupportedError: If filesystem layout is not
supported.
@@ -2143,7 +2163,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
vhost.enabled = True
return
def enable_mod(self, mod_name, temp=False): # pylint: disable=unused-argument
def enable_mod(self, mod_name, temp=False): # pylint: disable=unused-argument
"""Enables module in Apache.
Both enables and reloads Apache so module is active.
@@ -2180,7 +2200,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:raises .errors.MisconfigurationError: If reload fails
"""
error = ""
try:
util.run_script(self.option("restart_cmd"))
except errors.SubprocessError as err:
@@ -2254,7 +2273,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
###########################################################################
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
"""Return list of challenge preferences."""
return [challenges.HTTP01, challenges.TLSSNI01]
return [challenges.HTTP01]
def perform(self, achalls):
"""Perform the configuration related challenge.
@@ -2267,20 +2286,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self._chall_out.update(achalls)
responses = [None] * len(achalls)
http_doer = http_01.ApacheHttp01(self)
sni_doer = tls_sni_01.ApacheTlsSni01(self)
for i, achall in enumerate(achalls):
# Currently also have chall_doer hold associated index of the
# challenge. This helps to put all of the responses back together
# when they are all complete.
if isinstance(achall.chall, challenges.HTTP01):
http_doer.add_chall(achall, i)
else: # tls-sni-01
sni_doer.add_chall(achall, i)
http_doer.add_chall(achall, i)
http_response = http_doer.perform()
sni_response = sni_doer.perform()
if http_response or sni_response:
if http_response:
# Must reload in order to activate the challenges.
# Handled here because we may be able to load up other challenge
# types
@@ -2291,7 +2305,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
time.sleep(3)
self._update_responses(responses, http_response, http_doer)
self._update_responses(responses, sni_response, sni_doer)
return responses
@@ -2326,7 +2339,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
Enable the AutoHSTS enhancement for defined domains
:param _unused_lineage: Certificate lineage object, unused
:type _unused_lineage: certbot.storage.RenewableCert
:type _unused_lineage: certbot._internal.storage.RenewableCert
:param domains: List of domains in certificate to enhance
:type domains: str
@@ -2369,7 +2382,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Do the initial AutoHSTS deployment to a vhost
:param ssl_vhost: The VirtualHost object to deploy the AutoHSTS
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` or None
:type ssl_vhost: :class:`~certbot_apache._internal.obj.VirtualHost` or None
:raises errors.PluginEnhancementAlreadyPresent: When already enhanced
@@ -2451,7 +2464,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
and changes the HSTS max-age to a high value.
:param lineage: Certificate lineage object
:type lineage: certbot.storage.RenewableCert
:type lineage: certbot._internal.storage.RenewableCert
"""
self._autohsts_fetch_state()
if not self._autohsts:
@@ -2496,4 +2509,4 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self._autohsts_save_state()
AutoHSTSEnhancement.register(ApacheConfigurator) # pylint: disable=no-member
AutoHSTSEnhancement.register(ApacheConfigurator)

View File

@@ -1,6 +1,7 @@
"""Apache plugin constants."""
import pkg_resources
from certbot.compat import os
MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
@@ -9,6 +10,7 @@ MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
UPDATED_MOD_SSL_CONF_DIGEST = ".updated-options-ssl-apache-conf-digest.txt"
"""Name of the hash of the updated or informed mod_ssl_conf as saved in `IConfig.config_dir`."""
# NEVER REMOVE A SINGLE HASH FROM THIS LIST UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING!
ALL_SSL_OPTIONS_HASHES = [
'2086bca02db48daf93468332543c60ac6acdb6f0b58c7bfdf578a5d47092f82a',
'4844d36c9a0f587172d9fa10f4f1c9518e3bcfa1947379f155e16a70a728c21a',
@@ -18,11 +20,15 @@ ALL_SSL_OPTIONS_HASHES = [
'cfdd7c18d2025836ea3307399f509cfb1ebf2612c87dd600a65da2a8e2f2797b',
'80720bd171ccdc2e6b917ded340defae66919e4624962396b992b7218a561791',
'c0c022ea6b8a51ecc8f1003d0a04af6c3f2bc1c3ce506b3c2dfc1f11ef931082',
'717b0a89f5e4c39b09a42813ac6e747cfbdeb93439499e73f4f70a1fe1473f20',
'0fcdc81280cd179a07ec4d29d3595068b9326b455c488de4b09f585d5dafc137',
'86cc09ad5415cd6d5f09a947fe2501a9344328b1e8a8b458107ea903e80baa6c',
'06675349e457eae856120cdebb564efe546f0b87399f2264baeb41e442c724c7',
]
"""SHA256 hashes of the contents of previous versions of all versions of MOD_SSL_CONF_SRC"""
AUGEAS_LENS_DIR = pkg_resources.resource_filename(
"certbot_apache", "augeas_lens")
"certbot_apache", os.path.join("_internal", "augeas_lens"))
"""Path to the Augeas lens directory"""
REWRITE_HTTPS_ARGS = [

View File

@@ -1,15 +1,13 @@
"""Contains UI methods for Apache operations."""
import logging
import os
import zope.component
from certbot import errors
from certbot import interfaces
from certbot.compat import os
import certbot.display.util as display_util
logger = logging.getLogger(__name__)
@@ -26,7 +24,7 @@ def select_vhost_multiple(vhosts):
return list()
tags_list = [vhost.display_repr()+"\n" for vhost in vhosts]
# Remove the extra newline from the last entry
if len(tags_list):
if tags_list:
tags_list[-1] = tags_list[-1][:-1]
code, names = zope.component.getUtility(interfaces.IDisplay).checklist(
"Which VirtualHosts would you like to install the wildcard certificate for?",
@@ -62,8 +60,7 @@ def select_vhost(domain, vhosts):
code, tag = _vhost_menu(domain, vhosts)
if code == display_util.OK:
return vhosts[tag]
else:
return None
return None
def _vhost_menu(domain, vhosts):
"""Select an appropriate Apache Vhost.
@@ -80,7 +77,7 @@ def _vhost_menu(domain, vhosts):
if free_chars < 2:
logger.debug("Display size is too small for "
"certbot_apache.display_ops._vhost_menu()")
"certbot_apache._internal.display_ops._vhost_menu()")
# This runs the edge off the screen, but it doesn't cause an "error"
filename_size = 1
disp_name_size = 1
@@ -93,7 +90,7 @@ def _vhost_menu(domain, vhosts):
for vhost in vhosts:
if len(vhost.get_names()) == 1:
disp_name = next(iter(vhost.get_names()))
elif len(vhost.get_names()) == 0:
elif not vhost.get_names():
disp_name = ""
else:
disp_name = "Multiple Names"

View File

@@ -1,23 +1,32 @@
""" Entry point for Apache Plugin """
from certbot import util
# Pylint does not like disutils.version when running inside a venv.
# See: https://github.com/PyCQA/pylint/issues/73
from distutils.version import LooseVersion # pylint: disable=no-name-in-module,import-error
from certbot_apache import configurator
from certbot_apache import override_arch
from certbot_apache import override_darwin
from certbot_apache import override_debian
from certbot_apache import override_centos
from certbot_apache import override_gentoo
from certbot_apache import override_suse
from certbot import util
from certbot_apache._internal import configurator
from certbot_apache._internal import override_arch
from certbot_apache._internal import override_centos
from certbot_apache._internal import override_darwin
from certbot_apache._internal import override_debian
from certbot_apache._internal import override_fedora
from certbot_apache._internal import override_gentoo
from certbot_apache._internal import override_suse
OVERRIDE_CLASSES = {
"arch": override_arch.ArchConfigurator,
"cloudlinux": override_centos.CentOSConfigurator,
"darwin": override_darwin.DarwinConfigurator,
"debian": override_debian.DebianConfigurator,
"ubuntu": override_debian.DebianConfigurator,
"centos": override_centos.CentOSConfigurator,
"centos linux": override_centos.CentOSConfigurator,
"fedora": override_centos.CentOSConfigurator,
"fedora_old": override_centos.CentOSConfigurator,
"fedora": override_fedora.FedoraConfigurator,
"linuxmint": override_debian.DebianConfigurator,
"ol": override_centos.CentOSConfigurator,
"oracle": override_centos.CentOSConfigurator,
"redhatenterpriseserver": override_centos.CentOSConfigurator,
"red hat enterprise linux server": override_centos.CentOSConfigurator,
"rhel": override_centos.CentOSConfigurator,
"amazon": override_centos.CentOSConfigurator,
@@ -25,14 +34,24 @@ OVERRIDE_CLASSES = {
"gentoo base system": override_gentoo.GentooConfigurator,
"opensuse": override_suse.OpenSUSEConfigurator,
"suse": override_suse.OpenSUSEConfigurator,
"sles": override_suse.OpenSUSEConfigurator,
"scientific": override_centos.CentOSConfigurator,
"scientific linux": override_centos.CentOSConfigurator,
}
def get_configurator():
""" Get correct configurator class based on the OS fingerprint """
os_info = util.get_os_info()
os_name, os_version = util.get_os_info()
os_name = os_name.lower()
override_class = None
# Special case for older Fedora versions
if os_name == 'fedora' and LooseVersion(os_version) < LooseVersion('29'):
os_name = 'fedora_old'
try:
override_class = OVERRIDE_CLASSES[os_info[0].lower()]
override_class = OVERRIDE_CLASSES[os_name]
except KeyError:
# OS not found in the list
os_like = util.get_systemd_os_like()
@@ -45,4 +64,5 @@ def get_configurator():
override_class = configurator.ApacheConfigurator
return override_class
ENTRYPOINT = get_configurator()

View File

@@ -1,16 +1,19 @@
"""A class that performs HTTP-01 challenges for Apache"""
import logging
import os
from acme.magic_typing import List, Set # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
from certbot.plugins import common
from certbot_apache.obj import VirtualHost # pylint: disable=unused-import
from certbot_apache.parser import get_aug_path
from certbot_apache._internal.obj import VirtualHost # pylint: disable=unused-import
from certbot_apache._internal.parser import get_aug_path
logger = logging.getLogger(__name__)
class ApacheHttp01(common.TLSSNI01):
class ApacheHttp01(common.ChallengePerformer):
"""Class that performs HTTP-01 challenges within the Apache configurator."""
CONFIG_TEMPLATE22_PRE = """\
@@ -165,8 +168,7 @@ class ApacheHttp01(common.TLSSNI01):
def _set_up_challenges(self):
if not os.path.isdir(self.challenge_dir):
os.makedirs(self.challenge_dir)
os.chmod(self.challenge_dir, 0o755)
filesystem.makedirs(self.challenge_dir, 0o755)
responses = []
for achall in self.achalls:
@@ -182,7 +184,7 @@ class ApacheHttp01(common.TLSSNI01):
self.configurator.reverter.register_file_creation(True, name)
with open(name, 'wb') as f:
f.write(validation.encode())
os.chmod(name, 0o644)
filesystem.chmod(name, 0o644)
return response
@@ -192,8 +194,8 @@ class ApacheHttp01(common.TLSSNI01):
if vhost not in self.moded_vhosts:
logger.debug(
"Adding a temporary challenge validation Include for name: %s " +
"in: %s", vhost.name, vhost.filep)
"Adding a temporary challenge validation Include for name: %s in: %s",
vhost.name, vhost.filep)
self.configurator.parser.add_dir_beginning(
vhost.path, "Include", self.challenge_conf_pre)
self.configurator.parser.add_dir(

View File

@@ -1,7 +1,7 @@
"""Module contains classes used by the Apache Configurator."""
import re
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from certbot.plugins import common
@@ -24,9 +24,9 @@ class Addr(common.Addr):
return not self.__eq__(other)
def __repr__(self):
return "certbot_apache.obj.Addr(" + repr(self.tup) + ")"
return "certbot_apache._internal.obj.Addr(" + repr(self.tup) + ")"
def __hash__(self):
def __hash__(self): # pylint: disable=useless-super-delegation
# Python 3 requires explicit overridden for __hash__ if __eq__ or
# __cmp__ is overridden. See https://bugs.python.org/issue2235
return super(Addr, self).__hash__()
@@ -47,8 +47,7 @@ class Addr(common.Addr):
return 0
elif self.get_addr() == "*":
return 1
else:
return 2
return 2
def conflicts(self, addr):
r"""Returns if address could conflict with correct function of self.
@@ -99,7 +98,7 @@ class Addr(common.Addr):
return self.get_addr_obj(port)
class VirtualHost(object): # pylint: disable=too-few-public-methods
class VirtualHost(object):
"""Represents an Apache Virtualhost.
:ivar str filep: file path of VH
@@ -127,7 +126,6 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods
def __init__(self, filep, path, addrs, ssl, enabled, name=None,
aliases=None, modmacro=False, ancestor=None):
# pylint: disable=too-many-arguments
"""Initialize a VH."""
self.filep = filep
self.path = path

View File

@@ -1,11 +1,11 @@
""" Distribution specific override class for Arch Linux """
import pkg_resources
import zope.interface
from certbot import interfaces
from certbot.compat import os
from certbot_apache._internal import configurator
from certbot_apache import configurator
@zope.interface.provider(interfaces.IPluginFactory)
class ArchConfigurator(configurator.ApacheConfigurator):
@@ -27,5 +27,5 @@ class ArchConfigurator(configurator.ApacheConfigurator):
handle_sites=False,
challenge_location="/etc/httpd/conf",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "options-ssl-apache.conf")
"certbot_apache", os.path.join("_internal", "options-ssl-apache.conf"))
)

View File

@@ -0,0 +1,215 @@
""" Distribution specific override class for CentOS family (RHEL, Fedora) """
import logging
import pkg_resources
import zope.interface
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from certbot import errors
from certbot import interfaces
from certbot import util
from certbot.compat import os
from certbot.errors import MisconfigurationError
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal import parser
logger = logging.getLogger(__name__)
@zope.interface.provider(interfaces.IPluginFactory)
class CentOSConfigurator(configurator.ApacheConfigurator):
"""CentOS specific ApacheConfigurator override class"""
OS_DEFAULTS = dict(
server_root="/etc/httpd",
vhost_root="/etc/httpd/conf.d",
vhost_files="*.conf",
logs_root="/var/log/httpd",
ctl="apachectl",
version_cmd=['apachectl', '-v'],
restart_cmd=['apachectl', 'graceful'],
restart_cmd_alt=['apachectl', 'restart'],
conftest_cmd=['apachectl', 'configtest'],
enmod=None,
dismod=None,
le_vhost_ext="-le-ssl.conf",
handle_modules=False,
handle_sites=False,
challenge_location="/etc/httpd/conf.d",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", os.path.join("_internal", "centos-options-ssl-apache.conf"))
)
def config_test(self):
"""
Override config_test to mitigate configtest error in vanilla installation
of mod_ssl in Fedora. The error is caused by non-existent self-signed
certificates referenced by the configuration, that would be autogenerated
during the first (re)start of httpd.
"""
os_info = util.get_os_info()
fedora = os_info[0].lower() == "fedora"
try:
super(CentOSConfigurator, self).config_test()
except errors.MisconfigurationError:
if fedora:
self._try_restart_fedora()
else:
raise
def _try_restart_fedora(self):
"""
Tries to restart httpd using systemctl to generate the self signed keypair.
"""
try:
util.run_script(['systemctl', 'restart', 'httpd'])
except errors.SubprocessError as err:
raise errors.MisconfigurationError(str(err))
# Finish with actual config check to see if systemctl restart helped
super(CentOSConfigurator, self).config_test()
def _prepare_options(self):
"""
Override the options dictionary initialization in order to support
alternative restart cmd used in CentOS.
"""
super(CentOSConfigurator, self)._prepare_options()
self.options["restart_cmd_alt"][0] = self.option("ctl")
def get_parser(self):
"""Initializes the ApacheParser"""
return CentOSParser(
self.option("server_root"), self.option("vhost_root"),
self.version, configurator=self)
def _deploy_cert(self, *args, **kwargs): # pylint: disable=arguments-differ
"""
Override _deploy_cert in order to ensure that the Apache configuration
has "LoadModule ssl_module..." before parsing the VirtualHost configuration
that was created by Certbot
"""
super(CentOSConfigurator, self)._deploy_cert(*args, **kwargs)
if self.version < (2, 4, 0):
self._deploy_loadmodule_ssl_if_needed()
def _deploy_loadmodule_ssl_if_needed(self):
"""
Add "LoadModule ssl_module <pre-existing path>" to main httpd.conf if
it doesn't exist there already.
"""
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]
for m in loadmods:
noarg_path = m.rpartition("/")[0]
path_args = self.parser.get_all_args(noarg_path)
if loadmod_args:
if loadmod_args != path_args:
msg = ("Certbot encountered multiple LoadModule directives "
"for LoadModule ssl_module with differing library paths. "
"Please remove or comment out the one(s) that are not in "
"use, and run Certbot again.")
raise MisconfigurationError(msg)
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:
# LoadModule already in the main configuration file
if ("ifmodule/" in noarg_path.lower() or
"ifmodule[1]" in noarg_path.lower()):
# It's the first or only IfModule in the file
return
# Populate the list of known !mod_ssl.c IfModules
nodir_path = noarg_path.rpartition("/directive")[0]
correct_ifmods.append(nodir_path)
else:
loadmod_paths.append(noarg_path)
if not loadmod_args:
# Do not try to enable mod_ssl
return
# Force creation as the directive wasn't found from the beginning of
# httpd.conf
rootconf_ifmod = self.parser.create_ifmod(
parser.get_aug_path(self.parser.loc["default"]),
"!mod_ssl.c", beginning=True)
# parser.get_ifmod returns a path postfixed with "/", remove that
self.parser.add_dir(rootconf_ifmod[:-1], "LoadModule", loadmod_args)
correct_ifmods.append(rootconf_ifmod[:-1])
self.save_notes += "Added LoadModule ssl_module to main configuration.\n"
# Wrap LoadModule mod_ssl inside of <IfModule !mod_ssl.c> if it's not
# configured like this already.
for loadmod_path in loadmod_paths:
nodir_path = loadmod_path.split("/directive")[0]
# Remove the old LoadModule directive
self.parser.aug.remove(loadmod_path)
# Create a new IfModule !mod_ssl.c if not already found on path
ssl_ifmod = self.parser.get_ifmod(nodir_path, "!mod_ssl.c",
beginning=True)[:-1]
if ssl_ifmod not in correct_ifmods:
self.parser.add_dir(ssl_ifmod, "LoadModule", loadmod_args)
correct_ifmods.append(ssl_ifmod)
self.save_notes += ("Wrapped pre-existing LoadModule ssl_module "
"inside of <IfModule !mod_ssl> block.\n")
class CentOSParser(parser.ApacheParser):
"""CentOS specific ApacheParser override class"""
def __init__(self, *args, **kwargs):
# CentOS specific configuration file for Apache
self.sysconfig_filep = "/etc/sysconfig/httpd"
super(CentOSParser, self).__init__(*args, **kwargs)
def update_runtime_variables(self):
""" Override for update_runtime_variables for custom parsing """
# Opportunistic, works if SELinux not enforced
super(CentOSParser, self).update_runtime_variables()
self.parse_sysconfig_var()
def parse_sysconfig_var(self):
""" Parses Apache CLI options from CentOS configuration file """
defines = apache_util.parse_define_file(self.sysconfig_filep, "OPTIONS")
for k in defines:
self.variables[k] = defines[k]
def not_modssl_ifmodule(self, path):
"""Checks if the provided Augeas path has argument !mod_ssl"""
if "ifmodule" not in path.lower():
return False
# Trim the path to the last ifmodule
workpath = path.lower()
while workpath:
# Get path to the last IfModule (ignore the tail)
parts = workpath.rpartition("ifmodule")
if not parts[0]:
# IfModule not found
break
ifmod_path = parts[0] + parts[1]
# Check if ifmodule had an index
if parts[2].startswith("["):
# Append the index from tail
ifmod_path += parts[2].partition("/")[0]
# Get the original path trimmed to correct length
# This is required to preserve cases
ifmod_real_path = path[0:len(ifmod_path)]
if "!mod_ssl.c" in self.get_all_args(ifmod_real_path):
return True
# Set the workpath to the heading part
workpath = parts[0]
return False

View File

@@ -1,11 +1,11 @@
""" Distribution specific override class for macOS """
import pkg_resources
import zope.interface
from certbot import interfaces
from certbot.compat import os
from certbot_apache._internal import configurator
from certbot_apache import configurator
@zope.interface.provider(interfaces.IPluginFactory)
class DarwinConfigurator(configurator.ApacheConfigurator):
@@ -27,5 +27,5 @@ class DarwinConfigurator(configurator.ApacheConfigurator):
handle_sites=False,
challenge_location="/etc/apache2/other",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "options-ssl-apache.conf")
"certbot_apache", os.path.join("_internal", "options-ssl-apache.conf"))
)

View File

@@ -1,19 +1,20 @@
""" Distribution specific override class for Debian family (Ubuntu/Debian) """
import logging
import os
import pkg_resources
import pkg_resources
import zope.interface
from certbot import errors
from certbot import interfaces
from certbot import util
from certbot_apache import apache_util
from certbot_apache import configurator
from certbot.compat import filesystem
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
logger = logging.getLogger(__name__)
@zope.interface.provider(interfaces.IPluginFactory)
class DebianConfigurator(configurator.ApacheConfigurator):
"""Debian specific ApacheConfigurator override class"""
@@ -34,7 +35,7 @@ class DebianConfigurator(configurator.ApacheConfigurator):
handle_sites=True,
challenge_location="/etc/apache2",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "options-ssl-apache.conf")
"certbot_apache", os.path.join("_internal", "options-ssl-apache.conf"))
)
def enable_site(self, vhost):
@@ -44,14 +45,14 @@ class DebianConfigurator(configurator.ApacheConfigurator):
modules are enabled appropriately.
:param vhost: vhost to enable
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
:type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`
:raises .errors.NotSupportedError: If filesystem layout is not
supported.
"""
if vhost.enabled:
return
return None
enabled_path = ("%s/sites-enabled/%s" %
(self.parser.root,
@@ -64,26 +65,25 @@ class DebianConfigurator(configurator.ApacheConfigurator):
try:
os.symlink(vhost.filep, enabled_path)
except OSError as err:
if os.path.islink(enabled_path) and os.path.realpath(
if os.path.islink(enabled_path) and filesystem.realpath(
enabled_path) == vhost.filep:
# Already in shape
vhost.enabled = True
return
else:
logger.warning(
"Could not symlink %s to %s, got error: %s", enabled_path,
vhost.filep, err.strerror)
errstring = ("Encountered error while trying to enable a " +
"newly created VirtualHost located at {0} by " +
"linking to it from {1}")
raise errors.NotSupportedError(errstring.format(vhost.filep,
enabled_path))
return None
logger.warning(
"Could not symlink %s to %s, got error: %s", enabled_path,
vhost.filep, err.strerror)
errstring = ("Encountered error while trying to enable a " +
"newly created VirtualHost located at {0} by " +
"linking to it from {1}")
raise errors.NotSupportedError(errstring.format(vhost.filep,
enabled_path))
vhost.enabled = True
logger.info("Enabling available site: %s", vhost.filep)
self.save_notes += "Enabled site %s\n" % vhost.filep
return None
def enable_mod(self, mod_name, temp=False):
# pylint: disable=unused-argument
"""Enables module in Apache.
Both enables and reloads Apache so module is active.

View File

@@ -0,0 +1,98 @@
""" Distribution specific override class for Fedora 29+ """
import pkg_resources
import zope.interface
from certbot import errors
from certbot import interfaces
from certbot import util
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal import parser
@zope.interface.provider(interfaces.IPluginFactory)
class FedoraConfigurator(configurator.ApacheConfigurator):
"""Fedora 29+ specific ApacheConfigurator override class"""
OS_DEFAULTS = dict(
server_root="/etc/httpd",
vhost_root="/etc/httpd/conf.d",
vhost_files="*.conf",
logs_root="/var/log/httpd",
ctl="httpd",
version_cmd=['httpd', '-v'],
restart_cmd=['apachectl', 'graceful'],
restart_cmd_alt=['apachectl', 'restart'],
conftest_cmd=['apachectl', 'configtest'],
enmod=None,
dismod=None,
le_vhost_ext="-le-ssl.conf",
handle_modules=False,
handle_sites=False,
challenge_location="/etc/httpd/conf.d",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
# TODO: eventually newest version of Fedora will need their own config
"certbot_apache", os.path.join("_internal", "centos-options-ssl-apache.conf"))
)
def config_test(self):
"""
Override config_test to mitigate configtest error in vanilla installation
of mod_ssl in Fedora. The error is caused by non-existent self-signed
certificates referenced by the configuration, that would be autogenerated
during the first (re)start of httpd.
"""
try:
super(FedoraConfigurator, self).config_test()
except errors.MisconfigurationError:
self._try_restart_fedora()
def get_parser(self):
"""Initializes the ApacheParser"""
return FedoraParser(
self.option("server_root"), self.option("vhost_root"),
self.version, configurator=self)
def _try_restart_fedora(self):
"""
Tries to restart httpd using systemctl to generate the self signed keypair.
"""
try:
util.run_script(['systemctl', 'restart', 'httpd'])
except errors.SubprocessError as err:
raise errors.MisconfigurationError(str(err))
# Finish with actual config check to see if systemctl restart helped
super(FedoraConfigurator, self).config_test()
def _prepare_options(self):
"""
Override the options dictionary initialization to keep using apachectl
instead of httpd and so take advantages of this new bash script in newer versions
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'
class FedoraParser(parser.ApacheParser):
"""Fedora 29+ specific ApacheParser override class"""
def __init__(self, *args, **kwargs):
# Fedora 29+ specific configuration file for Apache
self.sysconfig_filep = "/etc/sysconfig/httpd"
super(FedoraParser, self).__init__(*args, **kwargs)
def update_runtime_variables(self):
""" Override for update_runtime_variables for custom parsing """
# Opportunistic, works if SELinux not enforced
super(FedoraParser, self).update_runtime_variables()
self._parse_sysconfig_var()
def _parse_sysconfig_var(self):
""" Parses Apache CLI options from Fedora configuration file """
defines = apache_util.parse_define_file(self.sysconfig_filep, "OPTIONS")
for k in defines:
self.variables[k] = defines[k]

View File

@@ -1,13 +1,13 @@
""" Distribution specific override class for Gentoo Linux """
import pkg_resources
import zope.interface
from certbot import interfaces
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal import parser
from certbot_apache import apache_util
from certbot_apache import configurator
from certbot_apache import parser
@zope.interface.provider(interfaces.IPluginFactory)
class GentooConfigurator(configurator.ApacheConfigurator):
@@ -30,7 +30,7 @@ class GentooConfigurator(configurator.ApacheConfigurator):
handle_sites=False,
challenge_location="/etc/apache2/vhosts.d",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "options-ssl-apache.conf")
"certbot_apache", os.path.join("_internal", "options-ssl-apache.conf"))
)
def _prepare_options(self):
@@ -44,7 +44,7 @@ class GentooConfigurator(configurator.ApacheConfigurator):
def get_parser(self):
"""Initializes the ApacheParser"""
return GentooParser(
self.aug, self.option("server_root"), self.option("vhost_root"),
self.option("server_root"), self.option("vhost_root"),
self.version, configurator=self)
@@ -64,7 +64,7 @@ class GentooParser(parser.ApacheParser):
""" Parses Apache CLI options from Gentoo configuration file """
defines = apache_util.parse_define_file(self.apacheconfig_filep,
"APACHE2_OPTS")
for k in defines.keys():
for k in defines:
self.variables[k] = defines[k]
def update_modules(self):

View File

@@ -1,11 +1,11 @@
""" Distribution specific override class for OpenSUSE """
import pkg_resources
import zope.interface
from certbot import interfaces
from certbot.compat import os
from certbot_apache._internal import configurator
from certbot_apache import configurator
@zope.interface.provider(interfaces.IPluginFactory)
class OpenSUSEConfigurator(configurator.ApacheConfigurator):
@@ -27,5 +27,5 @@ class OpenSUSEConfigurator(configurator.ApacheConfigurator):
handle_sites=False,
challenge_location="/etc/apache2/vhosts.d",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "options-ssl-apache.conf")
"certbot_apache", os.path.join("_internal", "options-ssl-apache.conf"))
)

View File

@@ -2,21 +2,23 @@
import copy
import fnmatch
import logging
import os
import re
import subprocess
import sys
import six
from acme.magic_typing import Dict, List, Set # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from certbot import errors
from certbot.compat import os
from certbot_apache._internal import constants
logger = logging.getLogger(__name__)
class ApacheParser(object):
# pylint: disable=too-many-public-methods
"""Class handles the fine details of parsing the Apache Configuration.
.. todo:: Make parsing general... remove sites-available etc...
@@ -31,7 +33,7 @@ class ApacheParser(object):
arg_var_interpreter = re.compile(r"\$\{[^ \}]*}")
fnmatch_chars = set(["*", "?", "\\", "[", "]"])
def __init__(self, aug, root, vhostroot=None, version=(2, 4),
def __init__(self, root, vhostroot=None, version=(2, 4),
configurator=None):
# Note: Order is important here.
@@ -40,11 +42,20 @@ class ApacheParser(object):
# issues with aug.load() after adding new files / defines to parse tree
self.configurator = configurator
# Initialize augeas
self.aug = None
self.init_augeas()
if not self.check_aug_version():
raise errors.NotSupportedError(
"Apache plugin support requires libaugeas0 and augeas-lenses "
"version 1.2.0 or higher, please make sure you have you have "
"those installed.")
self.modules = set() # type: Set[str]
self.parser_paths = {} # type: Dict[str, List[str]]
self.variables = {} # type: Dict[str, str]
self.aug = aug
# Find configuration root and make sure augeas can parse it.
self.root = os.path.abspath(root)
self.loc = {"root": self._find_config_root()}
@@ -76,6 +87,146 @@ 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.
:param str lens: lens to check for errors
:raises .errors.PluginError: If there has been an error in parsing with
the specified lens.
"""
error_files = self.aug.match("/augeas//error")
for path in error_files:
# Check to see if it was an error resulting from the use of
# the httpd lens
lens_path = self.aug.get(path + "/lens")
# As aug.get may return null
if lens_path and lens in lens_path:
msg = (
"There has been an error in parsing the file {0} on line {1}: "
"{2}".format(
# Strip off /augeas/files and /error
path[13:len(path) - 6],
self.aug.get(path + "/line"),
self.aug.get(path + "/message")))
raise errors.PluginError(msg)
def check_aug_version(self):
""" Checks that we have recent enough version of libaugeas.
If augeas version is recent enough, it will support case insensitive
regexp matching"""
self.aug.set("/test/path/testing/arg", "aRgUMeNT")
try:
matches = self.aug.match(
"/test//*[self::arg=~regexp('argument', 'i')]")
except RuntimeError:
self.aug.remove("/test/path")
return False
self.aug.remove("/test/path")
return matches
def unsaved_files(self):
"""Lists files that have modified Augeas DOM but the changes have not
been written to the filesystem yet, used by `self.save()` and
ApacheConfigurator to check the file state.
:raises .errors.PluginError: If there was an error in Augeas, in
an attempt to save the configuration, or an error creating a
checkpoint
:returns: `set` of unsaved files
"""
save_state = self.aug.get("/augeas/save")
self.aug.set("/augeas/save", "noop")
# Existing Errors
ex_errs = self.aug.match("/augeas//error")
try:
# This is a noop save
self.aug.save()
except (RuntimeError, IOError):
self._log_save_errors(ex_errs)
# Erase Save Notes
self.configurator.save_notes = ""
raise errors.PluginError(
"Error saving files, check logs for more info.")
# Return the original save method
self.aug.set("/augeas/save", save_state)
# Retrieve list of modified files
# Note: Noop saves can cause the file to be listed twice, I used a
# set to remove this possibility. This is a known augeas 0.10 error.
save_paths = self.aug.match("/augeas/events/saved")
save_files = set()
if save_paths:
for path in save_paths:
save_files.add(self.aug.get(path)[6:])
return save_files
def ensure_augeas_state(self):
"""Makes sure that all Augeas dom changes are written to files to avoid
loss of configuration directives when doing additional augeas parsing,
causing a possible augeas.load() resulting dom reset
"""
if self.unsaved_files():
self.configurator.save_notes += "(autosave)"
self.configurator.save()
def save(self, save_files):
"""Saves all changes to the configuration files.
save() is called from ApacheConfigurator to handle the parser specific
tasks of saving.
:param list save_files: list of strings of file paths that we need to save.
"""
self.configurator.save_notes = ""
self.aug.save()
# Force reload if files were modified
# This is needed to recalculate augeas directive span
if save_files:
for sf in save_files:
self.aug.remove("/files/"+sf)
self.aug.load()
def _log_save_errors(self, ex_errs):
"""Log errors due to bad Augeas save.
:param list ex_errs: Existing errors before save
"""
# Check for the root of save problems
new_errs = self.aug.match("/augeas//error")
# logger.error("During Save - %s", mod_conf)
logger.error("Unable to save files: %s. Attempted Save Notes: %s",
", ".join(err[13:len(err) - 6] for err in new_errs
# Only new errors caused by recent save
if err not in ex_errs), self.configurator.save_notes)
def add_include(self, main_config, inc_path):
"""Add Include for a new configuration file if one does not exist
@@ -83,7 +234,7 @@ class ApacheParser(object):
:param str inc_path: path of file to include
"""
if len(self.find_dir(case_i("Include"), inc_path)) == 0:
if not self.find_dir(case_i("Include"), inc_path):
logger.debug("Adding Include %s to %s",
inc_path, get_aug_path(main_config))
self.add_dir(
@@ -93,12 +244,7 @@ class ApacheParser(object):
# Add new path to parser paths
new_dir = os.path.dirname(inc_path)
new_file = os.path.basename(inc_path)
if new_dir in self.existing_paths.keys():
# Add to existing path
self.existing_paths[new_dir].append(new_file)
else:
# Create a new path
self.existing_paths[new_dir] = [new_file]
self.existing_paths.setdefault(new_dir, []).append(new_file)
def add_mod(self, mod_name):
"""Shortcut for updating parser modules."""
@@ -138,8 +284,8 @@ class ApacheParser(object):
mods.add(mod_name)
mods.add(os.path.basename(mod_filename)[:-2] + "c")
else:
logger.debug("Could not read LoadModule directive from " +
"Augeas path: {0}".format(match_name[6:]))
logger.debug("Could not read LoadModule directive from Augeas path: %s",
match_name[6:])
self.modules.update(mods)
def update_runtime_variables(self):
@@ -229,8 +375,8 @@ class ApacheParser(object):
"Error running command %s for runtime parameters!%s",
command, os.linesep)
raise errors.MisconfigurationError(
"Error accessing loaded Apache parameters: %s",
command)
"Error accessing loaded Apache parameters: {0}".format(
command))
# Small errors that do not impede
if proc.returncode != 0:
logger.warning("Error in checking parameter list: %s", stderr)
@@ -256,12 +402,12 @@ class ApacheParser(object):
"""
filtered = []
if args == 1:
for i in range(len(matches)):
if matches[i].endswith("/arg"):
for i, match in enumerate(matches):
if match.endswith("/arg"):
filtered.append(matches[i][:-4])
else:
for i in range(len(matches)):
if matches[i].endswith("/arg[%d]" % args):
for i, match in enumerate(matches):
if match.endswith("/arg[%d]" % args):
# Make sure we don't cause an IndexError (end of list)
# Check to make sure arg + 1 doesn't exist
if (i == (len(matches) - 1) or
@@ -286,7 +432,7 @@ class ApacheParser(object):
"""
# TODO: Add error checking code... does the path given even exist?
# Does it throw exceptions?
if_mod_path = self._get_ifmod(aug_conf_path, "mod_ssl.c")
if_mod_path = self.get_ifmod(aug_conf_path, "mod_ssl.c")
# IfModule can have only one valid argument, so append after
self.aug.insert(if_mod_path + "arg", "directive", False)
nvh_path = if_mod_path + "directive[1]"
@@ -297,22 +443,54 @@ class ApacheParser(object):
for i, arg in enumerate(args):
self.aug.set("%s/arg[%d]" % (nvh_path, i + 1), arg)
def _get_ifmod(self, aug_conf_path, mod):
def get_ifmod(self, aug_conf_path, mod, beginning=False):
"""Returns the path to <IfMod mod> and creates one if it doesn't exist.
:param str aug_conf_path: Augeas configuration path
:param str mod: module ie. mod_ssl.c
:param bool beginning: If the IfModule should be created to the beginning
of augeas path DOM tree.
:returns: Augeas path of the requested IfModule directive that pre-existed
or was created during the process. The path may be dynamic,
i.e. .../IfModule[last()]
:rtype: str
"""
if_mods = self.aug.match(("%s/IfModule/*[self::arg='%s']" %
(aug_conf_path, mod)))
if len(if_mods) == 0:
self.aug.set("%s/IfModule[last() + 1]" % aug_conf_path, "")
self.aug.set("%s/IfModule[last()]/arg" % aug_conf_path, mod)
if_mods = self.aug.match(("%s/IfModule/*[self::arg='%s']" %
(aug_conf_path, mod)))
if not if_mods:
return self.create_ifmod(aug_conf_path, mod, beginning)
# Strip off "arg" at end of first ifmod path
return if_mods[0][:len(if_mods[0]) - 3]
return if_mods[0].rpartition("arg")[0]
def create_ifmod(self, aug_conf_path, mod, beginning=False):
"""Creates a new <IfMod mod> and returns its path.
:param str aug_conf_path: Augeas configuration path
:param str mod: module ie. mod_ssl.c
:param bool beginning: If the IfModule should be created to the beginning
of augeas path DOM tree.
:returns: Augeas path of the newly created IfModule directive.
The path may be dynamic, i.e. .../IfModule[last()]
:rtype: str
"""
if beginning:
c_path_arg = "{}/IfModule[1]/arg".format(aug_conf_path)
# Insert IfModule before the first directive
self.aug.insert("{}/directive[1]".format(aug_conf_path),
"IfModule", True)
retpath = "{}/IfModule[1]/".format(aug_conf_path)
else:
c_path = "{}/IfModule[last() + 1]".format(aug_conf_path)
c_path_arg = "{}/IfModule[last()]/arg".format(aug_conf_path)
self.aug.set(c_path, "")
retpath = "{}/IfModule[last()]/".format(aug_conf_path)
self.aug.set(c_path_arg, mod)
return retpath
def add_dir(self, aug_conf_path, directive, args):
"""Appends directive to the end fo the file given by aug_conf_path.
@@ -447,7 +625,7 @@ class ApacheParser(object):
# https://httpd.apache.org/docs/2.4/mod/core.html#include
for match in matches:
dir_ = self.aug.get(match).lower()
if dir_ == "include" or dir_ == "includeoptional":
if dir_ in ("include", "includeoptional"):
ordered_matches.extend(self.find_dir(
directive, arg,
self._get_include_path(self.get_arg(match + "/arg")),
@@ -458,6 +636,20 @@ class ApacheParser(object):
return ordered_matches
def get_all_args(self, match):
"""
Tries to fetch all arguments for a directive. See get_arg.
Note that if match is an ancestor node, it returns all names of
child directives as well as the list of arguments.
"""
if match[-1] != "/":
match = match+"/"
allargs = self.aug.match(match + '*')
return [self.get_arg(arg) for arg in allargs]
def get_arg(self, match):
"""Uses augeas.get to get argument value and interprets result.
@@ -473,8 +665,7 @@ class ApacheParser(object):
# e.g. strip now, not later
if not value:
return None
else:
value = value.strip("'\"")
value = value.strip("'\"")
variables = ApacheParser.arg_var_interpreter.findall(value)
@@ -601,9 +792,8 @@ class ApacheParser(object):
if sys.version_info < (3, 6):
# This strips off final /Z(?ms)
return fnmatch.translate(clean_fn_match)[:-7]
else: # pragma: no cover
# Since Python 3.6, it returns a different pattern like (?s:.*\.load)\Z
return fnmatch.translate(clean_fn_match)[4:-3]
# Since Python 3.6, it returns a different pattern like (?s:.*\.load)\Z
return fnmatch.translate(clean_fn_match)[4:-3] # pragma: no cover
def parse_file(self, filepath):
"""Parse file with Augeas
@@ -617,8 +807,7 @@ class ApacheParser(object):
use_new, remove_old = self._check_path_actions(filepath)
# Ensure that we have the latest Augeas DOM state on disk before
# calling aug.load() which reloads the state from disk
if self.configurator:
self.configurator.ensure_augeas_state()
self.ensure_augeas_state()
# Test if augeas included file for Httpd.lens
# Note: This works for augeas globs, ie. *.conf
if use_new:
@@ -685,10 +874,7 @@ class ApacheParser(object):
use_new = False
else:
use_new = True
if new_file_match == "*":
remove_old = True
else:
remove_old = False
remove_old = new_file_match == "*"
except KeyError:
use_new = True
remove_old = False

View File

@@ -1,207 +0,0 @@
"""Class of Augeas Configurators."""
import logging
from certbot import errors
from certbot.plugins import common
from certbot_apache import constants
logger = logging.getLogger(__name__)
class AugeasConfigurator(common.Installer):
"""Base Augeas Configurator class.
:ivar config: Configuration.
:type config: :class:`~certbot.interfaces.IConfig`
:ivar aug: Augeas object
:type aug: :class:`augeas.Augeas`
:ivar str save_notes: Human-readable configuration change notes
:ivar reverter: saves and reverts checkpoints
:type reverter: :class:`certbot.reverter.Reverter`
"""
def __init__(self, *args, **kwargs):
super(AugeasConfigurator, self).__init__(*args, **kwargs)
# Placeholder for augeas
self.aug = None
self.save_notes = ""
def init_augeas(self):
""" Initialize the actual Augeas instance """
import augeas
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))
# See if any temporary changes need to be recovered
# This needs to occur before VirtualHost objects are setup...
# because this will change the underlying configuration and potential
# vhosts
self.recovery_routine()
def check_parsing_errors(self, lens):
"""Verify Augeas can parse all of the lens files.
:param str lens: lens to check for errors
:raises .errors.PluginError: If there has been an error in parsing with
the specified lens.
"""
error_files = self.aug.match("/augeas//error")
for path in error_files:
# Check to see if it was an error resulting from the use of
# the httpd lens
lens_path = self.aug.get(path + "/lens")
# As aug.get may return null
if lens_path and lens in lens_path:
msg = (
"There has been an error in parsing the file {0} on line {1}: "
"{2}".format(
# Strip off /augeas/files and /error
path[13:len(path) - 6],
self.aug.get(path + "/line"),
self.aug.get(path + "/message")))
raise errors.PluginError(msg)
def ensure_augeas_state(self):
"""Makes sure that all Augeas dom changes are written to files to avoid
loss of configuration directives when doing additional augeas parsing,
causing a possible augeas.load() resulting dom reset
"""
if self.unsaved_files():
self.save_notes += "(autosave)"
self.save()
def unsaved_files(self):
"""Lists files that have modified Augeas DOM but the changes have not
been written to the filesystem yet, used by `self.save()` and
ApacheConfigurator to check the file state.
:raises .errors.PluginError: If there was an error in Augeas, in
an attempt to save the configuration, or an error creating a
checkpoint
:returns: `set` of unsaved files
"""
save_state = self.aug.get("/augeas/save")
self.aug.set("/augeas/save", "noop")
# Existing Errors
ex_errs = self.aug.match("/augeas//error")
try:
# This is a noop save
self.aug.save()
except (RuntimeError, IOError):
self._log_save_errors(ex_errs)
# Erase Save Notes
self.save_notes = ""
raise errors.PluginError(
"Error saving files, check logs for more info.")
# Return the original save method
self.aug.set("/augeas/save", save_state)
# Retrieve list of modified files
# Note: Noop saves can cause the file to be listed twice, I used a
# set to remove this possibility. This is a known augeas 0.10 error.
save_paths = self.aug.match("/augeas/events/saved")
save_files = set()
if save_paths:
for path in save_paths:
save_files.add(self.aug.get(path)[6:])
return save_files
def save(self, title=None, temporary=False):
"""Saves all changes to the configuration files.
This function first checks for save errors, if none are found,
all configuration changes made will be saved. According to the
function parameters. If an exception is raised, a new checkpoint
was not created.
:param str title: The title of the save. If a title is given, the
configuration will be saved as a new checkpoint and put in a
timestamped directory.
:param bool temporary: Indicates whether the changes made will
be quickly reversed in the future (ie. challenges)
"""
save_files = self.unsaved_files()
if save_files:
self.add_to_checkpoint(save_files,
self.save_notes, temporary=temporary)
self.save_notes = ""
self.aug.save()
# Force reload if files were modified
# This is needed to recalculate augeas directive span
if save_files:
for sf in save_files:
self.aug.remove("/files/"+sf)
self.aug.load()
if title and not temporary:
self.finalize_checkpoint(title)
def _log_save_errors(self, ex_errs):
"""Log errors due to bad Augeas save.
:param list ex_errs: Existing errors before save
"""
# Check for the root of save problems
new_errs = self.aug.match("/augeas//error")
# logger.error("During Save - %s", mod_conf)
logger.error("Unable to save files: %s. Attempted Save Notes: %s",
", ".join(err[13:len(err) - 6] for err in new_errs
# Only new errors caused by recent save
if err not in ex_errs), self.save_notes)
# Wrapper functions for Reverter class
def recovery_routine(self):
"""Revert all previously modified files.
Reverts all modified files that have not been saved as a checkpoint
:raises .errors.PluginError: If unable to recover the configuration
"""
super(AugeasConfigurator, self).recovery_routine()
# Need to reload configuration after these changes take effect
self.aug.load()
def revert_challenge_config(self):
"""Used to cleanup challenge configurations.
:raises .errors.PluginError: If unable to revert the challenge config.
"""
self.revert_temporary_config()
self.aug.load()
def rollback_checkpoints(self, rollback=1):
"""Rollback saved checkpoints.
:param int rollback: Number of checkpoints to revert
:raises .errors.PluginError: If there is a problem with the input or
the function is unable to correctly revert the configuration
"""
super(AugeasConfigurator, self).rollback_checkpoints(rollback)
self.aug.load()

View File

@@ -1,68 +0,0 @@
""" Distribution specific override class for CentOS family (RHEL, Fedora) """
import pkg_resources
import zope.interface
from certbot import interfaces
from certbot_apache import apache_util
from certbot_apache import configurator
from certbot_apache import parser
@zope.interface.provider(interfaces.IPluginFactory)
class CentOSConfigurator(configurator.ApacheConfigurator):
"""CentOS specific ApacheConfigurator override class"""
OS_DEFAULTS = dict(
server_root="/etc/httpd",
vhost_root="/etc/httpd/conf.d",
vhost_files="*.conf",
logs_root="/var/log/httpd",
ctl="apachectl",
version_cmd=['apachectl', '-v'],
restart_cmd=['apachectl', 'graceful'],
restart_cmd_alt=['apachectl', 'restart'],
conftest_cmd=['apachectl', 'configtest'],
enmod=None,
dismod=None,
le_vhost_ext="-le-ssl.conf",
handle_modules=False,
handle_sites=False,
challenge_location="/etc/httpd/conf.d",
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
"certbot_apache", "centos-options-ssl-apache.conf")
)
def _prepare_options(self):
"""
Override the options dictionary initialization in order to support
alternative restart cmd used in CentOS.
"""
super(CentOSConfigurator, self)._prepare_options()
self.options["restart_cmd_alt"][0] = self.option("ctl")
def get_parser(self):
"""Initializes the ApacheParser"""
return CentOSParser(
self.aug, self.option("server_root"), self.option("vhost_root"),
self.version, configurator=self)
class CentOSParser(parser.ApacheParser):
"""CentOS specific ApacheParser override class"""
def __init__(self, *args, **kwargs):
# CentOS specific configuration file for Apache
self.sysconfig_filep = "/etc/sysconfig/httpd"
super(CentOSParser, self).__init__(*args, **kwargs)
def update_runtime_variables(self):
""" Override for update_runtime_variables for custom parsing """
# Opportunistic, works if SELinux not enforced
super(CentOSParser, self).update_runtime_variables()
self.parse_sysconfig_var()
def parse_sysconfig_var(self):
""" Parses Apache CLI options from CentOS configuration file """
defines = apache_util.parse_define_file(self.sysconfig_filep, "OPTIONS")
for k in defines.keys():
self.variables[k] = defines[k]

View File

@@ -1 +0,0 @@
"""Certbot Apache Tests"""

View File

@@ -1,151 +0,0 @@
"""Test for certbot_apache.tls_sni_01."""
import shutil
import unittest
import mock
from certbot import errors
from certbot.plugins import common_test
from certbot_apache import obj
from certbot_apache.tests import util
from six.moves import xrange # pylint: disable=redefined-builtin, import-error
class TlsSniPerformTest(util.ApacheTest):
"""Test the ApacheTlsSni01 challenge."""
auth_key = common_test.AUTH_KEY
achalls = common_test.ACHALLS
def setUp(self): # pylint: disable=arguments-differ
super(TlsSniPerformTest, self).setUp()
config = util.get_apache_configurator(
self.config_path, self.vhost_path, self.config_dir,
self.work_dir)
config.config.tls_sni_01_port = 443
from certbot_apache import tls_sni_01
self.sni = tls_sni_01.ApacheTlsSni01(config)
def tearDown(self):
shutil.rmtree(self.temp_dir)
shutil.rmtree(self.config_dir)
shutil.rmtree(self.work_dir)
def test_perform0(self):
resp = self.sni.perform()
self.assertEqual(len(resp), 0)
@mock.patch("certbot.util.exe_exists")
@mock.patch("certbot.util.run_script")
def test_perform1(self, _, mock_exists):
self.sni.configurator.parser.modules.add("socache_shmcb_module")
self.sni.configurator.parser.modules.add("ssl_module")
mock_exists.return_value = True
self.sni.configurator.parser.update_runtime_variables = mock.Mock()
achall = self.achalls[0]
self.sni.add_chall(achall)
response = self.achalls[0].response(self.auth_key)
mock_setup_cert = mock.MagicMock(return_value=response)
# pylint: disable=protected-access
self.sni._setup_challenge_cert = mock_setup_cert
responses = self.sni.perform()
mock_setup_cert.assert_called_once_with(achall)
# Check to make sure challenge config path is included in apache config
self.assertEqual(
len(self.sni.configurator.parser.find_dir(
"Include", self.sni.challenge_conf)), 1)
self.assertEqual(len(responses), 1)
self.assertEqual(responses[0], response)
def test_perform2(self):
# Avoid load module
self.sni.configurator.parser.modules.add("ssl_module")
self.sni.configurator.parser.modules.add("socache_shmcb_module")
acme_responses = []
for achall in self.achalls:
self.sni.add_chall(achall)
acme_responses.append(achall.response(self.auth_key))
mock_setup_cert = mock.MagicMock(side_effect=acme_responses)
# pylint: disable=protected-access
self.sni._setup_challenge_cert = mock_setup_cert
with mock.patch(
"certbot_apache.override_debian.DebianConfigurator.enable_mod"):
sni_responses = self.sni.perform()
self.assertEqual(mock_setup_cert.call_count, 2)
# Make sure calls made to mocked function were correct
self.assertEqual(
mock_setup_cert.call_args_list[0], mock.call(self.achalls[0]))
self.assertEqual(
mock_setup_cert.call_args_list[1], mock.call(self.achalls[1]))
self.assertEqual(
len(self.sni.configurator.parser.find_dir(
"Include", self.sni.challenge_conf)),
1)
self.assertEqual(len(sni_responses), 2)
for i in xrange(2):
self.assertEqual(sni_responses[i], acme_responses[i])
def test_mod_config(self):
z_domains = []
for achall in self.achalls:
self.sni.add_chall(achall)
z_domain = achall.response(self.auth_key).z_domain
z_domains.append(set([z_domain.decode('ascii')]))
self.sni._mod_config() # pylint: disable=protected-access
self.sni.configurator.save()
self.sni.configurator.parser.find_dir(
"Include", self.sni.challenge_conf)
vh_match = self.sni.configurator.aug.match(
"/files" + self.sni.challenge_conf + "//VirtualHost")
vhs = []
for match in vh_match:
# pylint: disable=protected-access
vhs.append(self.sni.configurator._create_vhost(match))
self.assertEqual(len(vhs), 2)
for vhost in vhs:
self.assertEqual(vhost.addrs, set([obj.Addr.fromstring("*:443")]))
names = vhost.get_names()
self.assertTrue(names in z_domains)
def test_get_addrs_default(self):
self.sni.configurator.choose_vhost = mock.Mock(
return_value=obj.VirtualHost(
"path", "aug_path",
set([obj.Addr.fromstring("_default_:443")]),
False, False)
)
# pylint: disable=protected-access
self.assertEqual(
set([obj.Addr.fromstring("*:443")]),
self.sni._get_addrs(self.achalls[0]))
def test_get_addrs_no_vhost_found(self):
self.sni.configurator.choose_vhost = mock.Mock(
side_effect=errors.MissingCommandlineFlag(
"Failed to run Apache plugin non-interactively"))
# pylint: disable=protected-access
self.assertEqual(
set([obj.Addr.fromstring("*:443")]),
self.sni._get_addrs(self.achalls[0]))
if __name__ == "__main__":
unittest.main() # pragma: no cover

View File

@@ -1,174 +0,0 @@
"""A class that performs TLS-SNI-01 challenges for Apache"""
import os
import logging
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module
from certbot.plugins import common
from certbot.errors import PluginError, MissingCommandlineFlag
from certbot_apache import obj
logger = logging.getLogger(__name__)
class ApacheTlsSni01(common.TLSSNI01):
"""Class that performs TLS-SNI-01 challenges within the Apache configurator
:ivar configurator: ApacheConfigurator object
:type configurator: :class:`~apache.configurator.ApacheConfigurator`
:ivar list achalls: Annotated TLS-SNI-01
(`.KeyAuthorizationAnnotatedChallenge`) challenges.
:param list indices: Meant to hold indices of challenges in a
larger array. ApacheTlsSni01 is capable of solving many challenges
at once which causes an indexing issue within ApacheConfigurator
who must return all responses in order. Imagine ApacheConfigurator
maintaining state about where all of the http-01 Challenges,
TLS-SNI-01 Challenges belong in the response array. This is an
optional utility.
:param str challenge_conf: location of the challenge config file
"""
VHOST_TEMPLATE = """\
<VirtualHost {vhost}>
ServerName {server_name}
UseCanonicalName on
SSLStrictSNIVHostCheck on
LimitRequestBody 1048576
Include {ssl_options_conf_path}
SSLCertificateFile {cert_path}
SSLCertificateKeyFile {key_path}
DocumentRoot {document_root}
</VirtualHost>
"""
def __init__(self, *args, **kwargs):
super(ApacheTlsSni01, self).__init__(*args, **kwargs)
self.challenge_conf = os.path.join(
self.configurator.conf("challenge-location"),
"le_tls_sni_01_cert_challenge.conf")
def perform(self):
"""Perform a TLS-SNI-01 challenge."""
if not self.achalls:
return []
# Save any changes to the configuration as a precaution
# About to make temporary changes to the config
self.configurator.save("Changes before challenge setup", True)
# Prepare the server for HTTPS
self.configurator.prepare_server_https(
str(self.configurator.config.tls_sni_01_port), True)
responses = []
# Create all of the challenge certs
for achall in self.achalls:
responses.append(self._setup_challenge_cert(achall))
# Setup the configuration
addrs = self._mod_config()
self.configurator.save("Don't lose mod_config changes", True)
self.configurator.make_addrs_sni_ready(addrs)
# Save reversible changes
self.configurator.save("SNI Challenge", True)
return responses
def _mod_config(self):
"""Modifies Apache config files to include challenge vhosts.
Result: Apache config includes virtual servers for issued challs
:returns: All TLS-SNI-01 addresses used
:rtype: set
"""
addrs = set() # type: Set[obj.Addr]
config_text = "<IfModule mod_ssl.c>\n"
for achall in self.achalls:
achall_addrs = self._get_addrs(achall)
addrs.update(achall_addrs)
config_text += self._get_config_text(achall, achall_addrs)
config_text += "</IfModule>\n"
self.configurator.parser.add_include(
self.configurator.parser.loc["default"], self.challenge_conf)
self.configurator.reverter.register_file_creation(
True, self.challenge_conf)
logger.debug("writing a config file with text:\n %s", config_text)
with open(self.challenge_conf, "w") as new_conf:
new_conf.write(config_text)
return addrs
def _get_addrs(self, achall):
"""Return the Apache addresses needed for TLS-SNI-01."""
# TODO: Checkout _default_ rules.
addrs = set()
default_addr = obj.Addr(("*", str(
self.configurator.config.tls_sni_01_port)))
try:
vhost = self.configurator.choose_vhost(achall.domain,
create_if_no_ssl=False)
except (PluginError, MissingCommandlineFlag):
# We couldn't find the virtualhost for this domain, possibly
# because it's a new vhost that's not configured yet
# (GH #677). See also GH #2600.
logger.warning("Falling back to default vhost %s...", default_addr)
addrs.add(default_addr)
return addrs
for addr in vhost.addrs:
if "_default_" == addr.get_addr():
addrs.add(default_addr)
else:
addrs.add(
addr.get_sni_addr(
self.configurator.config.tls_sni_01_port))
return addrs
def _get_config_text(self, achall, ip_addrs):
"""Chocolate virtual server configuration text
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
TLS-SNI-01 challenge.
:param list ip_addrs: addresses of challenged domain
:class:`list` of type `~.obj.Addr`
:returns: virtual host configuration text
:rtype: str
"""
ips = " ".join(str(i) for i in ip_addrs)
document_root = os.path.join(
self.configurator.config.work_dir, "tls_sni_01_page/")
# TODO: Python docs is not clear how multiline string literal
# newlines are parsed on different platforms. At least on
# Linux (Debian sid), when source file uses CRLF, Python still
# parses it as "\n"... c.f.:
# https://docs.python.org/2.7/reference/lexical_analysis.html
return self.VHOST_TEMPLATE.format(
vhost=ips,
server_name=achall.response(achall.account_key).z_domain.decode('ascii'),
ssl_options_conf_path=self.configurator.mod_ssl_conf,
cert_path=self.get_cert_path(achall),
key_path=self.get_key_path(achall),
document_root=document_root).replace("\n", os.linesep)

View File

@@ -1,192 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-apache.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-apache.qhc"
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/certbot-apache"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-apache"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

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