Skip to content

Commit

Permalink
feat: push AccessDeniedTotal & IgnoredAccessDeniedTotal metrics to cl…
Browse files Browse the repository at this point in the history
…oudwatch, rm AccessDeniedByEvent metric
  • Loading branch information
EreminAnton committed Nov 8, 2024
1 parent b84244b commit 3411aa6
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 13 deletions.
2 changes: 2 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ module "lambda" {

DYNAMODB_TIME_TO_LIVE = var.dynamodb_time_to_live
DYNAMODB_TABLE_NAME = try(module.cloudtrail_to_slack_dynamodb_table[0].dynamodb_table_id, "")

PUSH_ACCESS_DENIED_CLOUDWATCH_METRICS = var.push_access_denied_cloudwatch_metrics
},
var.use_default_rules ? { USE_DEFAULT_RULES = "True" } : {}
)
Expand Down
1 change: 1 addition & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def __init__(self): # noqa: ANN101 ANN204

self.dynamodb_table_name: str | None = os.environ.get("DYNAMODB_TABLE_NAME")
self.dynamodb_time_to_live: int = int(os.environ.get("DYNAMODB_TIME_TO_LIVE", 900))
self.push_access_denied_cloudwatch_metrics: bool = os.environ.get("PUSH_ACCESS_DENIED_CLOUDWATCH_METRICS") # type: ignore # noqa: PGH003, E501

self.rules = []
if self.use_default_rules:
Expand Down
36 changes: 23 additions & 13 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,27 +168,38 @@ def should_message_be_processed(
return ProcessingResult(False, errors)


def push_cloudwatch_metrics(deny_type: str, event_name: str) -> None:
"""Pushes CloudWatch metrics: one for all AccessDenied events, and one grouped by event name."""
def push_total_access_denied_events_cloudwatch_metric() -> None:
"""Pushes CloudWatch metrics for all AccessDenied events."""
metrics = [
{
"MetricName": "TotalAccessDeniedEvents",
"Dimensions": [{"Name": "AccessDenied", "Value": "AccessDeniedTotal"}],
"Value": 1,
"Unit": "Count",
},
]
try:
cloudwatch_client.put_metric_data(Namespace="CloudTrailToSlack/AccessDeniedEvents", MetricData=metrics)
logger.info("Pushed TotalAccessDeniedEvents CloudWatch metric")
except Exception as e:
logger.exception("Failed to push CloudWatch metrics", extra={"error": e})


def push_total_ignored_access_denied_events_cloudwatch_metric() -> None:
"""Pushes CloudWatch metrics for ignored AccessDenied events only."""
metrics = [
{
"MetricName": "AccessDeniedByEvent",
"Dimensions": [{"Name": "DenyType", "Value": deny_type}, {"Name": "EventName", "Value": event_name}],
"MetricName": "TotalIgnoredAccessDeniedEvents",
"Dimensions": [{"Name": "AccessDenied", "Value": "IgnoredAccessDeniedTotal"}],
"Value": 1,
"Unit": "Count",
},
]
try:
cloudwatch_client.put_metric_data(Namespace="CloudTrail/AccessDeniedEvents", MetricData=metrics)
logger.info("Pushed CloudWatch metrics", extra={"deny_type": deny_type, "event_name": event_name})
cloudwatch_client.put_metric_data(Namespace="CloudTrailToSlack/AccessDeniedEvents", MetricData=metrics)
logger.info("Pushed TotalIgnoredAccessDeniedEvents CloudWatch metric")
except Exception as e:
logger.exception("Failed to push CloudWatch metrics", extra={"error": e, "deny_type": deny_type, "event_name": event_name})
logger.exception("Failed to push CloudWatch metrics", extra={"error": e})


def handle_event(
Expand All @@ -212,14 +223,13 @@ def handle_event(
slack_config=slack_config,
)

# log full event if it is AccessDenied
if "errorCode" in event and "AccessDenied" in event["errorCode"]:
event_as_string = json.dumps(event, indent=4)
logger.info({"errorCode": "AccessDenied", "log full event": event_as_string})
# Push CloudWatch metrics
push_cloudwatch_metrics(deny_type=event["errorCode"], event_name=event.get("eventName", "UnknownEvent"))
if "errorCode" in event and "AccessDenied" in event["errorCode"] and cfg.push_access_denied_cloudwatch_metrics is True:
push_total_access_denied_events_cloudwatch_metric()

if not result.should_be_processed:
if "errorCode" in event and "AccessDenied" in event["errorCode"] and cfg.push_access_denied_cloudwatch_metrics is True:
push_total_ignored_access_denied_events_cloudwatch_metric()
return
return

message = event_to_slack_message(event, source_file_object_key, account_id)
Expand Down
6 changes: 6 additions & 0 deletions vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,9 @@ variable "dynamodb_time_to_live" {
default = 900
type = number
}

variable "push_access_denied_cloudwatch_metrics" {
description = "If true, CloudWatch metrics will be pushed for all access denied events, including events ignored by rules."
type = bool
default = true
}

0 comments on commit 3411aa6

Please sign in to comment.