mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-09-30 03:23:15 +00:00
Add Bedrock invocation logging destinations (#12)
Audit finding H-20: no audit trail of model I/O for seahaven-alex, which returns payments, invoices, WO/PO, and HR/SA8000 data. S3 bucket (Glacier at 90d, expire 365d) + CloudWatch log group (90d) + delivery role assumable only by bedrock.amazonaws.com scoped by SourceAccount/SourceArn. The account-level logging configuration has no CloudFormation resource type, so it is applied via CLI post-deploy (documented in the construct header) - same pattern as the Config recorder (INFRA-17). Cross-reviewed: no BLOCKs. Verified live: converse invocation logged to /aws/bedrock/model-invocations.
This commit is contained in:
parent
14593440cf
commit
2289dcb0c9
2 changed files with 115 additions and 0 deletions
|
|
@ -7,6 +7,7 @@ import * as cloudtrail from "aws-cdk-lib/aws-cloudtrail";
|
|||
import { Construct } from "constructs";
|
||||
import { DetectiveControls } from "./detective-controls";
|
||||
import { GovernanceToggles } from "./governance-toggles";
|
||||
import { BedrockLogging } from "./bedrock-logging";
|
||||
import { CisMonitoring } from "./cis-monitoring";
|
||||
import { FlowLogs } from "./flow-logs";
|
||||
import { SesMonitoring } from "./ses-monitoring";
|
||||
|
|
@ -158,6 +159,12 @@ export class AccountBaselineStack extends cdk.Stack {
|
|||
// Shared CloudFront WAF WebACL (M-17); ARN published to SSM for app stacks.
|
||||
new AppWebAcl(this, "AppWebAcl");
|
||||
|
||||
// ── Day 5 AI governance ──
|
||||
// Bedrock model invocation logging destinations + delivery role (H-20).
|
||||
// The account-level logging configuration itself has no CFN resource type;
|
||||
// applied via CLI post-deploy (see lib/bedrock-logging.ts header).
|
||||
new BedrockLogging(this, "BedrockLogging");
|
||||
|
||||
cdk.Tags.of(this).add("Project", "account-baseline");
|
||||
cdk.Tags.of(this).add("Owner", "adam@seahavenind.com");
|
||||
cdk.Tags.of(this).add("Environment", "prod");
|
||||
|
|
|
|||
108
lib/bedrock-logging.ts
Normal file
108
lib/bedrock-logging.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import * as cdk from "aws-cdk-lib";
|
||||
import * as s3 from "aws-cdk-lib/aws-s3";
|
||||
import * as iam from "aws-cdk-lib/aws-iam";
|
||||
import * as logs from "aws-cdk-lib/aws-logs";
|
||||
import { Construct } from "constructs";
|
||||
|
||||
/**
|
||||
* Destinations + delivery role for Bedrock model invocation logging (audit
|
||||
* H-20). `seahaven-alex` is employee-facing and returns payments, invoices,
|
||||
* WO/PO, and HR/SA8000 data — model I/O needs an audit trail.
|
||||
*
|
||||
* CloudFormation has no resource type for the logging configuration itself
|
||||
* (account-level `PutModelInvocationLoggingConfiguration`), so — like the
|
||||
* Config recorder (INFRA-17) — the toggle is applied via CLI after deploy:
|
||||
*
|
||||
* aws bedrock put-model-invocation-logging-configuration --logging-config '{
|
||||
* "cloudWatchConfig": {
|
||||
* "logGroupName": "<BedrockInvocationLogGroup>",
|
||||
* "roleArn": "<BedrockLoggingRole ARN>",
|
||||
* "largeDataDeliveryS3Config": {"bucketName": "<bucket>", "keyPrefix": "large-payloads"}
|
||||
* },
|
||||
* "s3Config": {"bucketName": "<bucket>", "keyPrefix": "invocation-logs"},
|
||||
* "textDataDeliveryEnabled": true,
|
||||
* "imageDataDeliveryEnabled": true,
|
||||
* "embeddingDataDeliveryEnabled": false
|
||||
* }'
|
||||
*/
|
||||
export class BedrockLogging extends Construct {
|
||||
public readonly bucket: s3.Bucket;
|
||||
public readonly logGroup: logs.LogGroup;
|
||||
public readonly deliveryRole: iam.Role;
|
||||
|
||||
constructor(scope: Construct, id: string) {
|
||||
super(scope, id);
|
||||
|
||||
const stack = cdk.Stack.of(this);
|
||||
|
||||
this.bucket = new s3.Bucket(this, "InvocationLogsBucket", {
|
||||
bucketName: `seahaven-bedrock-invocation-logs-${stack.account}`,
|
||||
encryption: s3.BucketEncryption.S3_MANAGED,
|
||||
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
|
||||
enforceSSL: true,
|
||||
versioned: false,
|
||||
lifecycleRules: [
|
||||
{
|
||||
id: "transition-and-expire",
|
||||
transitions: [
|
||||
{
|
||||
storageClass: s3.StorageClass.GLACIER,
|
||||
transitionAfter: cdk.Duration.days(90),
|
||||
},
|
||||
],
|
||||
expiration: cdk.Duration.days(365),
|
||||
abortIncompleteMultipartUploadAfter: cdk.Duration.days(7),
|
||||
},
|
||||
],
|
||||
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
||||
});
|
||||
|
||||
// Bedrock writes invocation logs to S3 directly via bucket policy — no role.
|
||||
this.bucket.addToResourcePolicy(
|
||||
new iam.PolicyStatement({
|
||||
sid: "AmazonBedrockLogsWrite",
|
||||
effect: iam.Effect.ALLOW,
|
||||
principals: [new iam.ServicePrincipal("bedrock.amazonaws.com")],
|
||||
actions: ["s3:PutObject"],
|
||||
resources: [this.bucket.arnForObjects("*")],
|
||||
conditions: {
|
||||
StringEquals: { "aws:SourceAccount": stack.account },
|
||||
ArnLike: {
|
||||
"aws:SourceArn": `arn:aws:bedrock:${stack.region}:${stack.account}:*`,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
this.logGroup = new logs.LogGroup(this, "InvocationLogGroup", {
|
||||
logGroupName: "/aws/bedrock/model-invocations",
|
||||
retention: logs.RetentionDays.THREE_MONTHS,
|
||||
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
||||
});
|
||||
|
||||
// CloudWatch delivery requires a role Bedrock can assume, scoped to this
|
||||
// account/source and to the one log group.
|
||||
this.deliveryRole = new iam.Role(this, "DeliveryRole", {
|
||||
roleName: "seahaven-bedrock-invocation-logging",
|
||||
assumedBy: new iam.ServicePrincipal("bedrock.amazonaws.com", {
|
||||
conditions: {
|
||||
StringEquals: { "aws:SourceAccount": stack.account },
|
||||
ArnLike: {
|
||||
"aws:SourceArn": `arn:aws:bedrock:${stack.region}:${stack.account}:*`,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.deliveryRole.addToPolicy(
|
||||
new iam.PolicyStatement({
|
||||
actions: ["logs:CreateLogStream", "logs:PutLogEvents"],
|
||||
resources: [this.logGroup.logGroupArn, `${this.logGroup.logGroupArn}:log-stream:*`],
|
||||
}),
|
||||
);
|
||||
|
||||
new cdk.CfnOutput(this, "BedrockLogBucketName", { value: this.bucket.bucketName });
|
||||
new cdk.CfnOutput(this, "BedrockLogGroupName", { value: this.logGroup.logGroupName });
|
||||
new cdk.CfnOutput(this, "BedrockLoggingRoleArn", { value: this.deliveryRole.roleArn });
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue