From c2f2aa5ee076594ad8abc4570df4cbacf7f948b6 Mon Sep 17 00:00:00 2001 From: Adrien Ferrand Date: Fri, 15 Mar 2019 01:07:49 +0100 Subject: [PATCH] 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 --- .../configurators/common.py | 3 ++ .../certbot_compatibility_test/test_driver.py | 41 ++++++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py index 2a800c1c2..58ac58a15 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py @@ -23,6 +23,9 @@ class Proxy(object): def __init__(self, args): """Initializes the plugin with the given command line args""" self._temp_dir = tempfile.mkdtemp() + # tempfile.mkdtemp() creates folders with too restrictive permissions to be accessible + # to an Apache worker, leading to HTTP challenge failures. Let's fix that. + os.chmod(self._temp_dir, 0o755) self.le_config = util.create_le_config(self._temp_dir) config_dir = util.extract_configs(args.configs, self._temp_dir) self._configs = [ diff --git a/certbot-compatibility-test/certbot_compatibility_test/test_driver.py b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py index 9eea95e67..f94aee3c1 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/test_driver.py +++ b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py @@ -1,5 +1,6 @@ """Tests Certbot plugins against different server configurations.""" import argparse +import contextlib import filecmp import logging import os @@ -7,6 +8,7 @@ import shutil import tempfile import time import sys +from urllib3.util import connection import OpenSSL @@ -64,18 +66,19 @@ def test_authenticator(plugin, config, temp_dir): "Plugin failed to complete %s for %s in %s", type(achalls[i]), achalls[i].domain, config) success = False - elif isinstance(responses[i], challenges.TLSSNI01Response): - verified = responses[i].simple_verify(achalls[i].chall, - achalls[i].domain, - util.JWK.public_key(), - host="127.0.0.1", - port=plugin.https_port) + elif isinstance(responses[i], challenges.HTTP01Response): + # We fake the DNS resolution to ensure that any domain is resolved + # to the local HTTP server setup for the compatibility tests + with _fake_dns_resolution("127.0.0.1"): + verified = responses[i].simple_verify( + achalls[i].chall, achalls[i].domain, + util.JWK.public_key(), port=plugin.http_port) if verified: logger.info( - "tls-sni-01 verification for %s succeeded", achalls[i].domain) + "http-01 verification for %s succeeded", achalls[i].domain) else: logger.error( - "**** tls-sni-01 verification for %s in %s failed", + "**** http-01 verification for %s in %s failed", achalls[i].domain, config) success = False @@ -102,9 +105,9 @@ def _create_achalls(plugin): for domain in names: prefs = plugin.get_chall_pref(domain) for chall_type in prefs: - if chall_type == challenges.TLSSNI01: - chall = challenges.TLSSNI01( - token=os.urandom(challenges.TLSSNI01.TOKEN_SIZE)) + if chall_type == challenges.HTTP01: + chall = challenges.HTTP01( + token=os.urandom(challenges.HTTP01.TOKEN_SIZE)) challb = acme_util.chall_to_challb( chall, messages.STATUS_PENDING) achall = achallenges.KeyAuthorizationAnnotatedChallenge( @@ -369,5 +372,21 @@ def main(): sys.exit(1) +@contextlib.contextmanager +def _fake_dns_resolution(resolved_ip): + """Monkey patch urllib3 to make any hostname be resolved to the provided IP""" + _original_create_connection = connection.create_connection + + def _patched_create_connection(address, *args, **kwargs): + _, port = address + return _original_create_connection((resolved_ip, port), *args, **kwargs) + + try: + connection.create_connection = _patched_create_connection + yield + finally: + connection.create_connection = _original_create_connection + + if __name__ == "__main__": main()