#!/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 bash $MODULES/module-nostr.sh "$i" "$DATE" bot-search } bot-search() { echo "Searching for Web Crawalers...." 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 bash $MODULES/module-rate-limit.sh $HTTP_LIMIT else virtualization fi message "Starting Firewall" } 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" } 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 bash $MODULES/module-rate-limit.sh $HTTP_LIMIT } 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 bash $MODULES/module-akkoma-instance.sh "$i" "$DATE" bash $MODULES/module-akkoma-timeline-public.sh "$i" "$DATE" bash $MODULES/module-akkoma-timeline-home.sh "$i" "$DATE" bash $MODULES/module-akkoma-accounts.sh "$i" "$DATE" bash $MODULES/module-akkoma-search.sh "$i" "$DATE" bash $MODULES/module-lightning.sh "$i" "$DATE" bash $MODULES/module-php.sh "$i" "$DATE" bash $MODULES/module-go.sh "$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 bash $MODULES/module-rate-limit.sh $HTTP_LIMIT else bash $MODULES/module-rate-limit.sh $RATE_LIMITED_HTTP fi } message() { #Message Alert Module. Configure modules/module-message.sh with actions. bash $MODULES/module-message.sh "$1" } 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 $ACCESS | grep $IP echo read -p 'Press Enter to Continue ' -e } 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" = "automatic-status" ]; then automaticStatus elif [ "$1" = "status" ]; then status elif [ "$1" = "forgive" ]; then forgive elif [ "$1" = "watch" ]; then watch elif [ "$1" = "research" ]; then research elif [ "$1" = "stop" ]; then stop 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 bash $MODULES/module-menu.sh fi