Skip to content

Commit

Permalink
VPC stack adds IGW route to public subnet, Database stack updates RDS…
Browse files Browse the repository at this point in the history
… inbound rules with private subnet CIDRs, updated DynamoDB actions in Text Generation Lambda, And fixed prefix for secret creation
  • Loading branch information
nikhilsinclair committed Jan 17, 2025
1 parent e933da3 commit 92e1244
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 167 deletions.
27 changes: 14 additions & 13 deletions cdk/lib/api-gateway-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,14 +536,14 @@ export class ApiGatewayStack extends cdk.Stack {
"secretsmanager:PutSecretValue",
],
resources: [
`arn:aws:secretsmanager:${this.region}:${this.account}:secret:QuantumAI/*`,
`arn:aws:secretsmanager:${this.region}:${this.account}:secret:*`,
],
})
);

coglambdaRole.addToPolicy(new iam.PolicyStatement({
actions: ['ssm:GetParameter'],
resources: [`arn:aws:ssm:${this.region}:${this.account}:parameter/*`]
actions: ['ssm:GetParameter'],
resources: [`arn:aws:ssm:${this.region}:${this.account}:parameter/*`]
}));

const AutoSignupLambda = new lambda.Function(this, "addUserOnSignUp", {
Expand Down Expand Up @@ -584,19 +584,19 @@ export class ApiGatewayStack extends cdk.Stack {
handler: "preSignup.handler",
timeout: Duration.seconds(300),
environment: {
ALLOWED_EMAIL_DOMAINS: '/QuantumAI/AllowedEmailDomains',
ALLOWED_EMAIL_DOMAINS: '/QuantumAI/AllowedEmailDomains',
},
vpc: vpcStack.vpc,
functionName: `${resourcePrefix}-preSignupLambda`,
memorySize: 128,
role: coglambdaRole,
});

this.userPool.addTrigger(
cognito.UserPoolOperation.PRE_SIGN_UP,
preSignupLambda
cognito.UserPoolOperation.PRE_SIGN_UP,
preSignupLambda
);


this.userPool.addTrigger(
cognito.UserPoolOperation.POST_AUTHENTICATION,
Expand Down Expand Up @@ -699,7 +699,7 @@ export class ApiGatewayStack extends cdk.Stack {
description: "Parameter containing the DynamoDB table name",
stringValue: "DynamoDB-Conversation-Table",
});


/**
*
Expand Down Expand Up @@ -743,11 +743,11 @@ export class ApiGatewayStack extends cdk.Stack {
actions: ["bedrock:InvokeModel", "bedrock:InvokeEndpoint"],
resources: [
"arn:aws:bedrock:" +
this.region +
"::foundation-model/meta.llama3-70b-instruct-v1:0",
this.region +
"::foundation-model/meta.llama3-70b-instruct-v1:0",
"arn:aws:bedrock:" +
this.region +
"::foundation-model/amazon.titan-embed-text-v2:0",
this.region +
"::foundation-model/amazon.titan-embed-text-v2:0",
],
});

Expand Down Expand Up @@ -778,6 +778,7 @@ export class ApiGatewayStack extends cdk.Stack {
"dynamodb:DescribeTable",
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
],
resources: [`arn:aws:dynamodb:${this.region}:${this.account}:table/*`],
})
Expand Down
65 changes: 38 additions & 27 deletions cdk/lib/database-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export class DatabaseStack extends Stack {
public readonly secretPathAdminName: string;
public readonly secretPathUser: secretsmanager.Secret;
public readonly secretPathTableCreator: secretsmanager.Secret;
public readonly rdsProxyEndpoint: string;
public readonly rdsProxyEndpoint: string;
public readonly rdsProxyEndpointTableCreator: string;
public readonly rdsProxyEndpointAdmin: string;
constructor(scope: Construct, id: string, vpcStack: VpcStack, props?: StackProps){
public readonly rdsProxyEndpointAdmin: string;
constructor(scope: Construct, id: string, vpcStack: VpcStack, props?: StackProps) {
super(scope, id, props);

const resourcePrefix = this.node.tryGetContext('prefix');
Expand All @@ -34,8 +34,8 @@ export class DatabaseStack extends Stack {
* Create Empty Secret Manager
* Secrets will be populate at initalization of data
*/
this.secretPathAdminName = "QuantumAI/credentials/rdsDbCredential"; // Name in the Secret Manager to store DB credentials
const secretPathUserName = "QuantumAI/userCredentials/rdsDbCredential";
this.secretPathAdminName = `${id}-QuantumAI/credentials/rdsDbCredential`; // Name in the Secret Manager to store DB credentials
const secretPathUserName = `${id}-QuantumAI/userCredentials/rdsDbCredential`;
this.secretPathUser = new secretsmanager.Secret(this, secretPathUserName, {
secretName: secretPathUserName,
description: "Secrets for clients to connect to RDS",
Expand All @@ -47,7 +47,7 @@ export class DatabaseStack extends Stack {
})

const secretPathTableCreator = "QuantumAI/userCredentials/TableCreator";
this.secretPathTableCreator= new secretsmanager.Secret(this, secretPathTableCreator, {
this.secretPathTableCreator = new secretsmanager.Secret(this, secretPathTableCreator, {
secretName: secretPathTableCreator,
description: "Secrets for TableCreator to connect to RDS",
removalPolicy: RemovalPolicy.DESTROY,
Expand All @@ -58,13 +58,13 @@ export class DatabaseStack extends Stack {
})
const parameterGroup = new rds.ParameterGroup(this, "rdsParameterGroup2", {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_16_3,
version: rds.PostgresEngineVersion.VER_16_3,
}),
description: "Empty parameter group", // Might need to change this later
parameters: {
'rds.force_ssl': '0'
'rds.force_ssl': '0'
}
});
});

/**
*
Expand Down Expand Up @@ -101,15 +101,26 @@ export class DatabaseStack extends Stack {
parameterGroup: parameterGroup
});

// Add CIDR ranges of private subnets to inbound rules of RDS
const dbSecurityGroup = this.dbInstance.connections.securityGroups[0];
vpcStack.privateSubnetsCidrStrings.forEach((cidr) => {
dbSecurityGroup.addIngressRule(
ec2.Peer.ipv4(cidr),
ec2.Port.tcp(5432),
`Allow PostgreSQL traffic from private subnet CIDR range ${cidr}`
);
});

// Add CIDR ranges of public subnets to inbound rules of RDS
this.dbInstance.connections.securityGroups.forEach(function (securityGroup) {
// 10.0.0.0/16 match the cidr range in vpc stack
// Allow Postgres access in VPC
securityGroup.addIngressRule(
ec2.Peer.ipv4(vpcStack.vpcCidrString),
ec2.Port.tcp(5432),
"Postgres Ingress"
ec2.Peer.ipv4(vpcStack.vpcCidrString),
ec2.Port.tcp(5432),
"Allow PostgreSQL traffic from public subnets"
);
});


const rdsProxyRole = new iam.Role(this, "RDSProxyRole", {
assumedBy: new iam.ServicePrincipal('rds.amazonaws.com'),
Expand All @@ -119,23 +130,23 @@ export class DatabaseStack extends Stack {
rdsProxyRole.addToPolicy(new iam.PolicyStatement({
resources: ['*'],
actions: [
'rds-db:connect',
'rds-db:connect',
],
}));
}));

// /**
// /**
// *
// * Create an RDS proxy that sit between lambda and RDS
// */
const rdsProxy = this.dbInstance.addProxy(id+'-proxy', {
const rdsProxy = this.dbInstance.addProxy(id + '-proxy', {
secrets: [this.secretPathUser!],
vpc: vpcStack.vpc,
role: rdsProxyRole,
securityGroups: this.dbInstance.connections.securityGroups,
requireTLS: false,
dbProxyName: `${resourcePrefix}-${id}-proxy`
});
const rdsProxyTableCreator = this.dbInstance.addProxy(id+'+proxy', {
const rdsProxyTableCreator = this.dbInstance.addProxy(id + '+proxy', {
secrets: [this.secretPathTableCreator!],
vpc: vpcStack.vpc,
role: rdsProxyRole,
Expand All @@ -144,7 +155,7 @@ export class DatabaseStack extends Stack {
});

const secretPathAdmin = secretmanager.Secret.fromSecretNameV2(this, 'AdminSecret', this.secretPathAdminName);

const rdsProxyAdmin = this.dbInstance.addProxy(id + '-proxy-admin', {
secrets: [secretPathAdmin],
vpc: vpcStack.vpc,
Expand All @@ -154,20 +165,20 @@ export class DatabaseStack extends Stack {
});

// Workaround for bug where TargetGroupName is not set but required
let targetGroup = rdsProxy.node.children.find((child:any) => {
let targetGroup = rdsProxy.node.children.find((child: any) => {
return child instanceof rds.CfnDBProxyTargetGroup
}) as rds.CfnDBProxyTargetGroup

targetGroup.addPropertyOverride('TargetGroupName', 'default');
targetGroup.addPropertyOverride('TargetGroupName', 'default');

let targetGroupTableCreator = rdsProxyTableCreator.node.children.find((child:any) => {
let targetGroupTableCreator = rdsProxyTableCreator.node.children.find((child: any) => {
return child instanceof rds.CfnDBProxyTargetGroup
}) as rds.CfnDBProxyTargetGroup

targetGroup.addPropertyOverride('TargetGroupName', 'default');
targetGroupTableCreator.addPropertyOverride('TargetGroupName', 'default');
this.dbInstance.grantConnect(rdsProxyRole);
targetGroup.addPropertyOverride('TargetGroupName', 'default');
targetGroupTableCreator.addPropertyOverride('TargetGroupName', 'default');

this.dbInstance.grantConnect(rdsProxyRole);
this.rdsProxyEndpoint = rdsProxy.endpoint;
this.rdsProxyEndpointTableCreator = rdsProxyTableCreator.endpoint;
this.rdsProxyEndpointAdmin = rdsProxyAdmin.endpoint;
Expand Down
148 changes: 74 additions & 74 deletions cdk/lib/dbFlow-stack.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Stack, StackProps, triggers } from 'aws-cdk-lib';
import { Stack, StackProps, triggers } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Duration } from 'aws-cdk-lib';

Expand All @@ -8,83 +8,83 @@ import * as iam from 'aws-cdk-lib/aws-iam';

// Stack import
import { VpcStack } from './vpc-stack';
import {DatabaseStack} from './database-stack';
import { DatabaseStack } from './database-stack';
import { ApiGatewayStack } from './api-gateway-stack';

export class DBFlowStack extends Stack {
constructor(scope: Construct, id: string, vpcStack: VpcStack, db: DatabaseStack, apiStack: ApiGatewayStack, props?: StackProps){
super(scope, id, props);
constructor(scope: Construct, id: string, vpcStack: VpcStack, db: DatabaseStack, apiStack: ApiGatewayStack, props?: StackProps) {
super(scope, id, props);

const resourcePrefix = this.node.tryGetContext('prefix');
const resourcePrefix = this.node.tryGetContext('prefix');

/**
*
* Create an database initializer using lambda
*/
/**
*
* Create an database initializer using lambda
*/

const psycopgLambdaLayer = apiStack.getLayers()['psycopg2'];
const lambdaRole = new iam.Role(this, "lambda-vpc-role", {
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
description: "Role for all Lambda function inside VPC",
});
lambdaRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
// Secrets Manager
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue"
],
resources: [
`arn:aws:secretsmanager:${this.region}:${this.account}:secret:QuantumAI/*`,
],
})
);
lambdaRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
// CloudWatch Logs
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources: ["arn:aws:logs:*:*:*"],
})
);
lambdaRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeNetworkInterfaces",
],
resources: ["*"],
})
);
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess")
);
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonS3FullAccess")
);
// Create an initilizer for the RDS instance, only invoke during deployment
const initializerLambda = new triggers.TriggerFunction(this, "quantumAI-triggerLambda", {
functionName: `${resourcePrefix}-initializerFunction`,
runtime: lambda.Runtime.PYTHON_3_9,
handler: "initializer.handler",
timeout: Duration.seconds(300),
memorySize: 512,
environment: {
DB_SECRET_NAME: db.secretPathAdminName, // Admin Secret Manager name that only use once here.
DB_USER_SECRET_NAME: db.secretPathUser.secretName,
DB_PROXY: db.secretPathTableCreator.secretName,
},
vpc: db.dbInstance.vpc,
code: lambda.Code.fromAsset("lambda/initializer"),
layers: [psycopgLambdaLayer],
role: lambdaRole,
});
}
const psycopgLambdaLayer = apiStack.getLayers()['psycopg2'];
const lambdaRole = new iam.Role(this, "lambda-vpc-role", {
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
description: "Role for all Lambda function inside VPC",
});
lambdaRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
// Secrets Manager
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue"
],
resources: [
`arn:aws:secretsmanager:${this.region}:${this.account}:secret:*`,
],
})
);
lambdaRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
// CloudWatch Logs
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources: ["arn:aws:logs:*:*:*"],
})
);
lambdaRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeNetworkInterfaces",
],
resources: ["*"],
})
);
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess")
);
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonS3FullAccess")
);
// Create an initilizer for the RDS instance, only invoke during deployment
const initializerLambda = new triggers.TriggerFunction(this, "quantumAI-triggerLambda", {
functionName: `${resourcePrefix}-initializerFunction`,
runtime: lambda.Runtime.PYTHON_3_9,
handler: "initializer.handler",
timeout: Duration.seconds(300),
memorySize: 512,
environment: {
DB_SECRET_NAME: db.secretPathAdminName, // Admin Secret Manager name that only use once here.
DB_USER_SECRET_NAME: db.secretPathUser.secretName,
DB_PROXY: db.secretPathTableCreator.secretName,
},
vpc: db.dbInstance.vpc,
code: lambda.Code.fromAsset("lambda/initializer"),
layers: [psycopgLambdaLayer],
role: lambdaRole,
});
}
}
Loading

0 comments on commit 92e1244

Please sign in to comment.