Replace aws/sns key with CMK on alarm topics (#13)

Audit L-14, plus a latent Day-2 bug: seahaven-cis-alarms was
encrypted with the AWS-managed alias/aws/sns key, whose policy cannot
grant cloudwatch.amazonaws.com - CloudWatch alarms silently fail to
publish to topics it encrypts. All 15 CIS alarms would have fired
into the void.

New customer-managed key (rotation on) grants CloudWatch
GenerateDataKey*/Decrypt/DescribeKey scoped by SourceAccount. The
unmanaged site-alerts topic now uses the same key (set via CLI).

Cross-reviewed: no BLOCKs. Verified: forced ALARM on the payroll DLQ
alarm published successfully through the encrypted site-alerts.
This commit is contained in:
Adam Moussa 2026-06-03 15:33:52 -04:00 • committed by GitHub
parent 2289dcb0c9
commit 993702f421
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -148,12 +148,35 @@ export class CisMonitoring extends Construct {
constructor(scope: Construct, id: string, props: CisMonitoringProps) {
super(scope, id);
// Dedicated topic for security/CIS alarms, encrypted with the AWS-managed
// SNS key (also clears audit L-14 for this topic).
// Customer-managed key for alarm topics (audit L-14). The AWS-managed
// alias/aws/sns key CANNOT be used here: its key policy can't grant
// cloudwatch.amazonaws.com, so CloudWatch alarms silently fail to publish
// to topics it encrypts — which is exactly what these topics receive.
// Also used by the unmanaged site-alerts topic (set via CLI; ARN output below).
const alarmTopicKey = new kms.Key(this, "AlarmTopicKey", {
alias: "seahaven-alarm-topics",
description:
"SSE for SNS alarm topics; grants CloudWatch alarms publish-side usage",
enableKeyRotation: true,
});
alarmTopicKey.addToResourcePolicy(
new cdk.aws_iam.PolicyStatement({
sid: "AllowCloudWatchAlarmsUse",
principals: [new cdk.aws_iam.ServicePrincipal("cloudwatch.amazonaws.com")],
actions: ["kms:GenerateDataKey*", "kms:Decrypt", "kms:DescribeKey"],
resources: ["*"],
conditions: {
StringEquals: { "aws:SourceAccount": cdk.Stack.of(this).account },
},
})
);
new cdk.CfnOutput(this, "AlarmTopicKeyArn", { value: alarmTopicKey.keyArn });
// Dedicated topic for security/CIS alarms (audit H-1, L-14).
const topic = new sns.Topic(this, "CisAlarmTopic", {
topicName: "seahaven-cis-alarms",
displayName: "Sea Haven CIS / security alarms",
masterKey: kms.Alias.fromAliasName(this, "SnsKey", "alias/aws/sns"),
masterKey: alarmTopicKey,
});
topic.addSubscription(new subscriptions.EmailSubscription(props.alarmEmail));