• Please be aware: Kaspersky Anti-Virus has been deprecated
    With the upgrade to Plesk Obsidian 18.0.64, "Kaspersky Anti-Virus for Servers" will be automatically removed from the servers it is installed on. We recommend that you migrate to Sophos Anti-Virus for Servers.
  • The Horde webmail has been deprecated. Its complete removal is scheduled for April 2025. For details and recommended actions, see the Feature and Deprecation Plan.
  • We’re working on enhancing the Monitoring feature in Plesk, and we could really use your expertise! If you’re open to sharing your experiences with server and website monitoring or providing feedback, we’d love to have a one-hour online meeting with you.

Input Use Wordfence to fill the firewall

mr-wolf

Silver Pleskian
Plesk Guru
Some users have been using Wordfence to protect their sites. It detects nasty people trying to hack into other people's network.

I thought of harvesting these IP's and wrote a script that collects these and use them in iptables.
Each hour it scans all the WordPress sites on my server and adds IP's to the firewall. I have been running that script for a half year and works great.

But the script I wrote was not something to publish as it needed a special layout of the firewall file and had some other specifics. These prerequisites would for sure stop people from using it.

Only recently I discovered "ipset" which changed all this. I have been using scripts to manipulate iptables for years and if I discovered this ipset sooner it would have made that so much easier.
The code can now be much cleaner. Most of the original code was about housekeeping the live firewall and the file which keeps the firewall (used with iptables-restore)
Code:
 sudo apt-get install ipset

It only needs to have one entry in iptables which tells it what to do with an IP coming from that list. I tell it to log it with a specific text and block it.
Code:
:WordFenceDrop - [0:0]
-A WordFenceDrop -j LOG --log-prefix "[WordFence DROP] : " --log-uid --log-tcp-options --log-ip-options
-A WordFenceDrop -j DROP

I would use some whitelisting first (to prevent getting locked out yourself) and then use this somewhere reasonably high up in the chain to actually block them.
Code:
-A INPUT -m set --match-set WordFence src -j WordFenceDrop
Because it doesn't have to execute many iptables checks this is much faster than seperate entries. It is also much easier to dynamically manipulate the firewall with this.

You also need to expand MySQL with some special functions so it can extract the IP's out of the Wordfence tables. More information on how to install that can be found here:
watchmouse / mysql-udf-ipv6 — Bitbucket

I suggest to run the script as an hourly cronjob:
Code:
ln -s /usr/local/sbin/ipset-for-wordfence /etc/cron.hourly/

And watch it fill your firewall with all those nasty perpetrators.

After a reboot all the IP's are lost. For now I will not make any efforts to keep them. I assume most of you have uptimes of more than months and years. Maybe those perpetrators come back another time, but then they will be blocked out soon again.

EDIT:

I made a change to the code, so it now does not rely on an entry already in iptables.
It inserts a row itself in iptables and does this after the line that matches "$REFERENCE_ROW".

This is necessary as you can't use iptables-restore with an ipset line in it if that set has not been created yet. This would be after a reboot.
It also makes it easier to implement this code for those that don't understand iptables.


# cat /usr/local/sbin/ipset-for-wordfence
Code:
#!/bin/bash

# relies on ntop MySQL addition and ipset
#
# apt-get install ipset
#
# ntop in MySQL and can be installed with instructions from here
# https://bitbucket.org/watchmouse/mysql-udf-ipv6
# SELECT inet6_ntop(IP) FROM wp_wfBlocks

# For logging purposes I recommend creating a logging chain using these lines
#
# :WordFenceDrop - [0:0]
# -A WordFenceDrop -j LOG --log-prefix "[WordFence DROP] : " --log-uid --log-tcp-options --log-ip-options
# -A WordFenceDrop -j DROP
#
#
# -A INPUT -m set --match-set WordFence src -j WordFenceDrop
#
HEADLESS=
tty >/dev/null || HEADLESS=true

VHOSTS=/var/www/vhosts/
IPSET_NAME="WordFence"
MYSQL="mysql  -uadmin -p`cat /etc/psa/.psa.shadow `"
MAXELEM=65536
MAXELEM=262144

