In a previous post http://www.wiseoldcat.com/?p=176 I wrote about how to block dictionary attacks with iptables and an adaptive blacklist. I’ve moved the script to several different hosts and it worked on all of them but one. It’s an aging CentOS 5.5 system (I know, I know, it should be updated.) For some reason it wasn’t picking up on the active BLACKLIST entries. It would do its thing just fine when run from the bash prompt but not when run from crontab. Turns out it has to do with the environment.
The line in question looks like…
/sbin/iptables -L BLACKLIST -vn | egrep "^\s+[1-9]|^[1-9]+" | egrep DROP | tr -s " " | cut -d " " -f 9 | sort -u | sed 's/\(^.*$\)/iptables -A BLACKLIST -s \1 -j DROP -m comment --comment "active BLACKLIST"/g' >> /root/bin/blistrun.sh
To break it down in detail it pipes the existing BLACKLIST to egrep which searches for the start of a line that begins with one or more spaces followed by a digit in the range of 1 to 9 OR start of a line followed by one or more digits in the range of 1 to 9 which is piped to egrep which searches for the string DROP (line 1). That in turn is piped to tr (translate) which replaces all of the instances of multiple spaces with a singe space (line 2). The cut command picks out the 9th field (the IP address) and pipes that to the sort command that removes all of the duplicates (line 3). Finally the sed command wraps the IP address in the text of the iptables command that we want to run (line 4) and that is redirected into the script that does the deed. So with each step the output looks like (with a couple of steps left out)…
6 360 DROP all -- * * 220.127.116.11 0.0.0.0/0 /* active BLACKLIST */ 6 360 DROP all -- * * 18.104.22.168 0.0.0.0/0 /* active BLACKLIST */ 22.214.171.124 iptables -A BLACKLIST -s 126.96.36.199 -j DROP -m comment --comment "active BLACKLIST"
The net effect is to find all of the BLACKLIST entries that have been hit (packet counter > 0) since the last time the script was run and add them back into the BLACKLIST.
In order to track down the error I copied the command into another script and ran that from cron once a minute. By trial and error I found that egrep was not pattern matching this regex “^\s+[1-9]|^[1-9]+” and it was the beginning of line “^” pattern that it wasn’t matching on. After a couple of hours of Google searching and pounding my forehead to a pulp against my desk I found that adding the LANG environment variable to /etc/crontab fixed it.
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ LANG=en_US.UTF-8
No f*!cking wonder I drink.