Back to Checks
aws

Account has a CloudWatch Logs metric filter and alarm for AWS Management Console authentication failures

cloudwatch_log_metric_filter_authentication_failures
Service: cloudwatchSeverity: medium
OfficialBuilt-inVerified
Categories:logging
Resource Type:AwsCloudWatchAlarm

CloudWatch Logs metric filter and alarm for AWS Management Console authentication failures, sourced from CloudTrail (eventName=ConsoleLogin, errorMessage="Failed authentication").

Identifies whether these failures are converted into a metric and actively monitored by an alarm.

Risk

Absent visibility into failed console logins enables undetected brute-force and credential-stuffing attempts, extending attacker dwell time.

Successful guesses can grant console access, risking data confidentiality, configuration integrity, and availability through destructive changes.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks cloudwatch_log_metric_filter_authentication_failures

Recommendation

Implement a log metric filter for ConsoleLogin failures and attach a CloudWatch alarm with actionable notifications. Tune thresholds to reduce noise and route alerts to incident response.

Apply least privilege and enforce MFA to limit impact, and correlate alerts with source IP and user context.

Remediation

Terraform

# Metric filter and alarm for console authentication failures
resource "aws_cloudwatch_log_metric_filter" "metric" {
  name           = "<example_resource_name>"
  log_group_name = "<example_resource_name>"
  pattern        = "{($.eventName = ConsoleLogin) && ($.errorMessage = \"Failed authentication\") }" # Critical: detects failed console logins

  metric_transformation {
    name      = "<example_resource_name>"   # Critical: metric created by filter
    namespace = "<example_resource_name>"   # Critical: metric namespace
    value     = "1"
  }
}

resource "aws_cloudwatch_metric_alarm" "alarm" {
  metric_name          = aws_cloudwatch_log_metric_filter.metric.metric_transformation[0].name   # Critical: alarm references the filter's metric
  namespace            = aws_cloudwatch_log_metric_filter.metric.metric_transformation[0].namespace # Critical: must match
  comparison_operator  = "GreaterThanOrEqualToThreshold"
  evaluation_periods   = 1
  period               = 300
  statistic            = "Sum"
  threshold            = 1
}

NativeIaC

# CloudFormation: Metric filter and alarm for console authentication failures
Resources:
  MetricFilter:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: "<example_resource_name>"
      FilterPattern: '{ ($.eventName = ConsoleLogin) && ($.errorMessage = "Failed authentication") }'  # Critical: matches failed console login events
      MetricTransformations:
        - MetricValue: "1"
          MetricNamespace: "<example_resource_name>"  # Critical: creates metric namespace
          MetricName: "<example_resource_name>"       # Critical: creates metric name

  Alarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      MetricName: "<example_resource_name>"   # Critical: alarm targets metric from filter
      Namespace: "<example_resource_name>"    # Critical: must match metric's namespace
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1

Other

1In the AWS Console, open CloudWatch
2Go to Logs > Log groups and select the CloudTrail log group receiving events
3Open the Metric filters tab > Create metric filter
4- Filter pattern: { ($.eventName = ConsoleLogin) && ($.errorMessage = "Failed authentication") }
5- Assign any metric name and namespace, value 1, then create
6On the created metric filter, select it and choose Create alarm
7- Statistic: Sum, Period: 5 minutes, Threshold type: Static, Threshold: >= 1
8- Create the alarm

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
    cloudtrail_client,
)
from prowler.providers.aws.services.cloudwatch.cloudwatch_client import (
    cloudwatch_client,
)
from prowler.providers.aws.services.cloudwatch.lib.metric_filters import (
    build_metric_filter_pattern,
    check_cloudwatch_log_metric_filter,
)
from prowler.providers.aws.services.cloudwatch.logs_client import logs_client


class cloudwatch_log_metric_filter_authentication_failures(Check):
    """Account has a CloudWatch Logs metric filter and alarm for AWS Management Console authentication failures.

    Looks for a CloudWatch Logs metric filter matching the expected pattern on a
    log group used by a CloudTrail trail, with at least one alarm on its metric.

    - PASS: A matching metric filter with an associated alarm exists.
    - FAIL: No matching metric filter, or a filter without an alarm, was found.
    - MANUAL: CloudTrail trails, log groups, metric filters or alarms could
      not be listed in at least one region, so the absence of a filter/alarm
      cannot be asserted.
    """

    def execute(self) -> list[Check_Report_AWS]:
        """Evaluate the metric filter and alarm coverage for the account.

        Returns:
            list[Check_Report_AWS]: A single report for the account.
        """
        pattern = build_metric_filter_pattern(
            event_names=["ConsoleLogin"],
            extra_clauses=[("errorMessage", "=", "Failed authentication")],
        )
        findings = []

        report = check_cloudwatch_log_metric_filter(
            pattern,
            cloudtrail_client.trails,
            logs_client.metric_filters,
            cloudwatch_client.metric_alarms,
            self.metadata(),
        )

        inventory_unavailable = (
            cloudtrail_client.trails_unavailable
            or logs_client.log_groups_unavailable
            or logs_client.metric_filters_unavailable
            or cloudwatch_client.metric_alarms_unavailable
        )

        if report is None:
            report = Check_Report_AWS(metadata=self.metadata(), resource={})
            report.status = "FAIL"
            report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
            report.region = logs_client.region
            report.resource_id = logs_client.audited_account
            report.resource_arn = logs_client.log_group_arn_template
            report.resource_tags = []

        # A denied listing in any region means the inventory is incomplete: a
        # PASS is still backed by a real filter and alarm, but a FAIL (nothing
        # found, or a filter found without its alarm) cannot be trusted.
        if report.status == "FAIL" and inventory_unavailable:
            report.status = "MANUAL"
            report.status_extended = "Cannot evaluate CloudWatch metric filters and alarms: CloudTrail trails, log groups, metric filters or alarms could not be listed in at least one region. Verify that the scanning credentials are allowed to call cloudtrail:DescribeTrails, logs:DescribeLogGroups, logs:DescribeMetricFilters and cloudwatch:DescribeAlarms."

        findings.append(report)

        return findings