I’ve been trying to refine my firewall rules, and have the same question many others have posted: “How can I tell which requests solved a Challenge?”
GraphQL for Firewall Events keeps track of that. With much help from @cbrandt’s tutorial, I started on a script a long time ago to export Firewall Events as a CSV, and have now refined it to export requests that passed Challenge.
Assuming your device has a /tmp directory you can write to, all that’s left to add are a Token and Output file. The script will query the API to get all your zones, then use GraphQL to export all matching events:
#!/bin/bash
## Beginning of setup - assumes you have write access to /tmp ##
# Token for zone list and firewall events
# Only needs Analytics:Read permission for all zones
token="YOUR_TOKEN"
# egrep list of hostnames to exclude from CSV
badhosts="DOMAIN_1|DOMAIN_2"
# Final output file
savefile="/FULL_PATH/FILENAME.csv"
## End of setup ##
# Add headings to new CSV
echo "Host, Date/Time, Action, ASN, Country, IP, HTTP, ReqPath, Query String, Scheme, Referer, RefPath, Source, RuleID, User Agent" > $savefile
# Get Zone list
curl -sX GET "https://api.cloudflare.com/client/v4/zones/?per_page=50" -H "Authorization: Bearer $token" -H "Content-Type: application/json" | jq -r .result[].id > /tmp/zones.txt
## Get filtered firewall events for each zone ##
# Set up query
for zone in `cat /tmp/zones.txt`
do
DATENOW=`date -u +"%Y-%m-%dT%H:%M:%SZ"` # Current Date and Time
DATETHEN=`date -v -1d -u +"%Y-%m-%dT%H:%M:%SZ"` # 24 Hours Earlier
PAYLOAD='{ "query":
"query ListFirewallEvents($zoneTag: string, $filter: FirewallEventsAdaptiveFilter_InputObject) {
viewer {
zones(filter: { zoneTag: $zoneTag }) {
firewallEventsAdaptive(
filter: $filter
limit: 100
orderBy: [datetime_DESC]
) {
clientRequestHTTPHost
datetime
action
clientAsn
clientCountryName
clientIP
clientRequestHTTPProtocol
clientRequestPath
clientRequestQuery
clientRefererScheme
clientRefererHost
clientRefererPath
source
ruleId
userAgent
}
}
}
}",
"variables": {
"zoneTag": "'"$zone"'",
"filter": {
"datetime_geq": "'"$DATETHEN"'",
"datetime_leq": "'"$DATENOW"'",
"action_in": [
"challenge_solved",
"challenge_bypassed",
"js_challenge_solved",
"js_challenge_bypassed",
"managed_challenge_non_interactive_solved",
"managed_challenge_interactive_solved",
"managed_challenge_bypassed"
]
}
}
}'
# Query GraphQL and add to /tmp CSV file
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $token" \
--data "$(echo $PAYLOAD)" \
https://api.cloudflare.com/client/v4/graphql/ \
| jq -r '.data.viewer.zones[].firewallEventsAdaptive[] | [.clientRequestHTTPHost, .datetime, .action, .clientAsn, .clientCountryName, .clientIP, .clientRequestHTTPProtocol, .clientRequestPath, .clientRequestQuery, .clientRefererScheme, .clientRefererHost, .clientRefererPath, .source, .ruleId, .userAgent] | join(",")' >> /tmp/temp.csv
done
# Filter out unwanted host(s) from /tmp CSV file and populate final CSV
egrep -v "$badhosts" /tmp/temp.csv >> $savefile
# Cleanup
/bin/rm /tmp/temp.csv
/bin/rm /tmp/zones.txt
exit
The Actions I use in my filter are described here:
https://developers.cloudflare.com/logs/reference/security-fields/#securityactions
Token instructions are here:
https://developers.cloudflare.com/fundamentals/api/get-started/create-token/