diff --git a/cdk/blueprint/constants.py b/cdk/blueprint/constants.py index 84ae1cc..dc518cc 100644 --- a/cdk/blueprint/constants.py +++ b/cdk/blueprint/constants.py @@ -1,5 +1,4 @@ SERVICE_ROLE_ARN = 'ServiceRoleArn' -LAMBDA_BASIC_EXECUTION_ROLE = 'AWSLambdaBasicExecutionRole' SERVICE_ROLE = 'ServiceRole' CREATE_LAMBDA = 'SQSLambda' LAMBDA_LAYER_NAME = 'common' diff --git a/cdk/blueprint/service_stack.py b/cdk/blueprint/service_stack.py index cf4f2a4..9def971 100644 --- a/cdk/blueprint/service_stack.py +++ b/cdk/blueprint/service_stack.py @@ -1,5 +1,5 @@ from aws_cdk import Aspects, Stack, Tags -from cdk_nag import AwsSolutionsChecks, NagSuppressions +from cdk_nag import AwsSolutionsChecks, NagPackSuppression, NagSuppressions from constructs import Construct from cdk.blueprint.constants import OWNER_TAG, SERVICE_NAME, SERVICE_NAME_TAG @@ -26,20 +26,23 @@ def _add_stack_tags(self) -> None: Tags.of(self).add(SERVICE_NAME_TAG, SERVICE_NAME) Tags.of(self).add(OWNER_TAG, get_username()) + # Define the custom suppression condition + def custom_suppression_condition(policy_statement): + if 'Action' in policy_statement and 'logs:*' in policy_statement['Action']: + if 'Resource' in policy_statement and '*' in policy_statement['Resource']: + return True + return False + def _add_security_tests(self) -> None: Aspects.of(self).add(AwsSolutionsChecks(verbose=True)) # Suppress a specific rule for this resource NagSuppressions.add_stack_suppressions( - self, - [ - {'id': 'AwsSolutions-IAM4', 'reason': 'policy for cloudwatch logs.'}, - {'id': 'AwsSolutions-IAM5', 'reason': 'policy for cloudwatch logs.'}, - {'id': 'AwsSolutions-APIG2', 'reason': 'lambda does input validation'}, - {'id': 'AwsSolutions-APIG1', 'reason': 'not mandatory in a sample template'}, - {'id': 'AwsSolutions-APIG3', 'reason': 'not mandatory in a sample template'}, - {'id': 'AwsSolutions-APIG6', 'reason': 'not mandatory in a sample template'}, - {'id': 'AwsSolutions-APIG4', 'reason': 'authorization not mandatory in a sample template'}, - {'id': 'AwsSolutions-COG4', 'reason': 'not using cognito'}, - {'id': 'AwsSolutions-L1', 'reason': 'False positive'}, + stack=self, + suppressions=[ + NagPackSuppression( + id='AwsSolutions-IAM5', + reason='Suppressed for logs:* and xray permissions on all resources', + applies_to=['Action::logs:*', 'Action::xray:*', 'Resource::*'], + ) ], ) diff --git a/cdk/blueprint/sqs_lambda_s3_blueprint.py b/cdk/blueprint/sqs_lambda_s3_blueprint.py index 474be40..b009641 100644 --- a/cdk/blueprint/sqs_lambda_s3_blueprint.py +++ b/cdk/blueprint/sqs_lambda_s3_blueprint.py @@ -4,7 +4,6 @@ from aws_cdk import aws_lambda_event_sources as lambda_event_sources from aws_cdk import aws_s3 as s3 from aws_cdk.aws_lambda_python_alpha import PythonLayerVersion -from aws_cdk.aws_logs import RetentionDays from constructs import Construct import cdk.blueprint.constants as constants @@ -38,19 +37,30 @@ def _build_lambda_role(self, bucket: s3.Bucket) -> iam.Role: constants.SERVICE_ROLE_ARN, assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'), inline_policies={ - 'bucket': iam.PolicyDocument( + 'Bucket': iam.PolicyDocument( statements=[ iam.PolicyStatement( actions=['s3:PutObject', 's3:PutObjectAcl'], - resources=[f'{bucket.bucket_arn}/*'], + resources=[bucket.bucket_arn], + effect=iam.Effect.ALLOW, + ), + ] + ), + # similar to https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html + 'CloudwatchLogs': iam.PolicyDocument( + statements=[ + iam.PolicyStatement( + actions=[ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + ], + resources=['*'], effect=iam.Effect.ALLOW, ) ] ), }, - managed_policies=[ - iam.ManagedPolicy.from_aws_managed_policy_name(managed_policy_name=(f'service-role/{constants.LAMBDA_BASIC_EXECUTION_ROLE}')) - ], ) def _build_common_layer(self) -> PythonLayerVersion: @@ -85,7 +95,6 @@ def _create_lambda_function( memory_size=constants.API_HANDLER_LAMBDA_MEMORY_SIZE, layers=[self.common_layer], role=role, - log_retention=RetentionDays.ONE_DAY, log_format=_lambda.LogFormat.JSON.value, system_log_level=_lambda.SystemLogLevel.INFO.value, ) diff --git a/cdk/blueprint/sqs_redrive_construct.py b/cdk/blueprint/sqs_redrive_construct.py index ed8317e..aa08440 100644 --- a/cdk/blueprint/sqs_redrive_construct.py +++ b/cdk/blueprint/sqs_redrive_construct.py @@ -23,8 +23,6 @@ class RedrivableSQS(Construct): max_retry_attempts (int): The maximum number of times to retry processing a message in the SQS before sending it to the DLQ. Default is 3. #pylint: disable=line-too-long """ - LAMBDA_BASIC_EXECUTION_ROLE = 'AWSLambdaBasicExecutionRole' - def __init__( self, scope: Construct, @@ -105,12 +103,21 @@ def _create_redrive_function( ) ] ), + # similar to https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html + 'CloudwatchLogs': iam.PolicyDocument( + statements=[ + iam.PolicyStatement( + actions=[ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + ], + resources=['*'], + effect=iam.Effect.ALLOW, + ) + ] + ), }, - managed_policies=[ - iam.ManagedPolicy.from_aws_managed_policy_name( - managed_policy_name=f'service-role/{RedrivableSQS.LAMBDA_BASIC_EXECUTION_ROLE}', - ) - ], ) return _lambda.Function(