feat(slack-bot): codify kms:Decrypt grants for CMK'd DynamoDB readers (INFRA-95) #51
3 changed files with 44 additions and 0 deletions
|
|
@ -5,6 +5,8 @@ import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|||
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
|
||||
import * as logs from 'aws-cdk-lib/aws-logs';
|
||||
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
|
||||
import * as kms from 'aws-cdk-lib/aws-kms';
|
||||
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
||||
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
||||
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
||||
import * as bedrock from 'aws-cdk-lib/aws-bedrock';
|
||||
|
|
@ -123,6 +125,19 @@ export class BedrockAgentConstruct extends Construct {
|
|||
sitesTable.grantReadData(this.woPoLambda);
|
||||
paymentsTable.grantReadData(this.woPoLambda);
|
||||
|
||||
// WorkOrders, WorkOrderComments, purchase-orders and PaymentsDashboard are
|
||||
// SSE-encrypted with the shared customer-managed CMK (INFRA-95 / M-3). These
|
||||
// tables are imported by name, so grantReadData does not add KMS perms — this
|
||||
// cross-stack reader needs kms:Decrypt explicitly or every read on those four
|
||||
// tables fails with AccessDenied. (verified-sites is NOT CMK-encrypted; it is
|
||||
// unaffected.) The CMK key policy delegates to IAM via kms:ViaService.
|
||||
const dynamodbCmk = kms.Key.fromKeyArn(
|
||||
this,
|
||||
'DynamoDbCmk',
|
||||
ssm.StringParameter.valueForStringParameter(this, '/seahaven/dynamodb/cmk-arn'),
|
||||
);
|
||||
dynamodbCmk.grantDecrypt(this.woPoLambda);
|
||||
|
||||
// ── Bedrock Agent execution role ──────────────────────────────────────────
|
||||
const agentRole = new iam.Role(this, 'AgentRole', {
|
||||
roleName: 'AmazonBedrockExecutionRoleForAgents_seahaven',
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
|
|||
import * as logs from 'aws-cdk-lib/aws-logs';
|
||||
import * as s3 from 'aws-cdk-lib/aws-s3';
|
||||
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
|
||||
import * as kms from 'aws-cdk-lib/aws-kms';
|
||||
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
||||
import * as events from 'aws-cdk-lib/aws-events';
|
||||
import * as targets from 'aws-cdk-lib/aws-events-targets';
|
||||
import * as iam from 'aws-cdk-lib/aws-iam';
|
||||
|
|
@ -49,6 +51,17 @@ export class PoSyncConstruct extends Construct {
|
|||
// Grant read access to the purchase-orders table
|
||||
poTable.grantReadData(this.syncLambda);
|
||||
|
||||
// purchase-orders is SSE-encrypted with the shared customer-managed CMK
|
||||
// (INFRA-95 / M-3). The table is imported by name, so grantReadData does not
|
||||
// add KMS perms — grant kms:Decrypt explicitly or scans fail with
|
||||
// AccessDenied. (CMK key policy delegates to IAM via kms:ViaService.)
|
||||
const dynamodbCmk = kms.Key.fromKeyArn(
|
||||
this,
|
||||
'DynamoDbCmk',
|
||||
ssm.StringParameter.valueForStringParameter(this, '/seahaven/dynamodb/cmk-arn'),
|
||||
);
|
||||
dynamodbCmk.grantDecrypt(this.syncLambda);
|
||||
|
||||
// Grant read/write to the KB docs bucket (read to list+delete old, write new)
|
||||
props.kbDocsBucket.grantReadWrite(this.syncLambda);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
|
|||
import * as logs from 'aws-cdk-lib/aws-logs';
|
||||
import * as s3 from 'aws-cdk-lib/aws-s3';
|
||||
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
|
||||
import * as kms from 'aws-cdk-lib/aws-kms';
|
||||
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
||||
import * as events from 'aws-cdk-lib/aws-events';
|
||||
import * as targets from 'aws-cdk-lib/aws-events-targets';
|
||||
import * as iam from 'aws-cdk-lib/aws-iam';
|
||||
|
|
@ -54,6 +56,20 @@ export class WorkorderSyncConstruct extends Construct {
|
|||
workOrdersTable.grantReadData(this.syncLambda);
|
||||
commentsTable.grantReadData(this.syncLambda);
|
||||
|
||||
// WorkOrders + WorkOrderComments are SSE-encrypted with the shared
|
||||
// customer-managed CMK (INFRA-95 / M-3). Because the tables are imported by
|
||||
// name (fromTableName), CDK does not know about their encryption key, so
|
||||
// grantReadData does NOT add the KMS permissions — they must be granted
|
||||
// explicitly or every read fails with kms:Decrypt AccessDenied. The CMK key
|
||||
// policy delegates to IAM via kms:ViaService, so this identity grant is what
|
||||
// authorizes this cross-stack reader.
|
||||
const dynamodbCmk = kms.Key.fromKeyArn(
|
||||
this,
|
||||
'DynamoDbCmk',
|
||||
ssm.StringParameter.valueForStringParameter(this, '/seahaven/dynamodb/cmk-arn'),
|
||||
);
|
||||
dynamodbCmk.grantDecrypt(this.syncLambda);
|
||||
|
||||
// Grant read/write to the KB docs bucket (read to list+delete old, write new)
|
||||
props.kbDocsBucket.grantReadWrite(this.syncLambda);
|
||||
|
||||
|
|
|
|||
Reference in a new issue