#!/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)) #Log Files # 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 10/second } accept $NFT add rule ip6 filter input ct state new tcp dport $1 update @http_ratelimit { ip6 saddr limit rate 10/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