First of all, in the example you gave, where's the --set rule? Hashlimit works in the opposite way: the main rule is the one that allows traffic up to a certain limit; with Recent, the main rule is the one that blocks, and it has to come first, making logging annoying -- you need a duplicate rule with -j LOG instead of -j DROP. With hashlimit, the main rule allows legit traffic, so then the logging rule doesn't need to even load the hashlimit module (see below).
the recent module doesn't let you have a netmask for blocking. hashlimit does. Back when I was using that, I had it block the /24 that attacks came from.
Another thing I like about hashlimit is the ability to allow for a burst in addition to the normal rate limiting, since it's effectively a token bucket filter.
Chain ssh-ext (0 references)
pkts bytes target prot opt in out source destination
31957 1908K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 limit: up to 2/min burst 10 mode srcip srcmask 24
1276 76148 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4
1276 76148 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
That's what I used to use. With ipt-recent, it looks something like this.
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 60 hit_count: 3 name: tel side: source LOG flags 0 level 4
2 120 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 recent: UPDATE seconds: 60 hit_count: 3 name: tel side: source
3 180 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: tel side: source
That's why I think the hashlimit solution is cleaner and more flexible. If your default policy is REJECT/DROP, and you don't want to log, then a hashlimit setup requires only one rule. ipt-recent needs two.
The reason I ditched both of those solutions is because they're naive: they cannot distinguish between legit traffic (successful logins) and illegitimate traffic (failed logins), at least not without an absurd amount of additional rules to remove suspect addresses when a certain threshold of --state ESTABLISHED traffic is seen from them, and re-add them if enough --state NEW traffic is seen. If I have a script that logs in 20 times in rapid succession, it doesn't matter that it successfully logs in each time (using key auth, even). It still gets blocked.
If you can guarantee that you never need to login a bunch of times in quick succession, then that's not a problem.
Port knocking is great if you want to set it up, but the main reason to do that is to add a layer of security against pre-login ssh security holes. Password brute forcing is easily dealt with (first by picking a good password or disabling password auth entirely, then by using iptables to avoid having logs flooded with ssh login attempts) without resorting to port knocking. I have a gripe about port knocking, like I have gripes about everything. Using an iptables-only port knocking setup requires a lot of rules. Complicated rulesets mean more chance of mistakes, potentially bringing down the entire iptables house of cards.
http://www.zeroflux.org/projects/knockUsing userspace programs like sshguard or knock is also potentially problematic, since if something happens to those processes you lose the protection they offer. But I still like those solutions better than iptables-only.
It's also worth noting that while --rttl reduces the chances of successful DOS attempts, it also means that any attacker that has root-level access to the attacking host gets approximately 240-ish times as many guesses before being blocked, since they can change their TTL before each attempt.