Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion pkg/clouds/aws/cloudtrail_security_alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@ func (c CloudTrailSecurityAlertsConfig) RequiresTrailValidation() bool {

// CloudTrailAlertSelectors controls which security alerts are enabled.
// Each field maps to a CloudWatch metric filter + alarm on the CloudTrail log group.
// Alert names reference AWS Security Hub/CIS CloudWatch controls (CloudWatch.1-14).
// Built-in detectors track AWS Security Hub/CIS CloudWatch controls (CloudWatch.1-14)
// plus a set of high-value additions covering attacker-blinding moves and exposure paths
// that CIS does not cover (GuardDuty/SecurityHub disable, IAM access keys, S3 Block Public
// Access, public Lambda Function URLs, KMS key policy edits, AWS Organizations changes,
// anonymous external probes).
type CloudTrailAlertSelectors struct {
// CIS CloudWatch.1-14
RootAccountUsage bool `json:"rootAccountUsage,omitempty" yaml:"rootAccountUsage,omitempty"` // CIS CloudWatch.1
UnauthorizedApiCalls bool `json:"unauthorizedApiCalls,omitempty" yaml:"unauthorizedApiCalls,omitempty"` // CIS CloudWatch.2
ConsoleLoginWithoutMfa bool `json:"consoleLoginWithoutMfa,omitempty" yaml:"consoleLoginWithoutMfa,omitempty"` // CIS CloudWatch.3
Expand All @@ -68,6 +73,61 @@ type CloudTrailAlertSelectors struct {
NetworkGatewayChanges bool `json:"networkGatewayChanges,omitempty" yaml:"networkGatewayChanges,omitempty"` // CIS CloudWatch.12
RouteTableChanges bool `json:"routeTableChanges,omitempty" yaml:"routeTableChanges,omitempty"` // CIS CloudWatch.13
VpcChanges bool `json:"vpcChanges,omitempty" yaml:"vpcChanges,omitempty"` // CIS CloudWatch.14

// Beyond-CIS detectors. Default off so existing deployments don't gain new alerts on plugin upgrade.
GuardDutyDisabled bool `json:"guardDutyDisabled,omitempty" yaml:"guardDutyDisabled,omitempty"` // GuardDuty disabled/detector deleted
SecurityHubDisabled bool `json:"securityHubDisabled,omitempty" yaml:"securityHubDisabled,omitempty"` // Security Hub disabled or standards turned off
AccessKeyCreation bool `json:"accessKeyCreation,omitempty" yaml:"accessKeyCreation,omitempty"` // CreateAccessKey on any IAM user
S3PublicAccessChanges bool `json:"s3PublicAccessChanges,omitempty" yaml:"s3PublicAccessChanges,omitempty"` // Block Public Access toggled at account or bucket scope
LambdaUrlPublic bool `json:"lambdaUrlPublic,omitempty" yaml:"lambdaUrlPublic,omitempty"` // Lambda Function URL created/updated with AuthType=NONE
KmsKeyPolicyChanges bool `json:"kmsKeyPolicyChanges,omitempty" yaml:"kmsKeyPolicyChanges,omitempty"` // PutKeyPolicy / PutResourcePolicy on KMS
OrganizationsChanges bool `json:"organizationsChanges,omitempty" yaml:"organizationsChanges,omitempty"` // SCP / account-membership churn in AWS Organizations
AnonymousProbes bool `json:"anonymousProbes,omitempty" yaml:"anonymousProbes,omitempty"` // userIdentity.type=AWSAccount AccessDenied probes from public IPs

// Overrides allows per-detector tuning without forking the plugin. Keyed by detector
// selector name (e.g. "iamPolicyChanges"). An override can:
// - bake exclusion clauses into the CloudWatch metric filter pattern (preferred
// for governed automation that contributes nothing but noise — Pulumi CI bots,
// known scanners, AWS service-linked roles);
// - raise the alarm threshold so a single matched event no longer trips the alarm
// (CIS Benchmark guidance for unauthorized-api-calls);
// - adjust the evaluation window.
//
// Suppression happens at the metric-filter layer, not in the Lambda enrichment
// step — excluded events never increment the metric, the alarm never trips, and
// no Slack notification is sent. The CW alarm dashboard therefore reflects real
// signal rather than known-good noise (preserves SOC2/ISO audit clarity).
Overrides map[string]CloudTrailAlertOverride `json:"overrides,omitempty" yaml:"overrides,omitempty"`
}

// CloudTrailAlertOverride tunes a single detector. All fields are optional; zero values
// mean "use plugin default."
type CloudTrailAlertOverride struct {
// Threshold raises the alarm threshold (events per period). 0 = use the plugin default
// for this detector (1 for most, 5 for unauthorizedApiCalls, 10 for anonymousProbes).
Threshold float64 `json:"threshold,omitempty" yaml:"threshold,omitempty"`
// Period in seconds. 0 = 300 (5 min). CloudWatch supports 60, 300, 3600.
Period int `json:"period,omitempty" yaml:"period,omitempty"`
// EvaluationPeriods is how many consecutive periods must breach. 0 = 1.
EvaluationPeriods int `json:"evaluationPeriods,omitempty" yaml:"evaluationPeriods,omitempty"`

// ExcludeUserNames excludes events where $.userIdentity.userName matches the given
// names exactly. Useful for IAMUser-type principals like CI bot accounts.
ExcludeUserNames []string `json:"excludeUserNames,omitempty" yaml:"excludeUserNames,omitempty"`
// ExcludePrincipalIds excludes by $.userIdentity.principalId (AIDA..., AROA..., AKIA...).
ExcludePrincipalIds []string `json:"excludePrincipalIds,omitempty" yaml:"excludePrincipalIds,omitempty"`
// ExcludeUserArns excludes by exact $.userIdentity.arn match.
ExcludeUserArns []string `json:"excludeUserArns,omitempty" yaml:"excludeUserArns,omitempty"`
// ExcludeUserArnGlobs excludes by $.userIdentity.arn glob (CloudWatch metric filter
// patterns support * within string values, e.g.
// "arn:aws:sts::*:assumed-role/AWSServiceRoleFor*/*"). One glob per list entry.
ExcludeUserArnGlobs []string `json:"excludeUserArnGlobs,omitempty" yaml:"excludeUserArnGlobs,omitempty"`
// ExcludeUserTypes excludes by $.userIdentity.type (e.g. "AWSService", "AWSAccount",
// "AssumedRole"). Useful for stripping AWS internal plumbing from unauthorized-api-calls.
ExcludeUserTypes []string `json:"excludeUserTypes,omitempty" yaml:"excludeUserTypes,omitempty"`
// ExcludeInvokedBy excludes by $.userIdentity.invokedBy (e.g. "s3.amazonaws.com").
// This is the canonical way to strip AWS service self-probes.
ExcludeInvokedBy []string `json:"excludeInvokedBy,omitempty" yaml:"excludeInvokedBy,omitempty"`
}

func ReadCloudTrailSecurityAlertsConfig(config *api.Config) (api.Config, error) {
Expand Down
Loading
Loading