Basics of Securing SSH
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
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.
/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
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
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:
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.
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.”
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]
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
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.
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
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.
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
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
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