diff --git a/labs/unicorn-store/infrastructure/cdk/pom.xml b/labs/unicorn-store/infrastructure/cdk/pom.xml index efa27f7fa..328c2b624 100644 --- a/labs/unicorn-store/infrastructure/cdk/pom.xml +++ b/labs/unicorn-store/infrastructure/cdk/pom.xml @@ -10,7 +10,7 @@ UTF-8 UTF-8 - 2.257.0 + 2.261.0 6.1.0 @@ -21,8 +21,8 @@ maven-compiler-plugin 3.15.0 - 17 - 17 + 25 + 25 @@ -48,7 +48,7 @@ io.github.cdklabs cdknag - 2.38.2 + 3.0.1 diff --git a/labs/unicorn-store/infrastructure/cdk/src/main/java/com/unicorn/UnicornStoreApp.java b/labs/unicorn-store/infrastructure/cdk/src/main/java/com/unicorn/UnicornStoreApp.java index fe38c4cd4..ac99980b4 100644 --- a/labs/unicorn-store/infrastructure/cdk/src/main/java/com/unicorn/UnicornStoreApp.java +++ b/labs/unicorn-store/infrastructure/cdk/src/main/java/com/unicorn/UnicornStoreApp.java @@ -1,6 +1,6 @@ package com.unicorn; -import java.util.List; +import java.util.HashMap; import com.unicorn.alternatives.UnicornAuditService; import com.unicorn.alternatives.UnicornStoreMicronaut; @@ -9,11 +9,8 @@ import com.unicorn.core.InfrastructureStack; import io.github.cdklabs.cdknag.AwsSolutionsChecks; -import io.github.cdklabs.cdknag.NagPackSuppression; -import io.github.cdklabs.cdknag.NagSuppressions; -import software.amazon.awscdk.App; -import software.amazon.awscdk.Aspects; -import software.amazon.awscdk.StackProps; +import io.github.cdklabs.cdknag.NagPackProps; +import software.amazon.awscdk.*; public class UnicornStoreApp { @@ -23,51 +20,54 @@ public static void main(final String[] args) { var infrastructureStack = new InfrastructureStack(app, "UnicornStoreInfrastructure", StackProps.builder() .build()); - var unicornStoreSpring = new UnicornStoreStack(app, "UnicornStoreSpringApp", StackProps.builder() + new UnicornStoreStack(app, "UnicornStoreSpringApp", StackProps.builder() .build(), infrastructureStack); - var unicornStoreMicronaut = new UnicornStoreMicronaut(app, "UnicornStoreMicronautApp", StackProps.builder() + new UnicornStoreMicronaut(app, "UnicornStoreMicronautApp", StackProps.builder() .build(), infrastructureStack); - var unicornStoreSpringGraalVM = new UnicornStoreSpringGraalVM(app, "UnicornStoreSpringGraalVMApp", StackProps.builder() + new UnicornStoreSpringGraalVM(app, "UnicornStoreSpringGraalVMApp", StackProps.builder() .build(), infrastructureStack); - var unicornStoreQuarkus = new UnicornStoreQuarkus(app, "UnicornStoreQuarkusApp", StackProps.builder() + new UnicornStoreQuarkus(app, "UnicornStoreQuarkusApp", StackProps.builder() .build(), infrastructureStack); - var unicornAuditService = new UnicornAuditService(app, "UnicornAuditServiceApp", StackProps.builder() + new UnicornAuditService(app, "UnicornAuditServiceApp", StackProps.builder() .build(), infrastructureStack); - //Add CDK-NAG checks: https://github.com/cdklabs/cdk-nag + Validations.of(app).addPlugins(new AwsSolutionsChecks(app, + NagPackProps.builder().writeSuppressionsToCloudFormation(true).build())); + //Add suppression to exclude certain findings that are not needed for Workshop environment - Aspects.of(app).add(new AwsSolutionsChecks()); - var suppression = List.of( - new NagPackSuppression.Builder().id("AwsSolutions-APIG4").reason("The workshop environment does not require API-Gateway authorization").build(), - new NagPackSuppression.Builder().id("AwsSolutions-COG4").reason("The workshop environment does not require Cognito User Pool authorization").build(), - new NagPackSuppression.Builder().id("AwsSolutions-RDS3").reason("Workshop environment does not need a Multi-AZ setup to reduce cost").build(), - new NagPackSuppression.Builder().id("AwsSolutions-IAM4").reason("AWS Managed policies are acceptable for the workshop").build(), - new NagPackSuppression.Builder().id("AwsSolutions-IAM5").reason("A wildcard is acceptable for this workshop to allow parallel creation of resources").build(), - new NagPackSuppression.Builder().id("AwsSolutions-RDS10").reason("Workshop environment is ephemeral and the database should be deleted by the end of the workshop").build(), - new NagPackSuppression.Builder().id("AwsSolutions-RDS11").reason("Database is in a private subnet and can use the default port").build(), - new NagPackSuppression.Builder().id("AwsSolutions-APIG2").reason("API Gateway request validation is not needed for workshop").build(), - new NagPackSuppression.Builder().id("AwsSolutions-APIG1").reason("API Gateway access logging not needed for workshop setup").build(), - new NagPackSuppression.Builder().id("AwsSolutions-APIG6").reason("API Gateway access logging not needed for workshop setup").build(), - new NagPackSuppression.Builder().id("AwsSolutions-VPC7").reason("Workshop environment does not need VPC flow logs").build(), - new NagPackSuppression.Builder().id("AwsSolutions-SMG4").reason("Ephemeral workshop environment does not need to rotate secrets").build(), - new NagPackSuppression.Builder().id("AwsSolutions-RDS2").reason("Workshop non-sensitive test database does not need encryption at rest").build(), - new NagPackSuppression.Builder().id("AwsSolutions-APIG3").reason("Workshop API Gateways do not need AWS WAF assigned").build(), - new NagPackSuppression.Builder().id("AwsSolutions-EC23").reason("Not needed").build(), - new NagPackSuppression.Builder().id("AwsSolutions-RDS13").reason("Workshop Database does not need backups").build(), - new NagPackSuppression.Builder().id("CdkNagValidationFailure").reason("Suppress warnings see: https://github.com/cdklabs/cdk-nag/issues/817").build() + Validations.of(app).acknowledge( + new Acknowledgment.Builder().id("AwsSolutions-APIG4").reason("The workshop environment does not require API-Gateway authorization").build(), + new Acknowledgment.Builder().id("AwsSolutions-COG4").reason("The workshop environment does not require Cognito User Pool authorization").build(), + new Acknowledgment.Builder().id("AwsSolutions-RDS3").reason("Workshop environment does not need a Multi-AZ setup to reduce cost").build(), + new Acknowledgment.Builder().id("AwsSolutions-RDS10").reason("Workshop environment is ephemeral and the database should be deleted by the end of the workshop").build(), + new Acknowledgment.Builder().id("AwsSolutions-RDS11").reason("Database is in a private subnet and can use the default port").build(), + new Acknowledgment.Builder().id("AwsSolutions-APIG2").reason("API Gateway request validation is not needed for workshop").build(), + new Acknowledgment.Builder().id("AwsSolutions-APIG1").reason("API Gateway access logging not needed for workshop setup").build(), + new Acknowledgment.Builder().id("AwsSolutions-APIG6").reason("API Gateway access logging not needed for workshop setup").build(), + new Acknowledgment.Builder().id("AwsSolutions-VPC7").reason("Workshop environment does not need VPC flow logs").build(), + new Acknowledgment.Builder().id("AwsSolutions-SMG4").reason("Ephemeral workshop environment does not need to rotate secrets").build(), + new Acknowledgment.Builder().id("AwsSolutions-RDS2").reason("Workshop non-sensitive test database does not need encryption at rest").build(), + new Acknowledgment.Builder().id("AwsSolutions-APIG3").reason("Workshop API Gateways do not need AWS WAF assigned").build(), + new Acknowledgment.Builder().id("AwsSolutions-EC23").reason("Not needed").build(), + new Acknowledgment.Builder().id("AwsSolutions-RDS13").reason("Workshop Database does not need backups").build(), + new Acknowledgment.Builder().id("CdkNagValidationFailure").reason("Suppress warnings see: https://github.com/cdklabs/cdk-nag/issues/817").build() ); - NagSuppressions.addStackSuppressions(infrastructureStack, suppression); - NagSuppressions.addStackSuppressions(unicornStoreSpring, suppression); - NagSuppressions.addStackSuppressions(unicornStoreMicronaut, suppression); - NagSuppressions.addStackSuppressions(unicornStoreSpringGraalVM, suppression); - NagSuppressions.addStackSuppressions(unicornStoreQuarkus, suppression); - NagSuppressions.addStackSuppressions(unicornAuditService, suppression); + // Suppress parameterized IAM findings directly via metadata + // (Validations.acknowledge() rejects IDs containing '::' due to CDK delimiter validation bug) + var iamSuppressions = new HashMap(); + iamSuppressions.put("AwsSolutions-IAM4", "AWS Managed policies are acceptable for the workshop"); + iamSuppressions.put("AwsSolutions-IAM5", "A wildcard is acceptable for this workshop to allow parallel creation of resources"); + iamSuppressions.put("AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", "AWS Managed policies are acceptable for the workshop"); + iamSuppressions.put("AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole]", "AWS Managed policies are acceptable for the workshop"); + iamSuppressions.put("AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs]", "AWS Managed policies are acceptable for the workshop"); + iamSuppressions.put("AwsSolutions-IAM5[Resource::arn:aws:secretsmanager:*:*:secret:unicornstore-db-secret-*]", "A wildcard is acceptable for this workshop"); + app.getNode().addMetadata(Validations.ACKNOWLEDGED_RULES_METADATA_KEY, iamSuppressions); app.synth(); }