Basics of Securing SSH
I am not a security expert, and this is not an authoritative list, but it’s a good start. For more security measures, I suggest that you peruse Server Fault or read this CentOS how-to entry.
Note that I’m not describing here how to use public/private keys for authentication. Not only does this allow you to avoid entering a password when logging in, but it allows you to disable password authentication completely. This is because part of me wants to be able to log in from any client, so long as I can remember my password.
Switch away from port 22
The majority of attacks attempt to log in on port 22, and if this fails, they simply move on to trying port 22 on another IP address. While the new port of sshd
can be found with a simple port scan, most attacks are not that sophisticated.
Choose a new port less than 1024, also called a well-known port. A service listening on such a port is either running as root
or was started as root
. This makes it difficult for an attacker without root access to terminate sshd
and then replace it with a SSH service under the control of the attacker. Consult the IANA port number assignment table to avoid picking an important well-known port, such as 443 for HTTPS.
For this post, I’m going to choose port 789. And to be safe, we’re going to change sshd
to listen both on this port and on port 22 before cutting off access to the latter.
First, we must add a rule to the configuration of iptables so that it accepts TCP connections on port 789. On many servers, these rules are stored in the file /etc/sysconfig/iptables
. If this file does not exist, consult the documentation of your distribution to find or even create this file. Once you’ve opened it, the easiest way to add the rule is to copy and then modify the existing rule that accepts TCP connections on port 22. Although the definition of this rule can vary between distributions, it will contain both --dport 22
and -j ACCEPT
. On one of my servers, this rule is -A INPUT -m state --state NEW -p tcp --dport 22 -j ACCEPT
. Add a rule below it that is similar but uses the new port. If switching to port 789, the file now contains:
-A INPUT -m state --state NEW -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -p tcp --dport 789 -j ACCEPT
Next, reload iptables to use this new configuration. On some distributions, you may need to run /etc/init.d/iptables reload
. On others, you may need to run service iptables reload
.
Now that your server accepts TCP connections on port 789, we need sshd
to listen on that port in addition to port 22. Open the file /etc/sshd/sshd_config
and find the line Port 22
. Add a line below it that uses the new port. If switching to port 789, the file now contains:
Port 22
Port 789
Next, reload sshd
to use this new configuration. On some distributions, you may need to run /etc/init.d/sshd reload
. On others, you may need to run service sshd reload
.
Now test logging into server on the new port. If switching to port 789, we log in like so:
$ ssh -p 789 myserver
Once this succeeds, it’s time to close port 22 and complete switching away.
First, open /etc/sysconfig/iptables
again and delete the line containing --dport 22
and -j ACCEPT
. Then reload iptables like you did before. Second, open /etc/sshd/sshd_config
again and delete the line Port 22
. Then reload sshd
like you did before.
Once this is done, logging into the server on the new port should still succeed, but logging in through port 22 should fail:
$ ssh myserver
ssh: connect to host myserver port 22: Connection refused
Disable root access
The majority of attacks attempt to log in as root
. Otherwise, the attacker must find a username with root access before proceeding with the attack. By disabling SSH logins for the root
user, we eliminate these unsophisticated attacks.
First, ensure that your user has permission to log in via ssh and run programs as root
using sudo
. If not, you must add your username to the file /etc/sudoers
. You don’t want to disable logging into the server as root
, and then later find out that you have no way to run programs as root
.
Next, open the file /etc/sshd/sshd_config
again and search for a line containing PermitRootLogin
. It may or may not be commented out. Regardless, change this line to:
PermitRootLogin no
Next, reload sshd
to use this new configuration, just as you did when switching away from port 22. Attempting to log in as root
should prompt the user for a password like normal:
$ ssh -p 789 root@myserver
root@myserver's password:
Even if you enter the correct password, the server will respond with Permission denied, please try again.
, just as if you had entered an incorrect password.
Install Fail2ban
From its website:
“Fail2ban scans log files and bans IPs that show the malicious signs – too many password failures, seeking for exploits, etc. Generally Fail2Ban then used to update firewall rules to reject the IP addresses for a specified amount of time, although any arbitrary other action could also be configured.”
The jail.conf
file, which may be in the directory /etc/fail2ban
, declares available jails. Each jail combines a filter, which specifies how login failures are detected, with one or more actions, which specify how to enforce a ban from too many login failures.
Near the top of jail.conf
you will find default settings for jails:
ignoreip = 127.0.0.1/8
bantime = 600
findtime = 600
maxretry = 3
These fit together like so: If a jail finds a host generating maxretry
login failures in the last findtime
seconds, and that host is not in ignoreip
, then the jail bans that host for bantime
seconds. Each jail declaration can override any of these settings. By changing these settings, you can make the banning criteria more or less restrictive.
Just below that you will find the declaration of the ssh-iptables
jail, which uses iptables to block login attempts from a banned host:
[ssh-iptables]
enabled = true
filter = sshd
logpath = /var/log/sshd.log
action = iptables[name=SSH, port=789, protocol=tcp]
The logpath
parameter specifies what log file is monitored for login failures. If /var/log/sshd.log
does not exist, you may need to find or change where sshd
is logging to, such as /var/log/secure
.
The filter
line specifies the regular expressions that find login failures. The value sshd
corresponds to the file /etc/fail2ban/filter.d/sshd.conf
, which contains the lines:
failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
^%(__prefix_line)sFailed (?:password|publickey) for .* from <HOST>(?: port \d*)?(?: ssh\d*)?\s*$
...
Any line in the file logpath
matching one of these regular expressions is a login failure.
The action
line specifies the one or more actions to take when a host should be banned or unbanned. The value iptables
corresponds to the file /etc/fail2ban/action.d/iptables.conf
. The name
, port
, and protocol
parameters enclosed in square brackets override the default values defined in that file. It is important that the port
value equals the new port number that sshd
now listens on; above, we use 789.
The sshd-iptables
jail adds its own chain to iptables. When a host fails logging in too many times and should be banned, this jail executes the actionban
command defined in iptables.conf
. This adds a DROP
rule to its chain, thereby rejecting login attempts from the host:
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
After bantime
seconds elapse, this jail executes the actionunban
command defined in iptables.conf
. This removes the rule:
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
Other jail declarations follow ssh-iptables
in jail.conf
, each with a filter in /etc/fail2ban/filter.d/
and actions in /etc/fail2ban/action.d/
. Any number of jails can be enabled.
For more information on Fail2ban, consult its manual.
comments powered by Disqus