firewall/firewall.sh
Your Name 13864fc39f s
2024-09-25 03:17:02 +00:00

619 lines
16 KiB
Bash
Executable File

#!/bin/bash
DATE="$(date +%d/%b/%Y:%H:%M -d '1 minute ago')"
MY_IP=($(redis-cli --raw SMEMBERS my_ip))
ATTACK_THRESHOLD="50"
NGINX_ACCESS="/tmp/access.log"
ACCESS="/tmp/minute.log"
HTTP_LIMIT="100"
RATE_LIMITED_HTTP="30"
MODULES="/opt/firewall/modules"
grep $DATE $NGINX_ACCESS >$ACCESS
#Firewall Port Configuration
#
declare -A portConfig
portConfig["https"]="443"
portConfig["http"]="80"
portConfig["cups"]="631"
portConfig["WireGuard"]="57692"
portConfig["AdGuard-1"]="3000"
portConfig["AdGuard-2"]="8082"
portConfig["AdGuard-3"]="853"
portConfig["Uptime"]="4001"
portConfig["DNS-1"]="53"
portConfig["DNS-2"]="67"
portConfig["DNS-3"]="68"
portConfig["CUPS-1"]="631"
portConfig["CUPS-2"]="5353"
#portConfig["Bitcoin-1"]="8333"
#portConfig["Bitcoin-2"]="8332"
#portConfig["Bitcoin-3"]="8333"
#portConfig["Bitcoin-4"]="4050"
#portConfig["KDE-Connect"]="1714-1764"
#portConfig["Lightning-1"]="10009"
#portConfig["Lightning-1"]="9735"
#portConfig["Lightning-2"]="8080"
#portConfig["Lightning-3"]="28334"
#portConfig["Lightning-4"]="28333"
#portConfig["Lightning-5"]="19998"
#portConfig["Lightning-6"]="29000"
portConfig["SyncThing-1"]="22000"
portConfig["SyncThing-2"]="8384"
portConfig["SyncThing-3"]="21027"
#portConfig["NFS-1"]="2049"
#portConfig["NFS-2"]="111"
portConfig["Jellyfin-1"]="8096"
portConfig["Jellyfin-1"]="7359"
portConfig["SSH"]="22"
MACHINES=(127.0.0.1)
VIRT_BRIDGE="virbr0"
#### NFT CONFIG ####
#
NFT='/usr/bin/nft'
NFT_CACHE='/tmp/nft.cache'
TMP_BLOCK=($(redis-cli --raw SMEMBERS tmp_block))
#Redis DB
#
SAVED_BOTS=($(redis-cli --raw SMEMBERS bots))
CRAWLER_DB=($(redis-cli --raw SMEMBERS crawlers))
SAFE_TRAFFIC=($(redis-cli --raw SMEMBERS safe_traffic))
RULE_SET='/opt/firewall/nft.rules'
MENU_TOP="=============================FireWall================================="
MENU_BOTTOM="====================================================================="
nft list table filter >$NFT_CACHE
ipBlockParser() {
if [[ "$1" == *":"* ]]; then
$NFT insert rule ip6 filter input position 0 ip6 saddr $1 drop
else
$NFT insert rule ip filter input position 0 ip saddr "$1" drop
fi
}
portOpenParser() {
if [[ "$1" == "443" || "$1" == "80" ]]; then
echo "Skipping $1"
else
$NFT add rule ip filter input position 0 tcp dport $1 accept
$NFT add rule ip filter input position 0 udp dport $1 accept
$NFT add rule ip6 filter input position 0 tcp dport $1 accept
$NFT add rule ip6 filter input position 0 udp dport $1 accept
fi
}
ipDeleteParser() {
if [[ "$1" == *":"* ]]; then
$NFT delete rule ip6 filter input handle $HANDLE &>/dev/null
else
$NFT delete rule ip filter input handle $HANDLE &>/dev/null
fi
redis-cli SREM tmp_block $i
redis-cli SREM bots $i
}
blockCountry() {
DATA=($(redis-cli SMEMBERS country_ip))
for i in "${DATA[@]}"; do
echo "Blocking $i"
ipBlockParser $i
done
}
wireguard-networking() {
$NFT add table nat
$NFT add chain nat postrouting
$NFT add rule nat postrouting oif wg0 iif enp11s0
$NFT add rule nat postrouting oif enp11s0 iif wg0
$NFT add rule nat postrouting masquerade
$NFT add rule filter forward iifname wg0 oif enp11s0 accept
$NFT add rule filter forward iifname enp11s0 oif wg0 accept
$NFT add rule ip filter input ip saddr 192.168.5.0/24 accept
}
attacker-protection() {
watch
module-nostr
bot-search
}
bot-search() {
CRAWLERS=($(grep $DATE $ACCESS | grep -vi $MY_IP | grep -Evi 'Guro|spank|report|rape|block' | grep -Ff <(printf '%s\n' "${CRAWLER_DB[@]}") | grep -Fivf <(printf '%s\n' "${SAFE_TRAFFIC[@]}") | cut -d "-" -f1 | sort -u))
echo
echo "Processing Web Crawler list into NFT....."
echo
for i in "${CRAWLERS[@]}"; do
CHECK=$(cat $NFT_CACHE | grep $i)
if [ "$CHECK" = "" ]; then
ipBlockParser $i
redis-cli SADD bots $i
else
echo
echo "Skipping Duplicate IP $i"
echo
fi
done
}
basic-security() {
$NFT flush ruleset
$NFT -f /opt/firewall/ipv4-filter.nft
$NFT -f /opt/firewall/ipv6-filter.nft
$NFT add rule filter input icmp type echo-request drop
$NFT rule filter output accept
$NFT rule filter forward accept
$NFT insert rule filter input ct state established accept
$NFT insert rule filter input iif lo accept
for i in "${!portConfig[@]}"; do
echo "Enabling Port for: $i"
portOpenParser "${portConfig[$i]}"
done
#$NFT add rule filter input drop
#$NFT add rule ip6 filter input drop
}
virtualization() {
ip link del virbr0
killall dnsmasq
/usr/bin/systemctl restart libvirtd
/usr/bin/virsh net-start default
/usr/bin/systemctl restart pleroma
$NFT insert rule filter input iif $VIRT_BRIDGE accept
}
trust() {
for i in "${MACHINES[@]}"; do
$NFT add rule filter input ip saddr $i accept
done
}
import-saved() {
for i in "${SAVED_BOTS[@]}"; do
ipBlockParser $i
done
}
start() {
basic-security
if [[ $HOSTNAME == *"nas"* ]]; then
sysctl -w net.ipv4.conf.all.forwarding=1
import-saved
blockCountry
wireguard-networking
docker restart uptime-kuma
#Docker
$NFT insert rule filter input iif docker0 accept
#HTTP Rate Limit
rateLimit $HTTP_LIMIT
else
virtualization
fi
}
research() {
STATS=($(redis-cli --raw SMEMBERS tmp_block | sort -u))
for i in "${STATS[@]}"; do
echo $MENU_TOP
echo " [Researching $i] "
echo
DATA=$(grep $i $NGINX_LOG | grep -Fivf <(printf '%s\n' "${SAFE_TRAFFIC[@]}"))
echo "$DATA"
echo
echo $MENU_BOTTOM
echo
read -p 'Press Enter to Continue ' -e
done
}
automaticStatus() {
status
sleep 30
automaticStatus
}
status() {
clear
sleep 2
RATE=$(cat $NFT_CACHE | grep 443 | cut -d ' ' -f14)
DATE="$(date +%d/%b/%Y:%H:%M -d '1 minute ago')"
STATS=$(grep $DATE $ACCESS | grep -vi $MY_IP | wc -l)
GET=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep GET | wc -l)
POST=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep POST | wc -l)
PUT=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep -i PUT | wc -l)
NOT_FOUND=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep 404 | wc -l)
GATEWAY=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep 502 | wc -l)
SUCCESS=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep 200 | wc -l)
CRAWL=$(grep $DATE $ACCESS | grep -vi $MY_IP | grep -Ff <(printf '%s\n' "${CRAWLER_DB[@]}") | wc -l)
echo $MENU_TOP
echo "Attack Threshold: $ATTACK_THRESHOLD"
echo "Firewall Rules: $($NFT list table filter | wc -l)"
echo "Rate Limit: $RATE"
echo
echo "Traffic Last Minute: $STATS"
echo " GET: $GET"
echo " PUT: $PUT"
echo " POST: $POST"
echo " Crawlers: $CRAWL"
echo
echo "Query Stats:: "
echo " 200: $SUCCESS"
echo " 404: $NOT_FOUND"
echo " 502: $GATEWAY"
echo
#echo "Dropped Traffic: $($NFT list table filter | grep -Ei 'log counter packets' | cut -d ' ' -f6)"
echo
echo "Rate-limited IP's:"
echo
redis-cli --raw SMEMBERS tmp_block | sort -u
echo $MENU_BOTTOM
}
stop() {
#forgive
#$NFT -s list ruleset | tee $RULE_SET
$NFT flush ruleset
$NFT -f /usr/share/nftables/ipv4-filter.nft
$NFT add rule filter input icmp type echo-request accept
$NFT rule filter input accept
$NFT rule filter output accept
$NFT rule filter forward accept
$NFT insert rule filter input ct state established accept
$NFT insert rule filter input iif lo accept
$NFT -f /opt/firewall/ipv6-filter.nft
# $NFT add rule ip6 filter input icmpv6 type nd-neighbor-solicit accept
# $NFT add rule ip6 filter input icmpv6 type nd-router-advert accept
message "Stopping Firewall"
}
rateLimit() {
HANDLE=($(nft -n -a list ruleset | grep "ct state 0x8 tcp dport" | grep -E '80|443' | grep handle | cut -d '#' -f2 | cut -d ' ' -f3))
for i in "${HANDLE[@]}"; do
if [[ "$i" == *":"* ]]; then
$NFT delete rule ip6 filter input handle $i &>/dev/null
else
$NFT delete rule filter input handle $i &>/dev/null
fi
done
echo "Setting Rate Limit to : $1"
echo
$NFT add rule ip6 filter input ct state new tcp dport 443 update @http_ratelimit { ip6 saddr limit rate $1/second } accept
$NFT add rule ip6 filter input ct state new tcp dport 80 update @http_ratelimit { ip6 saddr limit rate $1/second } accept
$NFT add rule ip filter input ct state new tcp dport 443 update @http_ratelimit { ip saddr limit rate $1/second } accept
$NFT add rule ip filter input ct state new tcp dport 80 update @http_ratelimit { ip saddr limit rate $1/second } accept
}
forgive() {
IP=($(redis-cli --raw SMEMBERS tmp_block | sort -u))
echo $IP
for i in "${IP[@]}"; do
HANDLE=$(nft -n -a list ruleset | grep $i | grep handle | cut -d '#' -f2 | cut -d ' ' -f3)
echo "Removing: $i Handle: $HANDLE"
ipDeleteParser $HANDLE
done
echo "Clearing old $TMP_BLOCK"
redis-cli DEL tmp_blocked
rateLimit $HTTP_LIMIT
}
module-akkoma-accounts() {
ATTACK="module-akkoma-accounts DDOS Attack "
SEARCH_SPAM=$(grep $2 $ACCESS | grep "api/v1/accounts" | grep $1 | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $1)
if [[ "$SEARCH_SPAM" -gt 30 ]]; then
echo "$IP $CHECK $COUNT"
if [ "$CHECK" = "" ]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $1
message "$ATTACK $1"
echo "$ATTACK $1"
else
echo "$ATTACK Ignoring Duplicate IP: $1"
fi
fi
}
module-akkoma-search() {
ATTACK="module-akkoma-search DDOS Attack "
SEARCH_SPAM=$(grep $2 $ACCESS | grep "api/v2/search" | grep $1 | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $1)
if [[ "$SEARCH_SPAM" -gt 10 ]]; then
echo "$IP $CHECK $COUNT"
if [ "$CHECK" = "" ]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $1
message "$ATTACK $1"
echo "$ATTACK $1"
else
echo "$ATTACK Ignoring Duplicate IP: $1"
fi
fi
}
module-akkoma-instance() {
ATTACK="module-akkoma-instance DDOS Attack "
SEARCH_SPAM=$(grep $2 $ACCESS | grep -E "api/v1/instance" | grep $1 | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $1)
if [[ "$SEARCH_SPAM" -gt 30 ]]; then
echo "$IP $CHECK $COUNT"
if [ "$CHECK" = "" ]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $1
message "$ATTACK $1"
echo "$ATTACK $1"
else
echo "$ATTACK Ignoring Duplicate IP: $1"
fi
fi
}
module-akkoma-timeline-public() {
ATTACK="module-akkoma-timeline-public DDOS Attack "
SEARCH_SPAM=$(grep $2 $ACCESS | grep "timelines/public" | grep $1 | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $1)
if [[ "$SEARCH_SPAM" -gt 30 ]]; then
echo "$IP $CHECK $COUNT"
if [ "$CHECK" = "" ]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $1
message "$ATTACK $1"
echo "$ATTACK $1"
else
echo "$ATTACK Ignoring Duplicate IP: $1"
fi
fi
}
module-akkoma-timeline-home() {
ATTACK="module-akkoma-timeline-home DDOS Attack "
SEARCH_SPAM=$(grep $2 $ACCESS | grep -E "timelines/home" | grep $1 | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $1)
if [[ "$SEARCH_SPAM" -gt 30 ]]; then
echo "$IP $CHECK $COUNT"
if [ "$CHECK" = "" ]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $1
message "$ATTACK $1"
echo "$ATTACK $1"
else
echo "$ATTACK Ignoring Duplicate IP: $1"
fi
fi
}
module-get-spam() {
ATTACK="module-get-spam DDOS Attack "
GET_SPAM=$(grep $2 $ACCESS | grep -E "GET / HTTP" | wc -l)
if [[ "$GET_SPAM" -gt 20 ]]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $1
message "$ATTACK $1"
fi
}
module-lightning() {
ATTACK="module-lightning DDOS Attack "
LN_SPAM=$(grep $2 $ACCESS | grep "lnurlp/verita84" | wc -l)
if [[ "$LN_SPAM" -gt 5 ]]; then
ipBlockParser "$1"
message "$ATTACK $1"
redis-cli SADD tmp_block $1
fi
}
watch() {
echo "Scanning $DATE"
echo
IP=($(grep $DATE $ACCESS | grep -Fivf <(printf '%s\n' "${SAFE_TRAFFIC[@]}") | grep -Fivf <(printf '%s\n' "${CRAWLER_DB[@]}") | grep -Fivf <(printf '%s\n' "${SAVED_BOTS[@]}") | grep -vi $MY_IP | grep -vi '127.0.0.1' | cut -d ' ' -f1 | sort -u))
for i in "${IP[@]}"; do
module-akkoma-instance "$i" "$DATE"
module-akkoma-timeline-public "$i" "$DATE"
module-akkoma-timeline-home "$i" "$DATE"
module-akkoma-accounts "$i" "$DATE"
module-akkoma-search "$i" "$DATE"
module-lightning "$i" "$DATE"
bash $MODULES/module-php.sh "$i" "$DATE"
bash $MODULES/module-go.sh "$i" "$DATE"
module-get-spam "$i" "$DATE"
COUNT=$(grep $DATE $ACCESS | grep $i | grep -Fivf <(printf '%s\n' "${SAFE_TRAFFIC[@]}") | grep -Fivf <(printf '%s\n' "${SAVED_BOTS[@]}") | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $i)
if [[ "$COUNT" -gt $ATTACK_THRESHOLD ]]; then
if [ "$CHECK" = "" ]; then
echo "Danger! Blocking IP: $i Count: $COUNT"
logger "Blocking IP: $i with a count of: $COUNT"
redis-cli SADD tmp_block $i
ipBlockParser $i
message "Blocking IP: $i with a count of: $COUNT"
else
echo
echo "Skipping Duplicate IP"
echo
fi
else
echo
echo "$i count: $COUNT below Threshhold: $ATTACK_THRESHOLD"
echo
fi
done
BLOCK_CHECK=$(redis-cli --raw SMEMBERS tmp_block)
if [[ "$BLOCK_CHECK" == *"empty"* || "$BLOCK_CHECK" == "" ]]; then
rateLimit $HTTP_LIMIT
else
rateLimit $RATE_LIMITED_HTTP
fi
}
message() {
echo "$1" | /root/go/bin/algia dm-post -u 33c74427f3b2b73d5e38f3e6c991c122a55d204072356f71da49a0e209fb6940 --stdin
}
module-nostr() {
IP=($(grep $DATE $ACCESS | grep "/block=" | cut -d '=' -f2 | cut -d ' ' -f1 | sed 's/"//'))
for i in "${IP[@]}"; do
echo $i
if [[ "$i" == *"npub"* ]]; then
bash /opt/strfry-policies/block.sh $i
else
echo "No Npubs to block"
fi
done
}
test-bots() {
for i in "${SAVED_BOTS[@]}"; do
DATA=$(grep $i $ACCESS | grep -Fivf <(printf '%s\n' "${CRAWLER_DB[@]}"))
if [ "$DATA" = "" ]; then
echo "No Data. Probably OK"
else
echo $DATA
echo
read -p 'Press Enter to Continue ' -e
fi
done
}
research-ip() {
echo "Enter an IP Address to search"
read -p 'IP Address: ' -e IP
cat $ACCESS | grep $IP
echo
read -p 'Press Enter to Continue ' -e
}
menu() {
clear
echo
echo $MENU_TOP
echo "1. Start"
echo "2. Stop"
echo "3. Reseearch"
echo "4. Forgive"
echo "5. Status"
echo "6. Live Traffic"
echo "7. Test Bot Search Rules"
echo "8. Research IP"
echo "9. View Current Rule Set"
echo "0. Quit"
echo $MENU_BOTTOM
echo
read -p 'Choice: ' CHOICE
echo
if [ "$CHOICE" = "1" ]; then
echo
echo "Starting Firewall"
start
read -p 'Press Enter to Continue ' -e-
elif [ "$CHOICE" = "2" ]; then
echo
echo "Stopping Firewall"
stop
read -p 'Press Enter to Continue ' -e
elif [ "$CHOICE" = "3" ]; then
research
read -p 'Press Enter to Continue ' -e
elif [ "$CHOICE" = "4" ]; then
forgive
elif [ "$CHOICE" = "55" ]; then
automaticStatus
elif [ "$CHOICE" = "5" ]; then
status
read -p 'Press Enter to Continue ' -e
elif [ "$CHOICE" = "6" ]; then
tail -f $ACCESS | grep -Fivf <(printf '%s\n' "${SAFE_TRAFFIC[@]}") | grep -Fivf <(printf '%s\n' "${CRAWLER_DB[@]}")
read -p 'Press Enter to Continue ' -e
elif [ "$CHOICE" = "7" ]; then
test-bots
read -p 'Press Enter to Continue ' -e
elif [ "$CHOICE" = "8" ]; then
research-ip
read -p 'Press Enter to Continue ' -e
elif [ "$CHOICE" = "9" ]; then
nft -s list ruleset | less
elif [ "$CHOICE" = "0" ]; then
exit
fi
echo
menu
}
importDB() {
DATA=($(cat safe.txt))
for i in "${DATA[@]}"; do redis-cli SADD safe_traffic $i; done
DATA=($(cat bots.txt))
for i in "${DATA[@]}"; do redis-cli SADD bots $i; done
DATA=($(cat crawlers.txt))
for i in "${DATA[@]}"; do redis-cli SADD crawlers $i; done
redis-cli SADD my_ip $(curl ifconfig.me)
redis-cli SADD country https://www.ipdeny.com/ipblocks/data/countries/il.zone \
https://www.ipdeny.com/ipblocks/data/countries/cn.zone
COUNTRY=($(redis-cli SMEMBERS country))
for i in "${COUNTRY[@]}"; do
echo
echo "Blocking $i"
DB=($(curl $i))
for j in "${DB[@]}"; do
redis-cli SADD country_ip $j
done
done
}
exportDB() {
rm -f crawlers.txt
rm -f bots.txt
rm -f safe.txt
for i in "${CRAWLER_DB[@]}"; do echo $i >>crawlers.txt; done
for i in "${SAVED_BOTS[@]}"; do echo $i >>bots.txt; done
for i in "${SAFE_TRAFFIC[@]}"; do echo $i >>safe.txt; done
}
if [ "$1" = "start" ]; then
start
elif [ "$1" = "import-db" ]; then
importDB
elif [ "$1" = "export-db" ]; then
exportDB
elif [ "$1" = "virt" ]; then
virtualization
elif [ "$1" = "bot-search" ]; then
bot-search
elif [ "$1" = "attacker-protection" ]; then
attacker-protection
elif [ "$1" = "status" ]; then
automaticStatus
elif [ "$1" = "forgive" ]; then
forgive
elif [ "$1" = "watch" ]; then
watch
elif [ "$1" = "research" ]; then
research
elif [ "$1" = "stop" ]; then
stop
elif [ "$1" = "message" ]; then
message $2
elif [ "$1" = "test" ]; then
test-bots
elif [ "$1" = "message" ]; then
message "$2"
elif [ "$1" = "ipBlockParser" ]; then
ipBlockParser "$2"
elif [ "$1" = "import" ]; then
import-saved
else
menu
fi