214 lines
6.6 KiB
Bash
214 lines
6.6 KiB
Bash
#!/bin/bash
|
|
|
|
###
|
|
# https://github.com/sefinek/UFW-AbuseIPDB-Reporter
|
|
##
|
|
|
|
LOG_FILE="/var/log/ufw.log"
|
|
ENCODED_API_KEY_FILE="./.abuseipdb_token"
|
|
REPORTED_IPS_FILE="/tmp/ufw-abuseipdb-reporter.cache"
|
|
REPORT_INTERVAL=43200 # 12h (in seconds)
|
|
|
|
declare -A reported_ips
|
|
|
|
log() {
|
|
local level="$1"
|
|
local message="$2"
|
|
echo "[$level] $message"
|
|
}
|
|
|
|
# Check if the API key file exists and decode it
|
|
if [[ -f "$ENCODED_API_KEY_FILE" ]]; then
|
|
DECODED_API_KEY=$(openssl enc -d -base64 -in "$ENCODED_API_KEY_FILE")
|
|
if [[ -z "$DECODED_API_KEY" ]]; then
|
|
log "ERROR" "Failed to decode API key from $ENCODED_API_KEY_FILE"
|
|
exit 1
|
|
fi
|
|
else
|
|
log "ERROR" "API key file not found at $ENCODED_API_KEY_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
ABUSEIPDB_API_KEY="$DECODED_API_KEY"
|
|
|
|
# Check if jq, curl, or wget packages are available
|
|
if ! command -v jq &> /dev/null; then
|
|
log "ERROR" "jq is not installed. Please install jq to run this script."
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then
|
|
log "ERROR" "Neither curl nor wget is available. Please install one of them to continue."
|
|
exit 1
|
|
fi
|
|
|
|
load_reported_ips() {
|
|
if [[ -f "$REPORTED_IPS_FILE" ]]; then
|
|
while IFS= read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
IFS=' ' read -r ip report_time <<< "$line"
|
|
if [[ -n "$ip" && -n "$report_time" ]]; then
|
|
reported_ips["$ip"]=$report_time
|
|
else
|
|
log "WARN" "Invalid line format: '$line'"
|
|
fi
|
|
done < "$REPORTED_IPS_FILE"
|
|
log "INFO" "Loaded ${#reported_ips[@]} IPs from $REPORTED_IPS_FILE"
|
|
else
|
|
log "INFO" "$REPORTED_IPS_FILE does not exist. No data to load."
|
|
fi
|
|
}
|
|
|
|
save_reported_ips() {
|
|
: > "$REPORTED_IPS_FILE"
|
|
for ip in "${!reported_ips[@]}"; do
|
|
echo "$ip ${reported_ips[$ip]}" >> "$REPORTED_IPS_FILE"
|
|
done
|
|
}
|
|
|
|
is_local_ip() {
|
|
local ip="$1"
|
|
[[ "$ip" =~ ^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.|fc|fd|fe80|::1) ]]
|
|
}
|
|
|
|
report_to_abuseipdb() {
|
|
local ip="$1" categories="$2" proto="$3" spt="$4" dpt="$5" ttl="$6" len="$7" tos="$8" timestamp="$9"
|
|
|
|
local comment="Blocked by UFW ($proto on $dpt)
|
|
Source port: $spt"
|
|
|
|
[[ -n "$ttl" ]] && comment+="
|
|
TTL: $ttl"
|
|
|
|
[[ -n "$len" ]] && comment+="
|
|
Packet length: $len"
|
|
|
|
[[ -n "$tos" ]] && comment+="
|
|
TOS: $tos"
|
|
|
|
comment+="
|
|
|
|
This report (for $ip) was generated by:
|
|
https://github.com/sefinek/UFW-AbuseIPDB-Reporter" # Please do not remove the URL to the repository of this script. I would be really grateful. 💙
|
|
|
|
local res
|
|
if command -v curl >/dev/null 2>&1; then
|
|
res=$(curl -s -X POST "https://api.abuseipdb.com/api/v2/report" \
|
|
--data-urlencode "ip=$ip" \
|
|
--data-urlencode "categories=$categories" \
|
|
--data-urlencode "comment=$comment" \
|
|
-H "Key: $ABUSEIPDB_API_KEY" \
|
|
-H "Accept: application/json")
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
res=$(wget -qO- --post-data="ip=$ip&categories=$categories&comment=$comment" \
|
|
--header="Key: $ABUSEIPDB_API_KEY" \
|
|
--header="Accept: application/json" \
|
|
"https://api.abuseipdb.com/api/v2/report")
|
|
else
|
|
log "ERROR" "Neither curl nor wget is available to send the report."
|
|
return 1
|
|
fi
|
|
|
|
local abuse_confidence_score
|
|
abuse_confidence_score=$(echo "$res" | jq -r '.data.abuseConfidenceScore')
|
|
|
|
if [[ "$abuse_confidence_score" =~ ^[0-9]+$ ]]; then
|
|
log "INFO" "Successfully reported IP $ip to AbuseIPDB (score $abuse_confidence_score)"
|
|
return 0
|
|
else
|
|
log "ERROR" "Failed to report IP $ip to AbuseIPDB: $res"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
is_ip_reported_recently() {
|
|
local ip="$1"
|
|
local current_time
|
|
current_time=$(date +%s)
|
|
|
|
if [[ -v reported_ips["$ip"] ]]; then
|
|
local report_time=${reported_ips["$ip"]}
|
|
(( current_time - report_time < REPORT_INTERVAL )) && return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
mark_ip_as_reported() {
|
|
local ip="$1"
|
|
reported_ips["$ip"]=$(date +%s)
|
|
}
|
|
|
|
determine_categories() {
|
|
local proto="$1"
|
|
local dpt="$2"
|
|
|
|
# See: https://www.abuseipdb.com/categories
|
|
case "$proto" in
|
|
"TCP")
|
|
case "$dpt" in
|
|
22) echo "14,22,18" ;; # Port Scan | SSH | Brute-Force
|
|
80 | 443 | 8080) echo "14,21" ;; # Port Scan | Web App Attack
|
|
25) echo "14,11" ;; # Port Scan | Email Spam
|
|
21) echo "14,5,18" ;; # Port Scan | FTP Brute-Force | Brute-Force
|
|
53) echo "14,1,2" ;; # Port Scan | DNS Compromise | DNS Poisoning
|
|
23 | 3389) echo "14,15,18" ;; # Port Scan | Hacking | Brute-Force
|
|
3306) echo "14,16" ;; # Port Scan | SQL Injection
|
|
6666 | 6667 | 6668 | 6669) echo "14,8" ;; # Port Scan | Fraud VoIP
|
|
9999) echo "14,6" ;; # Port Scan | Ping of Death
|
|
*) echo "14" ;; # Port Scan
|
|
esac
|
|
;;
|
|
"UDP")
|
|
case "$dpt" in
|
|
53) echo "14,1,2" ;; # Port Scan | DNS Compromise | DNS Poisoning
|
|
123) echo "14,17" ;; # Port Scan | Spoofing
|
|
*) echo "14" ;; # Port Scan
|
|
esac
|
|
;;
|
|
*) echo "14" ;; # Port Scan
|
|
esac
|
|
}
|
|
|
|
process_log_line() {
|
|
local line="$1"
|
|
if [[ "$line" == *"[UFW BLOCK]"* ]]; then
|
|
local timestamp src_ip proto spt dpt ttl len tos categories
|
|
|
|
timestamp=$(echo "$line" | awk '{print $1, $2, $3}')
|
|
[[ -z "$timestamp" ]] && timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
src_ip=$(echo "$line" | grep -oP 'SRC=\K[^\s]+')
|
|
|
|
if is_local_ip "$src_ip"; then
|
|
log "INFO" "Ignoring local IP: $src_ip"
|
|
return
|
|
fi
|
|
|
|
proto=$(echo "$line" | grep -oP 'PROTO=\K[^\s]+')
|
|
spt=$(echo "$line" | grep -oP 'SPT=\K[^\s]+')
|
|
dpt=$(echo "$line" | grep -oP 'DPT=\K[^\s]+')
|
|
ttl=$(echo "$line" | grep -oP 'TTL=\K[^\s]+')
|
|
len=$(echo "$line" | grep -oP 'LEN=\K[^\s]+')
|
|
tos=$(echo "$line" | grep -oP 'TOS=\K[^\s]+')
|
|
|
|
if is_ip_reported_recently "$src_ip"; then
|
|
log "INFO" "IP $src_ip ($proto) was reported recently"
|
|
return
|
|
fi
|
|
|
|
categories=$(determine_categories "$proto" "$dpt")
|
|
|
|
log "INFO" "Reporting IP $src_ip ($proto $dpt) with categories $categories..."
|
|
if report_to_abuseipdb "$src_ip" "$categories" "$proto" "$spt" "$dpt" "$ttl" "$len" "$tos" "$timestamp"; then
|
|
mark_ip_as_reported "$src_ip"
|
|
save_reported_ips
|
|
fi
|
|
fi
|
|
}
|
|
|
|
load_reported_ips
|
|
|
|
log "INFO" "Starting to monitor $LOG_FILE"
|
|
|
|
tail -Fn0 "$LOG_FILE" | while read -r line; do
|
|
process_log_line "$line"
|
|
done
|