Anyone that runs servers knows that every address on the internet is constantly being probed by attackers. A good security measure is to run an automated system like
fail2ban that will detect and block attacks.
But I wasn’t satisfied with that.
While working in Network Operations, I would occasionally recieve emails directed to the
abuse@ alias. As a NOC Technician, it was important to me that our network remain secure and free of abuse, so I always appreciated these emails. I decided that it might be fun to have
fail2ban automatically email other network administrators.
The basic idea is simple:
fail2banis triggered, the
actionbanfires my script and passes in the IP address and the log file.
- The script then performs a
whoislookup on the IP address and grabs everything that looks like an email address.
- It also
greps out the relevant log lines.
- Finally, it builds a small email and sends it off!
This script isn’t perfect, but it has served me well for over 3 years. The main bug is that all email addresses from the WHOIS lookup are contacted and by RFC, I should probably only email the
OrgAbuseEmail. However, WHOIS records vary so dramatically that the
OrgAbuseEmail is not always populated. In fact, many WHOIS records provide no email contacts at all. Additionally, I don’t follow RWHOIS or handle
NET- lookups. (A very friendly NOC Technician in Oregon pointed out the latter issue when I contacted them directly.) I suspect that many ISPs (China, Eastern Europe, Russia) redirect all abuse-related mail to
#!/usr/bin/python import os, socket, sys import smtplib from email.mime.text import MIMEText # assign the arguments ip = sys.argv logfile = sys.argv # grab the abuse@ and other contacts fin,fout = os.popen4( "whois " + ip + " | grep -o '[[:alnum:]+\.\_\-]*@[[:alnum:]+\.\_\-]*' | sort | uniq" ) emails = fout.read() # header message = "THIS IS AN AUTOMATED MAILER.\n\n" \ "The IP " + ip + " was recorded as making multiple brute-force attempts. " \ "Your email is listed as a contact on the whois information for " + ip + ".\n" \ "Below are the relevant log lines. All times are Pacific time.\n\n" \ # log entries fin,fout = os.popen4('grep ' + sys.argv + ' ' + sys.argv) lines = fout.read() # mail variables hostname = socket.getfqdn() src = 'fail2ban@' + hostname dest = [ 'root@' + hostname ] for each in emails.split(): dest.append(each) print dest # build the message... msg = MIMEText(message + lines) msg['Subject'] = 'Brute Force Attempt from ' + ip msg['From'] = src msg['To'] = ', '.join(dest) # ... and send! s = smtplib.SMTP('localhost') s.sendmail(src, dest, msg.as_string()) s.quit()
The hardest part is the
fail2ban configuration. I simply modified one of the files in
/etc/fail2ban/action.d to contain the following line:
actionban = /usr/local/sbin/abuse.py <ip> <logpath>
I later discovered that
/etc/fail2ban/action.d/complain actually does everything my script does. One of the most interesting things about systems administration is discovering when you’ve re-invented the wheel. It’s always a good learning experience, both during the re-invention but also in analyzing what others have done before you.