From d05ee61dd00db07cfedaeeef7d4cee883f5b62da Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Fri, 26 Sep 2025 22:24:31 -0400 Subject: [PATCH 1/8] Refactoring how env variables are threaded through configuration. Added timeout params to dcv checker (configuration not yet updated to set them). Removed vestigial reuse_http_client parameter in DCV checker. Added new integration test but it is not yet passing. --- README.md | 2 +- config.example.yaml | 6 +- configure.py | 21 +++--- pyproject.toml | 5 +- src/aws_lambda_mpic/__about__.py | 2 +- .../mpic_coordinator_lambda_function.py | 2 - .../mpic_dcv_checker_lambda_function.py | 20 +++++- tests/integration/test_deployed_mpic_api.py | 72 +++++++++++++++---- 8 files changed, 100 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 072f37f..183ba26 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ All requirements for running the API are packaged and uploaded to AWS as a lambd For convenience: * `hatch run lambda:prepare` will run steps 2-5 in a single command. -* `hatch run lambda:deploy-no-dnssec` or `hatch run lambda:deploy-dnssec` will clean the environment and then run steps 2-6 with DNSSEC validation enabled or disabled respectively. +* `hatch run lambda:deploy-dnssec` or `hatch run lambda:deploy-no-dnssec` will clean the environment and then run steps 2-6 with DNSSEC validation enabled or disabled respectively. Note: the above commands do not run `tofu init`. During first time environment setup this will need to be run in the `open-tofu` dir for these commands to work. diff --git a/config.example.yaml b/config.example.yaml index 9012e72..4233b81 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,6 +1,6 @@ # A list of perspectives with the format . TODO link to pages. perspectives: - - us-west-1 + - us-east-2 - us-west-2 - ca-west-1 - eu-central-2 @@ -24,3 +24,7 @@ caa-domains: absolute-max-attempts: 3 log-level: INFO +coordinator-http-client-timeout-seconds: 25 +dcv-http-client-timeout-seconds: 20 +dns-timeout-seconds: 5 +dns-resolution-lifetime-seconds: 11 \ No newline at end of file diff --git a/configure.py b/configure.py index 1d74cc5..60aad1f 100755 --- a/configure.py +++ b/configure.py @@ -109,11 +109,7 @@ def main(raw_args=None): # Set the source path for the lambda functions. main_tf_string = main_tf_string.replace("{{source-path}}", f"{config['source-path']}") - # Set log level if present. - if "log-level" in config: - main_tf_string = main_tf_string.replace("{{log-level-with-key}}", f"log_level = \"{config['log-level']}\"") - else: - main_tf_string = main_tf_string.replace("{{log-level-with-key}}", "") + main_tf_string = set_common_env_configuration(main_tf_string, config) # Derive the out file from the input file name. if not args.main_tf_template.endswith(".tf.template"): @@ -152,11 +148,7 @@ def main(raw_args=None): # Set the source path for the lambda functions. aws_perspective_tf_region = aws_perspective_tf_region.replace("{{source-path}}", f"{config['source-path']}") - # Set log level if present. - if "log-level" in config: - aws_perspective_tf_region = aws_perspective_tf_region.replace("{{log-level-with-key}}", f"log_level = \"{config['log-level']}\"") - else: - aws_perspective_tf_region = aws_perspective_tf_region.replace("{{log-level-with-key}}", "") + aws_perspective_tf_region = set_common_env_configuration(aws_perspective_tf_region, config) if not args.aws_perspective_tf_template.endswith(".tf.template"): print(f"Error: invalid tf template name: {args.aws_perspective_tf_template}. Make sure all tf template files end in '.tf.template'.") @@ -167,6 +159,15 @@ def main(raw_args=None): out_stream.write(aws_perspective_tf_region) +def set_common_env_configuration(tf_string:str, config:dict) -> str: + # set log level if present + if "log-level" in config: + tf_string = tf_string.replace("{{log-level-with-key}}", f"log_level = \"{config['log-level']}\"") + else: + tf_string = tf_string.replace("{{log-level-with-key}}", "") + return tf_string + + # Main module init for direct invocation. if __name__ == '__main__': main() diff --git a/pyproject.toml b/pyproject.toml index 8108d02..dfb21c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,9 +31,10 @@ dependencies = [ "requests==2.32.4", "dnspython==2.7.0", "pydantic==2.11.7", - "aiohttp==3.12.13", + "aiohttp==3.12.14", "aws-lambda-powertools[parser]==3.15.1", - "open-mpic-core==5.10.0", + #"open-mpic-core==6.1.0", + "open-mpic-core @ git+https://github.com/open-mpic/open-mpic-core-python.git@ds-allow-caa-lookup-failure-flag", "aioboto3~=14.3.0", "black==25.1.0", ] diff --git a/src/aws_lambda_mpic/__about__.py b/src/aws_lambda_mpic/__about__.py index 67bc602..5b60188 100644 --- a/src/aws_lambda_mpic/__about__.py +++ b/src/aws_lambda_mpic/__about__.py @@ -1 +1 @@ -__version__ = "1.3.0" +__version__ = "1.5.0" diff --git a/src/aws_lambda_mpic/mpic_coordinator_lambda/mpic_coordinator_lambda_function.py b/src/aws_lambda_mpic/mpic_coordinator_lambda/mpic_coordinator_lambda_function.py index b80c7c9..f05cd93 100644 --- a/src/aws_lambda_mpic/mpic_coordinator_lambda/mpic_coordinator_lambda_function.py +++ b/src/aws_lambda_mpic/mpic_coordinator_lambda/mpic_coordinator_lambda_function.py @@ -6,8 +6,6 @@ import asyncio import aioboto3 -from asyncio import Queue -from collections import defaultdict from importlib import resources from pydantic import TypeAdapter, ValidationError, BaseModel from aws_lambda_powertools.utilities.parser import event_parser, envelopes diff --git a/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py b/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py index ed923e5..84835a7 100644 --- a/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py +++ b/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py @@ -12,12 +12,30 @@ class MpicDcvCheckerLambdaHandler: def __init__(self): self.log_level = os.environ["log_level"] if "log_level" in os.environ else None + self.http_client_timeout_seconds = ( + float(os.environ["dcv_http_client_timeout_seconds"]) + if "dcv_http_client_timeout_seconds" in os.environ and float(os.environ["dcv_http_client_timeout_seconds"]) + else 30 + ) + self.dns_timeout_seconds = ( + float(os.environ["dns_timeout_seconds"]) if "dns_timeout_seconds" in os.environ else None + ) + self.dns_resolution_lifetime_seconds = ( + float(os.environ["dns_resolution_lifetime_seconds"]) + if "dns_resolution_lifetime_seconds" in os.environ + else None + ) self.logger = logger.getChild(self.__class__.__name__) if self.log_level: self.logger.setLevel(self.log_level) - self.dcv_checker = MpicDcvChecker(reuse_http_client=False, log_level=self.logger.level) + self.dcv_checker = MpicDcvChecker( + log_level=self.logger.level, + http_client_timeout=self.http_client_timeout_seconds, + dns_timeout=self.dns_timeout_seconds, + dns_resolution_lifetime=self.dns_resolution_lifetime_seconds, + ) def process_invocation(self, dcv_request: DcvCheckRequest): self.logger.debug("(debug log) Processing DCV check request: %s", dcv_request) diff --git a/tests/integration/test_deployed_mpic_api.py b/tests/integration/test_deployed_mpic_api.py index 575e85a..bc79a5b 100644 --- a/tests/integration/test_deployed_mpic_api.py +++ b/tests/integration/test_deployed_mpic_api.py @@ -10,7 +10,6 @@ DcvAcmeDns01ValidationParameters, DcvDnsChangeValidationParameters, ) -from open_mpic_core import DcvCheckParameters from open_mpic_core import CertificateType, CheckType, DnsRecordType from open_mpic_core import MpicCaaRequest, MpicDcvRequest, MpicResponse, PerspectiveResponse from open_mpic_core import MpicRequestOrchestrationParameters @@ -50,12 +49,16 @@ def api_should_return_200_and_passed_corroboration_given_successful_caa_check(se print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) - # response_body_as_json = response.json() - assert response.status_code == 200 - # assert response body has a list of perspectives with length 2, and each element has response code 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) - print("\nResponse:\n", json.dumps(mpic_response.model_dump(), indent=4)) # pretty print response body + + if response.status_code != 200: + print("\nResponse:\n", response.text) + + assert response.status_code == 200 assert mpic_response.is_valid is True + print("\nResponse:\n", json.dumps(mpic_response.model_dump(), indent=4)) # pretty print response body + + # assert response body has a list of perspectives with length 2, and each element has response code 200 perspectives_list: list[PerspectiveResponse] = mpic_response.perspectives assert len(perspectives_list) == request.orchestration_parameters.perspective_count assert ( @@ -106,6 +109,24 @@ def api_should_return_is_valid_false_for_all_tests_in_do_not_issue_caa_test_suit mpic_response = self.mpic_response_adapter.validate_json(response.text) assert mpic_response.is_valid is False + def api_should_return_is_valid_true_for_caa_lookup_failure_if_allow_lookup_failure_flag_is_true( + self, api_client + ): + request = MpicCaaRequest( + domain_or_ip_target="nonexistentdomainforsure.open-mpic.org", + orchestration_parameters=MpicRequestOrchestrationParameters(perspective_count=3, quorum_count=2), + caa_check_parameters=CaaCheckParameters( + certificate_type=CertificateType.TLS_SERVER, + caa_domains=["example.com"], + allow_caa_lookup_failure=True, + ), + ) + response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + mpic_response = self.mpic_response_adapter.validate_json(response.text) + if mpic_response.is_valid is False: + print("\nResponse:\n", response.text) + assert mpic_response.is_valid is True + # NOTE: Open MPIC AWS-Lambda-Python currently is not able to communicate with an IPv6 only nameserver. # This case is handled in a compliant manner as it is treated as a lookup failure. # The test for proper communication with an IPv6 nameserver can be enabled with the following additional parameter to the list below. @@ -142,6 +163,8 @@ def api_should_return_is_valid_true_for_valid_tests_in_caa_test_suite_when_caa_d ) response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) mpic_response = self.mpic_response_adapter.validate_json(response.text) + if mpic_response.is_valid is False: + print("\nResponse:\n", response.text) assert mpic_response.is_valid is True @pytest.mark.skip(reason="Behavior not required in RFC 8659") @@ -185,9 +208,12 @@ def api_should_return_200_given_valid_dns_01_validation(self, api_client, domain print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) - assert mpic_response.is_valid is True # fmt: off @@ -211,9 +237,12 @@ def api_should_return_200_is_valid_false_given_invalid_dns_01_validation( print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) - assert mpic_response.is_valid is False # fmt: off @@ -233,9 +262,12 @@ def api_should_return_200_given_valid_http_01_validation( ) print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) - assert mpic_response.is_valid is True # fmt: off @@ -258,9 +290,12 @@ def api_should_return_200_given_invalid_dns_01_validation( print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) - assert mpic_response.is_valid is False # fmt: off @@ -282,9 +317,12 @@ def api_should_return_200_given_valid_website_change_validation( ) print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) - assert mpic_response.is_valid is True # fmt: off @@ -309,6 +347,10 @@ def api_should_return_200_is_valid_true_given_valid_dns_change_validation( print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) print("\nResponse:\n", json.dumps(json.loads(response.text), indent=4)) # pretty print request body + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 mpic_response = self.mpic_response_adapter.validate_json(response.text) assert mpic_response.is_valid is True @@ -321,6 +363,10 @@ def api_should_return_200_and_failed_corroboration_given_failed_dcv_check(self, print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) + + if response.status_code != 200: + print("\nResponse:\n", response.text) + assert response.status_code == 200 response_body = json.loads(response.text) print("\nResponse:\n", json.dumps(response_body, indent=4)) # pretty print response body @@ -338,9 +384,10 @@ def api_should_return_400_given_invalid_orchestration_parameters_in_request(self print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) - assert response.status_code == 400 response_body = json.loads(response.text) print("\nResponse:\n", json.dumps(response_body, indent=4)) # pretty print response body + + assert response.status_code == 400 assert response_body["error"] == MpicRequestValidationMessages.REQUEST_VALIDATION_FAILED.key assert any( issue["issue_type"] == MpicRequestValidationMessages.INVALID_QUORUM_COUNT.key @@ -359,7 +406,8 @@ def api_should_return_400_given_invalid_check_type_in_request(self, api_client): print("\nRequest:\n", json.dumps(request.model_dump(), indent=4)) # pretty print request body response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) - assert response.status_code == 400 response_body = json.loads(response.text) print("\nResponse:\n", json.dumps(response_body, indent=4)) + + assert response.status_code == 400 assert response_body["error"] == MpicRequestValidationMessages.REQUEST_VALIDATION_FAILED.key From e45a938ca9174b2ce61d07df44521b02782889aa Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sat, 27 Sep 2025 02:25:48 -0400 Subject: [PATCH 2/8] added tests for setting timeout configs for DCV from environment --- .../mpic_dcv_checker_lambda_function.py | 4 ++-- tests/integration/test_deployed_mpic_api.py | 2 +- .../test_dcv_checker_lambda.py | 19 ++++++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py b/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py index 84835a7..06ae11a 100644 --- a/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py +++ b/src/aws_lambda_mpic/mpic_dcv_checker_lambda/mpic_dcv_checker_lambda_function.py @@ -13,8 +13,8 @@ class MpicDcvCheckerLambdaHandler: def __init__(self): self.log_level = os.environ["log_level"] if "log_level" in os.environ else None self.http_client_timeout_seconds = ( - float(os.environ["dcv_http_client_timeout_seconds"]) - if "dcv_http_client_timeout_seconds" in os.environ and float(os.environ["dcv_http_client_timeout_seconds"]) + float(os.environ["http_client_timeout_seconds"]) + if "http_client_timeout_seconds" in os.environ and float(os.environ["http_client_timeout_seconds"]) else 30 ) self.dns_timeout_seconds = ( diff --git a/tests/integration/test_deployed_mpic_api.py b/tests/integration/test_deployed_mpic_api.py index bc79a5b..2fcaf11 100644 --- a/tests/integration/test_deployed_mpic_api.py +++ b/tests/integration/test_deployed_mpic_api.py @@ -118,7 +118,7 @@ def api_should_return_is_valid_true_for_caa_lookup_failure_if_allow_lookup_failu caa_check_parameters=CaaCheckParameters( certificate_type=CertificateType.TLS_SERVER, caa_domains=["example.com"], - allow_caa_lookup_failure=True, + allow_lookup_failure=True, ), ) response = api_client.post(MPIC_REQUEST_PATH, json.dumps(request.model_dump())) diff --git a/tests/unit/aws_lambda_mpic/test_dcv_checker_lambda.py b/tests/unit/aws_lambda_mpic/test_dcv_checker_lambda.py index 682aae9..c423ce2 100644 --- a/tests/unit/aws_lambda_mpic/test_dcv_checker_lambda.py +++ b/tests/unit/aws_lambda_mpic/test_dcv_checker_lambda.py @@ -5,9 +5,12 @@ from open_mpic_core import DcvHttpCheckResponseDetails from open_mpic_core import DcvValidationMethod from open_mpic_core import DcvCheckResponse + import aws_lambda_mpic.mpic_dcv_checker_lambda.mpic_dcv_checker_lambda_function as mpic_dcv_checker_lambda_function from open_mpic_core_test.test_util.valid_check_creator import ValidCheckCreator + +from aws_lambda_mpic.mpic_dcv_checker_lambda.mpic_dcv_checker_lambda_function import MpicDcvCheckerLambdaHandler from unit.aws_lambda_mpic.conftest import setup_logging @@ -16,7 +19,13 @@ class TestDcvCheckerLambda: @staticmethod @pytest.fixture(scope="class") def set_env_variables(): - envvars = {"AWS_REGION": "us-east-1", "log_level": "TRACE"} + envvars = { + "AWS_REGION": "us-east-1", + "log_level": "TRACE", + "http_client_timeout_seconds": "11", + "dns_timeout_seconds": "12", + "dns_resolution_lifetime_seconds": "13", + } with pytest.MonkeyPatch.context() as class_scoped_monkeypatch: for k, v in envvars.items(): class_scoped_monkeypatch.setenv(k, v) @@ -68,6 +77,14 @@ def lambda_handler__should_set_log_level_of_dcv_checker(self, set_env_variables, log_contents = setup_logging.getvalue() assert all(text in log_contents for text in ["MpicDcvChecker", "TRACE"]) # Verify the log level was set + async def lambda_handler__should_set_dns_timeout_configuration_of_dcv_checker(self, set_env_variables): + # a bit leaky in terms of implementation but there's not an easy way to enforce this otherwise + configured_dcv_checker = MpicDcvCheckerLambdaHandler().dcv_checker + assert configured_dcv_checker.resolver.timeout == 12.0 + assert configured_dcv_checker.resolver.lifetime == 13.0 + async with configured_dcv_checker.get_async_http_client() as http_client: + assert http_client.timeout.total == 11.0 + @staticmethod def create_dcv_check_response(): return DcvCheckResponse( From e5ea121ead6486b6a25038d0326baa9bb9dea66c Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sat, 27 Sep 2025 02:29:32 -0400 Subject: [PATCH 3/8] added tests for setting timeout config for CAA from enviroment --- .../mpic_caa_checker_lambda_function.py | 13 ++++++++++++- .../aws_lambda_mpic/test_caa_checker_lambda.py | 16 ++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/aws_lambda_mpic/mpic_caa_checker_lambda/mpic_caa_checker_lambda_function.py b/src/aws_lambda_mpic/mpic_caa_checker_lambda/mpic_caa_checker_lambda_function.py index e1188ed..6c3923a 100644 --- a/src/aws_lambda_mpic/mpic_caa_checker_lambda/mpic_caa_checker_lambda_function.py +++ b/src/aws_lambda_mpic/mpic_caa_checker_lambda/mpic_caa_checker_lambda_function.py @@ -14,13 +14,24 @@ class MpicCaaCheckerLambdaHandler: def __init__(self): self.default_caa_domain_list = os.environ["default_caa_domains"].split("|") self.log_level = os.environ["log_level"] if "log_level" in os.environ else None + self.dns_timeout_seconds = ( + float(os.environ["dns_timeout_seconds"]) if "dns_timeout_seconds" in os.environ else None + ) + self.dns_resolution_lifetime_seconds = ( + float(os.environ["dns_resolution_lifetime_seconds"]) + if "dns_resolution_lifetime_seconds" in os.environ + else None + ) self.logger = logger.getChild(self.__class__.__name__) if self.log_level: self.logger.setLevel(self.log_level) self.caa_checker = MpicCaaChecker( - default_caa_domain_list=self.default_caa_domain_list, log_level=self.logger.level + default_caa_domain_list=self.default_caa_domain_list, + log_level=self.logger.level, + dns_timeout=self.dns_timeout_seconds, + dns_resolution_lifetime=self.dns_resolution_lifetime_seconds, ) def process_invocation(self, caa_request: CaaCheckRequest): diff --git a/tests/unit/aws_lambda_mpic/test_caa_checker_lambda.py b/tests/unit/aws_lambda_mpic/test_caa_checker_lambda.py index dfe7379..7ff9c55 100644 --- a/tests/unit/aws_lambda_mpic/test_caa_checker_lambda.py +++ b/tests/unit/aws_lambda_mpic/test_caa_checker_lambda.py @@ -7,6 +7,7 @@ from open_mpic_core_test.test_util.mock_dns_object_creator import MockDnsObjectCreator from open_mpic_core_test.test_util.valid_check_creator import ValidCheckCreator import aws_lambda_mpic.mpic_caa_checker_lambda.mpic_caa_checker_lambda_function as mpic_caa_checker_lambda_function +from aws_lambda_mpic.mpic_caa_checker_lambda.mpic_caa_checker_lambda_function import MpicCaaCheckerLambdaHandler # noinspection PyMethodMayBeStatic @@ -14,7 +15,13 @@ class TestCaaCheckerLambda: @staticmethod @pytest.fixture(scope="class") def set_env_variables(): - envvars = {"AWS_REGION": "us-east-1", "default_caa_domains": "ca1.com|ca2.org|ca3.net", "log_level": "TRACE"} + envvars = { + "AWS_REGION": "us-east-1", + "default_caa_domains": "ca1.com|ca2.org|ca3.net", + "log_level": "TRACE", + "dns_timeout_seconds": "12", + "dns_resolution_lifetime_seconds": "13", + } with pytest.MonkeyPatch.context() as class_scoped_monkeypatch: for k, v in envvars.items(): class_scoped_monkeypatch.setenv(k, v) @@ -47,10 +54,15 @@ def lambda_handler__should_set_log_level_of_caa_checker(self, set_env_variables, log_contents = setup_logging.getvalue() assert all(text in log_contents for text in ["MpicCaaChecker", "TRACE"]) # Verify the log level was set + async def lambda_handler__should_set_dns_timeout_configuration_of_dcv_checker(self, set_env_variables): + # a bit leaky in terms of implementation but there's not an easy way to enforce this otherwise + configured_caa_checker = MpicCaaCheckerLambdaHandler().caa_checker + assert configured_caa_checker.resolver.timeout == 12.0 + assert configured_caa_checker.resolver.lifetime == 13.0 + @staticmethod def create_caa_check_response(): return CaaCheckResponse( - perspective_code="us-east-1", check_passed=True, details=CaaCheckResponseDetails(caa_record_present=True, found_at="example.com"), timestamp_ns=time.time_ns(), From 6fe6310ce754361664027560ba3d25844af546d9 Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sat, 27 Sep 2025 02:35:52 -0400 Subject: [PATCH 4/8] updating configure and TF scripts to thread through timeout env config for checkers --- config.example.yaml | 3 +-- configure.py | 17 ++++++++++++++++- open-tofu/aws-perspective.tf.template | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/config.example.yaml b/config.example.yaml index 4233b81..8a4f0e9 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -24,7 +24,6 @@ caa-domains: absolute-max-attempts: 3 log-level: INFO -coordinator-http-client-timeout-seconds: 25 -dcv-http-client-timeout-seconds: 20 +http-client-timeout-seconds: 20 dns-timeout-seconds: 5 dns-resolution-lifetime-seconds: 11 \ No newline at end of file diff --git a/configure.py b/configure.py index 60aad1f..dc8b68d 100755 --- a/configure.py +++ b/configure.py @@ -159,12 +159,27 @@ def main(raw_args=None): out_stream.write(aws_perspective_tf_region) -def set_common_env_configuration(tf_string:str, config:dict) -> str: +def set_common_env_configuration(tf_string: str, config: dict) -> str: # set log level if present if "log-level" in config: tf_string = tf_string.replace("{{log-level-with-key}}", f"log_level = \"{config['log-level']}\"") else: tf_string = tf_string.replace("{{log-level-with-key}}", "") + + # set timeouts if present + if "http-client-timeout-seconds" in config: + tf_string = tf_string.replace("{{http-client-timeout-with-key}}", f"http_client_timeout_seconds = {config['http-client-timeout-seconds']}") + else: + tf_string = tf_string.replace("{{http-client-timeout-with-key}}", "") + if "dns-timeout-seconds" in config: + tf_string = tf_string.replace("{{dns-timeout-with-key}}", f"dns_timeout_seconds = {config['dns-timeout-seconds']}") + else: + tf_string = tf_string.replace("{{dns-timeout-with-key}}", "") + if "dns-resolution-lifetime-seconds" in config: + tf_string = tf_string.replace("{{dns-resolution-lifetime-with-key}}", f"dns_resolution_lifetime_seconds = {config['dns-resolution-lifetime-seconds']}") + else: + tf_string = tf_string.replace("{{dns-resolution-lifetime-with-key}}", "") + return tf_string diff --git a/open-tofu/aws-perspective.tf.template b/open-tofu/aws-perspective.tf.template index cc5ef47..e922ef6 100644 --- a/open-tofu/aws-perspective.tf.template +++ b/open-tofu/aws-perspective.tf.template @@ -222,6 +222,9 @@ resource "aws_lambda_function" "mpic_dcv_checker_lambda_{{region}}" { environment { variables = { {{log-level-with-key}} + {{http-client-timeout-with-key}} + {{dns-timeout-with-key}} + {{dns-resolution-lifetime-with-key}} } } } @@ -254,6 +257,8 @@ resource "aws_lambda_function" "mpic_caa_checker_lambda_{{region}}" { variables = { default_caa_domains = {{default-caa-domains}} {{log-level-with-key}} + {{dns-timeout-with-key}} + {{dns-resolution-lifetime-with-key}} } } } \ No newline at end of file From 610e6c2a323d11bc7ddeb4ddc2026e7ff8eb7b05 Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sat, 27 Sep 2025 03:08:23 -0400 Subject: [PATCH 5/8] added integration test to test allow_lookup_failure flag --- tests/integration/test_deployed_mpic_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_deployed_mpic_api.py b/tests/integration/test_deployed_mpic_api.py index 2fcaf11..b2f1eb8 100644 --- a/tests/integration/test_deployed_mpic_api.py +++ b/tests/integration/test_deployed_mpic_api.py @@ -113,7 +113,7 @@ def api_should_return_is_valid_true_for_caa_lookup_failure_if_allow_lookup_failu self, api_client ): request = MpicCaaRequest( - domain_or_ip_target="nonexistentdomainforsure.open-mpic.org", + domain_or_ip_target="servfail.caatestsuite-dnssec.com", orchestration_parameters=MpicRequestOrchestrationParameters(perspective_count=3, quorum_count=2), caa_check_parameters=CaaCheckParameters( certificate_type=CertificateType.TLS_SERVER, From d156b4edb26a84288ff26ea4b8980750185a52b5 Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sat, 27 Sep 2025 03:14:17 -0400 Subject: [PATCH 6/8] bumped api spec version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dfb21c0..fcb595b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ Source = "https://github.com/open-mpic/aws-lambda-python" #virtual = ".hatch" [tool.api] -spec_version = "3.5.0" +spec_version = "3.6.0" spec_repository = "https://github.com/open-mpic/open-mpic-specification" [tool.hatch] From ee424508c204638a9ac096ce21e7ecb99ac35e33 Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sat, 27 Sep 2025 13:25:40 -0400 Subject: [PATCH 7/8] updated core dependency --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fcb595b..4b62f59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,7 @@ dependencies = [ "pydantic==2.11.7", "aiohttp==3.12.14", "aws-lambda-powertools[parser]==3.15.1", - #"open-mpic-core==6.1.0", - "open-mpic-core @ git+https://github.com/open-mpic/open-mpic-core-python.git@ds-allow-caa-lookup-failure-flag", + "open-mpic-core==6.1.0", "aioboto3~=14.3.0", "black==25.1.0", ] From fb4c0be009398cd20ef8ac4d2a5b9f1af4bed0da Mon Sep 17 00:00:00 2001 From: Dmitry Sharkov Date: Sun, 28 Sep 2025 20:40:38 -0400 Subject: [PATCH 8/8] reverted example config change, changed README to point to the latest revision of the spec --- README.md | 2 +- config.example.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 183ba26..82f3536 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ The above sample must be run from the root directory of a deployed Open MPIC aws The API is compliant with the [Open MPIC Specification](https://github.com/open-mpic/open-mpic-specification). -There is [documentation based on the API specification used in this version](https://open-mpic.org/documentation.html?commit=65f7409f102995747b966e4cb0c86bfd7f621211). +There is [documentation based on the API specification used in this version](https://open-mpic.org/documentation.html?commit=150a21d8c8e1c4758494f75d4e6811a1c6d05058). ## Development Code changes can easily be deployed by editing the .py files and then rezipping the project via `./zip-all.sh` and `./2-package.sh` in the `layer` directory. Then, running `tofu apply` run from the open-tofu directory will update only on the required resources and leave the others unchanged. If any `.tf.template` files are changed or `config.yaml` is edited, `hatch run ./configure.py` must be rerun followed by `tofu apply` in the open-tofu directory. diff --git a/config.example.yaml b/config.example.yaml index 8a4f0e9..b0dc7ea 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,6 +1,6 @@ # A list of perspectives with the format . TODO link to pages. perspectives: - - us-east-2 + - us-west-1 - us-west-2 - ca-west-1 - eu-central-2