Helpful Shell Scripts
These scripts may assist with more functionality. The directories will differ depending on use of Debian or Linux2023, and php version and its socket.
For example, /home/admin or /home/ec2-user.
LetsEencrypt certbot auto-renewal script
This script is shown in the Debian 11/12 articles.
crontab start up scripts
Crontab startup scripts
Add your scripts. e.g. blackllist.sh where you have a script that uses iptables/ipset to block ports from various countries.
Edit crontab: crontab -e @reboot sudo sh /home/admin/blacklist.sh >/dev/null 2>&1 [save and exit]
After reboot, in this example, run iptable -L -vn and you should see the output worked.
Further below are the blacklist.sh, cidr.sh and firewall.sh scripts.
iptables/ipset blacklisting scripts
Blacklisting scripts – using ipset/iptables
I use this with Nginx. I use ip2location with httpd/apache2. You can modify and test for apache if you wish. iptables is close to the kernel, so I like it.
This will block countries, selected CIDR ranges, domains, and individual IP addresses as they are needed.
Debian needs to install the ipset package which includes iptables, whereas Linux2023 needs both ipset and iptable from the dnf command.
In this version we add a directory /var/www/html/firewall to contain multiple country .txt files we want to block. These were created from ip2location's website. Each .txt file is under /var/www/html/firewall. You may only execute the script once, otherwise you have duplicates. cd /home/admin vi blacklist.sh #!/bin/sh # IP blacklisting script for Linux servers # Pawel Krawczyk 2014-2015 # documentation https://github.com/kravietz/blacklist-scripts # iptables logging limit LIMIT="10/minute" # try to load config file # it should contain one blacklist URL per line config_file="/etc/ip-blacklist.conf" if [ -f "${config_file}" ]; then source ${config_file} else # if no config file is available, load default set of blacklists # URLs for further blocklists are appended using the classical # shell syntax: "$URLS new_url" # Emerging Threats lists offensive IPs such as botnet command servers URLS="http://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt" # Blocklist.de collects reports from fail2ban probes, listing password brute-forces, scanners and other offenders URLS="$URLS https://www.blocklist.de/downloads/export-ips_all.txt" # YOUR OWN BLACKLISTS # URLS="$URLS https://mydomain.com/firewall/block.txt" # YOUR OWN WordPress bad loggins (if using it via the wplogin.sh script - see further below # URLS="$URLS https://mydomain.com/firewall/wplogin.txt" URLS="$URLS https://mydomain.com/firewall/japan.txt" URLS="$URLS https://mydomain.com/firewall/russia.txt" URLS="$URLS https://mydomain.com/firewall/china.txt" URLS="$URLS https://mydomain.com/firewall/northkorea.txt" URLS="$URLS https://mydomain.com/firewall/iran.txt" URLS="$URLS https://mydomain.com/firewall/india.txt" URLS="$URLS https://mmydomain.com/firewall/turkey.txt" URLS="$URLS https://mydomain.com/firewall/poland.txt" URLS="$URLS https://mydomain.com/firewall/romania.txt" URLS="$URLS https://mydomain.com/firewall/southkorea.txt" URLS="$URLS https://mydomain.com/firewall/netherlands.txt" URLS="$URLS https://mydomain.com/firewall/kazakhstan.txt" URLS="$URLS https://mydomain.com/firewall/germany.txt" URLS="$URLS https://mydomain.com/firewall/brazil.txt" fi link_set () { if [ "$3" = "log" ]; then iptables -A "$1" -m set --match-set "$2" src,dst -m limit --limit "$LIMIT" -j LOG --log-prefix "BLOCK $2 " fi iptables -A "$1" -m set --match-set "$2" src -j DROP iptables -A "$1" -m set --match-set "$2" dst -j DROP } # This is how it will look like on the server # Chain blocklists (2 references) # pkts bytes target prot opt in out source destination # 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 match-set manual-blacklist src,dst limit: avg 10/min burst 5 LOG flags 0 level 4 prefix "BLOCK manual-blacklist " # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set manual-blacklist src,dst # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set rules.emergingthreats src # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set rules.emergingthreats dst # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set www.blocklist.de src # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set www.blocklist.de dst # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set www.badips.com src # 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 match-set www.badips.com dst blocklist_chain_name=blocklists # check if we are on OpenWRT if [ "$(which uci 2>/dev/null)" ]; then # we're on OpenWRT wan_iface=pppoe-wan IN_OPT="-i $wan_iface" INPUT=input_rule FORWARD=forwarding_rule COMPRESS_OPT="" else COMPRESS_OPT="--compressed" INPUT=INPUT FORWARD=FORWARD fi # create main blocklists chain if ! iptables -nL | grep -q "Chain ${blocklist_chain_name}"; then iptables -N ${blocklist_chain_name} fi # inject references to blocklist in the beginning of input and forward chains if ! iptables -nL ${INPUT} | grep -q ${blocklist_chain_name}; then iptables -I ${INPUT} 1 ${IN_OPT} -j ${blocklist_chain_name} fi if ! iptables -nL ${FORWARD} | grep -q ${blocklist_chain_name}; then iptables -I ${FORWARD} 1 ${IN_OPT} -j ${blocklist_chain_name} fi # flush the chain referencing blacklists, they will be restored in a second iptables -F ${blocklist_chain_name} # create the "manual" blacklist set # this can be populated manually using ipset command: # ipset add manual-blacklist a.b.c.d set_name="manual-blacklist" if ! ipset list | grep -q "Name: ${set_name}"; then ipset create "${set_name}" hash:net fi link_set "${blocklist_chain_name}" "${set_name}" "$1" # download and process the dynamic blacklists for url in $URLS do # initialize temp files unsorted_blocklist=$(mktemp) sorted_blocklist=$(mktemp) new_set_file=$(mktemp) headers=$(mktemp) # download the blocklist set_name=$(echo "$url" | awk -F/ '{print substr($3,0,21);}') # set name is derived from source URL hostname curl -L -v -s ${COMPRESS_OPT} -k "$url" >"${unsorted_blocklist}" 2>"${headers}" # this is required for blocklist.de that sends compressed content regardless of asked or not if [ -z "$COMPRESS_OPT" ]; then if grep -qi 'content-encoding: gzip' "${headers}"; then mv "${unsorted_blocklist}" "${unsorted_blocklist}.gz" gzip -d "${unsorted_blocklist}.gz" fi fi # autodetect iblocklist.com format as it needs additional conversion if echo "${url}" | grep -q 'iblocklist.com'; then if [ -f /etc/range2cidr.awk ]; then mv "${unsorted_blocklist}" "${unsorted_blocklist}.gz" gzip -d "${unsorted_blocklist}.gz" awk_tmp=$(mktemp) awk -f /etc/range2cidr.awk <"${unsorted_blocklist}" >"${awk_tmp}" mv "${awk_tmp}" "${unsorted_blocklist}" else echo "/etc/range2cidr.awk script not found, cannot process ${unsorted_blocklist}, skipping" continue fi fi sort -u <"${unsorted_blocklist}" | egrep "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(/[0-9]{1,2})?$" >"${sorted_blocklist}" # calculate performance parameters for the new set if [ "${RANDOM}" ]; then # bash tmp_set_name="tmp_${RANDOM}" else # non-bash tmp_set_name="tmp_$$" fi new_list_size=$(wc -l "${sorted_blocklist}" | awk '{print $1;}' ) hash_size=$(expr $new_list_size / 2) if ! ipset -q list ${set_name} >/dev/null ; then ipset create ${set_name} hash:net family inet fi # start writing new set file echo "create ${tmp_set_name} hash:net family inet hashsize ${hash_size} maxelem ${new_list_size}" >>"${new_set_file}" # convert list of IPs to ipset statements while read line; do echo "add ${tmp_set_name} ${line}" >>"${new_set_file}" done <"$sorted_blocklist" # replace old set with the new, temp one - this guarantees an atomic update echo "swap ${tmp_set_name} ${set_name}" >>"${new_set_file}" # clear old set (now under temp name) echo "destroy ${tmp_set_name}" >>"${new_set_file}" # actually execute the set update ipset -! -q restore < "${new_set_file}" link_set "${blocklist_chain_name}" "${set_name}" "$1" # clean up temp files rm "${unsorted_blocklist}" "${sorted_blocklist}" "${new_set_file}" "${headers}" done [save and exit] chmod 777 blacklist.sh; chown root blacklist.sh; chgrp admin blacklist.sh
We now add the firewall_clear.sh script to set the iptable back to nothing, which is good if you want to renew settings without a reboot.
cd /home/admin vi firewall_clear.sh #!/bin/bash iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT exit [save and exit] chown root firewall_clear.sh; chgrp admin firewall_clear.sh; chmod 777 firewall_clear.sh
We now add the cidr.sh script that is designed to stop people flooding a port by leaving the port open until the operating system freezes.
I also add lines to block ranges of IP addresses. (Careful you don’t block Amazon, your own IP, or Letsencrypt etc.]
cd /home/admin vi cidr.sh #!/bin/sh iptables -A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 10 -j DROP iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 10 -j DROP iptables -A INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 5 -j DROP # ALLOW Uptime Robot IP4 address in Sydney iptables -A INPUT -s 54.79.28.129 -j ACCEPT # Block bad domains for i in $(cat https://mydomain.com/firewall/domains.txt); do echo "Blocking all traffic to and from $i" iptables -I INPUT -s $i -j DROP iptables -I OUTPUT -d $i -j REJECT done # Drop CIDR ranges # USA DIGITALOCEAN-ASN scammers - this is an example. Your /var/log/nginx/ logs will show bad actors. iptables -A INPUT -s 104.248.64.0/22 -j DROP # Tokyo and Kagoya problems from Japan iptables -A INPUT -s 210.128.0.0/13 -j DROP iptables -A INPUT -s 153.127.224.0/19 -j DROP iptables -L -vn exit
Create a file /var/www/html/firewall/domains.txt
Here is an example:
cd /var/www/html/firewall vi domains.txt binance.com [save and exit]
If you want to add your own block.txt file to catch bad actors individually, here is an example I use. It keeps growing as I add addresses.
You uncomment the block.txt line in blacklist.sh
cd /var/www/html/firewall vi block.txt 152.32.252.116 157.230.236.124 167.172.160.194 167.99.129.24 24.199.92.243 2.63.211.145 80.66.76.130 91.238.181.21 92.255.85.107 92.255.85.253 167.94.145.100 185.224.128.67 34.31.81.100 172.203.163.52 119.28.156.186 51.75.147.222 134.122.26.31 185.196.220.26 66.249.66.21 109.202.99.36 159.65.82.252 47.88.6.178 138.68.178.173 45.113.95.191 172.66.40.245 139.59.123.61 104.234.115.58 206.168.34.57 1.14.7.100 162.142.125.199 151.80.67.229 35.185.221.46 156.233.225.192 [save and exit]
Finally, we can write our own scripts to check for attempted bad logins to WordPress, and add them to a list if not already in the list.
This tends to keep growing. A reboot or firewall_clean.sh will clear the growing list, then you can manually run blacklist.sh and cidr.sh again using a line in blacklist.sh for the file wplogin.txt. Initially create a wplogin.txt file with no content in it.
I do the script with crontab, and use my own static IP address to ensure I don’t block myself. Hence, this script is only if you have a static IP address at home. You can modify the script for additional addresses.
Create a dummy file called /var/www/html/firewall/wplogin.txt. Files are permissions 664, owner/group nginx. ./firewall is 2775, nginx.
If using apache2 or httpd the owner/group will likely be www-data or ec2-user (just check your permissions and modify scripts for /home/ec2-user or /home/admin. You would have to modify the script below if not nginx and test it.
This only works with Nginx and the /var/log/nginx/access.log file name, and your own static IP4 address. cd /home/admin vi wplogin.sh #!/bin/bash # BASED ON HAVING MY OWN STATIC IP ADDRESS d=`date | awk '{print $2,$3,$NF}'|tr " " "-"` echo "wplogin hacker IPs:" $d >> /home/admin/info.log for i in `sort /var/log/nginx/access.log|grep "wp-login.php" | awk '{print $1}'|grep -v "XXX.XXX.XXX.XXX" | sort -u` do a="" a=`grep $i /var/www/html/firewall/wplogin.txt` if [ "$a" = "" ] ; then echo $i >> /var/www/html/firewall/wplogin.txt iptables -I INPUT -p tcp -s $i -j DROP echo $i >> /home/ec2-admin/info.log fi done cp -p /var/log/nginx/access.log /var/log/nginx/archive-$d-access.log :> /var/log/nginx/access.log exit [save and exit] chmod 777 wplogin.sh; chown root wplogin.sh; chgrp admin wplogin.sh;
Your crontab entry can be extended to include blacklist.sh and cidr.sh, and optionally the WordPress wplogin.sh script (using bash, not sh)
crontab -e @reboot sudo sh /home/admin/blacklist.sh >/dev/null 2>&1 @reboot sudo sh /home/admin/cidr.sh >/dev/null 2>&1 # 3am each night 0 3 * * * sudo bash /home/ec2-user/wplogin.sh >/dev/null 2>&1 [save and exit]
You should be set to manually test both scripts. The command iptables -L -vn will show you the output.
Here is the countries.tar.zip file with 14 countries. You need to add your own block.txt, wplogin.txt, and domains.txt files, and set the file permissions when you extract them to /var/www/html/firewall. (YOu can of course configure your own directories and names.)
This will not automatically capture all IP4 addresses from each country in the list. You could add those manually is an issue. You need to use ip2location’s website to download or update lists manually, using the format shown in the .txt files.