Skip to content

Commit e9f4658

Browse files
author
Yuriy Bezsonov
committed
Add app and cdk nag
1 parent b02570a commit e9f4658

16 files changed

Lines changed: 1797 additions & 119 deletions

.kiro/specs/infra/tasks.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,3 +783,140 @@
783783
- Verify base template uses AdministratorAccess
784784
- Verify other templates use custom IdeUserPolicy
785785
- _Requirements: 5.6_
786+
787+
788+
## VPC Endpoint Cleanup and SSM Parameters (900.x)
789+
790+
- [x] 900.1 Create VPC Endpoint Cleanup construct
791+
- Create infra/cdk/src/main/resources/lambda/cfn-pre-delete-cleanup.py
792+
- Lambda finds GuardDuty VPC endpoints by VPC ID, deletes them, waits for deletion
793+
- Create infra/cdk/src/main/java/sample/com/constructs/CfnPreDeleteCleanup.java
794+
- Custom Resource triggers cleanup on stack delete only
795+
- _Requirements: 5.6_
796+
797+
- [x] 900.2 Integrate CfnPreDeleteCleanup into WorkshopStack
798+
- Add CfnPreDeleteCleanup for java-on-aws-immersion-day and java-on-amazon-eks templates
799+
- Pass VPC ID to ensure only workshop VPC endpoints are deleted
800+
- _Requirements: 5.6_
801+
802+
- [x] 900.3 Add workshop-vpc-id SSM parameter
803+
- Update Vpc.java to create SSM parameter with VPC ID
804+
- Parameter name: workshop-vpc-id
805+
- Available in all stacks for cross-stack reference
806+
- _Requirements: 5.6_
807+
808+
- [x] 900.4 Test and validate
809+
- Generate all templates: `npm run generate`
810+
- Verify CfnPreDeleteCleanup resources in EKS templates
811+
- Verify workshop-vpc-id SSM parameter in all templates
812+
- _Requirements: 5.6_
813+
814+
815+
## CDK Nag Integration (1000.x)
816+
817+
- [x] 1000.1 Add CDK Nag dependency
818+
- Add io.github.cdklabs:cdknag:2.36.2 to pom.xml
819+
- Add cdknag.version property for version management
820+
- _Requirements: 5.6_
821+
822+
- [x] 1000.2 Configure CDK Nag in WorkshopApp
823+
- Add AwsSolutionsChecks aspect to app
824+
- Add workshop-appropriate suppressions for:
825+
- API Gateway (APIG1-6, COG4) - no auth/logging needed for workshop
826+
- IAM (IAM4, IAM5) - managed policies and wildcards acceptable
827+
- RDS (RDS2, RDS3, RDS6, RDS10, RDS11, RDS13) - ephemeral workshop database
828+
- VPC (VPC7, EC23) - no flow logs needed
829+
- Secrets Manager (SMG4) - no rotation needed
830+
- CloudFront (CFR1-5) - HTTP origin acceptable for workshop
831+
- EKS (EKS1, EKS2) - public access and no logging for workshop
832+
- EC2 (EC28, EC29) - no autoscaling/termination protection
833+
- CodeBuild (CB4) - default CMK acceptable
834+
- S3 (S1) - no access logs needed
835+
- Lambda (L1) - CDK default runtimes acceptable
836+
- ELB (ELB2) - no ALB logs needed
837+
- ECS (ECS2, ECS4) - temporary containers, no insights
838+
- _Requirements: 5.6_
839+
840+
- [x] 1000.3 Enable SSL enforcement on S3 bucket
841+
- Add enforceSsl(true) to WorkshopBucket construct
842+
- Fixes AwsSolutions-S10 CDK Nag finding
843+
- _Requirements: 5.6_
844+
845+
- [x] 1000.4 Test and validate CDK Nag
846+
- Generate all templates: `npm run generate`
847+
- Verify no CDK Nag errors (only suppressed warnings)
848+
- All templates pass validation
849+
- _Requirements: 5.6_
850+
851+
852+
## S3 HTTPS Verification (1100.x)
853+
854+
- [x] 1100.1 Verify S3 bucket SSL enforcement
855+
- WorkshopBucket.java has enforceSsl(true) which enforces HTTPS at bucket policy level ✅
856+
- Any HTTP requests to the bucket will be denied by AWS ✅
857+
- _Requirements: 5.6_
858+
859+
- [x] 1100.2 Verify S3 client usage in Lambda
860+
- thread-dump-lambda/src/index.py uses boto3.client('s3') for put_object operations ✅
861+
- boto3 S3 client uses HTTPS by default - no code changes needed ✅
862+
- S3 operations: put_object for thread dumps and analysis results ✅
863+
- _Requirements: 5.6_
864+
865+
- [x] 1100.3 Verify S3 permissions in CDK constructs
866+
- ThreadAnalysis.java passes bucket name to Lambda via S3_BUCKET_NAME environment variable ✅
867+
- JvmAnalysis.java grants S3 permissions to Pod Identity role ✅
868+
- Unicorn.java grants S3 permissions to EKS pod role ✅
869+
- All use bucket.grantReadWrite() which doesn't affect transport protocol ✅
870+
- _Requirements: 5.6_
871+
872+
- [x] 1100.4 Verify scripts don't use HTTP for S3
873+
- tools.sh Session Manager download uses AWS-hosted URL (not our bucket) - acceptable ✅
874+
- No scripts manually construct S3 URLs that might use HTTP ✅
875+
- All S3 interactions go through AWS SDK/CLI which use HTTPS by default ✅
876+
- _Requirements: 5.6_
877+
878+
879+
## Unicorn Store Spring Setup (1200.x)
880+
881+
- [x] 1200.1 Create unicorn-store-spring.sh setup script
882+
- Created infra/scripts/setup/unicorn-store-spring.sh ✅
883+
- Copies ~/java-on-aws/apps/unicorn-store-spring to ~/environment ✅
884+
- Logs in to ECR using aws ecr get-login-password ✅
885+
- Builds Docker image with docker build ✅
886+
- Tags and pushes with 'initial' tag ✅
887+
- Tags and pushes with 'latest' tag ✅
888+
- Emits "✅ Success: Unicorn Store Spring" for bootstrap summary ✅
889+
- _Requirements: 5.6_
890+
891+
- [x] 1200.2 Integrate into java-on-aws-immersion-day template
892+
- Added Phase 4: Unicorn Store Spring to java-on-aws-immersion-day.sh ✅
893+
- Calls unicorn-store-spring.sh after analysis setup ✅
894+
- _Requirements: 5.6_
895+
896+
- [x] 1200.3 Integrate into java-on-amazon-eks template
897+
- Added Phase 4: Unicorn Store Spring to java-on-amazon-eks.sh ✅
898+
- Calls unicorn-store-spring.sh after analysis setup ✅
899+
- _Requirements: 5.6_
900+
901+
902+
## Stack Cleanup Enhancements (1300.x)
903+
904+
- [x] 1300.1 Enhance cleanup Lambda with CloudWatch logs and S3 cleanup
905+
- Updated cfn-pre-delete-cleanup.py to also clean up CloudWatch logs and S3 buckets ✅
906+
- Deletes log groups with workshop- or unicornstore- prefix ✅
907+
- Empties S3 buckets with workshop- prefix ✅
908+
- Execution order: start VPC endpoint deletion → cleanup logs and S3 → wait for VPC endpoints ✅
909+
- _Requirements: 5.6_
910+
911+
- [x] 1300.2 Update CfnPreDeleteCleanup construct with additional IAM permissions
912+
- Added logs:DescribeLogGroups and logs:DeleteLogGroup permissions ✅
913+
- Added s3:ListAllMyBuckets, s3:ListBucket, s3:ListBucketVersions, s3:DeleteObject, s3:DeleteObjectVersion permissions ✅
914+
- Updated Lambda description to reflect expanded cleanup scope ✅
915+
- _Requirements: 5.6_
916+
917+
- [x] 1300.3 Rename construct and Lambda for clarity
918+
- Renamed VpcEndpointCleanup.java to CfnPreDeleteCleanup.java ✅
919+
- Renamed vpc-endpoint-cleanup.py to cfn-pre-delete-cleanup.py ✅
920+
- Updated Lambda function name to {prefix}-cfn-pre-delete-cleanup ✅
921+
- Updated WorkshopStack.java to use new class name ✅
922+
- _Requirements: 5.6_

