seahaven-org-baseline/lib/logs-key.ts

70 lines
2.6 KiB
TypeScript
Raw Permalink Normal View History

2026-06-08 19:04:36 -04:00
import * as cdk from "aws-cdk-lib";
import * as kms from "aws-cdk-lib/aws-kms";
import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
/**
* Dedicated customer-managed CMK for encrypting sensitive CloudWatch Logs
* groups (audit M-24 / INFRA-96).
*
* Used by the account CloudTrail log group and the finance/PII Lambda log
* groups (exec-aide, payments, po/vendor email processors, qbo). This is a
* SEPARATE key from `alias/seahaven-alarm-topics` (SNS alarm topics) and
* `alias/cloudtrail-logs` (the CloudTrail S3 log-file CMK) — those have
* different service principals and encryption contexts.
*
* The key policy MUST grant the CloudWatch Logs service principal use of the
* key, scoped by the `kms:EncryptionContext:aws:logs:arn` condition, or log
* delivery to any CMK-encrypted group silently stops. (AWS docs: "Encrypt log
* data in CloudWatch Logs using AWS KMS".) Cross-reviewed 2026-06-08
* (INFRA-96): dropped `Describe*` to the specific `DescribeKey`; CreateGrant is
* not required for plain log-group encryption so it is omitted.
*/
export class LogsKey extends Construct {
public readonly key: kms.Key;
constructor(scope: Construct, id: string) {
super(scope, id);
const stack = cdk.Stack.of(this);
this.key = new kms.Key(this, "Key", {
alias: "seahaven-logs",
description:
"Encrypts sensitive CloudWatch Logs groups (CloudTrail + finance/PII Lambdas) — M-24/INFRA-96",
enableKeyRotation: true,
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
// CloudWatch Logs service principal must be able to use the key to deliver
// (encrypt) and read (decrypt) log events. The encryption-context condition
// binds the grant to this account's log groups only, so the key cannot be
// used to decrypt arbitrary data under the logs service principal.
this.key.addToResourcePolicy(
new iam.PolicyStatement({
sid: "AllowCloudWatchLogsUseOfKey",
effect: iam.Effect.ALLOW,
principals: [
new iam.ServicePrincipal(`logs.${stack.region}.amazonaws.com`),
],
actions: [
"kms:Encrypt*",
"kms:Decrypt*",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
],
resources: ["*"],
conditions: {
ArnLike: {
"kms:EncryptionContext:aws:logs:arn": `arn:${stack.partition}:logs:${stack.region}:${stack.account}:log-group:*`,
},
},
})
);
new cdk.CfnOutput(this, "LogsKeyArn", { value: this.key.keyArn });
new cdk.CfnOutput(this, "LogsKeyAlias", { value: "alias/seahaven-logs" });
}
}