1. Change your sshd
listening port
As simple as it sounds, changing your port from the default 22
to a custom value (e.g. 2200
) on a public IP could reduce the number of port scans from thousands a day to a few dozen. For a tutorial see here.
That being said, while this does reduce the annoyance of being port-scanned, it DOES NOT provide real security. "Security by obscurity" is a myth and nothing more.
2. Use public key login and disable password login
Bots might guess a password right, but they're never going to guess a private key right. As long as you use modern algorithms and don't leak your key accidentally. For a tutorial see here.
Note that this does mean you will no longer be able to login from any random machine, unless you carry the key with you (which you need to secure via some other way).
3. Use fail2ban
If they fail 5 times, ban them from trying again for a day or something. That'll show them. Very effective against brute force attempts. For a tutorial see here.
The downside is that you might lock yourself out if you've got wobbly fingers one day (drunk or something, I don't know).
4. Pre-ban a list of IPs known for abusive use
Sources like AbuseIPDB maintain big lists of known malicious IPs accessible via API. You will need an API key to use it, but you can register a free account easily enough. Pull the list and use something like iptables
to ban those known IPs in bulk. Set up a cron job to automate it periodically for best effect. Here's a very simple script I wrote (and am personally using) to do just that. Feel free to use it as reference, but DON'T USE IT IN PRODUCTION.
In my personal experience, this method is equally effective (if not more so) as method #3.
Personally, I use all 4 methods listed above, and my VPS might get one or two malicious login attempts in my security log at most, in a bad month. Here's the interception history of iptables
via method #4:
$ abip-hist
Chain abip-ban (1 references)
pkts bytes target prot opt in out source destination
362 14480 DROP all
307 12280 DROP all
288 12672 DROP all
277 19911 DROP all
250 11000 DROP all
230 9200 DROP all
215 8600 DROP all
214 8560 DROP all
202 8080 DROP all
201 8040 DROP all
199 7960 DROP all
197 7880 DROP all
197 7880 DROP all
196 7840 DROP all
196 7840 DROP all
196 7840 DROP all
189 7560 DROP all
184 7360 DROP all
184 7360 DROP all
182 7280 DROP all
181 7240 DROP all
180 7200 DROP all
176 7040 DROP all
...
this goes on for 1700 lines...
$ uptime
06:36:49 up 16 days, 15:24