SSH service running on port 22 is one of the most widely used services on the Internet. There are numerous reasons for its widespread use, among which is direct access to the remote system over a security encrypted communication channel. SSH service distinguishes among lesser used SSH-1 and most common SSH-2 versions. SSH was developed to replace insecure protocols like Telnet and rsh/rexec, which communicate over unencrypted communication channels by sending messages in plaintext.
Despite SSH being very secure by itself, there are still various configuration options that can be used to harden SSH security. Most common security options related to the sshd2 daemon running on port 22 are read from /etc/sshd/sshd_config and are presented in the table below.
Table 1: Configuration options in /etc/ssh/sshd_config
|Protocol||2||Enable protocol version SSH-1 or SSH-2. Note that protocol version SSH-1 is an older protocol, and should not be used due to the various security weaknesses. Only protocol version SSH-2 should be enabled.|
|X11Forwarding||no||Disables X11 forwarding, which can be enabled temporarily only when we have to install a GUI program to the remote system.|
|Subsystem||sftp /usr/lib/openssh/sftp-server||Configures an external subsystem, which will get executed upon a subsystem request: usually a file transfer request.|
|StrictModes||yes||Check the ownership of user’s home directory before accepting login, which is desirable when a chance exists that users left their home directory world-writable.|
|ListenAddress||IP Address||Listen on a specified IP address, which is useful in systems with multiple interfaces to enable the SSH daemon on certain interfaces (IP addresses) only.|
|AllowTcpForwarding||no||TCP forwarding should be disabled, so users won’t be able to forward TCP connections. Note that disabling TCP forwarding improves security only when users are denied shell access, because otherwise they can install their own forwarders.|
|AllowAgentForwarding||yes||Enables agent forwarding, which can be used to allow communication with the SSH server by using local SSH keys instead of remote SSH keys on the server, which mustn’t have passwords set. This directive accesses the ssh-agent running on the client, which keeps the keys loaded in memory, so we don’t have to re-enter our password every time we login to the server.|
|UsePrivilegeSeparation||yes||After successful authentication, the SSH daemon forks an unprivileged child process with user’s privileges to prevent privilege escalation attacks, which is one of the most important settings in the sshd_config configuration file.|
|AllowUsers||user||Specifies a space-separated list of users for which login is allowed; we can use ? and * wildcards when specifying user names. By default all users are allowed to connect to the server, so providing a list of allowed users should enhance security.|
|AllowGroups||group||Specifies a space-separated list of groups for which login is allowed; we can use ? and * wildcards when specifying group names. By default all groups are allowed to connect to the server, so providing a list of allowed groups should enhance security.|
|DenyUsers||user||Specifies a space-separated list of users for which login is disallowed; we can use ? and * wildcards when specifying user names. The best practice is to run the command below to determine all users supposedly allowed to login to the system and add them to DenyUsers directive. Note that at least one user should be removed from the list in order to not ourselves lock out of the system.
# cat /etc/passwd | grep -v “false” | cut -d ‘:’ -f 1 | tr ‘\n’ ‘,’
|DenyGroups||group||Specifies a space-separated list of groups for which login is disallowed; we can use ? and * wildcards when specifying user names.|
|PasswordAuthentication||no||Disables password authentication, which is by default enabled, which greatly enhances the SSH security. Since users can no longer login with passwords, they have to use alternative more secure ways of authentication. This also prevents password bruteforce attacks, which are common in every day hacking activity.|
|PermitEmptyPasswords||no||Specifies that accounts with empty passwords cannot be used to authentication to the system, which is only relevant when password authentication is used. Users usually don’t use empty passwords, but we can never be too careful.|
|PubkeyAuthentication||yes||Enables public authentication, which is a more secure way of authenticating to the SSH server. The preferred way of authenticating to the SSH server is disabling password authentication and allowing only authentication with public-private key pairs.|
|ChallengeResponseAuthentication||no||Disables challenge response authentication option.|
|PermitRootLogin||no||Prevents user root to authentication to the system. The directive can be set to: no, yes, without-password or forced-commands-only. The value ‘no’ disables the root login completely, while the value ‘yes’ enables root login. The ‘without-password’ value can be used to disable root logins with password authentication while allowing the public key authentication. The ‘forced-commands-only’ value can be used to run specific commands together with public key authentication, which is useful for running remote backups.|
|ChrootDirectory||path||A path to directory where users are chrooted after authentication. The chrooted environment must provide all necessary tools and services for user session to be useful. This option provides an additional layer of defense, because users are put in a chrooted jail and cannot access the host system directly.|
|ForceCommand||command||Specifies a command, which will be executed in user’s login shell by using the -c option, while the user’s actual command is ignored. The directive can specify a list of commands the user can run against the server, while the rest of the commands are disabled.|
|Ciphers||ciphers||Specifies a comma-separated list of ciphers that will be used to encrypt the communication channel. This directive can be used to enable only 256-bit cyphers and disabling the 128/192-bit ciphers.|
|LoginGraceTime||10||The number of seconds the server will wait for the user to login, after which the connection will be terminated.|
|MaxStartups||1||Specifies the maximum number of simultaneous client unauthenticated connections, after which additional connections will be terminated. This option directly influences the number of threads different tools can use to bruteforce user’s credentials.|
|Banner||/etc/issue.net||Sets a warning displayed to the user prior to authentication, to let him know he is connecting to a restricted SSH server.|
Harden SSH Access
Systems in the public cloud are usually accessible from anywhere on the Internet. When SSH access is enabled (most of the time it is), an attacker might use a dictionary or bruteforce attack to try to get access to the system. Most of the time, there aren’t any security measures in place that would limit the number of connection attempts or allow only certain IPs from reaching the server. Therefore, attackers might use a botnet to bruteforce the SSH password, which results in gaining faster access to the system.
In order to protect against such attacks, we can use various security implications that harden the security of SSH servers.
TCP wrappers can be used to specify which IPs are allowed/denied access to the SSH server by utilizing the /etc/hosts.allow and /etc/hosts.deny files. To check whether the sshd daemon was compiled with TCP wrappers, we can issue the ldd command to check whether the libwrap is one of the shared libraries of the executable.
An example of good security is adding your own IP address into /etc/hosts.allow to prevent locking yourself out of the server. By specifying your IP address as allowed, that IP will not be blocked even if you issue a bruteforce attack against one of the users.
We’ve already presented that attackers are often trying to bruteforce the password used by users that have access to the SSH server. There are multiple solutions available, but a widely used option is DenyHosts, which will be described here.
DenyHosts monitors invalid login attempts and blocks the originating IP from where the authentication requests are coming from. To enable the protection, we basically have to install the denyhosts package by using our package manager like apt-get. Then we have to edit the /etc/denyhosts.conf configuration file and change the SMTP settings to allow sending emails to the administrator upon blocking a certain IP. After editing the configuration file, we only have to restart the denyhosts service.
Port Knocking and Single Packet Authorization
Port knocking is a technique used together with a firewall to open specific ports upon receiving a port knock. We’ve already said that SSH is often enabled in cloud servers, because of a need for remote administration, but in such cases port 22 needs to be open in order for clients to be able to connect to the server. With port knocking this needn’t be the case –most of the time, ports to access the SSH server can be closed and can open only on a specific port knock.
Port knocking is a technique which uses packet headers to communicate secret information to the server in order to open specific port. Secret information transferred to the server to open specific ports can be encoded by using different techniques:
- Port Knocking using Port Sequence: the secret knock can be embedded as an ordered sequence of ports for which SYN packets need to be received in order for secret knock to be valid. When the correct sequence of SYN packets to correct ports is received, a client will be allowed to access the port. This method is basically quite limited, as it implements security through obscurity, because an attacker can still bruteforce the order of ports for which SYN packets needs to be sent. Other attacks, such as packet relaying attack, are also possible and defeat the port sequence port knocking security measure: this is because packets needed to be sent to the remote server are always the same and do not change, which makes them easy to sniff and replay.
- Single Packet Authorization using Packet Payload: by using this technique, the secret information is not embedded in packet headers, but rather in a packet payload. Prior to issuing a port knocking sequence, a secure encrypted communication channel is established with the server, which prevents replay attacks, because packets are never the same, since a proper measure of randomness is achieved during the connection establishment. Therefore, even if the attacker is able to sniff every packet exchanged during port knocking, he will not be able to replay them. This option is also safer to use, because the usual packets sent during port knocking don’t look like port scanning attacks, so the firewall won’t block them.
The packet payload port knocking technique can be used by installing and configuring fwknop client/server. First we have to install the fwknop-server by using the apt-get:
# apt-get install fwknop-server
First, we have to edit the /etc/fwknop/fwknop.conf configuration file and change the appropriate configuration option. We shouldn’t change much really; we should set our email address in the EMAIL_ADDRESSES variable and set the interface network card on which SSH is listening to PCAP_INTF (usually the default ‘eth0’ value is correct). The PCAP_FILTER directive should be set to the UDP port where the fwknop will be listening for the knock.
Edit the /etc/fwknop/access.conf configuration file to set the IP addresses the fwknop is allowed to open – the OPEN_PORTS directive. The KEY specifies the key that must be shared between the client and the server in order for the port knocking to be successful. Note that the KEY needn’t be comprised of numbers only, but letters and special characters can also be used.
Then we need to edit iptables rules to deny access to the SSH server, by REJECTing connections to destination port 22 as presented below. Note that the first iptables rule will lock you out of the server if you’re running it over SSH, so you first need to add a rule to temporarily allow your IP no matter what. The iptables-save command saves the current iptables rules to a file, which is important so we can restore from it later on upon booting the computer – otherwise access to the SSH server won’t be rejected. The last command saves the iptables-restore command to the /etc/rc.local, which will be called upon boot to restore the current iptables rules.
# iptables -A INPUT -i ppp0 -p tcp -dport 22 -j REJECT # iptables-save > /root/firewall-config # echo "iptables-restore < /root/firewall-config" >> /etc/rc.local
Let’s now scan the server by using the nmap command. Below we can see the picture when the server was scanned prior to running fwknopd.
Next we can start fwknopd by running a simple command as presented below.
Afterwards access to the SSH server will be prohibited. If we scan port 22 again, we’ll see it’s filtered.
To instruct fwknop to add appropriate iptables rules to allow access to the server, we have to execute the command below, where the must be substituted with the server’s IP address and needs to be substituted with PCAL_FILTER port where the knock will be sent to.
# fwknop --server-port <port> --access 'tcp/22' --source-ip --destination <ip>
After running the command, we’ll have to provide the KEY secret key, which was set in the /etc/fwknop/access.conf configuration file. Entering the password to the fwknop command every time before gaining access to the server would be tedious, which is why we can save the password to .knock-keyfile and it will be automatically applied to the fwknop command. To do that we first have to save the password to the .knock-keyfile in our home directory, where the needs to be substituted with the IP of the server and the with the actual secret knock key.
# cat "<ip>: <key>" >> ~.knock-keyfile
When running the fwknop command, we should use an additional —get-key parameter to instruct fwknop to read the .knock-keyfile, which contains the secret key.
# fwknop --server-port <port> --access 'tcp/22' --source-ip --destination <IP> --get-key ~/.knock-keyfile
In this article we’ve presented different ways of protecting and hardening our SSH server. Since the SSH server is quite often one of the few services available in a public cloud environment, it’s important to properly secure it by using different security implications. By following the tutorial above, attackers won’t be able to determine whether the SSH server is present or not, let alone bruteforcing the password or using any other attack vector to cause harm or possibly gain access to the system.
 sshd_config – OpenSSH SSH daemon configuration file,