From 13b61c51faf75d151cfff3a08859463fd838b903 Mon Sep 17 00:00:00 2001 From: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Date: Mon, 23 Nov 2020 10:18:23 -0800 Subject: [PATCH] feature: Support for Lambda Code Signing (#53) (#1825) * feature: add support for CFN fields for lambda signing (#53) * feature: add support for CFN fields for lambda signing * feature: add support for CFN fields for lambda signing (update formatting) * feature: add support for CFN fields for lambda signing (update patching) * feature: add support for CFN fields for lambda signing (update template) * Revert "feat: add explicit UpdateReplacePolicy (#1481)" (#1568) * docs: document IpV6 option on Domain Configuration object (#1588) * chore: Exclude test modules in whl (#1597) * feat: Add Step Function Resource (#1601) Co-authored-by: Jacob Fuss * Release Changes for 1.25.0 * feature: add support for CFN fields for lambda signing * feature: add support for CFN fields for lambda signing (slight code update) * feature: add support for CFN fields for lambda signing (update globals.py) Co-authored-by: Shreya Co-authored-by: Timo Schilling Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood * Move Tests to Appveyor (#1801) * print python version * update path vars * update linux cmd * update linux cmd * update linux cmd * update whitelist in tox * update passenv * update tox whitelisting * update tox whitelisting Co-authored-by: Shreya Co-authored-by: Timo Schilling Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood --- samtranslator/model/lambda_.py | 1 + samtranslator/model/sam_resources.py | 3 + samtranslator/plugins/globals/globals.py | 1 + .../input/function_with_signing_profile.yaml | 24 ++++++ .../aws-cn/function_with_signing_profile.json | 85 +++++++++++++++++++ .../function_with_signing_profile.json | 85 +++++++++++++++++++ .../error_globals_unsupported_property.json | 4 +- .../output/function_with_signing_profile.json | 85 +++++++++++++++++++ tests/translator/test_function_resources.py | 31 +++++++ tests/translator/test_translator.py | 1 + 10 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 tests/translator/input/function_with_signing_profile.yaml create mode 100644 tests/translator/output/aws-cn/function_with_signing_profile.json create mode 100644 tests/translator/output/aws-us-gov/function_with_signing_profile.json create mode 100644 tests/translator/output/function_with_signing_profile.json diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index 0cb75661ce..b91f00e75b 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -23,6 +23,7 @@ class LambdaFunction(Resource): "Layers": PropertyType(False, list_of(one_of(is_str(), is_type(dict)))), "ReservedConcurrentExecutions": PropertyType(False, any_type()), "FileSystemConfigs": PropertyType(False, list_of(is_type(dict))), + "CodeSigningConfigArn": PropertyType(False, is_str()), } runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")} diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 007a3ded1d..c6cf507609 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -82,6 +82,7 @@ class SamFunction(SamResourceMacro): "VersionDescription": PropertyType(False, is_str()), "ProvisionedConcurrencyConfig": PropertyType(False, is_type(dict)), "FileSystemConfigs": PropertyType(False, list_of(is_type(dict))), + "CodeSigningConfigArn": PropertyType(False, is_str()), } event_resolver = ResourceTypeResolver( samtranslator.model.eventsources, @@ -412,6 +413,8 @@ def _construct_lambda_function(self): if self.DeadLetterQueue: lambda_function.DeadLetterConfig = {"TargetArn": self.DeadLetterQueue["TargetArn"]} + lambda_function.CodeSigningConfigArn = self.CodeSigningConfigArn + return lambda_function def _add_event_invoke_managed_policy(self, dest_config, logical_id, condition, dest_arn): diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index cf5fb7fb07..f75beb3ec0 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -41,6 +41,7 @@ class Globals(object): "AssumeRolePolicyDocument", "EventInvokeConfig", "FileSystemConfigs", + "CodeSigningConfigArn", ], # Everything except # DefinitionBody: because its hard to reason about merge of Swagger dictionaries diff --git a/tests/translator/input/function_with_signing_profile.yaml b/tests/translator/input/function_with_signing_profile.yaml new file mode 100644 index 0000000000..09a0323670 --- /dev/null +++ b/tests/translator/input/function_with_signing_profile.yaml @@ -0,0 +1,24 @@ +Resources: + + FunctionWithSigningProfile: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/member_portal.zip + Handler: index.gethtml + Runtime: nodejs12.x + CodeSigningConfigArn: !Ref MySignedFunctionCodeSigningConfig + + MySignedFunctionCodeSigningConfig: + Type: AWS::Lambda::CodeSigningConfig + Properties: + Description: "Code Signing for MySignedLambdaFunction" + AllowedPublishers: + SigningProfileVersionArns: + - !GetAtt SigningProfile.ProfileVersionArn + CodeSigningPolicies: + UntrustedArtifactOnDeployment: "Enforce" + + SigningProfile: + Type: AWS::Signer::SigningProfile + Properties: + PlatformId: AWSLambda-SHA384-ECDSA \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_signing_profile.json b/tests/translator/output/aws-cn/function_with_signing_profile.json new file mode 100644 index 0000000000..d25c7e5266 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_signing_profile.json @@ -0,0 +1,85 @@ +{ + "Resources": { + "FunctionWithSigningProfile": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Role": { + "Fn::GetAtt": [ + "FunctionWithSigningProfileRole", + "Arn" + ] + }, + "CodeSigningConfigArn": { + "Ref": "MySignedFunctionCodeSigningConfig" + } + } + }, + "FunctionWithSigningProfileRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MySignedFunctionCodeSigningConfig": { + "Type": "AWS::Lambda::CodeSigningConfig", + "Properties": { + "CodeSigningPolicies": { + "UntrustedArtifactOnDeployment": "Enforce" + }, + "AllowedPublishers": { + "SigningProfileVersionArns": [ + { + "Fn::GetAtt": [ + "SigningProfile", + "ProfileVersionArn" + ] + } + ] + }, + "Description": "Code Signing for MySignedLambdaFunction" + } + }, + "SigningProfile": { + "Type": "AWS::Signer::SigningProfile", + "Properties": { + "PlatformId": "AWSLambda-SHA384-ECDSA" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_signing_profile.json b/tests/translator/output/aws-us-gov/function_with_signing_profile.json new file mode 100644 index 0000000000..fb4065fa59 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_signing_profile.json @@ -0,0 +1,85 @@ +{ + "Resources": { + "FunctionWithSigningProfile": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Role": { + "Fn::GetAtt": [ + "FunctionWithSigningProfileRole", + "Arn" + ] + }, + "CodeSigningConfigArn": { + "Ref": "MySignedFunctionCodeSigningConfig" + } + } + }, + "FunctionWithSigningProfileRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MySignedFunctionCodeSigningConfig": { + "Type": "AWS::Lambda::CodeSigningConfig", + "Properties": { + "CodeSigningPolicies": { + "UntrustedArtifactOnDeployment": "Enforce" + }, + "AllowedPublishers": { + "SigningProfileVersionArns": [ + { + "Fn::GetAtt": [ + "SigningProfile", + "ProfileVersionArn" + ] + } + ] + }, + "Description": "Code Signing for MySignedLambdaFunction" + } + }, + "SigningProfile": { + "Type": "AWS::Signer::SigningProfile", + "Properties": { + "PlatformId": "AWSLambda-SHA384-ECDSA" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/error_globals_unsupported_property.json b/tests/translator/output/error_globals_unsupported_property.json index a97faaf6a2..2b2dfbf18c 100644 --- a/tests/translator/output/error_globals_unsupported_property.json +++ b/tests/translator/output/error_globals_unsupported_property.json @@ -1,8 +1,8 @@ { "errors": [ { - "errorMessage": "'Globals' section is invalid. 'SomeKey' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'Layers', 'DeploymentPreference', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'EventInvokeConfig', 'FileSystemConfigs']" + "errorMessage": "'Globals' section is invalid. 'SomeKey' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'Layers', 'DeploymentPreference', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn']" } ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'SomeKey' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'Layers', 'DeploymentPreference', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs']" + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'SomeKey' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'Layers', 'DeploymentPreference', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn']" } diff --git a/tests/translator/output/function_with_signing_profile.json b/tests/translator/output/function_with_signing_profile.json new file mode 100644 index 0000000000..1b878970d3 --- /dev/null +++ b/tests/translator/output/function_with_signing_profile.json @@ -0,0 +1,85 @@ +{ + "Resources": { + "FunctionWithSigningProfile": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Role": { + "Fn::GetAtt": [ + "FunctionWithSigningProfileRole", + "Arn" + ] + }, + "CodeSigningConfigArn": { + "Ref": "MySignedFunctionCodeSigningConfig" + } + } + }, + "FunctionWithSigningProfileRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MySignedFunctionCodeSigningConfig": { + "Type": "AWS::Lambda::CodeSigningConfig", + "Properties": { + "CodeSigningPolicies": { + "UntrustedArtifactOnDeployment": "Enforce" + }, + "AllowedPublishers": { + "SigningProfileVersionArns": [ + { + "Fn::GetAtt": [ + "SigningProfile", + "ProfileVersionArn" + ] + } + ] + }, + "Description": "Code Signing for MySignedLambdaFunction" + } + }, + "SigningProfile": { + "Type": "AWS::Signer::SigningProfile", + "Properties": { + "PlatformId": "AWSLambda-SHA384-ECDSA" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_function_resources.py b/tests/translator/test_function_resources.py index f5c8c2ab1f..aa2d57f4eb 100644 --- a/tests/translator/test_function_resources.py +++ b/tests/translator/test_function_resources.py @@ -25,6 +25,37 @@ def setUp(self): self.lambda_func = self._make_lambda_function(self.sam_func.logical_id) self.lambda_version = self._make_lambda_version("VersionLogicalId", self.sam_func) + @patch("boto3.session.Session.region_name", "us-west-2") + def test_sam_function_with_code_signer(self): + code_signing_config_arn = "code_signing_config_arn" + func = { + "Type": "AWS::Serverless::Function", + "Properties": { + "CodeUri": self.code_uri, + "Runtime": "nodejs12.x", + "Handler": "index.handler", + "CodeSigningConfigArn": code_signing_config_arn, + }, + } + + sam_func = SamFunction.from_dict(logical_id="foo", resource_dict=func) + + kwargs = {} + kwargs["managed_policy_map"] = {"a": "b"} + kwargs["event_resources"] = [] + kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = { + "S3Bucket": "bucket", + "S3Key": "key", + "S3ObjectVersion": "version", + } + resources = sam_func.to_cloudformation(**kwargs) + + lambda_functions = [r.to_dict() for r in resources if r.resource_type == LambdaFunction.resource_type] + self.assertEqual(len(lambda_functions), 1) + expected_code_signing_config_arn = lambda_functions[0]["foo"]["Properties"]["CodeSigningConfigArn"] + self.assertEqual(expected_code_signing_config_arn, code_signing_config_arn) + @patch("boto3.session.Session.region_name", "ap-southeast-1") @patch.object(SamFunction, "_get_resolved_alias_name") def test_sam_function_with_alias(self, get_resolved_alias_name_mock): diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 4db7f1f23d..cc84e40847 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -251,6 +251,7 @@ class TestTranslatorEndToEnd(TestCase): "function_with_conditional_policy_template", "function_with_conditional_policy_template_and_ref_no_value", "function_with_request_parameters", + "function_with_signing_profile", "global_handle_path_level_parameter", "globals_for_function", "globals_for_api",