DROP_CHAIN=WordFenceDrop
REFERENCE_ROW=".*0\.0\.0\.0.*INVALID$"
# REFERENCE_ROW="logaccept.*tcp.*0\.0\.0\.0/0.*0\.0\.0\.0/0.*dpt:22"

# Check if ipset is installed
if ! ipset -v >/dev/null 2>&1 ; then
  echo "This script relies on ipset, without it this will NOT work!" >&2
  echo "Use sudo apt-get install ipset !!!" >&2
  exit 1
fi

# Create ipset list if it does not exist.
if ! ipset list ${IPSET_NAME} >/dev/null ; then
  echo "Set name \"${IPSET_NAME}\" does not exist, I will create it!" >&2
  ipset -N ${IPSET_NAME} iphash maxelem ${MAXELEM}
fi

TMPDIR=`mktemp -t -d ${0//*\/}.XXXXXXXXXX`

ipset list ${IPSET_NAME} >${TMPDIR}/IPSETLIST                              # Extract ipset list

# Create file with all IPs usable with egrep -f
grep -A99999999 Members ${TMPDIR}/IPSETLIST | tail -n+2 | sed 's/\./\\./g;s/*/.*/g;s/.*/^&$/g' >${TMPDIR}/SET_IPS

LISTELEM=`grep -c '\.' ${TMPDIR}/SET_IPS`
REFERENCES=`head ${TMPDIR}/IPSETLIST | grep -i "^Reference" | awk -F: '{print $2}' | tr -cd '0-9'`
MAXELEM=`head ${TMPDIR}/IPSETLIST    | grep -oi maxelem.* | awk '{print $2}' | tr -cd '0-9'`

if [ ${LISTELEM} -ge ${MAXELEM} ] ; then
  echo "I've reached the capacity of the ipset list \"${IPSET_NAME}\", I can't add more IP's" >&2
  echo "The capacity is ${MAXELEM} and there are ${LISTELEM} in the list already"
  exit 1
fi

# Check if you actually added the ipset list to your iptables
if [ ${REFERENCES} ] ; then
  if [ "${REFERENCES}" = "0" ] ; then

    ROW=`iptables --line-numbers -nL INPUT | grep "${REFERENCE_ROW}" | tail -n1 | awk '{print $1}'`
    let ROW+=1

    if [ -z "${ROW}" ] ; then
      echo "I was unable to find a row in iptables matching \"${REFERENCE_ROW}\""  >&2
      echo "I can't add a line in iptables for you, so I quit"  >&2
      rm -r ${TMPDIR}
      exit 1
    else

      # Create the logging chain
      if iptables -N ${DROP_CHAIN} 2>/dev/null ; then
        iptables -A ${DROP_CHAIN} -j LOG --log-prefix "[WordFence DROP] : " --log-uid --log-tcp-options --log-ip-options
        iptables -A ${DROP_CHAIN} -j DROP
      fi

      if iptables -I INPUT ${ROW} -m set --match-set ${IPSET_NAME} src -j ${DROP_CHAIN} ; then
        echo "I inserted the line before row ${ROW} in iptables"  >&2
      else
        echo "I was unable to add a line to iptables " >&2
        echo "Not much use in going on!"
        rm -r ${TMPDIR}
        exit 1
      fi
    fi
  fi
else
  echo "Something's wrong in obtaining the references in ipset, this should never happen!" >&2
  rm -r ${TMPDIR}
  exit 1
fi

find ${VHOSTS} -maxdepth 4 -type f -name wp-config.php >${TMPDIR}/WPCONFIGS

while read CONFIG ; do
   SITEFOLDER=`echo "${CONFIG}" | awk -F/ '{print $5}'`

   # Find out databasename and the Wordpress prefix
   DBNAME="`grep -i DB_NAME ${CONFIG}      | awk -F\' '{print $4}'`"
   PREFIX=`grep -i table_prefix ${CONFIG} | awk -F\' '{print $2}'`

   # Extract the Wordfence entries
   ${MYSQL} "${DBNAME}" -e "SELECT inet6_ntop(IP) FROM ${PREFIX}wfBlocks" 2>/dev/null | grep ffff | sed "s/.*/${SITEFOLDER}, &/g" >>${TMPDIR}/WFIPS

done<${TMPDIR}/WPCONFIGS

