diff --git a/certbot-apache/certbot_apache/_internal/entrypoint.py b/certbot-apache/certbot_apache/_internal/entrypoint.py index 6452643cf..5430e9d79 100644 --- a/certbot-apache/certbot_apache/_internal/entrypoint.py +++ b/certbot-apache/certbot_apache/_internal/entrypoint.py @@ -56,7 +56,7 @@ def get_configurator(): os_like = util.get_systemd_os_like() if os_like: for os_name in os_like: - if os_name in OVERRIDE_CLASSES: # pylint: disable=consider-using-get + if os_name in OVERRIDE_CLASSES: override_class = OVERRIDE_CLASSES[os_name] if not override_class: # No override class found, return the generic configurator diff --git a/certbot/CHANGELOG.md b/certbot/CHANGELOG.md index 8df29801d..0a5849d82 100644 --- a/certbot/CHANGELOG.md +++ b/certbot/CHANGELOG.md @@ -20,6 +20,8 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). * Fixed an issue on Windows where the `web.config` created by Certbot would sometimes conflict with preexisting configurations (#9088). +* Fixed an issue on Windows where the `webroot` plugin would crash when multiple domains + had the same webroot. This affected Certbot 1.21.0. More details about these changes can be found on our GitHub repo. diff --git a/certbot/certbot/_internal/plugins/webroot.py b/certbot/certbot/_internal/plugins/webroot.py index 81f46b204..1a4892ac9 100644 --- a/certbot/certbot/_internal/plugins/webroot.py +++ b/certbot/certbot/_internal/plugins/webroot.py @@ -221,7 +221,7 @@ to serve all files under specified web root ({0}).""" if os.path.exists(web_config_path): logger.info("A web.config file has not been created in " "%s because another one already exists.", self.full_roots[name]) - return + continue logger.info("Creating a web.config file in %s to allow IIS " "to serve challenge files.", self.full_roots[name]) with safe_open(web_config_path, mode="w", chmod=0o644) as web_config: diff --git a/certbot/certbot/crypto_util.py b/certbot/certbot/crypto_util.py index f7dca341b..e3a62cc9e 100644 --- a/certbot/certbot/crypto_util.py +++ b/certbot/certbot/crypto_util.py @@ -363,9 +363,8 @@ def verify_renewable_cert_sig(renewable_cert: interfaces.RenewableCert) -> None: with open(renewable_cert.cert_path, 'rb') as cert_file: cert = x509.load_pem_x509_certificate(cert_file.read(), default_backend()) pk = chain.public_key() - with warnings.catch_warnings(): - verify_signed_payload(pk, cert.signature, cert.tbs_certificate_bytes, - cert.signature_hash_algorithm) + verify_signed_payload(pk, cert.signature, cert.tbs_certificate_bytes, + cert.signature_hash_algorithm) except (IOError, ValueError, InvalidSignature) as e: error_str = "verifying the signature of the certificate located at {0} has failed. \ Details: {1}".format(renewable_cert.cert_path, e) @@ -387,22 +386,16 @@ def verify_signed_payload(public_key: Union[DSAPublicKey, 'Ed25519PublicKey', 'E :raises InvalidSignature: If signature verification fails. :raises errors.Error: If public key type is not supported """ - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - if isinstance(public_key, RSAPublicKey): - verifier = public_key.verifier( - signature, PKCS1v15(), signature_hash_algorithm - ) - verifier.update(payload) - verifier.verify() - elif isinstance(public_key, EllipticCurvePublicKey): - verifier = public_key.verifier( - signature, ECDSA(signature_hash_algorithm) - ) - verifier.update(payload) - verifier.verify() - else: - raise errors.Error("Unsupported public key type.") + if isinstance(public_key, RSAPublicKey): + public_key.verify( + signature, payload, PKCS1v15(), signature_hash_algorithm + ) + elif isinstance(public_key, EllipticCurvePublicKey): + public_key.verify( + signature, payload, ECDSA(signature_hash_algorithm) + ) + else: + raise errors.Error("Unsupported public key type.") def verify_cert_matches_priv_key(cert_path: str, key_path: str) -> None: diff --git a/certbot/docs/using.rst b/certbot/docs/using.rst index 1f441cc55..c9bbaf324 100644 --- a/certbot/docs/using.rst +++ b/certbot/docs/using.rst @@ -747,53 +747,50 @@ commands into your individual environment. and when renewal is not necessary. .. _renewal-config-file: +.. _Modifying the Renewal Configuration File: +Modifying the Renewal Configuration of Existing Certificates +------------------------------------------------------------ -Modifying the Renewal Configuration File ----------------------------------------- +When creating a certificate, Certbot will keep track of all of the relevant options chosen by the user. At renewal +time, Certbot will remember these options and apply them once again. -When a certificate is issued, by default Certbot creates a renewal configuration file that -tracks the options that were selected when Certbot was run. This allows Certbot -to use those same options again when it comes time for renewal. These renewal -configuration files are located at ``/etc/letsencrypt/renewal/CERTNAME``. +Sometimes, you may encounter the need to change some of these options for future certificate renewals. To achieve this, +you will need to perform the following steps: -For advanced certificate management tasks, it is possible to manually modify the certificate's -renewal configuration file, but this is discouraged since it can easily break Certbot's -ability to renew your certificates. If you choose to modify the renewal configuration file -we advise you to test its validity with the ``certbot renew --dry-run`` command. +1. Perform a *dry run renewal* with the amended options on the command line. This allows you to confirm that the change + is valid and will result in successful future renewals. +2. If the dry run is successful, perform a *live renewal* of the certificate. This will persist the change for future + renewals. If the certificate is not yet due to expire, you will need to force a renewal using ``--force-renewal``. -.. warning:: Modifying any files in ``/etc/letsencrypt`` can damage them so Certbot can no longer properly manage its certificates, and we do not recommend doing so. +.. note:: Rate limits from the certificate authority may prevent you from performing multiple renewals in a short + period of time. It is strongly recommended to perform the second step only once, when you have decided on what + options should change. -For most tasks, it is safest to limit yourself to pointing symlinks at the files there, or using -``--deploy-hook`` to copy / make new files based upon those files, if your operational situation requires it -(for instance, combining certificates and keys in different way, or having copies of things with different -specific permissions that are demanded by other programs). +As a practical example, if you were using the ``webroot`` authenticator and had relocated your website to another directory, +you would need to change the ``--webroot-path`` to the new directory. Following the above advice: -If the contents of ``/etc/letsencrypt/archive/CERTNAME`` are moved to a new folder, first specify -the new folder's name in the renewal configuration file, then run ``certbot update_symlinks`` to -point the symlinks in ``/etc/letsencrypt/live/CERTNAME`` to the new folder. +1. Perform a *dry-run renewal* of the individual certificate with the amended options:: -If you would like the live certificate files whose symlink location Certbot updates on each run to -reside in a different location, first move them to that location, then specify the full path of -each of the four files in the renewal configuration file. Since the symlinks are relative links, -you must follow this with an invocation of ``certbot update_symlinks``. + certbot renew --cert-name example.com --webroot-path /path/to/new/location --dry-run -For example, say that a certificate's renewal configuration file previously contained the following -directives:: +2. If the dry-run was successful, make the change permanent by performing a *live renewal* of the certificate with the + amended options, including ``--force-renewal``:: - archive_dir = /etc/letsencrypt/archive/example.com - cert = /etc/letsencrypt/live/example.com/cert.pem - privkey = /etc/letsencrypt/live/example.com/privkey.pem - chain = /etc/letsencrypt/live/example.com/chain.pem - fullchain = /etc/letsencrypt/live/example.com/fullchain.pem + certbot renew --cert-name example.com --webroot-path /path/to/new/location --force-renewal -The following commands could be used to specify where these files are located:: + ``--cert-name`` selects the particular certificate to be modified. Without this option, all certificates will be selected. + + ``--webroot-path`` is the option intended to be changed. All other previously selected options will be kept the same + and do not need to be included in the command. + +For advanced certificate management tasks, it is also possible to manually modify the certificate's renewal configuration +file, but this is discouraged since it can easily break Certbot's ability to renew your certificates. These renewal +configuration files are located at ``/etc/letsencrypt/renewal/CERTNAME.conf``. If you choose to modify the renewal +configuration file we advise you to make a backup of the file beforehand and test its validity with the ``certbot renew --dry-run`` command. + +.. warning:: Manually modifying files under ``/etc/letsencrypt/renewal/`` can damage them if done improperly and we do not recommend doing so. - mv /etc/letsencrypt/archive/example.com /home/user/me/certbot/example_archive - sed -i 's,/etc/letsencrypt/archive/example.com,/home/user/me/certbot/example_archive,' /etc/letsencrypt/renewal/example.com.conf - mv /etc/letsencrypt/live/example.com/*.pem /home/user/me/certbot/ - sed -i 's,/etc/letsencrypt/live/example.com,/home/user/me/certbot,g' /etc/letsencrypt/renewal/example.com.conf - certbot update_symlinks Automated Renewals ------------------ diff --git a/certbot/tests/plugins/webroot_test.py b/certbot/tests/plugins/webroot_test.py index 54c51ae1d..d7e961596 100644 --- a/certbot/tests/plugins/webroot_test.py +++ b/certbot/tests/plugins/webroot_test.py @@ -115,6 +115,22 @@ class AuthenticatorTest(unittest.TestCase): from certbot._internal.plugins.webroot import _WEB_CONFIG_SHA256SUMS self.assertTrue(webconfig_hash not in _WEB_CONFIG_SHA256SUMS) + @unittest.skipIf(filesystem.POSIX_MODE, reason='Test specific to Windows') + def test_foreign_webconfig_multiple_domains(self): + # Covers bug https://github.com/certbot/certbot/issues/9091 + achall_2 = achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.chall_to_challb(challenges.HTTP01(token=b"bingo"), "pending"), + domain="second-thing.com", account_key=KEY) + self.config.webroot_map["second-thing.com"] = self.path + + challenge_path = os.path.join(self.path, ".well-known", "acme-challenge") + filesystem.makedirs(challenge_path) + + webconfig_path = os.path.join(challenge_path, "web.config") + with open(webconfig_path, "w") as file: + file.write("something") + self.auth.perform([self.achall, achall_2]) + @test_util.patch_display_util() def test_webroot_from_list_help_and_cancel(self, mock_get_utility): self.config.webroot_path = [] diff --git a/tools/pinning/current/pyproject.toml b/tools/pinning/current/pyproject.toml index 63cc8a9b2..ca04a0ada 100644 --- a/tools/pinning/current/pyproject.toml +++ b/tools/pinning/current/pyproject.toml @@ -63,6 +63,10 @@ mock = "*" # here in addition to certbot/setup.py because otherwise the pre-release # version of poetry will not be installed. poetry = ">=1.2.0a1" +# setuptools-rust 1.0+ causes some of our tests to fail. In the long term, this +# should be fixed, but this is a quick fix to get tests passing again. See +# https://github.com/certbot/certbot/issues/9111 for more info. +setuptools-rust = "<1" # Library traitlets is a transitive dependency of ipdb (traitlets -> ipython -> ipdb). # Version 5.x is incompatible with Python 3.6 but for some reasons, poetry fails to # add the appropriate marker and allows this version to be installed under Python 3.6. diff --git a/tools/requirements.txt b/tools/requirements.txt index b7b31df5a..66591240d 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -9,18 +9,18 @@ alabaster==0.7.12; python_version >= "3.6" apacheconfig==0.3.2; python_version >= "3.6" appdirs==1.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" appnope==0.1.2; python_version == "3.6" and sys_platform == "darwin" or python_version >= "3.7" and sys_platform == "darwin" -astroid==2.8.5; python_version >= "3.6" and python_version < "4.0" +astroid==2.8.6; python_version >= "3.6" and python_version < "4.0" atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0" attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -awscli==1.22.8; python_version >= "3.6" +awscli==1.22.12; python_version >= "3.6" azure-devops==6.0.0b4; python_version >= "3.6" babel==2.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" backcall==0.2.0; python_version == "3.6" or python_version >= "3.7" bcrypt==3.2.0; python_version >= "3.6" beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" and python_full_version > "3.0.0" bleach==4.1.0; python_version >= "3.6" -boto3==1.20.8; python_version >= "3.6" -botocore==1.23.8; python_version >= "3.6" +boto3==1.20.12; python_version >= "3.6" +botocore==1.23.12; python_version >= "3.6" cachecontrol==0.12.10; python_version >= "3.6" and python_version < "4.0" cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" cachetools==4.2.4; python_version >= "3.5" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") @@ -35,7 +35,7 @@ configargparse==1.5.3; python_version >= "3.6" and python_full_version < "3.0.0" configobj==5.0.6; python_version >= "3.6" coverage==6.1.2; python_version >= "3.6" or python_version >= "3.6" crashtest==0.3.1; python_version >= "3.6" and python_version < "4.0" -cryptography==35.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux" +cryptography==36.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux" cython==0.29.24; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") dataclasses==0.8; python_version >= "3.6" and python_version < "3.7" decorator==5.1.0; python_version == "3.6" or python_version > "3.6" or python_version >= "3.5" or python_version >= "3.7" @@ -101,13 +101,13 @@ pathlib2==2.3.6; python_version >= "3.6" pexpect==4.8.0; python_version >= "3.6" and python_version < "4.0" or python_version == "3.6" and sys_platform != "win32" or python_version >= "3.7" and sys_platform != "win32" pickleshare==0.7.5; python_version == "3.6" or python_version >= "3.7" pip==21.3.1; python_version >= "3.6" -pkginfo==1.7.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" +pkginfo==1.8.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" platformdirs==2.4.0; python_version >= "3.6" and python_version < "4.0" pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" ply==3.11; python_version >= "3.6" poetry-core==1.1.0a6; python_version >= "3.6" and python_version < "4.0" poetry==1.2.0a2; python_version >= "3.6" and python_version < "4.0" -prompt-toolkit==3.0.3; python_version == "3.6" or python_version >= "3.7" +prompt-toolkit==3.0.22; python_version == "3.6" and python_full_version >= "3.6.2" or python_version >= "3.7" and python_full_version >= "3.6.2" protobuf==3.19.1; python_version >= "3.6" ptyprocess==0.7.0; python_version >= "3.6" and python_version < "4.0" py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" @@ -148,7 +148,9 @@ rfc3986==1.5.0; python_version >= "3.6" rsa==4.7.2; python_version >= "3.6" and python_version < "4" or python_version >= "3.5" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") s3transfer==0.5.0; python_version >= "3.6" secretstorage==3.3.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux" -setuptools==59.1.1; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7" or python_version >= "3.6" and python_version < "4.0" +semantic-version==2.8.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" +setuptools-rust==0.12.1; python_version >= "3.6" +setuptools==59.2.0; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7" or python_version >= "3.6" and python_version < "4.0" shellingham==1.4.0; python_version >= "3.6" and python_version < "4.0" six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0" snowballstemmer==2.2.0; python_version >= "3.6" @@ -171,22 +173,22 @@ tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or pytho traitlets==4.3.3 twine==3.3.0; python_version >= "3.6" typed-ast==1.4.3; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6" -types-cryptography==3.3.8; python_version >= "3.6" +types-cryptography==3.3.9; python_version >= "3.6" types-enum34==1.1.1; python_version >= "3.6" types-ipaddress==1.0.1; python_version >= "3.6" types-mock==4.0.3; python_version >= "3.6" -types-pyopenssl==21.0.0; python_version >= "3.6" +types-pyopenssl==21.0.1; python_version >= "3.6" types-pyrfc3339==1.1.0; python_version >= "3.6" -types-python-dateutil==2.8.2; python_version >= "3.6" -types-pytz==2021.3.0; python_version >= "3.6" +types-python-dateutil==2.8.3; python_version >= "3.6" +types-pytz==2021.3.1; python_version >= "3.6" types-requests==2.26.0; python_version >= "3.6" -types-setuptools==57.4.2; python_version >= "3.6" +types-setuptools==57.4.3; python_version >= "3.6" types-six==1.16.2; python_version >= "3.6" typing-extensions==4.0.0; python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10" uritemplate==4.1.1; python_version >= "3.6" urllib3==1.26.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.6" virtualenv==20.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" -wcwidth==0.2.5; python_version == "3.6" +wcwidth==0.2.5; python_version == "3.6" and python_full_version >= "3.6.2" webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6" websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version >= "3.6" wheel==0.37.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"