firewall/firewall.sh
Your Name 552f792f6b fix
2024-09-23 23:04:37 -06:00

520 lines
14 KiB
Bash
Executable File

#!/bin/bash
MY_IP=($(redis-cli --raw SMEMBERS my_ip))
ATTACK_THRESHOLD="50"
NGINX_ACCESS="/tmp/access.log"
#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="====================================================================="
#Cache the Date and Current Firewall Rules at every launch
DATE="$(date +%d/%b/%Y:%H:%M -d '1 minute ago')"
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
$NFT add rule ip filter input ct state new tcp dport $1 update @http_ratelimit { ip saddr limit rate 15/second } accept
$NFT add rule ip6 filter input ct state new tcp dport $1 update @http_ratelimit { ip6 saddr limit rate 15/second } accept
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
else
$NFT delete rule ip filter input handle $HANDLE
fi
}
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 $NGINX_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
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
STATS=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | wc -l)
GET=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep GET | wc -l)
POST=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep POST | wc -l)
PUT=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep -i PUT | wc -l)
NOT_FOUND=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep 404 | wc -l)
GATEWAY=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep 502 | wc -l)
SUCCESS=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep 200 | wc -l)
CRAWL=$(grep $DATE $NGINX_ACCESS | grep -vi $MY_IP | grep -Fivf <(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
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"
}
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"
echo $NFT delete rule ip filter input handle $HANDLE
ipDeleteParser $HANDLE
redis-cli SREM tmp_block $i
done
echo "Clearing old $TMP_BLOCK"
redis-cli DEL tmp_blocked
}
module-go() {
GO_SPAM=$(grep $2 $NGINX_ACCESS | grep -E "Go-http-client" | wc -l)
if [[ "$GO_SPAM" -gt 10 ]]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $i
message "Go Spam Attack!"
fi
}
module-akkoma() {
SEARCH_SPAM=$(grep $2 $NGINX_ACCESS | grep -E "api/v1/instance|api/v1/notifications|api/v1/accounts|api/v2/search|timelines/public|timelines/home|/api/v1/accounts" | grep $1 | wc -l)
CHECK=$(cat $NFT_CACHE | sort -u | grep $i)
if [[ "$SEARCH_SPAM" -gt 30 ]]; then
echo "$IP $CHECK $COUNT"
if [ "$CHECK" = "" ]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $i
message "module-akkoma: Spam Attack! $i"
echo "module-akkoma: Spam $1"
else
echo "module-akkoma: Ignoring Duplicate IP: $i"
fi
fi
}
module-get-spam() {
GET_SPAM=$(grep $2 $NGINX_ACCESS | grep -E "GET / HTTP" | wc -l)
if [[ "$GET_SPAM" -gt 5 ]]; then
ipBlockParser "$1"
redis-cli SADD tmp_block $i
message "GET Spam Attack! $1"
fi
}
module-php() {
PHP_SPAM=$(grep $2 $NGINX_ACCESS | grep -E ".php|cgi-bin|wp-content|wp-admin|wp-includes" | wc -l)
if [[ "$PHP_SPAM" -gt 1 ]]; then
ipBlockParser "$1"
message "PHP Attack!"
redis-cli SADD tmp_block $i
fi
}
module-lightning() {
LN_SPAM=$(grep $2 $NGINX_ACCESS | grep "lnurlp/verita84" | wc -l)
if [[ "$LN_SPAM" -gt 5 ]]; then
ipBlockParser "$1"
message "Lightning Spam Attack!"
redis-cli SADD tmp_block $i
fi
}
message() {
echo "$1" | /root/go/bin/algia dm-post -u 33c74427f3b2b73d5e38f3e6c991c122a55d204072356f71da49a0e209fb6940 --stdin
}
watch() {
echo "Scanning $DATE"
echo
IP=($(grep $DATE $NGINX_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 "$i" "$DATE"
module-lightning "$i" "$DATE"
module-php "$i" "$DATE"
module-go "$i" "$DATE"
module-get-spam "$i" "$DATE"
COUNT=$(grep $DATE $NGINX_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
}
module-nostr() {
IP=($(grep $DATE $NGINX_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 $NGINX_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 $NGINX_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 $NGINX_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" = "import" ]; then
import-saved
else
menu
fi