ERROR=0
if [ -f  ${TMPDIR}/WFIPS  ] ; then   # did we find any IP's in the WordFence tables?
  # convert them to clean ipv4 addresses
  awk -F, '{print $2}' ${TMPDIR}/WFIPS | awk -F::ffff: '{print $2}' | sort -nu >${TMPDIR}/IPS

  [ ${HEADLESS} ] || echo "`grep -c '' ${TMPDIR}/IPS` unique IP's found in total"

  NEWIPS=0
  while read IP4 ; do
    if ! echo "${IP4}" | egrep -qf ${TMPDIR}/SET_IPS ; then   # Check if IP is not already in set
      let NEWIPS+=1

      [ ${HEADLESS} ] ||  echo "Add IP: ${IP4}"
      logger -t $0 "${IP4} found for Wordfence"
      ipset -A ${IPSET_NAME} ${IP4} 2>${TMPDIR}/error >/dev/null

      if grep -q 'Hash is full' ${TMPDIR}/error  ; then
        ERROR=1
        logger -t $0 "${IP4} can NOT be added as the set \"${IPSET_NAME}\" is FULL"
        echo "${IP4} can NOT be added as the set \"${IPSET_NAME}\" is FULL" >&2
      elif [ -s ${TMPDIR}/error ] ; then
        ERROR=1
        logger -t $0 "`cat ${TMPDIR}/error`"
        cat ${TMPDIR}/error >&2
      else
        logger -t $0 "${IP4} added for Wordfence"
        echo "${IP4} added for Wordfence"
      fi

    fi
  done<${TMPDIR}/IPS

  [ ${HEADLESS} ] || echo "${NEWIPS} new IP's added to list"
  if [ ${NEWIPS} -gt 0 ] ; then
    logger -t $0 "${NEWIPS} new IP's found for Wordfence"
  fi

fi

rm -rf ${TMPDIR}
exit ${ERROR}
 
Last edited:
I will look into that, although that may be more work than the code itself.

First there will be a change of the code.
It's something I've written already, but is now in its cool-down period. When I'm sure it works as I want it to, it will get published.

The current implementation has the possibility to collect many, many IP's and block those forever.

My next implementation will have 2 ipsets. 1 for even months and 1 for uneven months.
On the first day of the month it will flush all the IP's of the 2 month old ipset. This way no IP will be blocked forever and will it never reach a zillion IP's.

I may enhance this to a 3 strikes out system.
 
I will look into that, although that may be more work than the code itself.

First there will be a change of the code.
It's something I've written already, but is now in its cool-down period. When I'm sure it works as I want it to, it will get published.

The current implementation has the possibility to collect many, many IP's and block those forever.

My next implementation will have 2 ipsets. 1 for even months and 1 for uneven months.
On the first day of the month it will flush all the IP's of the 2 month old ipset. This way no IP will be blocked forever and will it never reach a zillion IP's.

I may enhance this to a 3 strikes out system.

Wouldn't it make more sense to build this as an additional filter for fail2ban? The core mechanism and control system exists already with fail2ban.
 
No fail2ban is quite different....

I now know a bit more of ipset to make the code more efficient.
Gone are the temporary files that I used as I can check the existence of an IP in the current set with ipset itself (which is much better and efficient)
The code would have been much shorter..... If I would have left it as is....

I've created a complete ecosystem with a 2-strikes out system to prevent the accumulating of thousands of IP addresses.

I will have 2 different monthly sets in iptables (even and uneven months).
On the first day of the month a new empty set will be created. But before this is done, it will write its current set to a spare one.
The IP's in that spare set will not be blocked...
If in the new month an IP is found that's already in that spare set, then it will be added to the permanent set. These IP's will be kept forever (or until the sets are destroyed).

I have also made use of the ipdeny.com website which makes it possible to download IP addresses from a specific country. I live in the Netherlands, so it's wise for me to whitelist the IP's of my home country.

This doesn't mean that those will not get blacklisted by WordFence. They just aren't added to the firewall, which is a server-wide thing...

In my example I'm blacklisting the Seychelles. If you're from there I would use another country there, but my guess is that you would not be interested in such a script anyhow as you're all too busy hacking other sites. On my servers those islands are blacklisted.

This is a new script and I feel confident that it's good enough for a production server.
It may well contain bugs. Especially because it's very hard to test if everything's exactly working as I intended it to... Only after several months will it be clear how efficient it is.
It has ample logging, so I should be able to find out

