diff --git a/certbot-ci/src/certbot_integration_tests/certbot_tests/context.py b/certbot-ci/src/certbot_integration_tests/certbot_tests/context.py index 4547ac713..1e323d4da 100644 --- a/certbot-ci/src/certbot_integration_tests/certbot_tests/context.py +++ b/certbot-ci/src/certbot_integration_tests/certbot_tests/context.py @@ -38,30 +38,34 @@ class IntegrationTestsContext: os.close(probe[0]) self.hook_probe = probe[1] - self.manual_dns_auth_hook = ( - '{0} -c "import os; import requests; import json; ' - "assert not os.environ.get('CERTBOT_DOMAIN').startswith('fail'); " - "data = {{'host':'_acme-challenge.{{0}}.'.format(os.environ.get('CERTBOT_DOMAIN'))," - "'value':os.environ.get('CERTBOT_VALIDATION')}}; " - "request = requests.post('{1}/set-txt', data=json.dumps(data)); " - "request.raise_for_status(); " - '"' - ).format(sys.executable, self.challtestsrv_url) - self.manual_dns_auth_hook_allow_fail = ( - '{0} -c "import os; import requests; import json; ' - "data = {{'host':'_acme-challenge.{{0}}.'.format(os.environ.get('CERTBOT_DOMAIN'))," - "'value':os.environ.get('CERTBOT_VALIDATION')}}; " - "request = requests.post('{1}/set-txt', data=json.dumps(data)); " - "request.raise_for_status(); " - '"' - ).format(sys.executable, self.challtestsrv_url) - self.manual_dns_cleanup_hook = ( - '{0} -c "import os; import requests; import json; ' - "data = {{'host':'_acme-challenge.{{0}}.'.format(os.environ.get('CERTBOT_DOMAIN'))}}; " - "request = requests.post('{1}/clear-txt', data=json.dumps(data)); " - "request.raise_for_status(); " - '"' - ).format(sys.executable, self.challtestsrv_url) + self.manual_dns_auth_hook = self.generate_dns_auth_hook('_acme-challenge', True) + self.manual_dns_auth_hook_allow_fail = self.generate_dns_auth_hook('_acme-challenge', False) + self.manual_dns_persist_auth_hook = self.generate_dns_auth_hook('_validation-persist', True) + self.manual_dns_cleanup_hook = self.generate_dns_cleanup_hook('_acme-challenge') + self.manual_dns_persist_cleanup_hook = self.generate_dns_cleanup_hook('_validation-persist') + + def generate_dns_auth_hook(self, challenge_subdomain: str, fail_on_subdomain: bool) -> str: + """Generates a python one-liner script which sets a DNS challenge TXT record challtestsrv URL, + and optionally fails if the subdomain starts with the word "fail" to simulate a faulty script""" + script_lines = ['import os', 'import requests', 'import json'] + if fail_on_subdomain: + script_lines.append("assert not os.environ.get('CERTBOT_DOMAIN').startswith('fail')") + script_lines.append(f"data = {{'host':'{challenge_subdomain}.{{0}}.'.format(" + "os.environ.get('CERTBOT_DOMAIN')), 'value':" + "os.environ.get('CERTBOT_VALIDATION')}") + script_lines.append(f"request = requests.post('{self.challtestsrv_url}/set-txt', data=json.dumps(data))") + script_lines.append("request.raise_for_status()") + script = '; '.join(script_lines) + return f'{sys.executable} -c "{script}"' + + def generate_dns_cleanup_hook(self, challenge_subdomain: str) -> str: + """Generates a python one-liner script which cleans up the TXT record made by `generate_dns_auth_hook`""" + script_lines = ['import os', 'import requests', 'import json'] + script_lines.append(f"data = {{'host':'{challenge_subdomain}.{{0}}.'.format(os.environ.get('CERTBOT_DOMAIN'))}}") + script_lines.append(f"request = requests.post('{self.challtestsrv_url}/clear-txt', data=json.dumps(data))") + script_lines.append("request.raise_for_status()") + script = '; '.join(script_lines) + return f'{sys.executable} -c "{script}"' def cleanup(self) -> None: """Cleanup the integration test context.""" diff --git a/certbot-ci/src/certbot_integration_tests/certbot_tests/test_main.py b/certbot-ci/src/certbot_integration_tests/certbot_tests/test_main.py index afd902f5c..916d575cd 100644 --- a/certbot-ci/src/certbot_integration_tests/certbot_tests/test_main.py +++ b/certbot-ci/src/certbot_integration_tests/certbot_tests/test_main.py @@ -206,6 +206,22 @@ def test_manual_dns_auth(context: IntegrationTestsContext) -> None: assert_cert_count_for_lineage(context.config_dir, certname, 2) +def test_manual_dns_persist_auth(context: IntegrationTestsContext) -> None: + """Test the DNS-PERSIST-01 challenge using manual plugin.""" + certname = context.get_domain('dns-persist') + context.certbot([ + '-a', 'manual', '-d', certname, '--preferred-challenges', 'dns-persist', + 'run', '--cert-name', certname, + '--manual-auth-hook', context.manual_dns_persist_auth_hook, + '--manual-cleanup-hook', context.manual_dns_persist_cleanup_hook, + '--deploy-hook', misc.echo('deploy', context.hook_probe), + ]) + + assert_hook_execution(context.hook_probe, 'deploy') + assert_saved_deploy_hook(context.config_dir, certname) + assert_cert_count_for_lineage(context.config_dir, certname, 1) + + def test_certonly(context: IntegrationTestsContext) -> None: """Test the certonly verb on certbot.""" context.certbot(['certonly', '--cert-name', 'newname', '-d', context.get_domain('newname')]) diff --git a/certbot-ci/src/certbot_integration_tests/utils/pebble_artifacts.py b/certbot-ci/src/certbot_integration_tests/utils/pebble_artifacts.py index de9956847..7d7562b37 100644 --- a/certbot-ci/src/certbot_integration_tests/utils/pebble_artifacts.py +++ b/certbot-ci/src/certbot_integration_tests/utils/pebble_artifacts.py @@ -15,7 +15,7 @@ import requests from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT -PEBBLE_VERSION = 'v2.8.0' +PEBBLE_VERSION = 'v2.10.1' def fetch(workspace: str, http_01_port: int = DEFAULT_HTTP_01_PORT) -> tuple[str, str, str]: