Skip to content

Commit 528dbad

Browse files
author
Yuriy Bezsonov
committed
improve security of thread dump lab
1 parent c56ee27 commit 528dbad

7 files changed

Lines changed: 437 additions & 296 deletions

File tree

infrastructure/cdk/src/main/java/com/unicorn/constructs/VSCodeIde.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public static class VSCodeIdeProps {
8181
private String availabilityZone;
8282
private IMachineImage machineImage = MachineImage.latestAmazonLinux2023();
8383
private InstanceType instanceType = InstanceType.of(InstanceClass.T3, InstanceSize.MEDIUM);
84-
private String codeServerVersion = "4.101.2";
84+
private String codeServerVersion = "4.103.1";
8585
private List<IManagedPolicy> additionalIamPolicies = new ArrayList<>();
8686
private List<ISecurityGroup> additionalSecurityGroups = new ArrayList<>();
8787
private int bootstrapTimeoutMinutes = 30;

infrastructure/cdk/src/main/java/com/unicorn/core/InfrastructureMonitoringJVM.java

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22

33
import com.unicorn.constructs.EksCluster;
44
import software.amazon.awscdk.*;
5+
import software.amazon.awscdk.services.apigateway.*;
56
import software.amazon.awscdk.services.ec2.*;
67
import software.amazon.awscdk.services.iam.*;
78
import software.amazon.awscdk.services.lambda.*;
89
import software.amazon.awscdk.services.logs.LogGroup;
910
import software.amazon.awscdk.services.logs.RetentionDays;
1011
import software.amazon.awscdk.services.s3.Bucket;
11-
import software.amazon.awscdk.services.sns.Topic;
12-
import software.amazon.awscdk.services.sns.TopicPolicy;
13-
import software.amazon.awscdk.services.sns.subscriptions.LambdaSubscription;
14-
import software.amazon.awscdk.services.eks.CfnCluster;
1512
import software.constructs.Construct;
1613

1714
import java.util.List;
@@ -23,11 +20,8 @@ public class InfrastructureMonitoringJVM extends Construct {
2320
public InfrastructureMonitoringJVM(Construct scope, String id, Bucket s3Bucket, EksCluster eksCluster, IVpc vpc) {
2421
super(scope, id);
2522

26-
// Create Lambda function first (from InfrastructureLambdaBedrock)
27-
Function threadDumpFunction = createThreadDumpLambda(s3Bucket, eksCluster, vpc);
28-
29-
// Create monitoring infrastructure (from MonitoringConstruct)
30-
createMonitoringInfrastructure(vpc, eksCluster.getCluster(), threadDumpFunction);
23+
// Create Lambda function
24+
createThreadDumpLambda(s3Bucket, eksCluster, vpc);
3125
}
3226

3327
private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster, IVpc vpc) {
@@ -62,17 +56,20 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
6256
"Allow Lambda access to Kubernetes API"
6357
);
6458

65-
// IAM Role for Bedrock
66-
Role bedrockRole = Role.Builder.create(this, "BedrockAccessRole")
67-
.assumedBy(new ServicePrincipal("bedrock.amazonaws.com"))
68-
.description("Role for Bedrock Claude 3.7 access")
59+
// Create separate security group for VPC endpoint
60+
SecurityGroup vpcEndpointSg = SecurityGroup.Builder.create(this, "VpcEndpointSecurityGroup")
61+
.vpc(vpc)
62+
.securityGroupName("unicornstore-apigw-vpce-sg")
63+
.description("Security group for API Gateway VPC endpoint")
64+
.allowAllOutbound(true)
6965
.build();
7066

71-
bedrockRole.addToPolicy(PolicyStatement.Builder.create()
72-
.effect(Effect.ALLOW)
73-
.actions(List.of("bedrock:InvokeModel", "bedrock:ListFoundationModels"))
74-
.resources(List.of("arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0"))
75-
.build());
67+
// Allow VPC traffic to access API Gateway via VPC endpoint
68+
vpcEndpointSg.addIngressRule(
69+
Peer.ipv4(vpc.getVpcCidrBlock()),
70+
Port.tcp(443),
71+
"Allow VPC traffic to API Gateway VPC endpoint"
72+
);
7673

7774
// IAM Role for Lambda
7875
Role lambdaRole = Role.Builder.create(this, "LambdaBedrockRole")
@@ -90,12 +87,12 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
9087
.effect(Effect.ALLOW)
9188
.actions(List.of(
9289
"logs:CreateLogGroup",
93-
"logs:CreateLogStream",
90+
"logs:CreateLogStream",
9491
"logs:PutLogEvents"
9592
))
9693
.resources(List.of(
97-
String.format("arn:aws:logs:%s:%s:log-group:/aws/lambda/unicornstore-thread-dump-lambda*",
98-
Stack.of(this).getRegion(),
94+
String.format("arn:aws:logs:%s:%s:log-group:/aws/lambda/unicornstore-thread-dump-lambda*",
95+
Stack.of(this).getRegion(),
9996
Stack.of(this).getAccount())
10097
))
10198
.build());
@@ -108,7 +105,9 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
108105
"bedrock:InvokeModelWithResponseStream"
109106
))
110107
.resources(List.of(
111-
"arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0"
108+
"arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0",
109+
"arn:aws:bedrock:*:*:foundation-model/anthropic.claude-3-7-sonnet-20250219-v1:0",
110+
"arn:aws:bedrock:*:*:foundation-model/openai.gpt-oss-120b-1:0"
112111
))
113112
.build());
114113

@@ -204,44 +203,50 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
204203
.removalPolicy(RemovalPolicy.DESTROY)
205204
.build();
206205

207-
FunctionUrl.Builder.create(this, "ThreadDumpFunctionUrl")
208-
.function(threadDumpFunction)
209-
.authType(FunctionUrlAuthType.NONE)
206+
// Create VPC endpoint for API Gateway
207+
InterfaceVpcEndpoint apiGatewayVpcEndpoint = InterfaceVpcEndpoint.Builder.create(this, "ApiGatewayVpcEndpoint")
208+
.vpc(vpc)
209+
.service(InterfaceVpcEndpointAwsService.APIGATEWAY)
210+
.subnets(SubnetSelection.builder()
211+
.subnetType(SubnetType.PRIVATE_WITH_EGRESS)
212+
.build())
213+
.privateDnsEnabled(true)
214+
.securityGroups(List.of(lambdaSg))
215+
.build();
216+
217+
// Create private API Gateway
218+
RestApi api = RestApi.Builder.create(this, "ThreadDumpApi")
219+
.restApiName("unicornstore-thread-dump-api")
220+
.endpointConfiguration(EndpointConfiguration.builder()
221+
.types(List.of(EndpointType.PRIVATE))
222+
.vpcEndpoints(List.of(apiGatewayVpcEndpoint))
223+
.build())
224+
.policy(PolicyDocument.Builder.create()
225+
.statements(List.of(
226+
PolicyStatement.Builder.create()
227+
.effect(Effect.ALLOW)
228+
.principals(List.of(new AnyPrincipal()))
229+
.actions(List.of("execute-api:Invoke"))
230+
.resources(List.of("*"))
231+
.conditions(Map.of(
232+
"StringEquals", Map.of(
233+
"aws:SourceVpce", apiGatewayVpcEndpoint.getVpcEndpointId()
234+
)
235+
))
236+
.build()
237+
))
238+
.build())
210239
.build();
211240

212-
threadDumpFunction.addPermission("AllowPublicInvocation",
213-
software.amazon.awscdk.services.lambda.Permission.builder()
214-
.principal(new AnyPrincipal())
215-
.action("lambda:InvokeFunctionUrl")
216-
.functionUrlAuthType(FunctionUrlAuthType.NONE)
217-
.build());
241+
// Add Lambda integration
242+
LambdaIntegration integration = new LambdaIntegration(threadDumpFunction);
243+
api.getRoot().addMethod("POST", integration);
244+
api.getRoot().addProxy();
218245

219246
if (eksCluster != null) {
220247
eksCluster.createAccessEntry(lambdaRole.getRoleArn(), eksCluster.getCluster().getName(), "lambda-eks-access-role");
221248
}
222249

