Skip to content

Commit

Permalink
Merge pull request #117 from awslabs/feature/cdk_nag_fixes
Browse files Browse the repository at this point in the history
feat(appsec): Feature/cdk nag fixes
  • Loading branch information
dineshSajwan authored Nov 27, 2023
2 parents 3158d51 + 3e189cd commit 8534135
Show file tree
Hide file tree
Showing 23 changed files with 510 additions and 142 deletions.
4 changes: 4 additions & 0 deletions .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .projen/tasks.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
// description: undefined, /* The description is just a string that helps people understand the purpose of the package. */
keywords: ['constructs', 'aws-cdk', 'generative-ai', 'emerging-tech'],
devDeps: ['eslint-plugin-header'],
deps: ['cdk-nag'],

// Keep synchronized with https://github.com/nodejs/release#release-schedule
minNodeVersion: '18.12.0', // 'MAINTENANCE' (first LTS)
Expand Down
1 change: 0 additions & 1 deletion docs/emerging_tech_cdk_constructs.drawio

This file was deleted.

1 change: 1 addition & 0 deletions docs/generative_ai_cdk_constructs.drawio

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion resources/gen-ai/aws-qa-appsync-opensearch/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type QADocs @aws_iam @aws_cognito_user_pools {

input QADocsInput {
jobid: ID
question: String
question: String @constraint(pattern: "^[0-9a-zA-Z]*$?-_,", maxLength: 300)
streaming: Boolean
max_docs: Int
filename: String
Expand Down
1 change: 1 addition & 0 deletions src/common/helpers/redis-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export function buildRedisCluster(scope: Construct, props: RedisProps): elastica
numCacheNodes: numCacheNodes,
cacheSubnetGroupName: getRedisSubnetGroup(scope, props).ref,
vpcSecurityGroupIds: [props.redisSecurityGroup!.securityGroupId],
port: 8787,
});
return redisCulster;
}
Expand Down
2 changes: 2 additions & 0 deletions src/patterns/gen-ai/aws-langchain-common-layer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ If choosing to interface with a third-party LLM provider (outside of Amazon Bedr

The primary piece of data sent to the third-party LLM provider is the prompt to perform inference on. Depending on the use case, the prompt can contain the user’s input, previous interactions (for example, chat history), and document excerpts sourced from the configured knowledge base (for example, Amazon OpenSearch search result).

AWS CloudTrail provides a number of security features to consider as you develop and implement your own security policies. Please follow the related best practices through the [official documentation](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/best-practices-security.html).

> **Warning**
> This construct allows you to interact with models from third party providers. Your use of the third-party generative AI (GAI) models is governed by the terms provided to you by the third-party GAI model providers when you acquired your license to use them (for example, their terms of service, license agreement, acceptable use policy, and privacy policy).
>
Expand Down
4 changes: 4 additions & 0 deletions src/patterns/gen-ai/aws-model-deployment-sagemaker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ When you build systems on AWS infrastructure, security responsibilities are shar

You can visit the [official documentation](https://docs.aws.amazon.com/sagemaker/latest/dg/best-practice-endpoint-security.html) for security best practices related to Amazon SageMaker endpoints.

If you grant access to a user to your account where this construct is deployed, this user may access information stored by the construct (Amazon CloudWatch logs). To help secure your AWS resources, please follow the best practices for [AWS Identity and Access Management (IAM)](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html).

AWS CloudTrail provides a number of security features to consider as you develop and implement your own security policies. Please follow the related best practices through the [official documentation](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/best-practices-security.html).

> **Warning**
> This construct allows you to interact with models from third party providers. Your use of the third-party generative AI (GAI) models is governed by the terms provided to you by the third-party GAI model providers when you acquired your license to use them (for example, their terms of service, license agreement, acceptable use policy, and privacy policy).
>
Expand Down
17 changes: 16 additions & 1 deletion src/patterns/gen-ai/aws-qa-appsync-opensearch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Where:
- jobstatus: this field will be used by the subscription to update the status of the question answering process for the file specified
- max_docs: maximum number of documents (chunks) retrieved from the knowledge base if the Retrieveal Augmented Generation (RAG) approach is used
- question: question to ask as a base64 encoded string
- verbose: boolean indicating if the Langchain chain call verbosity should be enabled or not
- verbose: boolean indicating if the [Langchain chain call verbosity](https://python.langchain.com/docs/guides/debugging#chain-verbosetrue) should be enabled or not
- streaming: boolean indicating if the streaming capability of Bedrock is used. If set to true, tokens will be send back to the subscriber as they are generated. If set to false, the entire response will be sent back to the subscriber once generated.
- filename: optional. Name of the file stored in the input S3 bucket, in txt format.

Expand Down Expand Up @@ -274,6 +274,21 @@ This construct requires you to provide an existing Amazon Cognito User Pool and
- [Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/security.html)
- [Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/security.html)

Optionnaly, you can provide existing resources to the constructs (marked optional in the construct pattern props). If you chose to do so, please refer to the official documentation on best practices to secure each service:
- [Amazon Simple Storage Service](https://docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html)
- [Amazon VPC](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-best-practices.html)
- [Amazon EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-security.html)
- [AWS AppSync](https://docs.aws.amazon.com/appsync/latest/devguide/best-practices.html)

If you grant access to a user to your account where this construct is deployed, this user may access information stored by the construct (Amazon Simple Storage Service bucket, Amazon OpenSearch cluster, Amazon CloudWatch logs). To help secure your AWS resources, please follow the best practices for [AWS Identity and Access Management (IAM)](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html).

AWS CloudTrail provides a number of security features to consider as you develop and implement your own security policies. Please follow the related best practices through the [official documentation](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/best-practices-security.html).

> **Note**
> This construct requires you to provide documents in the input assets bucket. You should validate each file in the bucket before using this construct. See [here](https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html) for file input validation best practices.
> Ensure you only ingest the appropriate documents into your knowledge base. Any results returned by the knowledge base is eligible for inclusion into the prompt; and therefore, being sent to the LLM. If using a third-party LLM, ensure you audit the documents contained within your knowledge base.
> This construct provides several configurable options for logging. Please consider security best practices when enabling or disabling logging and related features. Verbose logging, for instance, may log content of API calls. You can disable this functionality by ensuring observability flag is set to false.
## Supported AWS Regions

This solution optionally uses the Amazon Bedrock and Amazon OpenSearch Service, which is not currently available in all AWS Regions. You must launch this construct in an AWS Region where these services are available. For the most current availability of AWS services by Region, see the [AWS Regional Services List](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/).
Expand Down
Binary file modified src/patterns/gen-ai/aws-qa-appsync-opensearch/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 98 additions & 29 deletions src/patterns/gen-ai/aws-qa-appsync-opensearch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import * as logs from 'aws-cdk-lib/aws-logs';
import * as opensearchservice from 'aws-cdk-lib/aws-opensearchservice';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as secret from 'aws-cdk-lib/aws-secretsmanager';
import { NagSuppressions } from 'cdk-nag';
import { Construct } from 'constructs';
import * as s3_bucket_helper from '../../../common/helpers/s3-bucket-helper';
import * as vpc_helper from '../../../common/helpers/vpc-helper';


/**
* The properties for the QaAppsyncOpensearchProps class.
*/
Expand Down Expand Up @@ -183,14 +185,14 @@ export class QaAppsyncOpensearch extends Construct {
let enable_xray = true;
let api_log_config = {
fieldLogLevel: appsync.FieldLogLevel.ALL,
retention: logs.RetentionDays.ONE_YEAR,
retention: logs.RetentionDays.TEN_YEARS,
};
if (props.observability == false) {
enable_xray = false;
lambda_tracing = lambda.Tracing.DISABLED;
api_log_config = {
fieldLogLevel: appsync.FieldLogLevel.NONE,
retention: logs.RetentionDays.ONE_YEAR,
retention: logs.RetentionDays.TEN_YEARS,
};
};

Expand Down Expand Up @@ -221,6 +223,32 @@ export class QaAppsyncOpensearch extends Construct {
);
}

// vpc flowloggroup
const logGroup = new logs.LogGroup(this, 'qaConstructLogGroup');
const role = new iam.Role(this, 'qaConstructRole', {
assumedBy: new iam.ServicePrincipal('vpc-flow-logs.amazonaws.com'),
});

// vpc flowlogs
new ec2.FlowLog(this, 'FlowLog', {
resourceType: ec2.FlowLogResourceType.fromVpc(this.vpc),
destination: ec2.FlowLogDestination.toCloudWatchLogs(logGroup, role),
});

// bucket for storing server access logging
const serverAccessLogBucket = new s3.Bucket(this,
'serverAccessLogBucket'+stage,
{
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
bucketName: 'qa-server-access-logs',
enforceSSL: true,
versioned: true,
lifecycleRules: [{
expiration: Duration.days(90),
}],
});

// Bucket containing the inputs assets (documents - text format) uploaded by the user
let inputAssetsBucket: s3.IBucket;

Expand All @@ -232,6 +260,12 @@ export class QaAppsyncOpensearch extends Construct {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
bucketName: 'input-asset-qa-bucket'+stage+'-'+Aws.ACCOUNT_ID,
serverAccessLogsBucket: serverAccessLogBucket,
enforceSSL: true,
versioned: true,
lifecycleRules: [{
expiration: Duration.days(90),
}],
});
} else {
tmpBucket = new s3.Bucket(this, 'InputAssetsQABucket'+stage, props.bucketInputsAssetsProps);
Expand Down Expand Up @@ -324,34 +358,28 @@ export class QaAppsyncOpensearch extends Construct {
if (props.openSearchSecret) {SecretId = props.openSearchSecret.secretName;}

// Lambda function used to validate inputs in the step function
const question_answering_function = new lambda.DockerImageFunction(
this,
'lambda_question_answering'+stage,
{
code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../../../lambda/aws-qa-appsync-opensearch/question_answering/src')),
functionName: 'lambda_question_answering'+stage,
description: 'Lambda function for question answering',
vpc: this.vpc,
tracing: lambda_tracing,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
securityGroups: [this.securityGroup],
memorySize: 1_769 * 4,
timeout: Duration.minutes(15),
environment: {
GRAPHQL_URL: updateGraphQlApiEndpoint,
INPUT_BUCKET: this.s3InputAssetsBucketInterface.bucketName,
OPENSEARCH_DOMAIN_ENDPOINT: props.existingOpensearchDomain.domainEndpoint,
OPENSEARCH_INDEX: props.openSearchIndexName,
OPENSEARCH_SECRET_ID: SecretId,
},

const question_answering_function_role = new iam.Role(this, 'question_answering_function_role', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
inlinePolicies: {
LambdaFunctionServiceRolePolicy: new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
],
resources: [`arn:${Aws.PARTITION}:logs:${Aws.REGION}:${Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`],
})],
}),
},
);
});

