SSH_PORT=22 # I think ssh on !22 is silly, but to each their own
TRUSTED_SUBNET_OR_HOST=mybox.homeip.com # or a subnet you trust
iptables -A ssh_drop -j DROP -m comment --comment "SSH attack drop"
iptables -A INPUT -p tcp -m tcp -s $TRUSTED_SUBNET_OR_HOST --dport $SSH_PORT -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -m state --state NEW --dport $SSH_PORT -m recent --name sshattack --set
iptables -A INPUT -p tcp --dport $SSH_PORT -m state --state NEW -m recent --name sshattack --rcheck --seconds 60 --hitcount 3 -j LOG --log-prefix 'SSH Attack DROP: '
iptables -A INPUT -p tcp --dport $SSH_PORT -m state --state NEW -m recent --name sshattack --rcheck --seconds 60 --hitcount 3 -j ssh_drop
Basically, this counts connection attempts on port 22 and starts dropping attempts after the third one (obviously adjust --hitcount to taste). It times out after 60 seconds, so I don't accidentally lock myself out. I see a few attacks a day start then go away after the drop. I've never seen one come back after the window reopens (unless it was myself trying to "attack"). It's also 0 maintenance unlike the userspace solutions. A lot of people like tcp wrappers, but that still lets attackers hammer the port. Using both works nicely.
This and DenyHosts don't help with distributed attacks, which is why I'm moving towards key-only logins and maybe picking up some YubiKeys. (yubico.com)