This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
seahaven-external-dev-baseline/lib/flow-logs.ts
Adam Moussa 10555af9e2 Add account-local security baseline for seahaven-external-dev
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).
2026-06-15 11:26:23 -04:00

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 });
}
}