Date: 2007-03-21 19:38:00
three strikes and you're out

I used to get tons of invalid login attempts to my hosted server. A single IP address would repeatedly connect and try hundreds of account names, filling up my logs with junk. A few people I know use sshit to automatically firewall addresses that have too many unsuccessful connections. However, I tried to set up sshit and its weird dependencies on Perl IPC stuff didn't work so well for me. (Multithreaded perl is evil.)

So I took the concept of sshit and rewrote it as a little Python script that does everything I need it to do in 41 lines of code (sshit is 354). It connects to the syslog mechanism through /etc/syslog.conf:; |exec /usr/local/sbin/

The script just reads log entries from stdin as they happen, and looks for invalid login attempts, calling ipfw to firewall connections for a while if somebody's being annoying. Here's what it looks like in practice:

Mar 20 07:46:58 occam sshd[38074]: Invalid user test from
Mar 20 07:47:00 occam sshd[38076]: Invalid user guest from
Mar 20 07:47:03 occam sshd[38078]: Invalid user admin from
Mar 20 07:47:03 occam root: 29000 deny ip from to any
Mar 20 08:02:03 occam root: 29000        24         1852 deny ip from to any
Mar 20 16:10:21 occam sshd[48070]: Invalid user guest from
Mar 20 16:10:31 occam sshd[48076]: Invalid user adm from
Mar 20 16:10:34 occam sshd[48078]: Invalid user lp from
Mar 20 16:10:34 occam root: 29000 deny ip from to any
Mar 20 16:25:34 occam root: 29000        25         1924 deny ip from to any
Mar 20 17:42:10 occam sshd[49525]: Invalid user a from
Mar 20 17:42:14 occam sshd[49527]: Invalid user b from
Mar 20 17:42:20 occam sshd[49529]: Invalid user c from
Mar 20 17:42:20 occam root: 29000 deny ip from to any
Mar 20 17:57:20 occam root: 29000         9          532 deny ip from to any

Notice how the third invalid login attempt triggers the deny rule, then 15 minutes later the output of ipfw show for that rule shows how many packets and bytes were blocked. The rule is deleted after that 15 minutes.

If you'd like the source, you can grab it here.

I gave-up the good fight and set PasswordAuthentication no in sshd_config about three months ago.
I often seem to need to be able to log in without having my SSH private key handy. Disabling password authentication is certainly another good approach to this problem though.
Wouldn't deleting rule 29000 delete all rules with that number? So if one IP was blackholed in 29000 and you shitlist another IP before the 15 minutes are up, once you unblock the first IP wouldn't you have unblocked the second IP...?
Yes, that's true. That's why my script allocates a new rule number for each simultaneously blocked connection, so they can be individually deleted.
oh cool.
[info]ivo : neat!
You should package it and have dbaker make it into a port.
[info]decibel45 : Re: neat!
[info]decibel45 : Re: neat!
Of course, you could always do the packaging (or me, for that matter).
CPU states: 62.0% user, 24.4% nice, 13.2% system, 0.4% interrupt, 0.0% idle
Mem: 131M Active, 203M Inact, 105M Wired, 16M Cache, 60M Buf, 35M Free
Swap: 1024M Total, 872K Used, 1023M Free

91680 root 1 132 0 4252K 3124K RUN 9:35 72.36% python
587 dnetc 1 134 20 1100K 684K RUN 354.9H 23.05% dnetc

It sure is quite busy...
That doesn't seem right. For debugging, I just did this sort of thing:

tail -f /var/log/auth.log | python

I'm curious why it might be spinning up like that on your machine.
(anonymous) : Change ports
From my sshd_config:

#Port 22
Port 23

It's enough to confuse your average script kiddie, and it's not like there's anything else you're going to run on port 23.

Greg Hewgill <>