mirror of
https://github.com/certbot/certbot.git
synced 2026-07-28 16:54:56 +02:00
Generate a web.config file for IIS to serve properly the challenge files in webroot plugin (#9054)
* Generate a web.config file to serve properly challenge files with IIS * Fix cleanup, add test * FIx lint * Do not overwrite existing web.config. Delete only web.config when it has been created by Certbot and is unmodified. * Fix lint * Update certbot/certbot/_internal/plugins/webroot.py Co-authored-by: alexzorin <alex@zor.io> * Add log * Check for POSIX_MODE before web.config deletion attempt. * Add documentation * Update certbot/CHANGELOG.md Co-authored-by: alexzorin <alex@zor.io> * Update certbot/docs/using.rst Co-authored-by: alexzorin <alex@zor.io>
This commit is contained in:
co-authored by
alexzorin
parent
2375d87831
commit
94af235713
@@ -6,7 +6,9 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
### Added
|
||||
|
||||
*
|
||||
* Certbot will generate a `web.config` file on Windows in the challenge path
|
||||
when the `webroot` plugin is used, if one does not exist. This `web.config` file
|
||||
lets IIS serve challenge files while they do not have an extension.
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import List
|
||||
from typing import Set
|
||||
|
||||
from acme import challenges
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot._internal import cli
|
||||
@@ -24,6 +25,23 @@ from certbot.util import safe_open
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_WEB_CONFIG_CONTENT = """\
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--Generated by Certbot-->
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<mimeMap fileExtension="." mimeType="text/plain" />
|
||||
</staticContent>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
"""
|
||||
# This list references the hashes of all versions of the web.config files that Certbot could
|
||||
# have generated during an HTTP-01 challenge. If you modify _WEB_CONFIG_CONTENT, you MUST add
|
||||
# the new hash in this list.
|
||||
_WEB_CONFIG_SHA256SUMS = ["20c5ca1bd58fa8ad5f07a2f1be8b7cbb707c20fcb607a8fc8db9393952846a97"]
|
||||
|
||||
|
||||
class Authenticator(common.Plugin, interfaces.Authenticator):
|
||||
"""Webroot Authenticator."""
|
||||
|
||||
@@ -192,6 +210,19 @@ to serve all files under specified web root ({0})."""
|
||||
finally:
|
||||
filesystem.umask(old_umask)
|
||||
|
||||
# On Windows, generate a local web.config file that allows IIS to serve expose
|
||||
# challenge files despite the fact they do not have a file extension.
|
||||
if not filesystem.POSIX_MODE:
|
||||
web_config_path = os.path.join(self.full_roots[name], "web.config")
|
||||
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
|
||||
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:
|
||||
web_config.write(_WEB_CONFIG_CONTENT)
|
||||
|
||||
def _get_validation_path(self, root_path, achall):
|
||||
return os.path.join(root_path, achall.chall.encode("token"))
|
||||
|
||||
@@ -223,6 +254,19 @@ to serve all files under specified web root ({0})."""
|
||||
os.remove(validation_path)
|
||||
self.performed[root_path].remove(achall)
|
||||
|
||||
if not filesystem.POSIX_MODE:
|
||||
web_config_path = os.path.join(root_path, "web.config")
|
||||
if os.path.exists(web_config_path):
|
||||
sha256sum = crypto_util.sha256sum(web_config_path)
|
||||
if sha256sum in _WEB_CONFIG_SHA256SUMS:
|
||||
logger.info("Cleaning web.config file generated by Certbot in %s.",
|
||||
root_path)
|
||||
os.remove(web_config_path)
|
||||
else:
|
||||
logger.info("Not cleaning up the web.config file in %s "
|
||||
"because it is not generated by Certbot.", root_path)
|
||||
|
||||
|
||||
not_removed: List[str] = []
|
||||
while self._created_dirs:
|
||||
path = self._created_dirs.pop()
|
||||
|
||||
@@ -147,6 +147,10 @@ your webserver configuration, you might need to modify the configuration
|
||||
to ensure that files inside ``/.well-known/acme-challenge`` are served by
|
||||
the webserver.
|
||||
|
||||
Under Windows, Certbot will generate a ``web.config`` file, if one does not already exist,
|
||||
in ``/.well-known/acme-challenge`` in order to let IIS serve the challenge files even if they
|
||||
do not have an extension.
|
||||
|
||||
Nginx
|
||||
-----
|
||||
|
||||
|
||||
@@ -86,6 +86,35 @@ class AuthenticatorTest(unittest.TestCase):
|
||||
self.assertEqual(self.config.webroot_map[self.achall.domain],
|
||||
self.path)
|
||||
|
||||
@unittest.skipIf(filesystem.POSIX_MODE, reason='Test specific to Windows')
|
||||
@test_util.patch_display_util()
|
||||
def test_webconfig_file_generate_and_cleanup(self, mock_get_utility):
|
||||
mock_display = mock_get_utility()
|
||||
mock_display.menu.return_value = (display_util.OK, 1,)
|
||||
|
||||
self.auth.perform([self.achall])
|
||||
self.assertTrue(os.path.exists(os.path.join(self.root_challenge_path, "web.config")))
|
||||
self.auth.cleanup([self.achall])
|
||||
self.assertFalse(os.path.exists(os.path.join(self.root_challenge_path, "web.config")))
|
||||
|
||||
@unittest.skipIf(filesystem.POSIX_MODE, reason='Test specific to Windows')
|
||||
@test_util.patch_display_util()
|
||||
def test_foreign_webconfig_file_handling(self, mock_get_utility):
|
||||
mock_display = mock_get_utility()
|
||||
mock_display.menu.return_value = (display_util.OK, 1,)
|
||||
|
||||
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])
|
||||
from certbot import crypto_util
|
||||
webconfig_hash = crypto_util.sha256sum(webconfig_path)
|
||||
from certbot._internal.plugins.webroot import _WEB_CONFIG_SHA256SUMS
|
||||
self.assertTrue(webconfig_hash not in _WEB_CONFIG_SHA256SUMS)
|
||||
|
||||
@test_util.patch_display_util()
|
||||
def test_webroot_from_list_help_and_cancel(self, mock_get_utility):
|
||||
self.config.webroot_path = []
|
||||
|
||||
Reference in New Issue
Block a user