Stripped fork of seahaven-account-baseline for the isolated external-dev account (396287094661). Single stack: AWS Config, GuardDuty, Security Hub (FSBP + CIS v3.0), IAM Access Analyzer, VPC flow logs (VPC ids via context), and a $200/mo budget alerting adam@seahaven.com. Drops all org-level / prod-specific controls (local CloudTrail, CIS metric alarms, WAF, SES, Bedrock, DynamoDB CMK, Backup) per the isolated-account design; the org trail already covers this account centrally. Inspector2 is a documented post-deploy CLI step (no CloudFormation enable resource exists).
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
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 ec2 from "aws-cdk-lib/aws-ec2";
|
|
import { Construct } from "constructs";
|
|
|
|
/**
|
|
* VPC flow logs delivered to a hardened S3 bucket (ALL traffic), forensically
|
|
* queryable via Athena. Adapted from seahaven-account-baseline/lib/flow-logs.ts.
|
|
*
|
|
* Unlike the prod baseline, the VPC ids are NOT hardcoded — they are passed in
|
|
* via props (sourced from cdk context in bin/app.ts), because this account's
|
|
* VPCs change as the external dev team provisions their own infrastructure.
|
|
* Pass an empty list to create the hardened destination bucket without any
|
|
* flow logs attached yet (e.g. before the team's first VPC exists, or while the
|
|
* default VPC is pending deletion).
|
|
*/
|
|
export interface FlowLogsProps {
|
|
/** VPC ids to attach ALL-traffic flow logs to. May be empty. */
|
|
readonly vpcIds: string[];
|
|
}
|
|
|
|
export class FlowLogs extends Construct {
|
|
constructor(scope: Construct, id: string, props: FlowLogsProps) {
|
|
super(scope, id);
|
|
|
|
const stack = cdk.Stack.of(this);
|
|
|
|
const bucket = new s3.Bucket(this, "FlowLogsBucket", {
|
|
bucketName: `seahaven-extdev-vpc-flow-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,
|
|
});
|
|
|
|
// Log-delivery service permissions (scoped to this account) — the standard
|
|
// VPC-flow-logs-to-S3 bucket policy.
|
|
bucket.addToResourcePolicy(
|
|
new iam.PolicyStatement({
|
|
sid: "AWSLogDeliveryWrite",
|
|
effect: iam.Effect.ALLOW,
|
|
principals: [new iam.ServicePrincipal("delivery.logs.amazonaws.com")],
|
|
actions: ["s3:PutObject"],
|
|
resources: [bucket.arnForObjects(`AWSLogs/${stack.account}/*`)],
|
|
conditions: {
|
|
StringEquals: {
|
|
"s3:x-amz-acl": "bucket-owner-full-control",
|
|
"aws:SourceAccount": stack.account,
|
|
},
|
|
ArnLike: {
|
|
"aws:SourceArn": `arn:${stack.partition}:logs:${stack.region}:${stack.account}:*`,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
bucket.addToResourcePolicy(
|
|
new iam.PolicyStatement({
|
|
sid: "AWSLogDeliveryAclCheck",
|
|
effect: iam.Effect.ALLOW,
|
|
principals: [new iam.ServicePrincipal("delivery.logs.amazonaws.com")],
|
|
actions: ["s3:GetBucketAcl"],
|
|
resources: [bucket.bucketArn],
|
|
conditions: {
|
|
StringEquals: { "aws:SourceAccount": stack.account },
|
|
ArnLike: {
|
|
"aws:SourceArn": `arn:${stack.partition}:logs:${stack.region}:${stack.account}:*`,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
|
|
props.vpcIds.forEach((vpcId, i) => {
|
|
const flowLog = new ec2.CfnFlowLog(this, `FlowLog${i}`, {
|
|
resourceId: vpcId,
|
|
resourceType: "VPC",
|
|
trafficType: "ALL",
|
|
logDestinationType: "s3",
|
|
logDestination: bucket.bucketArn,
|
|
maxAggregationInterval: 600,
|
|
tags: [{ key: "Name", value: `flow-log-${vpcId}` }],
|
|
});
|
|
flowLog.node.addDependency(bucket.policy!);
|
|
});
|
|
|
|
new cdk.CfnOutput(this, "FlowLogsBucketName", { value: bucket.bucketName });
|
|
}
|
|
}
|