diff --git a/lib/cis-monitoring.ts b/lib/cis-monitoring.ts index af41368..34b5740 100644 --- a/lib/cis-monitoring.ts +++ b/lib/cis-monitoring.ts @@ -28,6 +28,11 @@ interface CisControl { readonly metricName: string; readonly pattern: string; readonly description: string; + // Optional alarm-sensitivity override. Defaults to 1/1 (page on a single + // breaching 5-min period) which suits low-frequency security signals. Raise + // for noisy high-volume metrics where one-off breaches are expected. + readonly evaluationPeriods?: number; + readonly datapointsToAlarm?: number; } const CIS_CONTROLS: CisControl[] = [ @@ -37,6 +42,14 @@ const CIS_CONTROLS: CisControl[] = [ pattern: '{ ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*") && ($.sourceIPAddress != "delivery.logs.amazonaws.com") && ($.eventName != "HeadBucket") }', description: "CIS 4.1 — unauthorized API calls", + // This metric is high-volume: a single CloudFormation/CDK deploy can emit a + // burst of benign describe-API denials, and any one-off console fat-finger + // trips a 1/1 alarm and self-recovers, producing notification storms. Require + // 3 consecutive breaching 5-min periods so only *sustained* unauthorized + // activity (e.g. a misconfigured role failing every call) pages. Detection of + // a real persistent problem is preserved; transient bursts are filtered. + evaluationPeriods: 3, + datapointsToAlarm: 3, }, { id: "ConsoleSigninNoMfa", @@ -209,7 +222,10 @@ export class CisMonitoring extends Construct { threshold: 1, comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, - evaluationPeriods: 1, + evaluationPeriods: c.evaluationPeriods ?? 1, + // Omitted (undefined) for default 1/1 controls so their templates are + // untouched; CloudWatch defaults datapointsToAlarm to evaluationPeriods. + datapointsToAlarm: c.datapointsToAlarm ?? c.evaluationPeriods, treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING, });