223250
return threadDumpFunction;
224251
}
225-
226-
private void createMonitoringInfrastructure(IVpc vpc, CfnCluster eksCluster, Function alertHandlerLambda) {
227-
Topic alarmTopic = Topic.Builder.create(this, "AlarmTopic")
228-
.topicName("UnicornStoreAlarms")
229-
.displayName("Unicorn Store Alarms")
230-
.build();
231-
232-
TopicPolicy.Builder.create(this, "AlarmTopicPolicy")
233-
.topics(List.of(alarmTopic))
234-
.build()
235-
.getDocument()
236-
.addStatements(PolicyStatement.Builder.create()
237-
.effect(Effect.DENY)
238-
.actions(List.of("sns:Publish"))
239-
.principals(List.of(new AnyPrincipal()))
240-
.resources(List.of(alarmTopic.getTopicArn()))
241-
.conditions(Map.of("Bool", Map.of("aws:SecureTransport", "false")))
242-
.build());
243-
244-
alarmTopic.addSubscription(new LambdaSubscription(alertHandlerLambda));
245-
}
246-
247252
}

infrastructure/cfn/ide-gitea-stack.yaml

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ Resources:
447447
VSCodeIdeGiteaIdeLogGroupCD76FEFA:
448448
Type: AWS::Logs::LogGroup
449449
Properties:
450-
LogGroupName: ide-bootstrap-log-20250729-054247
450+
LogGroupName: ide-bootstrap-log-20250825-200750
451451
RetentionInDays: 7
452452
UpdateReplacePolicy: Retain
453453
DeletionPolicy: Retain
@@ -879,9 +879,9 @@ Resources:
879879
description: Bootstrap IDE
880880
parameters:
881881
BootstrapScript:
882-
default: ""
883-
description: (Optional) Custom bootstrap script to run.
884882
type: String
883+
description: (Optional) Custom bootstrap script to run.
884+
default: ""
885885
mainSteps:
886886
- inputs:
887887
runCommand:
@@ -1142,16 +1142,56 @@ Resources:
11421142
/opt/aws/bin/cfn-signal -e $exit_code '${waitConditionHandleUrl}'
11431143
11441144
exit $exit_code
1145-
- splashUrl: ""
1146-
instanceIamRoleArn:
1145+
- instanceIamRoleArn:
11471146
Fn::GetAtt:
11481147
- VSCodeIdeGiteaIdeRole90308F47
11491148
- Arn
1150-
readmeUrl: ""
1151-
waitConditionHandleUrl:
1152-
Ref: VSCodeIdeGiteaIdeBootstrapWaitConditionHandle78036ED5
1153-
extensions: ms-kubernetes-tools.vscode-kubernetes-tools,ms-azuretools.vscode-docker
1154-
domain: ""
1149+
splashUrl: ""
1150+
instanceIamRoleName:
1151+
Ref: VSCodeIdeGiteaIdeRole90308F47
1152+
codeServerVersion: 4.103.1
1153+
customBootstrapScript: |
1154+
date
1155+
1156+
echo '=== Clone Git repository ==='
1157+
sudo -H -u ec2-user bash -c "git clone https://github.com/aws-samples/java-on-aws ~/java-on-aws/"
1158+
# sudo -H -u ec2-user bash -c "cd ~/java-on-aws && git checkout refactoring"
1159+
1160+
echo '=== Setup IDE ==='
1161+
sudo -H -i -u ec2-user bash -c "~/java-on-aws/infrastructure/scripts/setup/ide.sh"
1162+
sudo -H -i -u ec2-user bash -c "~/java-on-aws/infrastructure/scripts/setup/idp.sh"
1163+
passwordName:
1164+
Fn::Join:
1165+
- "-"
1166+
- - Fn::Select:
1167+
- 0
1168+
- Fn::Split:
1169+
- "-"
1170+
- Fn::Select:
1171+
- 6
1172+
- Fn::Split:
1173+
- ":"
1174+
- Ref: VSCodeIdeGiteaIdePasswordSecretD25F73F4
1175+
- Fn::Select:
1176+
- 1
1177+
- Fn::Split:
1178+
- "-"
1179+
- Fn::Select:
1180+
- 6
1181+
- Fn::Split:
1182+
- ":"
1183+
- Ref: VSCodeIdeGiteaIdePasswordSecretD25F73F4
1184+
- Fn::Select:
1185+
- 2
1186+
- Fn::Split:
1187+
- "-"
1188+
- Fn::Select:
1189+
- 6
1190+
- Fn::Split:
1191+
- ":"
1192+
- Ref: VSCodeIdeGiteaIdePasswordSecretD25F73F4
1193+
environmentContentsZip: ""
1194+
terminalOnStartup: "true"
11551195
installGitea: |
11561196
dnf install -y nerdctl cni-plugins
11571197
mkdir -p /gitea/config /gitea/data
@@ -1344,51 +1384,11 @@ Resources:
13441384
EOF
13451385
13461386
source /etc/profile.d/gitea.sh
1347-
terminalOnStartup: "true"
1348-
environmentContentsZip: ""
1349-
passwordName:
1350-
Fn::Join:
1351-
- "-"
1352-
- - Fn::Select:
1353-
- 0
1354-
- Fn::Split:
1355-
- "-"
1356-
- Fn::Select:
1357-
- 6
1358-
- Fn::Split:
1359-
- ":"
1360-
- Ref: VSCodeIdeGiteaIdePasswordSecretD25F73F4
1361-
- Fn::Select:
1362-
- 1
1363-
- Fn::Split:
1364-
- "-"
1365-
- Fn::Select:
1366-
- 6
1367-
- Fn::Split:
1368-
- ":"
1369-
- Ref: VSCodeIdeGiteaIdePasswordSecretD25F73F4
1370-
- Fn::Select:
1371-
- 2
1372-
- Fn::Split:
1373-
- "-"
1374-
- Fn::Select:
1375-
- 6
1376-
- Fn::Split:
1377-
- ":"
1378-
- Ref: VSCodeIdeGiteaIdePasswordSecretD25F73F4
1379-
customBootstrapScript: |
1380-
date
1381-
1382-
echo '=== Clone Git repository ==='
1383-
sudo -H -u ec2-user bash -c "git clone https://github.com/aws-samples/java-on-aws ~/java-on-aws/"
1384-
# sudo -H -u ec2-user bash -c "cd ~/java-on-aws && git checkout refactoring"
1385-
1386-
echo '=== Setup IDE ==='
1387-
sudo -H -i -u ec2-user bash -c "~/java-on-aws/infrastructure/scripts/setup/ide.sh"
1388-
sudo -H -i -u ec2-user bash -c "~/java-on-aws/infrastructure/scripts/setup/idp.sh"
1389-
codeServerVersion: 4.101.2
1390-
instanceIamRoleName:
1391-
Ref: VSCodeIdeGiteaIdeRole90308F47
1387+
domain: ""
1388+
extensions: ms-kubernetes-tools.vscode-kubernetes-tools,ms-azuretools.vscode-docker
1389+
waitConditionHandleUrl:
1390+
Ref: VSCodeIdeGiteaIdeBootstrapWaitConditionHandle78036ED5
1391+
readmeUrl: ""
13921392
name: IdeBootstrapFunction
13931393
action: aws:runShellScript
13941394
DocumentFormat: YAML
@@ -1528,10 +1528,10 @@ Resources:
15281528
- Arn
15291529
InstanceId:
15301530
Ref: VSCodeIdeGiteaIdeEC2Instance51274E6D
1531-
LogGroupName:
1532-
Ref: VSCodeIdeGiteaIdeLogGroupCD76FEFA
15331531
SsmDocument:
15341532
Ref: VSCodeIdeGiteaIdeBootstrapDocument7FC8732A
1533+
LogGroupName:
1534+
Ref: VSCodeIdeGiteaIdeLogGroupCD76FEFA
15351535
UpdateReplacePolicy: Delete
15361536
DeletionPolicy: Delete
15371537
Outputs:

0 commit comments

Comments
 (0)