infra/cdk/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<maven.compiler.target>25</maven.compiler.target>
1414
<cdk.version>2.224.0</cdk.version>
1515
<constructs.version>10.4.2</constructs.version>
16+
<cdknag.version>2.36.2</cdknag.version>
1617
<junit.version>5.11.3</junit.version>
1718
</properties>
1819

@@ -60,6 +61,13 @@
6061
<version>${cdk.version}-alpha.0</version>
6162
</dependency>
6263

64+
<!-- CDK Nag for best practices validation -->
65+
<dependency>
66+
<groupId>io.github.cdklabs</groupId>
67+
<artifactId>cdknag</artifactId>
68+
<version>${cdknag.version}</version>
69+
</dependency>
70+
6371
<!-- AWS Lambda Runtime -->
6472
<dependency>
6573
<groupId>com.amazonaws</groupId>
Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,98 @@
11
package sample.com;
22

3+
import io.github.cdklabs.cdknag.AwsSolutionsChecks;
4+
import io.github.cdklabs.cdknag.NagPackSuppression;
5+
import io.github.cdklabs.cdknag.NagSuppressions;
36
import software.amazon.awscdk.App;
47
import software.amazon.awscdk.AppProps;
8+
import software.amazon.awscdk.Aspects;
59
import software.amazon.awscdk.StackProps;
610
import software.amazon.awscdk.DefaultStackSynthesizer;
711
import software.amazon.awscdk.DefaultStackSynthesizerProps;
812

