|
| 1 | +#!/bin/bash |
| 2 | +SCRIPTNAME="Portscan Protection" |
| 3 | +VERSION="26-06-2023" |
| 4 | +SCRIPTLOCATION="/usr/local/sbin/portscan-protection.sh" |
| 5 | +WHITELISTLOCATION="/usr/local/sbin/portscan-protection-white.list" |
| 6 | +CRONLOCATION="/etc/cron.d/portscan-protection" |
| 7 | +GITHUBRAW="https://raw.githubusercontent.com/forwardemail/portscan-protection/master/portscan-protection.sh" |
| 8 | +AUTOUPDATE="YES" # Edit this variable to "NO" if you don't want to auto update this script (NOT RECOMMENDED) |
| 9 | +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
| 10 | + |
| 11 | +# |
| 12 | +# Define some functions |
| 13 | +# |
| 14 | + |
| 15 | +IPSETCOMMANDCHECK() |
| 16 | +{ |
| 17 | + # Check the ipset command |
| 18 | + ! command -v ipset > /dev/null && printf "\nipset command ${RED}not found${NC}.\n" && exit 6 || printf "ipset command found. ${GR}OK.${NC}\n" |
| 19 | +} |
| 20 | + |
| 21 | + |
| 22 | +IPTABLECOMMANDCHECK() |
| 23 | +{ |
| 24 | + # Check the iptables command |
| 25 | + ! command -v iptables > /dev/null && printf "iptables command ${RED}not found${NC}.\n" && exit 7 || printf "iptables command found. ${GR}OK.${NC}\n" |
| 26 | +} |
| 27 | + |
| 28 | + |
| 29 | +SETCRONTAB() |
| 30 | +{ |
| 31 | + [ ! -f "$CRONLOCATION" ] || ! grep -q "reboot root sleep" "$CRONLOCATION" && printf "# $SCRIPTNAME installed at $(date)\n@reboot root sleep 30 && $SCRIPTLOCATION --cron\n\n" > "$CRONLOCATION" && printf "Crontab entry has been set. ${GR}OK.${NC}\n" || printf "Crontab entry ${GR}already set.${NC}\n" |
| 32 | +} |
| 33 | + |
| 34 | + |
| 35 | +WHITELIST() |
| 36 | +{ |
| 37 | + [ ! -f "$WHITELISTLOCATION" ] && printf "# This file is part of $SCRIPTNAME\n# Add one IP per line to this file. These IP addresses will be never blocked. Note: Only IPv4 addresses are supported.\n# More info on GitHub: https://github.com/Feriman22/portscan-protection\n# If you found it useful, please donate via PayPal: https://paypal.me/BajzaFerenc\n\n# Thank you!\n\n127.0.0.1" > $WHITELISTLOCATION |
| 38 | + for i in nano vi vim; do |
| 39 | + if command -v $i > /dev/null; then |
| 40 | + $i "$WHITELISTLOCATION" |
| 41 | + "$SCRIPTLOCATION" --cron |
| 42 | + printf "Whitelist has been activated if the file has been modified.\n" ; FOUND="1" |
| 43 | + break |
| 44 | + fi |
| 45 | + done |
| 46 | + [ "$FOUND" != "1" ] && echo "nano, vi or vim is not found. Edit manually the whitelist: $WHITELISTLOCATION" |
| 47 | +} |
| 48 | + |
| 49 | + |
| 50 | +UPDATE() |
| 51 | +{ |
| 52 | + # Getting info about the latest GitHub version |
| 53 | + NEW=$(curl -s "$GITHUBRAW" | awk -F'"' '/^VERSION/ {print $2}') |
| 54 | + |
| 55 | + # Compare the installed and the GitHub stored version - Only internal, not available by any argument |
| 56 | + if [[ "$1" == "ONLYCHECK" ]] && [[ "$NEW" != "$VERSION" ]]; then |
| 57 | + [ "$1" != '--cron' ] && printf "New version ${YL}available!${NC}\n" |
| 58 | + else |
| 59 | + [[ "$1" == "ONLYCHECK" ]] && [ "$1" != '--cron' ] && printf "The downloaded $SCRIPTNAME is ${GR}up to date.${NC}\n\n" |
| 60 | + fi |
| 61 | + |
| 62 | + # Check the current installation |
| 63 | + if [[ "$1" != "ONLYCHECK" ]] && [ -f "$CRONLOCATION" ] && [ -x "$SCRIPTLOCATION" ]; then |
| 64 | + |
| 65 | + # Check the GitHub - Is it available? - Exit if not |
| 66 | + if [[ ! "$NEW" ]]; then |
| 67 | + [ "$1" != '--cron' ] && printf "GitHub is ${RED}not available now.${NC} Try again later.\n" |
| 68 | + exit 8 |
| 69 | + fi |
| 70 | + |
| 71 | + # Compare the installed and the GitHub stored version |
| 72 | + if [[ "$NEW" != "$VERSION" ]]; then |
| 73 | + curl -s -o "$SCRIPTLOCATION" "$GITHUBRAW" |
| 74 | + SETCRONTAB |
| 75 | + [ "$1" != '--cron' ] && printf "Script has been ${GR}updated.${NC}\n" |
| 76 | + else |
| 77 | + [ "$1" != '--cron' ] && printf "The installed $SCRIPTNAME is ${GR}up to date.${NC}\n\n" |
| 78 | + fi |
| 79 | + else |
| 80 | + [[ "$1" != "ONLYCHECK" ]] && [ "$1" != '--cron' ] && printf "Script ${RED}not installed.${NC} Install first then you can update it.\n" |
| 81 | + fi |
| 82 | +} |
| 83 | + |
| 84 | + |
| 85 | +if [ "$1" != '--cron' ]; then |
| 86 | + # Coloring |
| 87 | + RED='\033[0;31m' # Red Color |
| 88 | + GR='\033[0;32m' # Green Color |
| 89 | + YL='\033[0;33m' # Yellow Color |
| 90 | + NC='\033[0m' # No Color |
| 91 | + |
| 92 | + printf "\n$SCRIPTNAME\n" |
| 93 | + echo "Author: Feriman" |
| 94 | + echo "URL: https://github.com/Feriman22/portscan-protection" |
| 95 | + echo "Open GitHub page to read the manual and check new releases" |
| 96 | + echo "Current version: $VERSION" |
| 97 | + UPDATE ONLYCHECK # Check new version |
| 98 | + printf "${GR}If you found it useful${NC}, please donate via PayPal: https://paypal.me/BajzaFerenc\n\n" |
| 99 | +fi |
| 100 | + |
| 101 | +# Check the root permission |
| 102 | +[ ! "$(id -u)" = 0 ] && printf "${RED}Run as root!${NC}\n" && exit 5 |
| 103 | + |
| 104 | +# Check curl, ipset, iptables commands |
| 105 | +for i in curl ipset iptables; do ! command -v ipset > /dev/null && echo "$i command ${RED}not found${NC}" && NOT_FOUND="1"; done |
| 106 | +[ "$NOT_FOUND" == "1" ] && exit 10 |
| 107 | + |
| 108 | +# Define ipset and iptable rules - Used at magic and uninstall part |
| 109 | +IPSET1='port_scanners hash:ip family inet hashsize 32768 maxelem 65536 timeout 600' |
| 110 | +IPSET2='scanned_ports hash:ip,port family inet hashsize 32768 maxelem 65536 timeout 60' |
| 111 | +IPTABLE1='INPUT -m state --state INVALID -j DROP' |
| 112 | +IPTABLE2='INPUT -m state --state NEW -m set ! --match-set scanned_ports src,dst -m hashlimit --hashlimit-above 1/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name portscan --hashlimit-htable-expire 10000 -j SET --add-set port_scanners src --exist' |
| 113 | +IPTABLE3='INPUT -m state --state NEW -m set --match-set port_scanners src -j DROP' |
| 114 | +IPTABLE4='INPUT -m state --state NEW -j SET --add-set scanned_ports src,dst' |
| 115 | + |
| 116 | +# Enter in this section only if run by cron - It will do the magic |
| 117 | +if [ "$1" == '--cron' ]; then |
| 118 | + |
| 119 | + # Add Whitelist IPs if any |
| 120 | + if [ -f $WHITELISTLOCATION ]; then |
| 121 | + while read WHILELISTIP; do |
| 122 | + # Validate IP address |
| 123 | + if [[ "$WHILELISTIP" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]] && [ $(iptables -S | grep -cF -- "-A INPUT -s $WHILELISTIP/32 -j ACCEPT") -lt 1 ]; then |
| 124 | + iptables -I INPUT -s $WHILELISTIP -j ACCEPT |
| 125 | + fi |
| 126 | + done < <(grep -v "^#\|^$" $WHITELISTLOCATION) |
| 127 | + fi |
| 128 | + |
| 129 | + ipset list | grep -q port_scanners || ipset create $IPSET1 |
| 130 | + ipset list | grep -q scanned_ports || ipset create $IPSET2 |
| 131 | + iptables -S | grep -qF -- "-A $IPTABLE1" || iptables -A $IPTABLE1 |
| 132 | + iptables -S | grep -qF -- "-A $IPTABLE2" || iptables -A $IPTABLE2 |
| 133 | + iptables -S | grep -qF -- "-A $IPTABLE3" || iptables -A $IPTABLE3 |
| 134 | + iptables -S | grep -qF -- "-A $IPTABLE4" || iptables -A $IPTABLE4 |
| 135 | + |
| 136 | + # Auto update if not disabled |
| 137 | + [ "$AUTOUPDATE" == "YES" ] && UPDATE --cron |
| 138 | + |
| 139 | + # Exit, because it run by cron |
| 140 | + exit 0 |
| 141 | +fi |
| 142 | + |
| 143 | +# Call the menu |
| 144 | +if [ "$1" == "-i" ] || [ "$1" == "-u" ] || [ "$1" == "-v" ] || [ "$1" == "--install" ] || [ "$1" == "--uninstall" ] || [ "$1" == "--verify" ] || [ "$1" == "-up" ] || [ "$1" == "--update" ]; then |
| 145 | + OPT="$1" && OPTL="$1" && ARG="YES" |
| 146 | +else |
| 147 | + PS3='Please enter your choice: ' |
| 148 | + [ -f "$SCRIPTLOCATION" ] && options=("Verify" "Edit Whitelist" "Update from GitHub" "Uninstall" "Quit") |
| 149 | + [ ! -f "$SCRIPTLOCATION" ] && options=("Install" "Verify" "Quit") |
| 150 | + select opt in "${options[@]}" |
| 151 | + do |
| 152 | + case $opt in |
| 153 | + "Install") |
| 154 | + OPT='-i' && OPTL='--install' && break |
| 155 | + ;; |
| 156 | + "Uninstall") |
| 157 | + OPT='-u' && OPTL='--uninstall' && break |
| 158 | + ;; |
| 159 | + "Edit Whitelist") |
| 160 | + WHITELIST ; break |
| 161 | + ;; |
| 162 | + "Verify") |
| 163 | + OPT='-v' && OPTL='--verify' && break |
| 164 | + ;; |
| 165 | + "Update from GitHub") |
| 166 | + UPDATE && break |
| 167 | + ;; |
| 168 | + "Quit") |
| 169 | + break |
| 170 | + ;; |
| 171 | + *) echo "Invalid option $REPLY";; |
| 172 | + esac |
| 173 | + done |
| 174 | +fi |
| 175 | + |
| 176 | +## |
| 177 | +########### Menu: Install ########### |
| 178 | +## |
| 179 | + |
| 180 | +if [ "$OPT" == '-i' ] || [ "$OPTL" == '--install' ]; then |
| 181 | + |
| 182 | + # |
| 183 | + ### Start Installation ### |
| 184 | + # |
| 185 | + |
| 186 | + # Start count the time of install process |
| 187 | + SECONDS=0 |
| 188 | + |
| 189 | + IPSETCOMMANDCHECK |
| 190 | + |
| 191 | + IPTABLECOMMANDCHECK |
| 192 | + |
| 193 | + # Set crontab rule if it does not already exist |
| 194 | + SETCRONTAB |
| 195 | + |
| 196 | + # Copy the script to $SCRIPTLOCATION and add execute permission |
| 197 | + INSTALLERLOCATION=$(realpath $0) |
| 198 | + if [ "$INSTALLERLOCATION" != "$SCRIPTLOCATION" ]; then |
| 199 | + curl -s -o "$SCRIPTLOCATION" "$GITHUBRAW" && chmod +x "$SCRIPTLOCATION" && printf "$SCRIPTNAME has been copied in $SCRIPTLOCATION ${GR}OK.${NC}\n" |
| 200 | + else |
| 201 | + printf "$SCRIPTNAME already copied to destination. Nothing to do. ${GR}OK.${NC}\n" |
| 202 | + fi |
| 203 | + |
| 204 | + # First "cron like" run to activate the iptable rules |
| 205 | + "$SCRIPTLOCATION" --cron && printf "iptable rules have been activated. You are protected! ${GR}OK.${NC}\n" |
| 206 | + |
| 207 | + # Finish |
| 208 | + printf "\n${GR}Note:${NC} If you want to Edit Whitelist, or Verify the install, just run the below command:\nsudo $SCRIPTLOCATION\n\n" |
| 209 | + printf "${GR}Done.${NC} The install was $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec. That was so quick, wasn't?\n" |
| 210 | +fi |
| 211 | + |
| 212 | + |
| 213 | +## |
| 214 | +########### Menu: Uninstall ########### |
| 215 | +## |
| 216 | + |
| 217 | +if [ "$OPT" == '-u' ] || [ "$OPTL" == '--uninstall' ]; then |
| 218 | + if [ "$ARG" != 'YES' ]; then |
| 219 | + loop=true; |
| 220 | + while $loop; do |
| 221 | + printf "${RED}UNINSTALL${NC} $SCRIPTNAME on $(hostname).\n" |
| 222 | + read -p "Are you sure? [Y/n]: " var1 |
| 223 | + loop=false; |
| 224 | + if [ "$var1" == 'Y' ] || [ "$var1" == 'y' ]; then |
| 225 | + printf "Okay! You have 5 sec until starting the ${RED}UNINSTALL${NC} process on $(hostname). Press Ctrl + C to exit.\n" |
| 226 | + for i in {5..1}; do echo $i && sleep 1; done |
| 227 | + elif [ "$var1" == 'N' ] || [ "$var1" == 'n' ]; then |
| 228 | + echo "Okay, exit." |
| 229 | + exit 9 |
| 230 | + else |
| 231 | + echo "Enter a valid response Y or n"; |
| 232 | + loop=true; |
| 233 | + fi |
| 234 | + done |
| 235 | + fi |
| 236 | + |
| 237 | + # |
| 238 | + ### Starting Uninstall ### |
| 239 | + # |
| 240 | + |
| 241 | + # Remove crontab file |
| 242 | + if [ -f "$CRONLOCATION" ]; then |
| 243 | + rm -r "$CRONLOCATION" |
| 244 | + printf "\nCrontab file has been removed. ${GR}OK.${NC}\n" |
| 245 | + else |
| 246 | + printf "\nCrontab file not found. ${GR}OK.${NC}\n" |
| 247 | + fi |
| 248 | + |
| 249 | + # Remove the script |
| 250 | + [ -f "$SCRIPTLOCATION" ] && rm -f "$SCRIPTLOCATION" && printf "$SCRIPTNAME has been removed. ${GR}OK.${NC}\n" || printf "Script not found. ${GR}OK.${NC}\n" |
| 251 | + |
| 252 | + # Remove iptable rules |
| 253 | + N=1 |
| 254 | + for IPTABLERULE in "$IPTABLE1" "$IPTABLE2" "$IPTABLE3" "$IPTABLE4"; do |
| 255 | + if iptables -S | grep -qF -- "-A $IPTABLERULE"; then |
| 256 | + iptables -D $IPTABLERULE |
| 257 | + printf "#$N iptable rule has been removed. ${GR}OK.${NC}\n" |
| 258 | + else |
| 259 | + printf "#$N iptable rule not found. ${GR}OK.${NC}" |
| 260 | + fi |
| 261 | + ((N++)) |
| 262 | + done |
| 263 | + |
| 264 | + # Remove Whitelist rules |
| 265 | + if [ -f $WHITELISTLOCATION ]; then |
| 266 | + while read WHILELISTIP; do |
| 267 | + # Validate IP address |
| 268 | + if [[ "$WHILELISTIP" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]]; then |
| 269 | + iptables -D INPUT -s $WHILELISTIP -j ACCEPT |
| 270 | + fi |
| 271 | + done < <(grep -v "^#\|^$" $WHITELISTLOCATION) |
| 272 | + printf "Whitelist removed from iptables if any. ${GR}OK.${NC}" |
| 273 | + fi |
| 274 | + |
| 275 | + # Remove Whitelist |
| 276 | + [ -f "$WHITELISTLOCATION" ] && rm -f "$WHITELISTLOCATION" && printf "Whitelist has been removed. ${GR}OK.${NC}\n" || printf "Whitelist not found. ${GR}OK.${NC}\n" |
| 277 | + |
| 278 | + # Remove ipset rules |
| 279 | + for IPSETRULE in scanned_ports port_scanners; do |
| 280 | + if ipset list | grep -q "$IPSETRULE"; then |
| 281 | + sleep 1 |
| 282 | + ipset destroy $IPSETRULE |
| 283 | + printf "$IPSETRULE ipset rule has been removed. ${GR}OK.${NC}\n" |
| 284 | + else |
| 285 | + printf "$IPSETRULE ipset rule not found. ${GR}OK.${NC}\n" |
| 286 | + fi |
| 287 | + done |
| 288 | + |
| 289 | + printf "\nIf the $SCRIPTNAME removed accidently, run this below command to install it again:\n" |
| 290 | + printf "curl -s $GITHUBRAW | sudo bash /dev/stdin -i\n" |
| 291 | +fi |
| 292 | + |
| 293 | +## |
| 294 | +########### Menu: Verify ########### |
| 295 | +## |
| 296 | + |
| 297 | +if [ "$OPT" == '-v' ] || [ "$OPTL" == '--verify' ]; then |
| 298 | + |
| 299 | + # Crontab verify |
| 300 | + [ ! -f "$CRONLOCATION" ] && printf "\nCrontab entry ${RED}not found.${NC}\n" || printf "\nCrontab entry found. ${GR}OK.${NC}\n" |
| 301 | + |
| 302 | + # Verify script location |
| 303 | + if [ -f "$SCRIPTLOCATION" ]; then |
| 304 | + printf "Script found. ${GR}OK.${NC}\n" |
| 305 | + |
| 306 | + # Verify execute permission |
| 307 | + [ -x "$SCRIPTLOCATION" ] && printf "The script is executable. ${GR}OK.${NC}\n" || printf "The execute permission is ${RED}missing.${NC} Fix it by run: chmod +x $SCRIPTLOCATION\n" |
| 308 | + |
| 309 | + else |
| 310 | + printf "Script ${RED}not found.${NC}\n" |
| 311 | + fi |
| 312 | + |
| 313 | + IPSETCOMMANDCHECK |
| 314 | + |
| 315 | + IPTABLECOMMANDCHECK |
| 316 | + |
| 317 | + iptables -S | grep -q port_scanners && iptables -S | grep -q scanned_ports && printf "iptables rules have been configured. You are protected! ${GR}OK.${NC}\n" || printf "iptables rules are ${RED}not configured!${NC}\n" |
| 318 | + |
| 319 | + if [ -f $WHITELISTLOCATION ]; then |
| 320 | + while read WHILELISTIP; do |
| 321 | + # Validate IP address |
| 322 | + if [[ ! "$WHILELISTIP" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]]; then |
| 323 | + printf "$WHILELISTIP is ${RED}not valid${NC} IPv4 address in the Whitelist and it will be ignored. May you have to fix it by choose Edit Whitelist from the menu.\n" |
| 324 | + fi |
| 325 | + done < <(grep -v "^#\|^$" $WHITELISTLOCATION) |
| 326 | + fi |
| 327 | +fi |
| 328 | + |
| 329 | + |
| 330 | +## |
| 331 | +########### Menu: Update ########### |
| 332 | +## |
| 333 | + |
| 334 | +[ "$OPT" == '-up' ] || [ "$OPTL" == '--update' ] && UPDATE |
0 commit comments