Network ACL Changes Alarm Should Be Enabled
More Info:
AWS Network ACLs configuration changes should be monitored using CloudWatch alarms.
Risk Level
Medium
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- AWS Well Architected Framework
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS AWS
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- Cloudanix Best Practice
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- HITRUST CSF
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST CSF
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SOC2
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Using Console
Below are step‑by‑step instructions using only the AWS Management Console, assuming you already have a CloudTrail trail logging management events in all regions to a CloudWatch Logs log group. If not, I’ll include the prerequisite steps first.
0. Prerequisite: Ensure CloudTrail is Logging to CloudWatch Logs
- Open CloudTrail console.
- Go to Trails in the left menu.
- Select your main trail (or create one if you don’t have it).
- In the trail details page, under CloudWatch Logs, choose Edit (or Configure).
- For CloudWatch Logs log group, either:
- Choose an existing log group, or
- Type a new log group name (e.g.,
/aws/cloudtrail/management).
- Select or create an IAM role as prompted to allow CloudTrail to write to CloudWatch Logs.
- Save changes and confirm events are being delivered (check the log group after a few minutes).
1. Create a Metric Filter for Network ACL Changes
-
Open CloudWatch console.
-
In the left navigation pane, select Logs → Log groups.
-
Click the log group where CloudTrail is sending logs (e.g.,
/aws/cloudtrail/management). -
Select the Metric filters tab.
-
Click Create metric filter.
-
In Filter pattern, paste:
{ ($.eventSource = "ec2.amazonaws.com") &&(($.eventName = "CreateNetworkAcl") ||($.eventName = "CreateNetworkAclEntry") ||($.eventName = "DeleteNetworkAcl") ||($.eventName = "DeleteNetworkAclEntry") ||($.eventName = "ReplaceNetworkAclEntry") ||($.eventName = "ReplaceNetworkAclAssociation")) } -
Click Next to test; choose a log event sample if available and verify it would match where appropriate.
-
Click Next.
-
Under Assign metric, configure:
- Filter name:
NetworkACLChanges - Metric namespace:
Security/CloudTrail(or similar) - Metric name:
NetworkACLChangesCount - Metric value:
1 - Leave default for others unless you have a standard.
- Filter name:
-
Click Next, then Create metric filter.
2. Create a CloudWatch Alarm on That Metric
-
In CloudWatch, go to Alarms → All alarms.
-
Click Create alarm.
-
Click Select metric.
-
Navigate to your metric via:
- Browse → Security/CloudTrail (or your namespace) → Metrics with no dimensions (or the one you chose), then select
NetworkACLChangesCount.
- Browse → Security/CloudTrail (or your namespace) → Metrics with no dimensions (or the one you chose), then select
-
Click Select metric.
-
Configure the metric and conditions:
- Statistic:
Sum - Period: e.g.,
5 minutes(or1 minuteif you want very fast alerts). - Threshold type:
Static - Whenever NetworkACLChangesCount is…
Greater than or equal to - Threshold value:
1.
This means any NACL change in that period will fire the alarm.
- Statistic:
-
Click Next.
-
Configure notification:
- Under Notification, choose In alarm.
- For Select an SNS topic:
- Choose an existing topic (e.g.,
security-alerts), or - Click Create new topic, give it a name, and enter your email address (or distribution list).
- Choose an existing topic (e.g.,
- Confirm subscription from the email if you created a new topic.
-
Click Next.
-
Name and description:
- Alarm name:
NetworkACLChangesAlarm - Alarm description:
Alarm when any Network ACL is created, deleted, or modified.
- Alarm name:
-
Review the configuration, then click Create alarm.
3. Validate
- Make a test NACL change (e.g., add a temporary rule in a test VPC).
- Wait for CloudTrail to deliver the log and CloudWatch to evaluate the metric (1–5 minutes, depending on your period).
- Confirm:
- The metric
NetworkACLChangesCountshows a data point > 0. - The alarm enters ALARM state.
- A notification is sent to your SNS subscribers.
- The metric
Once this is in place, “Network ACL Changes Alarm” is effectively enabled and will notify you of any NACL modifications recorded by CloudTrail.
Using CLI
Below are the CLI-focused remediation steps to ensure you have a CloudWatch alarm on Network ACL changes (via CloudTrail logs).
Assumptions (adjust names/regions as needed):
- Region:
us-east-1 - Log group:
/aws/cloudtrail/netacl-logs - Metric name:
NetworkAclChanges - Metric namespace:
SecurityMonitoring - Alarm name:
NetworkAclChangesAlarm - SNS topic (for notifications):
arn:aws:sns:us-east-1:123456789012:SecurityAlerts
1. Ensure CloudTrail is logging to CloudWatch Logs
If you already have a CloudTrail configured to send logs to CloudWatch Logs, skip to step 2.
1.1 Create a log group (if needed)
aws logs create-log-group \
--log-group-name "/aws/cloudtrail/netacl-logs" \
--region us-east-1
1.2 Allow CloudTrail to write to the log group (IAM role/policy)
Create an IAM role for CloudTrail (if you don’t have one). Example trust policy file trust-policy.json:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "cloudtrail.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
aws iam create-role \
--role-name CloudTrail_CloudWatchLogs_Role \
--assume-role-policy-document file://trust-policy.json
Attach permissions policy file cloudtrail-cwlogs-policy.json:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/cloudtrail/netacl-logs:*"
}]
}
aws iam put-role-policy \
--role-name CloudTrail_CloudWatchLogs_Role \
--policy-name CloudTrail_CloudWatchLogs_Policy \
--policy-document file://cloudtrail-cwlogs-policy.json
1.3 Create / update CloudTrail to use that log group
aws cloudtrail create-trail \
--name "OrgTrail" \
--s3-bucket-name "my-cloudtrail-bucket" \
--is-multi-region-trail \
--cloud-watch-logs-log-group-arn "arn:aws:logs:us-east-1:123456789012:log-group:/aws/cloudtrail/netacl-logs" \
--cloud-watch-logs-role-arn "arn:aws:iam::123456789012:role/CloudTrail_CloudWatchLogs_Role"
Enable logging (if not already):
aws cloudtrail start-logging --name "OrgTrail"
2. Create a CloudWatch Logs Metric Filter for NACL changes
Create a filter pattern file nacl-filter-pattern.txt:
{ ($.eventSource = "ec2.amazonaws.com") && (
($.eventName = "CreateNetworkAcl") ||
($.eventName = "CreateNetworkAclEntry") ||
($.eventName = "DeleteNetworkAcl") ||
($.eventName = "DeleteNetworkAclEntry") ||
($.eventName = "ReplaceNetworkAclEntry") ||
($.eventName = "ReplaceNetworkAclAssociation")
)
}
Then create the metric filter:
aws logs put-metric-filter \
--log-group-name "/aws/cloudtrail/netacl-logs" \
--filter-name "NetworkAclChangeFilter" \
--filter-pattern file://nacl-filter-pattern.txt \
--metric-transformations \
metricName="NetworkAclChanges",metricNamespace="SecurityMonitoring",metricValue="1"
3. Create a CloudWatch alarm on the metric
Example: alarm when at least 1 NACL change occurs in a 5‑minute period.
aws cloudwatch put-metric-alarm \
--alarm-name "NetworkAclChangesAlarm" \
--alarm-description "Alarm when Network ACLs are created, modified, or deleted" \
--metric-name "NetworkAclChanges" \
--namespace "SecurityMonitoring" \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--treat-missing-data notBreaching \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:SecurityAlerts"
(Ensure the SNS topic exists and subscriptions are confirmed.)
4. Validate
- Generate a test NACL change (e.g., add/remove an entry).
- Confirm:
- The CloudTrail event appears in the log group.
- The metric
NetworkAclChangesincrements. NetworkAclChangesAlarmtransitions toALARMand SNS notification is sent.
Using Python
To fix this, you need to (1) ensure NACL changes are logged by CloudTrail, (2) create a CloudWatch Logs metric filter for those events, and (3) create a CloudWatch alarm on that metric. Below is a concise, step-by-step Python (boto3) example.
Assumptions:
- You already have:
- A CloudTrail trail delivering logs to a CloudWatch Logs log group (e.g.,
/aws/cloudtrail/logs) - An SNS topic ARN to notify (e.g.,
arn:aws:sns:us-east-1:123456789012:security-notifications)
- A CloudTrail trail delivering logs to a CloudWatch Logs log group (e.g.,
Replace all placeholder values with your actual ones.
1. Make sure CloudTrail logs to CloudWatch Logs
If your trail is not yet configured to send to CloudWatch Logs:
import boto3
cloudtrail = boto3.client('cloudtrail')
trail_name = "my-org-trail"
log_group_arn = "arn:aws:logs:us-east-1:123456789012:log-group:/aws/cloudtrail/logs:*"
role_arn = "arn:aws:iam::123456789012:role/CloudTrail_CloudWatchLogs_Role"
cloudtrail.update_trail(
Name=trail_name,
CloudWatchLogsLogGroupArn=log_group_arn,
CloudWatchLogsRoleArn=role_arn
)
Ensure the IAM role has permissions to write to that log group.
2. Create a CloudWatch Logs metric filter for NACL changes
Events to monitor (CloudTrail eventName):
CreateNetworkAcl, CreateNetworkAclEntry, DeleteNetworkAcl, DeleteNetworkAclEntry, ReplaceNetworkAclEntry, ReplaceNetworkAclAssociation
import boto3
logs = boto3.client('logs')
log_group_name = "/aws/cloudtrail/logs" # your log group
metric_filter_name = "NACLChangesFilter"
metric_namespace = "Security/NACL"
metric_name = "NACLChanges"
# Filter pattern that matches any of the listed eventName values
filter_pattern = (
'{ ($.eventSource = "ec2.amazonaws.com") && '
'($.eventName = "CreateNetworkAcl" || '
'$.eventName = "CreateNetworkAclEntry" || '
'$.eventName = "DeleteNetworkAcl" || '
'$.eventName = "DeleteNetworkAclEntry" || '
'$.eventName = "ReplaceNetworkAclEntry" || '
'$.eventName = "ReplaceNetworkAclAssociation") }'
)
logs.put_metric_filter(
logGroupName=log_group_name,
filterName=metric_filter_name,
filterPattern=filter_pattern,
metricTransformations=[
{
"metricName": metric_name,
"metricNamespace": metric_namespace,
"metricValue": "1"
}
]
)
3. Create a CloudWatch alarm on that metric
import boto3
cloudwatch = boto3.client('cloudwatch')
alarm_name = "NACLChangesAlarm"
sns_topic_arn = "arn:aws:sns:us-east-1:123456789012:security-notifications"
cloudwatch.put_metric_alarm(
AlarmName=alarm_name,
AlarmDescription="Alarm when Network ACL changes are detected via CloudTrail",
Namespace="Security/NACL",
MetricName="NACLChanges",
Statistic="Sum",
Period=300, # 5 minutes
EvaluationPeriods=1,
Threshold=1.0, # Trigger if >=1 change in the period
ComparisonOperator="GreaterThanOrEqualToThreshold",
AlarmActions=[sns_topic_arn],
TreatMissingData="notBreaching"
)
4. (Optional) Verify the configuration
You can list the metric filters and alarms to verify:
logs.describe_metric_filters(
logGroupName=log_group_name,
filterNamePrefix=metric_filter_name
)
cloudwatch.describe_alarms(
AlarmNames=[alarm_name]
)
Once this is in place, any NACL change recorded in CloudTrail will produce a CloudWatch metric data point and trigger the alarm, sending a notification via SNS.
Using Terraform
# CloudTrail log group that already receives your CloudTrail events
# Replace EXISTING_CLOUDTRAIL_LOG_GROUP_NAME with your actual log group name.
resource "aws_cloudwatch_log_group" "cloudtrail" {
name = "EXISTING_CLOUDTRAIL_LOG_GROUP_NAME"
}
# Metric filter for Network ACL changes
resource "aws_cloudwatch_log_metric_filter" "network_acl_changes" {
name = "NetworkAclChanges"
log_group_name = aws_cloudwatch_log_group.cloudtrail.name
# Matches Create/Delete/Replace Network ACL and entries
pattern = "{($.eventName=CreateNetworkAcl) || ($.eventName=CreateNetworkAclEntry) || ($.eventName=DeleteNetworkAcl) || ($.eventName=DeleteNetworkAclEntry) || ($.eventName=ReplaceNetworkAclAssociation) || ($.eventName=ReplaceNetworkAclEntry)}"
metric_transformation {
name = "NetworkAclChangesMetric"
namespace = "CloudTrailMetrics"
value = "1"
}
}
# CloudWatch alarm for Network ACL changes
# Replace EXISTING_SNS_TOPIC_ARN with the ARN of your SNS topic for notifications.
resource "aws_cloudwatch_metric_alarm" "network_acl_changes_alarm" {
alarm_name = "NetworkAclChangesAlarm"
alarm_description = "Triggers when an API call is made to create, delete, or change a Network ACL"
namespace = "CloudTrailMetrics"
metric_name = aws_cloudwatch_log_metric_filter.network_acl_changes.metric_transformation[0].name
statistic = "Sum"
period = 300
threshold = 1
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
alarm_actions = [
"EXISTING_SNS_TOPIC_ARN", # replace with your SNS Topic ARN
]
depends_on = [
aws_cloudwatch_log_metric_filter.network_acl_changes,
]
}
Notes:
- This creates a new metric filter and CloudWatch alarm, matching the provided CLI remediation.
- You must have an existing SNS topic for
EXISTING_SNS_TOPIC_ARN, and you must subscribe an endpoint (e.g., email) to that topic manually in AWS or via separate Terraform.
Verification with terraform plan should show:
aws_cloudwatch_log_metric_filter.network_acl_changesto be created (or updated if it already exists but differs).aws_cloudwatch_metric_alarm.network_acl_changes_alarmto be created (or updated if it already exists but differs).