13+
import java.util.List;
14+
915
public class WorkshopApp {
1016
public static void main(final String[] args) {
1117
App app = new App(AppProps.builder()
1218
.analyticsReporting(false)
1319
.build());
1420

15-
new WorkshopStack(app, "WorkshopStack", StackProps.builder()
21+
var workshopStack = new WorkshopStack(app, "WorkshopStack", StackProps.builder()
1622
.synthesizer(new DefaultStackSynthesizer(DefaultStackSynthesizerProps.builder()
17-
.generateBootstrapVersionRule(false) // This disables the bootstrap version parameter
23+
.generateBootstrapVersionRule(false)
1824
.build()))
1925
.build());
2026

27+
// Add CDK-NAG checks: https://github.com/cdklabs/cdk-nag
28+
Aspects.of(app).add(new AwsSolutionsChecks());
29+
30+
// Workshop environment suppressions - these are acceptable for ephemeral workshop environments
31+
var suppressions = List.of(
32+
// API Gateway
33+
new NagPackSuppression.Builder().id("AwsSolutions-APIG1").reason("API Gateway access logging not needed for workshop").build(),
34+
new NagPackSuppression.Builder().id("AwsSolutions-APIG2").reason("API Gateway request validation not needed for workshop").build(),
35+
new NagPackSuppression.Builder().id("AwsSolutions-APIG3").reason("Workshop API Gateways do not need AWS WAF").build(),
36+
new NagPackSuppression.Builder().id("AwsSolutions-APIG4").reason("Workshop environment does not require API Gateway authorization").build(),
37+
new NagPackSuppression.Builder().id("AwsSolutions-APIG6").reason("API Gateway access logging not needed for workshop").build(),
38+
new NagPackSuppression.Builder().id("AwsSolutions-COG4").reason("Workshop environment does not require Cognito User Pool authorization").build(),
39+
40+
// IAM
41+
new NagPackSuppression.Builder().id("AwsSolutions-IAM4").reason("AWS Managed policies are acceptable for workshop").build(),
42+
new NagPackSuppression.Builder().id("AwsSolutions-IAM5").reason("Wildcard permissions acceptable for workshop parallel resource creation").build(),
43+
44+
// RDS
45+
new NagPackSuppression.Builder().id("AwsSolutions-RDS2").reason("Workshop non-sensitive test database does not need encryption at rest").build(),
46+
new NagPackSuppression.Builder().id("AwsSolutions-RDS3").reason("Workshop environment does not need Multi-AZ to reduce cost").build(),
47+
new NagPackSuppression.Builder().id("AwsSolutions-RDS6").reason("Workshop environment uses user/password authentication").build(),
48+
new NagPackSuppression.Builder().id("AwsSolutions-RDS10").reason("Workshop environment is ephemeral - database deleted at end").build(),
49+
new NagPackSuppression.Builder().id("AwsSolutions-RDS11").reason("Database in private subnet can use default port").build(),
50+
new NagPackSuppression.Builder().id("AwsSolutions-RDS13").reason("Workshop database does not need backups").build(),
51+
52+
// VPC & Networking
53+
new NagPackSuppression.Builder().id("AwsSolutions-VPC7").reason("Workshop environment does not need VPC flow logs").build(),
54+
new NagPackSuppression.Builder().id("AwsSolutions-EC23").reason("Workshop security groups allow broad access for learning").build(),
55+
56+
// Secrets Manager
57+
new NagPackSuppression.Builder().id("AwsSolutions-SMG4").reason("Ephemeral workshop environment does not need secret rotation").build(),
58+
59+
// CloudFront
60+
new NagPackSuppression.Builder().id("AwsSolutions-CFR1").reason("Workshop environment should be accessible from any geo").build(),
61+
new NagPackSuppression.Builder().id("AwsSolutions-CFR2").reason("Ephemeral workshop environment does not need WAF").build(),
62+
new NagPackSuppression.Builder().id("AwsSolutions-CFR3").reason("Ephemeral workshop environment does not need logging").build(),
63+
new NagPackSuppression.Builder().id("AwsSolutions-CFR4").reason("Workshop IDE uses HTTP origin").build(),
64+
new NagPackSuppression.Builder().id("AwsSolutions-CFR5").reason("Workshop IDE uses HTTP origin").build(),
65+
66+
// EKS
67+
new NagPackSuppression.Builder().id("AwsSolutions-EKS1").reason("Workshop EKS cluster uses public access for learning").build(),
68+
new NagPackSuppression.Builder().id("AwsSolutions-EKS2").reason("Workshop EKS cluster does not need control plane logging").build(),
69+
70+
// EC2 & Auto Scaling
71+
new NagPackSuppression.Builder().id("AwsSolutions-EC28").reason("Workshop instance does not need detailed monitoring").build(),
72+
new NagPackSuppression.Builder().id("AwsSolutions-EC29").reason("Workshop instance does not need termination protection").build(),
73+
74+
// CodeBuild
75+
new NagPackSuppression.Builder().id("AwsSolutions-CB4").reason("CodeBuild uses default AWS-managed CMK for S3").build(),
76+
77+
// S3
78+
new NagPackSuppression.Builder().id("AwsSolutions-S1").reason("Workshop S3 bucket does not need access logs").build(),
79+
80+
// Lambda
81+
new NagPackSuppression.Builder().id("AwsSolutions-L1").reason("Workshop environment uses CDK default Lambda runtimes").build(),
82+
83+
// ELB
84+
new NagPackSuppression.Builder().id("AwsSolutions-ELB2").reason("Workshop environment does not need ALB logs").build(),
85+
86+
// ECS
87+
new NagPackSuppression.Builder().id("AwsSolutions-ECS2").reason("Workshop environment uses temporary containers").build(),
88+
new NagPackSuppression.Builder().id("AwsSolutions-ECS4").reason("Workshop environment does not need Container Insights").build(),
89+
90+
// CDK Nag validation
91+
new NagPackSuppression.Builder().id("CdkNagValidationFailure").reason("Suppress CDK Nag validation warnings").build()
92+
);
93+
94+
NagSuppressions.addStackSuppressions(workshopStack, suppressions);
95+
2196
app.synth();
2297
}
2398
}

infra/cdk/src/main/java/sample/com/WorkshopStack.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
125125
.database(database)
126126
.workshopBucket(workshopBucket.getBucket())
127127
.build());
128+
129+
// Pre-delete cleanup (removes VPC endpoints, CloudWatch logs, S3 contents before stack deletion)
130+
new CfnPreDeleteCleanup(this, "CfnPreDeleteCleanup",
131+
CfnPreDeleteCleanup.CfnPreDeleteCleanupProps.builder()
132+
.prefix(prefix)
133+
.vpc(vpc.getVpc())
134+
.build());
128135
}
129136
}
130137
}

0 commit comments

Comments
 (0)