From 142e221c470130600ecb09747c42f0f5fe7f73ad Mon Sep 17 00:00:00 2001 From: "seahaven-openswe[bot]" <296972425+seahaven-openswe[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 15:47:41 -0400 Subject: [PATCH] feat: harden CIS 4.1 detection depth with M-of-N alarm tuning and CloudTrail Insights (#38) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch UnauthorizedApiCalls alarm from 3/3 consecutive to 3/6 M-of-N so a single quiet 5-min window can't reset detection. The 3/3 setting pre-dates #36 and was sized to suppress CFN/Config noise that #36 now removes at the filter level, making a wider M-of-N evaluation window safe from flap risk. Enable CloudTrail Insights (ApiCallRateInsight + ApiErrorRateInsight) on seahaven-org-trail as a compensating control for the residual risk accepted in #36 — the CFN/Config-proxied denials intentionally excluded from CIS 4.1 — and as a backstop for low-and-slow patterns the 5-min alarm may miss. Cost ≈$35–$53/month at current org trail volume. Refs: #37 Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com> --- README.md | 2 +- lib/account-baseline-stack.ts | 13 +++++++++++++ lib/cis-monitoring.ts | 13 ++++++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ebc88a8..a7065ea 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The canonical map of Sea Haven's AWS infrastructure lives in Confluence. This pr | Resource | Logical ID | Notes | |---|---|---| -| Multi-region trail | `Trail` (`seahaven-org-trail`) | Management events read+write, global service events, **log-file validation on** | +| Multi-region trail | `Trail` (`seahaven-org-trail`) | Management events read+write, global service events, **log-file validation on**, **CloudTrail Insights on** (ApiCallRate + ApiErrorRate, §37) | | Log bucket | `TrailLogBucket` (`seahaven-cloudtrail-logs-328440206208`) | Private (Block Public Access all), SSE-KMS, versioned, **TLS-only**, **Object Lock GOVERNANCE 365d**, lifecycle (Glacier @90d, expire @365d), server access logging → `seahaven-s3-access-logs` | | KMS CMK | `TrailKey` (`alias/cloudtrail-logs`) | Encrypts log files; **automatic rotation enabled** | | CloudWatch Logs group | created by the L2 `Trail` | 365-day retention; this is the group the CIS Section 4 metric filters (H-1) attach to | diff --git a/lib/account-baseline-stack.ts b/lib/account-baseline-stack.ts index 7d960a0..606ed48 100644 --- a/lib/account-baseline-stack.ts +++ b/lib/account-baseline-stack.ts @@ -208,6 +208,19 @@ export class AccountBaselineStack extends cdk.Stack { sendToCloudWatchLogs: true, cloudWatchLogGroup: trailLogGroup, managementEvents: cloudtrail.ReadWriteType.ALL, + // CloudTrail Insights (§37): compensating control for the residual risk + // accepted in #36 (CFN/Config-proxied denials excluded from CIS 4.1) and + // the low-and-slow evasion surface in the UnauthorizedApiCalls alarm. + // ApiCallRateInsight flags anomalous write-API spikes; ApiErrorRateInsight + // flags anomalous errored/denied call rates — including the denials CIS + // 4.1 intentionally filters out. Per-event cost (≈$0.35/100k management + // events); an org trail with 10–15M management events/month adds roughly + // $35–$53/month. CIS 4.1 alarm + GuardDuty + Security Hub (CIS v3.0) are + // already live, so this is defence-in-depth, not an urgent gap-fill. + insightTypes: [ + cloudtrail.InsightType.API_CALL_RATE, + cloudtrail.InsightType.API_ERROR_RATE, + ], }); // ── Day 1 detective layer + governance toggles ── diff --git a/lib/cis-monitoring.ts b/lib/cis-monitoring.ts index 7283ccf..3b6debd 100644 --- a/lib/cis-monitoring.ts +++ b/lib/cis-monitoring.ts @@ -72,11 +72,14 @@ const CIS_CONTROLS: CisControl[] = [ pattern: '{ (($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*")) && ($.sourceIPAddress != "*cloudformation.amazonaws.com") && ($.sourceIPAddress != "config.amazonaws.com") && ($.sourceIPAddress != "delivery.logs.amazonaws.com") && ($.eventName != "HeadBucket") }', description: "CIS 4.1 — unauthorized API calls", - // Belt-and-suspenders after the service-noise exclusion above: still require - // 3 consecutive breaching 5-min periods so a one-off human fat-finger that - // self-recovers doesn't page, while sustained unauthorized activity (e.g. a - // misconfigured role failing every call) still trips within ~15 minutes. - evaluationPeriods: 3, + // M-of-N with N > M so a single quiet 5-min window cannot reset detection. + // Before #36, false positives from CFN/Config-proxied denials forced a + // conservative 3/3 consecutive; now that #36 scoped the metric-filter + // exclusion to those benign sources, we can widen the evaluation window + // while keeping the same alarm threshold. An attacker pacing denied calls to + // leave every third window empty would evade a 3/3 alarm but still trips a + // 3/6 — three breaching windows in any rolling 30-min span fire the alarm. + evaluationPeriods: 6, datapointsToAlarm: 3, }, {