// The lambda will access the opensearch credentials
if (props.openSearchSecret) {props.openSearchSecret.grantRead(question_answering_function);}
if (props.openSearchSecret) {props.openSearchSecret.grantRead(question_answering_function_role);}

// The lambda will pull processed files and create embeddings
question_answering_function.addToRolePolicy(
question_answering_function_role.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
Expand All @@ -367,7 +395,7 @@ export class QaAppsyncOpensearch extends Construct {
}),
);

question_answering_function.addToRolePolicy(new iam.PolicyStatement({
question_answering_function_role.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['es:*'],
resources: [
Expand All @@ -376,15 +404,56 @@ export class QaAppsyncOpensearch extends Construct {
],
}));


// Add Amazon Bedrock permissions to the IAM role for the Lambda function
question_answering_function.addToRolePolicy(new iam.PolicyStatement({
question_answering_function_role.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['bedrock:*'],
actions: [
'bedrock:InvokeModel',
'bedrock:InvokeModelWithResponseStream',
],
resources: [
'*',
'arn:aws:bedrock:'+Aws.REGION+'::foundation-model',
'arn:aws:bedrock:'+Aws.REGION+'::foundation-model/*',
],
}));

NagSuppressions.addResourceSuppressions(
question_answering_function_role,
[
{
id: 'AwsSolutions-IAM5',
reason: 'AWSLambdaBasicExecutionRole is used.',
},
],
true,
);

const question_answering_function = new lambda.DockerImageFunction(
this,
'lambda_question_answering'+stage,
{
code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../../../lambda/aws-qa-appsync-opensearch/question_answering/src')),
functionName: 'lambda_question_answering'+stage,
description: 'Lambda function for question answering',
vpc: this.vpc,
tracing: lambda_tracing,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
securityGroups: [this.securityGroup],
memorySize: 1_769 * 4,
timeout: Duration.minutes(15),
role: question_answering_function_role,
environment: {
GRAPHQL_URL: updateGraphQlApiEndpoint,
INPUT_BUCKET: this.s3InputAssetsBucketInterface.bucketName,
OPENSEARCH_DOMAIN_ENDPOINT: props.existingOpensearchDomain.domainEndpoint,
OPENSEARCH_INDEX: props.openSearchIndexName,
OPENSEARCH_SECRET_ID: SecretId,
},
},
);


const enableOperationalMetric = props.enableOperationalMetric || true;
const solution_id = 'genai_cdk_'+id;

Expand Down
Loading

0 comments on commit 8534135

Please sign in to comment.