cat /usr/local/sbin/ipset-for-wordfence
Code:
#!/bin/bash

# relies on ntop MySQL addition and ipset
#
# apt-get install aggregate
# apt-get install ipset
#
# ntop in MySQL and can be installed with instructions from here
# https://bitbucket.org/watchmouse/mysql-udf-ipv6
# SELECT inet6_ntop(IP) FROM wp_wfBlocks

# For logging purposes I recommend creating a logging chain using these lines
#
# :WordFenceDrop - [0:0]
# -A WordFenceDrop -j LOG --log-prefix "[WordFence DROP] : " --log-uid --log-tcp-options --log-ip-options
# -A WordFenceDrop -j DROP
#
#
# -A INPUT -m set --match-set WordFence src -j WordFenceDrop
#
HEADLESS=
tty >/dev/null || HEADLESS=true

THISSCRIPT="`readlink -f $0`"
BASE=${THISSCRIPT##*/}
[ -z "${BASE}" ] && BASE=${0##*/}


if [[ $(date +%s -r ${THISSCRIPT}) -gt $(date +%s --date="30 min ago") ]] ; then
  [ ${HEADLESS} ] || echo "${THISSCRIPT} has been recently modified, I will remove the cached iplists" >&2
  rm /tmp/${BASE}-* 2>/dev/null
fi

if [ -f ${THISSCRIPT}.blacklist ] ; then
  if [[ $(date +%s -r ${THISSCRIPT}.blacklist) -gt $(date +%s --date="30 min ago") ]] ; then
    [ ${HEADLESS} ] || echo "${THISSCRIPT}.conf has been recently modified, I will remove the cached iplists" >&2
    rm /tmp/${BASE}-blacklist 2>/dev/null
  fi
fi

[ ${HEADLESS} ] || echo "Starting ${BASE}..." >&2

EVEN=
FIRST_OF_MONTH=
[ $((`date +%-m` % 2)) -eq 0 ] && EVEN=true
[ `date +%-d`          -eq 1 ] && FIRST_OF_MONTH=true

VHOSTS=/var/www/vhosts/
IPSET_BASE="WordFence"
MYSQL="mysql  -uadmin -p`cat /etc/psa/.psa.shadow `"
MAXELEM=65536
MAXELEM=262144

IPDENY_PREFIX=http://www.ipdeny.com/ipblocks/data/aggregated
IPDENY_POSTFIX=aggregated.zone

WHITELIST_COUNTRIES=nl,be
BLACKLIST_COUNTRIES=sc

PERSONAL_WHITELIST='89.250.175.40/29'
PERSONAL_BLACKLIST=''

MAX_KEEP=100   # The maximum amount of IP's to keep when the ipset is destroyed at the first day of the month

DROP_CHAIN=WordFenceDrop
REFERENCE_ROW=".*0\.0\.0\.0.*INVALID$"
REFERENCE_ROW="accept.*tcp.*0\.0\.0\.0/0.*0\.0\.0\.0/0.*dpt:22$"

IPSET_BLACK="${IPSET_BASE}_blacklist"
IPSET_WHITE="${IPSET_BASE}_whitelist"
IPSET_PERM="${IPSET_BASE}_PERMANENT"
IPSET_DISC="${IPSET_BASE}_DISCARD"

IPSET_OTHER="${IPSET_BASE}_EVEN"
IPSET_NAME="${IPSET_BASE}_UNEVEN"

[ ${EVEN} ] && IPSET_NAME="${IPSET_BASE}_EVEN" && IPSET_OTHER="${IPSET_BASE}_UNEVEN"

# Check if ipset is installed
if ! ipset -v >/dev/null 2>&1 ; then
  echo "This script relies on ipset, without it this will NOT work!" >&2
  echo "Use sudo apt-get install ipset !!!" >&2
  exit 1
fi

ROW=`iptables --line-numbers -nL INPUT | grep -i "${REFERENCE_ROW}" | tail -n1 | awk '{print $1}'`
let ROW+=1
if [ -z "${ROW}" ] ; then
  echo "I was unable to find a row in iptables matching \"${REFERENCE_ROW}\""  >&2
  echo "I can't add a line in iptables for you, so I quit"  >&2
  exit 1
fi

if ! which aggregate >/dev/null 2>&1 ; then
  [ ${HEADLESS} ] || echo "\"aggregate\" is not found, I can't go on" >&2
  exit 1
fi

referenced_value()
{
  REFERENCES="`ipset -t list $1 | grep -i references | awk -F: '{print $2}'`"
  # Check if you actually added the ipset list to your iptables
  if [ -z "${REFERENCES}" ] ; then
    echo "Something's wrong in obtaining the references in ipset ${IPSET_NAME}, this should never happen!" >&2
    exit 1
  fi
  echo ${REFERENCES}
  return 0
}

is_referenced()
{
  [ `referenced_value $1` -eq 0 ] && return 1
  return 0
}

ipset -exist create ${IPSET_NAME}  hash:ip  maxelem ${MAXELEM} 2>/dev/null

ipset -exist create ${IPSET_DISC}  hash:ip 2>/dev/null
ipset -exist create ${IPSET_WHITE} hash:net
ipset -exist create ${IPSET_BLACK} hash:net
ipset -exist create ${IPSET_PERM}  hash:net

if [ -z "`find /tmp -maxdepth 1 -type f -mtime -200 -size +1 -name ${BASE}-blacklist`" ] ; then
  [ ${HEADLESS} ] || echo "/tmp/${BASE}-blacklist does not exist or needs to be renewed" >&2
  ipset flush ${IPSET_BLACK} 2>/dev/null
  echo "${PERSONAL_BLACKLIST}" >/tmp/${BASE}-blacklist.cum
  cat ${THISSCRIPT}.blacklist 2>/dev/null >>/tmp/${BASE}-blacklist.cum
  for i in ${BLACKLIST_COUNTRIES//,/ } ; do
    if wget -qO /tmp/${BASE}-blacklist.tmp ${IPDENY_PREFIX}/$i-${IPDENY_POSTFIX} ; then
      cat /tmp/${BASE}-blacklist.tmp >>/tmp/${BASE}-blacklist.cum
    fi
  done
  sed 's/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$/&\/32/g' /tmp/${BASE}-blacklist.cum | aggregate -q | sort -u >/tmp/${BASE}-blacklist
else
  [ ${HEADLESS} ] || echo "/tmp/${BASE}-blacklist already exists and is good enough, I will use it again" >&2
fi

if [ -z "`find /tmp -maxdepth 1 -type f -mtime -200 -size +1 -name ${BASE}-whitelist`" ] ; then
  [ ${HEADLESS} ] || echo "/tmp/${BASE}-whitelist does not exist or needs to be renewed" >&2
  ipset flush ${IPSET_WHITE} 2>/dev/null
  echo "${PERSONAL_WHITELIST}" >/tmp/${BASE}-whitelist.cum
  for i in ${WHITELIST_COUNTRIES//,/ } ; do
    if wget -qO /tmp/${BASE}-whitelist.tmp ${IPDENY_PREFIX}/$i-${IPDENY_POSTFIX} ; then
      cat /tmp/${BASE}-whitelist.tmp >>/tmp/${BASE}-whitelist.cum
    fi
  done
  sed 's/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$/&\/32/g' /tmp/${BASE}-whitelist.cum | aggregate -q | sort -u >/tmp/${BASE}-whitelist
else
  [ ${HEADLESS} ] || echo "/tmp/${BASE}-whitelist already exists and is good enough, I will use it again" >&2
fi
rm /tmp/${BASE}-*.tmp 2>/dev/null
rm /tmp/${BASE}-*.cum 2>/dev/null

# Start adding nets to the blacklist/whitelist
# If it wants to add a network that's already there, then break it of.
# It means the ipset is already filled
while read IPNET ; do
  ipset add ${IPSET_BLACK} ${IPNET} 2>/dev/null || break
done</tmp/${BASE}-blacklist

while read IPNET ; do
  ipset add ${IPSET_WHITE} ${IPNET} 2>/dev/null || break
done</tmp/${BASE}-whitelist

# End permanent blacklist code
continued in next post
 
Last edited:
.....continue
Code:
TMPDIR=`mktemp -t -d ${0//*\/}.XXXXXXXXXX`

# Begin Wordfence code

if [ ${FIRST_OF_MONTH} ] && [ ! -f /tmp/${BASE}`date +%y%m%d` ] ; then
  [ ${HEADLESS} ] || echo "It's the first day of the Month.. I will clean up" >&2
  # flush the discardable set which held a copy
  ipset swap ${IPSET_DISC} ${IPSET_NAME}
  ipset flush ${IPSET_NAME}
  ipset create ${IPSET_BASE}_temp hash:net
  # Clean up the permanent blacklist by aggregating all values
  ipset list ${IPSET_PERM} | egrep '[0-9]+\.[0-9]+\.' | sed 's/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$/&\/32/g' | aggregate -q >${TMPDIR}-permlist
  while read IPNET ; do
    ipset add ${IPSET_BASE}_temp ${IPNET} 2>/dev/null
  done<${TMPDIR}-permlist

  ipset swap ${IPSET_BASE}_temp ${IPSET_PERM}
  ipset destroy ${IPSET_BASE}_temp
  # prevent the script from running more than once on the first day
  touch /tmp/${BASE}`date +%y%m%d`
fi

# if the ipset is not referenced then create the lines in iptables
if is_referenced ${IPSET_NAME} ; then
  [ ${HEADLESS} ] || echo "Rule already created in iptables for ${IPSET_NAME}" >&2
else
  [ ${HEADLESS} ] || echo "A rule in iptables will be created for ${IPSET_NAME}" >&2
  # Create the logging chain
  if iptables -N ${DROP_CHAIN} 2>/dev/null ; then
    [ ${HEADLESS} ] || echo "Also needed to create a logging chain for ${IPSET_NAME}" >&2
    iptables -A ${DROP_CHAIN} -j LOG --log-prefix "[WordFence DROP] : " --log-uid --log-tcp-options --log-ip-options
    iptables -A ${DROP_CHAIN} -j DROP
  fi

  if iptables -I INPUT ${ROW} -m set --match-set ${IPSET_NAME} src -j ${DROP_CHAIN} ; then
    echo "I inserted the line before row ${ROW} in iptables"  >&2
  else
    echo "I was unable to add a line to iptables " >&2
    echo "Not much use in going on!"
    rm -r ${TMPDIR}
    exit 1
  fi
fi

if ! is_referenced ${IPSET_PERM} ; then
  if iptables -I INPUT ${ROW} -m set --match-set ${IPSET_PERM} src -j DROP ; then
    echo "I inserted the line before row ${ROW} in iptables"  >&2
  else
    echo "I was unable to add a line to iptables " >&2
    echo "Not much use in going on!"
    rm -r ${TMPDIR}
    exit 1
  fi
fi

if ! is_referenced ${IPSET_BLACK} ; then
  if iptables -I INPUT ${ROW} -m set --match-set ${IPSET_BLACK} src -j DROP ; then
    echo "I inserted the line before row ${ROW} in iptables"  >&2
  else
    echo "I was unable to add a line to iptables " >&2
    echo "Not much use in going on!"
    rm -r ${TMPDIR}
    exit 1
  fi
fi

# Actual WordFence code
find ${VHOSTS} -maxdepth 4 -type f -name wp-config.php >${TMPDIR}/WPCONFIGS

while read CONFIG ; do
   SITEFOLDER=`echo "${CONFIG}" | awk -F/ '{print $5}'`

   # Find out databasename and the Wordpress prefix
   DBNAME="`grep -i DB_NAME ${CONFIG}      | awk -F\' '{print $4}'`"
   PREFIX=`grep -i table_prefix ${CONFIG} | awk -F\' '{print $2}'`

   # Extract the Wordfence entries
   ${MYSQL} "${DBNAME}" -e "SELECT inet6_ntop(IP) FROM ${PREFIX}wfBlocks" 2>/dev/null | grep ffff | sed "s/.*/${SITEFOLDER}, &/g" >>${TMPDIR}/WFIPS

done<${TMPDIR}/WPCONFIGS

ERROR=0
if [ -f  ${TMPDIR}/WFIPS  ] ; then   # did we find any IP's in the WordFence tables?
  # convert them to clean ipv4 addresses
  awk -F, '{print $2}' ${TMPDIR}/WFIPS | awk -F::ffff: '{print $2}' | sort -nu >${TMPDIR}/IPS

  [ ${HEADLESS} ] || echo "`grep -c '' ${TMPDIR}/IPS` unique IP's found in total"

  NEWIPS=0
  while read IP4 ; do
    if ! ipset test ${IPSET_NAME} ${IP4} >/dev/null  ; then   # Check if IP is not already in set
      if ipset test ${IPSET_WHITE} ${IP4} >/dev/null ; then
        [ ${HEADLESS} ] || echo "I should block ${IP4}, but it's whitelisted" >&2
      else
        let NEWIPS+=1

        [ ${HEADLESS} ] ||  echo "Add IP: ${IP4}"
        logger -t $BASE "${IP4} found for Wordfence"
        ipset add ${IPSET_NAME} ${IP4} 2>${TMPDIR}/error >/dev/null

        if grep -q 'Hash is full' ${TMPDIR}/error  ; then
          ERROR=1
          logger -t $BASE "${IP4} can NOT be added as the set \"${IPSET_NAME}\" is FULL"
          echo "${IP4} can NOT be added as the set \"${IPSET_NAME}\" is FULL" >&2
        elif [ -s ${TMPDIR}/error ] ; then
          ERROR=1
          logger -t $BASE "`cat ${TMPDIR}/error`"
          cat ${TMPDIR}/error >&2
        else
          logger -t $BASE "${IP4} added for Wordfence"
          echo "${IP4} added for Wordfence"
        fi

        # if a just found IP was blacklisted before than put it in the permanent list
        if ipset test ${IPSET_DISC} ${IP4} >/dev/null ; then
          [ ${HEADLESS} ] ||  echo "${IP4} is a returning hacker, I will put it in the permanent list"
          logger -t $BASE "${IP4} returned, it will be permanently blacklisted"
          if ipset add ${IPSET_PERM}  ${IP4}/32 >/dev/null ; then
            [ ${HEADLESS} ] ||  echo "${IP4} is permanently blacklisted" >&2
            logger -t $BASE "${IP4} is permanently blacklisted"
          else
            [ ${HEADLESS} ] ||  echo "I failed to add ${IP4} in permanent blacklist" >&2
            logger -t $BASE "I failed to add ${IP4} in permanent blacklist"
          fi
        fi
      fi
    fi
  done<${TMPDIR}/IPS

  [ ${HEADLESS} ] || echo "${NEWIPS} new IP's added to list"
  if [ ${NEWIPS} -gt 0 ] ; then
    logger -t $BASE "${NEWIPS} new IP's found for Wordfence"
  fi

fi

rm -rf ${TMPDIR}
exit ${ERROR}
 
Hi Danilo,

I think I will...
But I never did a collaborative project in my life, so I need to look into that first.

How do you like it thus far?
 
Gitlab is really simple and free for what you're planning to do.
It's easier to discuss about the code once it's up there.

And since this is not really related to Plesk itself anymore this forum might even be the wrong place to talk about it. :)

Please post the link to your repository once u created it.
 
A newer version can be downloaded from here: https://hmvc.eu/ipset-for-wordfence

  • ipsets will now survive a reboot as they are regularly back-upped and get automatically restored upon start
  • some streamlining of code. Maybe I should use more functions
  • an existing set of the previous month would not be used in the firewall after an iptables flush. Now it is.
  • You can add or delete an ip to/from the whitelist or blacklist
  • Don't worry about overlapping subnets. aggregate will make the list as efficient as possible
Add an IP to the whitelist
Code:
ipset-for-wordfence whitelist 20.10.20.10
Add a subnet to the whitelist
Code:
ipset-for-wordfence whitelist 20.10.20.0/24
Delete an IP from the whitelist
Code:
ipset-for-wordfence -d whitelist 20.10.20.10
Delete an IP from the blacklist
Code:
ipset-for-wordfence -d whitelist 20.10.20.10
 
Last edited:
I am a CLI-type-of-guy, for whatever that means.
I am making this script in the first place for myself and by sharing the code I'm forcing myself to write it in a way that it is portable.

It would take me a lot of time to investigate how those extensions work and to be honest.... I don't have that time.

On top of that, this script requires some basic knowledge of iptables and Linux.
You still need to add the UDF's to your MySQL installation, for instance...

Is there a specific problem when implementing this script?
Maybe I can be of help?
 
Back
Top