OpenSSH is a good stout application; it’s battle-tested and reliable. You can lock it down even further with a few simple tweaks. Best of all, these cause little or no inconvenience after they are set up.
The first thing you should do is create some access controls that allow only authorized users to login. There are several options in /etc/ssh/sshd_configfor this:
ListenAddress 18.104.22.168 PermitRootLogin no Protocol 2 AllowUsers fred ethel lucy email@example.com AllowGroups admins
Use the ListenAddress directive when you have more than one network interface on your machine, such as border routers or firewalls, or multi-homed servers connected to several different subnets. Suppose your firewall has a WAN port and two LAN ports. You can have sshdlisten only on the LAN ports, or on just one of them, and not listen on the WAN port at all. You can do the same thing with a multi-homed server- the easy way to control SSH access on selected subnets is with the ListenAddress directive. You can check this with netstat:
router2:~# netstat -untap tcp 0 0 192.168.1.50:22 0.0.0.0:* LISTEN 539/sshd tcp 0 0 192.168.2.50:22 0.0.0.0:* LISTEN 539/sshd tcp 0 0 192.168.1.50:22 192.168.1.10:45397 ESTABLISHED754/0
That is on an Internet-facing router with three LAN interfaces and one WAN interface. It confirms that SSH access is allowed only on two of the LAN interfaces, and the third line shows an active connection.
Disallowing root logins over untrusted networks is a wise practice. It’s a bit of a pain having to log in as an unprivileged user, and then using su or sudowhen you need root privileges, but it’s a lot less pain than cleaning up after a successful intrusion. You definitely don’t want to expose the root user to brute-force password attacks. Even better is disallowing password logins entirely, which we’ll get to in a moment.
“Protocol 2” means do not allow connections from clients using the SSH1 protocol. SSH1 is old mold and not safe; anyone still using it is seriously behind the times.
AllowUsers is obvious. Restricting them to specific hosts locks them down further, so ricky cannot log in from just anywhere, like public terminals which doubtless are infested with keystroke loggers and other unpleasant pests.
AllowGroups can save you some work by creating a system group and stuffing your authorized ssh users into it.
Saving Your Logfiles
You’re probably familiar with the trick of telling sshdto listen on a different port:
Then you must specify the port when you connect:
$ ssh carla@remotehost -p 8000
This won’t fool a determined attacker, who will sniff out the listening port quickly enough. But it does foil 99% of the stupid automated brute-force attacks that infest the internet, and your logfiles will be a lot cleaner.
Using public-key logins is a great way to protect your system login and 100% foil brute-force attacks. You can use the same key to log into multiple machines, a different key for every machine, or mix-n-match.
The first step is to collect host keys from all the computers you’re going to connect to remotely. This is what happens the first time you log in to a remote ssh server, and it says
The authenticity of host ‘fooserv (192.168.1.50)’ can’t be established. RSA key fingerprint is 11:22:4a:1f:dd:ac:d1:88:2d:aa:65:00:a5:ee:b9:38. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added ‘fooserv,192.168.1.50’ (RSA) to the list of known hosts.
Now you must generate your personal identity key pair:
$ ssh-keygen -t rsa Generating public/private rsa key pair...
You may choose to protect it with a passphrase, or you may not. There are two schools of thought on this. One is that a passphrase-less private key is plenty secure, because you are a very careful person who does not lose her keys. My own view is human-user keys need passphrases, especially mobile human users or human users who work around other humans, who snoop and pry and make trouble.
chmod 400 your private key, to protect it from accidental overwrites. It is stored in your ~/.ssh/authorized_keys2directory. If this does not exist, create it and make it mode 0700.
Now you must distribute your public keys to your ~/.ssh/authorized_keys2 files on all of your remote hosts. Use the ssh-copy-idscript to do this:
carla@localmachine:~$ ssh-copy-id -i id_rsa.pub firstname.lastname@example.org
After you have done this and tested that everything works, go into sshd_configon the machines you are logging into and disable password logins:
You can take this a step further and disable local logins, so that a person who is sitting at the physical machine cannot log in:
# passwd -l carla
passwd -uenables the login.
Just keep in mind the old saying: she who has physical access to the machine owns it. This won’t stop a person with a bootable Linux CD or USB key getting in, unless you disable those too. Or just walking away with the whole works, if they want it badly enough.
If you create multiple personal identity keys you’ll have to give each one a different name. Use something descriptive so you know what they are:
$ ssh-keygen -t rsa -f id_httpserver1
Then you’ll need to specify the key when you log in:
$ ssh -i id_httpserver1 carla@remoteserver
Preserving Port 22 With Fail2ban
An alternative to putting sshd on a different port is to run the excellent program Fail2ban. Fail2ban is for all services, not just OpenSSH. Fail2ban is a brute-force blocker that writes rules to /etc/hosts.deny, or it will write iptablesrules, to block offending IP addresses.
The default installation automatically protects SSH. You don’t have to lift a finger. If you look in /etc/fail2ban/jail.conf, you’ll see this:
[ssh] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 6
You’ll also see entries for other servers such as Apache, and various SMTP and FTP servers. These are not enabled; to activate any of these change enabled = “false” to “true.”
You may wish to tweak the maxfailures = setting; the default is 6 failed attempts before Fail2ban writes a block. bantime = defaults to 600 seconds, or ten minutes; after ten minutes the ban is lifted. -1 is permanent.
For insurance, you could use the ignoreip =option to make sure you don’t block your own self with fat-fingered typing. This takes a space-separated list of IP addresses like this:
ignoreip = 127.0.0.1 192.168.1.50 192.168.1.15