Introduction
This is the fourth part of my attempt to create scripts for collecting evidence about AWS resources. AWS WAF is often enabled in multiple regions. In that case, collecting evidence region by region is tedious, so I created the script below.
This script has been confirmed to work only in CloudShell.
Script
#!/bin/bash
# Get a list of all regions
regions=$(aws ec2 describe-regions --query 'Regions[].RegionName' --output text)
for region in $regions; do
echo -e "\n Region: $region"
# Create folders for each region
mkdir -p "$region"
# Get a list of web ACLs for each region
web_acl_arns=$(aws wafv2 list-web-acls --scope REGIONAL --region $region --query 'WebACLs[].ARN' --output text)
if [ -z "$web_acl_arns" ]; then
echo "No WAF Web ACLs found"
else
echo " Web ACLs:"
for web_acl_arn in $web_acl_arns; do
# Get detailed information about the Web ACL
web_acl=$(aws wafv2 get-web-acl --name $(echo $web_acl_arn | awk -F'/' '{print $(NF-1)}') --scope REGIONAL --id $(echo $web_acl_arn | awk -F'/' '{print $NF}') --region $region)
echo " - $web_acl_arn"
echo "Name: $(echo $web_acl | jq -r '.WebACL.Name')"
echo " ID: $(echo $web_acl | jq -r '.WebACL.Id')"
echo "Description: $(echo $web_acl | jq -r '.WebACL.Description')"
# Show list of rules
rules=$(echo $web_acl | jq -r '.WebACL.Rules[] | .Name')
if [ -z "$rules" ]; then
echo "No rules found"
else
echo "List of rules:"
for rule in $rules; do
echo " - $rule"
done
fi
# Show default action
default_action=$(echo $web_acl | jq -r '.WebACL.DefaultAction')
echo "Default action: $(echo $default_action | jq -r 'keys[]')"
# Show CAPTCHA settings
captcha_config=$(echo $web_acl | jq -r '.WebACL.CaptchaConfig')
if [ "$captcha_config" != "null" ]; then
captcha_immunity_time=$(echo $captcha_config | jq -r '.ImmunityTimeProperty.ImmunityTime')
if [ "$captcha_immunity_time" == "null" ]; then
captcha_immunity_time=300
fi
else
captcha_immunity_time=300
fi
echo "Immunity time for CAPTCHA settings: $captcha_immunity_time"
# Show challenge settings
challenge_config=$(echo $web_acl | jq -r '.WebACL.ChallengeConfig')
if [ "$challenge_config" != "null" ]; then
challenge_immunity_time=$(echo $challenge_config | jq -r '.ImmunityTimeProperty.ImmunityTime')
if [ "$challenge_immunity_time" == "null" ]; then
challenge_immunity_time=300
fi
else
challenge_immunity_time=300
fi
echo "Immunity time for challenge settings: $challenge_immunity_time"
# Show token domain list
token_domain_list=$(echo $web_acl | jq -r '.WebACL.TokenDomains[]?')
if [ -z "$token_domain_list" ]; then
echo "No token domains found"
else
echo "Token domain list:"
for domain in $token_domain_list; do
echo " - $domain"
done
fi
# Display a list of resources associated with the Web ACL
resource_types=("APPLICATION_LOAD_BALANCER" "API_GATEWAY" "APPSYNC" "COGNITO_USER_POOL" "APP_RUNNER_SERVICE" "VERIFIED_ACCESS_INSTANCE")
echo "Associated resources:"
resource_found=false
all_resources=()
for resource_type in "${resource_types[@]}"; do
associated_resources=$(aws wafv2 list-resources-for-web-acl --web-acl-arn $web_acl_arn --resource-type $resource_type --region $region --query 'ResourceArns[]' --output json)
if [ "$(echo $associated_resources | jq '. | length')" -gt 0 ]; then
resource_found=true
all_resources+=" $resource_type\n$(echo $associated_resources | jq -c '.[]')\n\n"
echo " $resource_type:"
for resource in $(echo $associated_resources | jq -r '.[]'); do
echo " - $resource:"
done
fi
done
if [ "$resource_found" = false ]; then
echo "No associated resources found"
fi
# Get logging settings
logging_configuration=$(aws wafv2 get-logging-configuration --resource-arn $web_acl_arn --region $region)
if [ -z "$logging_configuration" ]; then
echo "No logging configuration found"
else
echo "Logging configuration ARN: $(echo $logging_configuration | jq -r '.LoggingConfiguration.ResourceArn')"
echo "Send logs to: $(echo $logging_configuration | jq -r '.LoggingConfiguration.LogDestinationConfigs[]?')"
echo "Excluded fields: $(echo $logging_configuration | jq -r '.LoggingConfiguration.RedactedFields[]?')"
fi
# Get sampled request settings
sampled_requests_enabled=$(echo $web_acl | jq -r '.WebACL.VisibilityConfig.SampledRequestsEnabled')
echo "Sampled requests enabled: $sampled_requests_enabled"
# Display the mapping between CloudWatch Metrics rule names and metric names
echo "CloudWatch Metrics rule-to-metric mapping:"
echo $web_acl | jq -r '.WebACL.Rules[] | "- Rule name: \(.Name), Metric name: \(.VisibilityConfig.MetricName)"'
echo -e "\n"
# File output
echo "$web_acl" > "$region/get-web-acl_$(echo $web_acl_arn | awk -F'/' '{print $(NF-1)}').json"
echo -e "$all_resources" > "$region/list-resources-for-web-acl_$(echo $web_acl_arn | awk -F'/' '{print $(NF-1)}').json"
echo "$logging_configuration" > "$region/get-logging-configuration_$(echo $web_acl_arn | awk -F'/' '{print $(NF-1)}').json"
done
fi
# Get list of IP sets for each region
ip_sets=$(aws wafv2 list-ip-sets --scope REGIONAL --region $region --query 'IPSets[].ARN' --output text)
if [ -z "$ip_sets" ];then
echo "No IP sets found"
else
echo "IP sets:"
for ip_set in $ip_sets; do
echo " - $ip_set"
ip_set_details=$(aws wafv2 get-ip-set --name $(echo $ip_set | awk -F'/' '{print $(NF-1)}') --scope REGIONAL --id $(echo $ip_set | awk -F'/' '{print $NF}') --region $region)
echo "Name: $(echo $ip_set_details | jq -r '.IPSet.Name')"
echo "Description: $(echo $ip_set_details | jq -r '.IPSet.Description')"
echo "IP version: $(echo $ip_set_details | jq -r '.IPSet.IPAddressVersion')"
echo "List of IPs: $(echo $ip_set_details | jq -r '.IPSet.Addresses | join(", ")')"
echo -e "\n"
# File output
echo "$ip_set_details" > "$region/get-ip-set_$(echo $ip_set | awk -F'/' '{print $(NF-1)}').json"
done
fi
# Get list of regular expression pattern sets for each region
regex_pattern_sets=$(aws wafv2 list-regex-pattern-sets --scope REGIONAL --region $region --query 'RegexPatternSets[].ARN' --output text)
if [ -z "$regex_pattern_sets" ];then
echo "No regular expression pattern sets found"
else
echo "Regular expression pattern sets:"
for regex_pattern_set in $regex_pattern_sets; do
echo " - $regex_pattern_set"
regex_pattern_set_details=$(aws wafv2 get-regex-pattern-set --name $(echo $regex_pattern_set | awk -F'/' '{print $(NF-1)}') --scope REGIONAL --id $(echo $regex_pattern_set | awk -F'/' '{print $NF}') --region $region)
echo "Name: $(echo $regex_pattern_set_details | jq -r '.RegexPatternSet.Name')"
echo "Description: $(echo $regex_pattern_set_details | jq -r '.RegexPatternSet.Description')"
echo "List of regular expression patterns:"
echo $regex_pattern_set_details | jq -r '.RegexPatternSet.RegularExpressionList[] | " - \(.RegexString)"'
echo -e "\n"
# File output
echo "$regex_pattern_set_details" > "$region/get-regex-pattern-set_$(echo $regex_pattern_set | awk -F'/' '{print $(NF-1)}').json"
done
fi
# Get list of rule groups for each region
rule_groups=$(aws wafv2 list-rule-groups --scope REGIONAL --region $region --query 'RuleGroups[].ARN' --output text)
if [ -z "$rule_groups" ];then
echo "No rule groups found"
else
echo "Rule groups:"
for rule_group in $rule_groups; do
echo " - $rule_group"
rule_group_details=$(aws wafv2 get-rule-group --name $(echo $rule_group | awk -F'/' '{print $(NF-1)}') --scope REGIONAL --id $(echo $rule_group | awk -F'/' '{print $NF}') --region $region)
echo "Name: $(echo $rule_group_details | jq -r '.RuleGroup.Name')"
echo "Description: $(echo $rule_group_details | jq -r '.RuleGroup.Description')"
echo "Capacity: $(echo $rule_group_details | jq -r '.RuleGroup.Capacity')"
echo "List of rules:"
echo $rule_group_details | jq -r '.RuleGroup.Rules[] | " - \(.Name)"'
echo -e "\n"
# File output
echo "$rule_group_details" > "$region/get-rule-group_$(echo $rule_group | awk -F'/' '{print $(NF-1)}').json"
done
fi
done
How to use
Run the script with the command below.
sh get-awswaf.sh
Use redirection if you want to save the script execution log as a file.
sh get-awswaf.sh > get-awswaf.logging
Output
The standard output looks like this. You can check the settings at a glance.
Region: ap-northeast-1
Web ACLs:
- arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/XXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXX
Name: XXXXXXXXXXXXXXXXXXXXXXXXX
ID: XXXXXXXXXXXXXXXXXXXXXXXXX
Description:
List of rules:
- XXXXXXXXXXXXXXXXXXXXXXXXX
Default action: Allow
CAPTCHA setting immunity time: 300
Challenge setting immunity time: 300
No token domains found
No associated resources found
Logging configuration ARN: arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/XXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXX
Send logs to: arn:aws:s3:::XXXXXXXXXXXXXXXXXXXXXXXX
Excluded fields:
Sampled requests enabled: true
CloudWatch Metrics rule-to-metric mapping:
- Rule name: XXXXXXXXXXXXXXXXXXXXXXX, Metric name: XXXXXXXXXXXXXXXXXXXXXXXXX
--- Omitted ---
IP sets:
- arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/ipset/XXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXX
Name: XXXXXXXXXXXXXXXXXXXXXXXXX
Description: XXXXXXXXXX
IP version: IPV4
List of IPs: XX.XX.XXX.XXX/XX, XXX.XXX.XX.XXX/XX
--- Omitted ---
Regular expression pattern sets:
- arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/regexpatternset/XXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXX
Name: XXXXXXXXXXXXXXXXXXXXXXXXX
Description:
List of regular expression patterns:
- .*\.(css|pdf|jpg|gif|txt|png|ico|js|jpeg|jpe)
--- Omitted ---
Rule groups:
- arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/rulegroup/XXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXX
Name: XXXXXXXXXXXXXXXXXXXXXXXXX
Description: XXXXXXXXXXXXXXXXXXXXXXX
Capacity: 100
List of rules:
- XXXXXXXXXXXXXXXXXXXXXXXXX
--- Omitted ---
A directory will be created for each region as shown below.
[cloudshell-user@ip-XX-XXX-XXX-XXX ~]$ ls -l
total 136
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:55 ap-northeast-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:55 ap-northeast-2
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:55 ap-northeast-3
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:54 ap-south-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:55 ap-southeast-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:56 ap-southeast-2
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:55 ca-central-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:56 eu-central-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:54 eu-north-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:54 eu-west-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:54 eu-west-2
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:54 eu-west-3
-rw-r--r--. 1 cloudshell-user cloudshell-user 20617 Jun 7 01:56 get-awswaf.log
-rw-r--r--. 1 cloudshell-user cloudshell-user 9535 Jun 7 01:54 get-awswaf.sh
-rw-r--r--. 1 cloudshell-user cloudshell-user 29389 Jun 7 02:00 get-awswaf.zip
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:55 sa-east-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:56 us-east-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:56 us-east-2
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:56 us-west-1
drwxr-xr-x. 2 cloudshell-user cloudshell-user 4096 Jun 7 01:56 us-west-2
[cloudshell-user@ip-XX-XXX-XXX-XXX ~]$
JSON will be saved in the region directory as shown below.
[cloudshell-user@ip-XX-XXX-XXX-XXX ~]$ ls -l ap-northeast-1/
total 160
-rw-r--r--. 1 cloudshell-user cloudshell-user 3567 Jun 7 01:55 get-ip-set_XXXXXXXXXXXXXXXXXXXXXXXXX.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 424 Jun 7 01:55 get-logging-configuration_XXXXXXXXXXXXXXXXXXXXXXXXX.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 666 Jun 7 01:55 get-regex-pattern-set_XXXXXXXXXXXXXXXXXXXXXXXXX.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 2712 Jun 7 01:55 get-rule-group_XXXXXXXXXXXXXXXXXXXXXXXXX.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 5493 Jun 7 01:55 get-web-acl_XXXXXXXXXXXXXXXXXXXXXXXXX.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 1 Jun 7 01:55 list-resources-for-web-acl_XXXXXXXXXXXXXXXXXXXXXXXXX.json
[cloudshell-user@ip-XX-XXX-XXX-XXX ~]$