How to install and harden SSH

Last updated on



Installing an SSH server can be very useful to manage your files from outside your home and knowing it encrypts the traffic one can remain relieved that the data is secure. Today we are going to install one and secure it!

This is part of a Server Setup series that I’m planning on doing including email, VPN, FTP… So stay tuned! ✌


To install it we just need to update our apps and repositories and install OpenSSH:

sudo apt update -y
sudo apt upgrade -y
sudo apt install openssh-server

Then we can verify if it’s running with:

sudo systemctl status ssh

If it’s not running, we can start the SSH service with:

sudo systemctl start ssh

We can enable it to run every time we boot the PC:

sudo systemctl enable ssh


In order to access SSH from outside the network, you need to allow incoming connections at port 22.

sudo ufw allow ssh
sudo ufw enable
sudo ufw status

At this point, we can already use the SSH to connect!



Even that SSH is encrypting our traffic cybercriminals can still perform some attacks on it depending on how it’s configured. It’s time to secure it!

I’ve put a lot of options to secure it, so choose whichever you like!

You have the list here.

Here is the default SSH config file from /etc/ssh/sshd_config:

#	$OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

Include /etc/ssh/sshd_config.d/*.conf

#Port 22
#AddressFamily any
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#PubkeyAuthentication yes

# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile	.ssh/authorized_keys .ssh/authorized_keys2

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /var/run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

# no default banner path
#Banner none

# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

# override default of no subsystems
Subsystem	sftp	/usr/lib/openssh/sftp-server

# Example of overriding settings on a per-user basis
#Match User anoncvs
#	X11Forwarding no
#	AllowTcpForwarding no
#	PermitTTY no
#	ForceCommand cvs server

We will review some of these settings.

Changing SSH port

It’s advised for every service to change the port so cybercriminals have a harder time at poking your ports. And this allows having multiple SSH servers in a network.

To change the port we just modify the Port parameter from 22 to whatever we like. I’ve put 2222.

Result: Port 2222

It’s time to restart the service so changes are applied.

sudo systemctl restart ssh

Now we need to change the UFW rules.

If you run the command below you’ll see that we have set up the rules for port 22, but now we need to delete them and create them again.

sudo ufw status numbered

To delete the rules:

sudo ufw delete <number of rule>

And we add the rules again with the new port, in my case 2222:

sudo ufw allow 2222

By running status it should print the new UFW rules:

sudo ufw status
New Port

Login Grace Time

The logingracetime parameter is the max time the user is allowed to try to log in. By default is at 2 minutes but can be modified.

Chroot Directory

By default, ChrootDirectory is set to “none”. Setting a Chroot directory allows to restraint a user from accessing any other folder than the specified (but can move freely in that directory), in common words we could say that is a jail.

Disabling Root Login

One of the most important reasons it should be disabled is that sending the root password via common traffic is not wise. Instead, is best to log in as a normal user, and then do privilege escalation to the root user as the access is local.

We need to modify the PermitRootLogin parameter from prohibit-password to no.

sudo nano /etc/ssh/sshd_config

And restart SSH:

sudo systemctl restart ssh

Usage of SSH Keys

Instead of a password, we can use a pair of keys (SSH Keys). One is the Public Key which identifies the server, and the other is the Private Key which identifies the client that will connect to the server.

I found a good explanation of why public and private keys are used on this page.

To generate these keys we just run this command:


It will ask us for the location where the keys id_rsa (Private Key) and id_rsa.pub (Public Key) will be saved, the default is .ssh folder.


To use them we issue the same ssh command but with the argument -i (identity_file):

ssh -i id_rsa username@hostname

If no password was set when creating the keys we should connect directly to the server.

Remove Password Access

Warning! ⚠

If you remove the possibility to use a password to log in and accidentally you lose the Private Key you won’t have access to the server unless you get again the Private Key.

We need to modify the PasswordAuthentication from yes to no.

sudo nano /etc/ssh/sshd_config

Result: PasswordAuthentication no

And restart SSH:

sudo systemctl restart ssh

Disallow Users with no Password set

When creating a user there’s the option of using SSH without a password, but it’s very risky!!

We can disallow it by removing the # before PermitEmptyPasswords.

sudo nano /etc/ssh/sshd_config

Result: PermitEmptyPasswords no

And restart SSH:

sudo systemctl restart ssh

Setting a max of Auth tries

After many tries of login in Windows or Android, they may lock themself due to the prevention of theft. In SSH we can enable the same.

We can modify the MaxAuthTries from 6 to what we want, in my case I’ve set it to 3. This way if we fail 3 times the connection will drop.

sudo nano /etc/ssh/sshd_config

Result: MaxAuthTries 3

And restart SSH:

sudo systemctl restart ssh

AFK timeout

We as System Administrator may not want a user to chew the resources away if they are being idle.

So, to modify it we can change the ClientAliveInterval from 0 (infinite) to what we want. In my case, I’ve set it to 5 minutes (300 seconds).

sudo nano /etc/ssh/sshd_config

Result: ClientAliveInterval 300

And restart SSH:

sudo systemctl restart ssh

Limit Access to some Users

If we have a multiuser server we may want some users to not have SSH access.

An example could be that it’s a paid option of our server, or that we just want a colleague to have access to the resources of the server.

To allow only some users we can append the AllowUsers user1 user2 ... parameter at the end of the file (where you want really).

sudo nano /etc/ssh/sshd_config

Result: AllowUsers nonuser aNewUser

And restart SSH:

sudo systemctl restart ssh


Link to Github.

As the description says:

Endlessh is an SSH tarpit that very slowly sends an endless, random SSH banner. It keeps SSH clients locked up for hours or even days at a time.

The idea is putting the real SSH server on another port not as common as 22 or 2222, so botnets or malicious hackers that try to execute exploits, rootkits, etc., or brute force your server, will get stuck and can’t do anything else.


First, we need to download the repository:

git clone https://github.com/skeeto/endlessh

Then we need to enter the folder and compile the program:

cd endlessh

It shouldn’t throw you any error, but if it does:

sudo apt install libc6-dev -y

Now we can move the final executable to the local user binaries folder (which is in the PATH), to be able to use it from any folder:

sudo mv endlessh /usr/local/bin

If we want to execute it as a service, to make it run whenever the computer is powered, we can run:

sudo cp util/endlessh.service /etc/systemd/system
sudo systemctl enable endlessh

What this does, is copying the service file to the services folder, and then enable Endlessh to run on boot.


We need to create the config file:

mkdir -p /etc/endlessh
printf "Port 22\nLogLevel 1" | sudo tee -a /etc/endlessh/config > /dev/null

You can change the port from 22 to 2222.

There are more configuration parameters in the repository.

That’s it!


Link to webpage.

I have a post about Fail2Ban.

Apart from the normal configuration, what interests us here is changing these lines:

# To use more aggressive sshd modes set filter parameter "mode" in jail.local:
# normal (default), ddos, extra or aggressive (combines all).
# See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details.
#mode   = normal
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s

In this case, changing the port if needed (default is 22) and change the mode if anything.