SSH server hardening on Ubuntu
Learn how to make your servers more secure using SSH hardening
Last updated
Learn how to make your servers more secure using SSH hardening
Last updated
Generally, intrusions only occur on ports that are actively serving requests. This might be port 22 (if you are using a standard SSH port), as well as port 80/443 if you are serving a web application. All requests to other ports would automatically be dropped given there is no service listening on those ports.
To secure your server, you should focus on limiting access to the services that listen to ports. In this tutorial, we'll secure SSH access, but these principles also apply to all other services that you may run on your server.
We include OpenSSH server on every TensorDock virtual machine. OpenSSH is the default SSH server software that is used within Ubuntu, Debian, CentOS, FreeBSD, and many other Linux-based systems.
It is important to properly secure your OpenSSH server, as it is the gateway for anyone to enter server. In this tutorial, you will learn about hardening your OpenSSH server by using different configuration options to ensure that remote access to your server is as secure as possible.
You can deploy a GPU on our Marketplace to your specific configurations based on cost, RAM, storage, and vCPUs. You can use either Ubuntu 20.04 or Ubuntu 22.04 as your installation.
In order to start securing your SSH server, we will start with a general secure configuration that will suit the majority of servers. Some advanced users may prefer to configure their servers depending on their own threat model and risk threshold, which is beyond the scope of this tutorial.
Most hardening configurations for OpenSSH will be using the standard OpenSSH server configuration file, located at /etc/ssh/sshd_config
.
Before you continue with this tutorial, it is best practice to take a backup of your existing configuration file, in case something goes wrong. Use the following command to do this:
This saves a backup of the file to /etc/ssh/sshd_config.bak
.
We can review the current options that are set using this command which runs the OpenSSH server in extended test mode, which will validate the full configuration file and print out the effective configuration values:
You can now open the configuration file using your preferred text editor and begin implementing the initial hardening measures:
When editing your configuration file, some options will be commented out by default using a single hash character (#
) at the start of the line, which can be seen above. In order to edit these options and/or have the commented option be recognized, you will have to uncomment them by removing the hash.
First, disable logging in via SSH as the root user by setting the following option:
We do this as it will prevent a potential attacker from logging in directly as root. Virtual machines do not include root users by default, but as a good measure, we'll prevent root user login in case you define a root user later on.
Next, limit the maximum number of authentication attempts for a particular login session by changing the following:
Generally, a value of 3
is acceptable for most setups, but you can set this higher or lower if you'd like. However, setting a value too low may prove problematic; if you fail to enter your password correctly three times in a row, you'll be blocked from SSH'ing onto your server via fail2ban, which we include on all of our Linux operating system templates.
If required, you can also set a reduced login grace period, which is the amount of time a user has to complete authentication after initially connecting to your SSH server:
This is automatically specified as seconds, unless otherwise specified.
Setting this to a lower value helps to prevent certain denial-of-service attacks where multiple authentication sessions are kept open for a prolonged period of time.
SSH keys are another method to authenticate a remote connection into a server. Learn more here.
First, if you haven't already, you'll need to define an SSH key pair. Read more on how to do so here.
Then, you'll need to transfer your computer's public key to the TensorDock server. You can do this by running the following command from your own computer:
If you have configured SSH keys for authentication, rather than using passwords, disable SSH password authentication to prevent leaked user passwords from allowing an attacker to log in:
As a further hardening measure related to passwords, you may also wish to disable authentication with empty passwords. This will prevent logins if a user’s password is set to a blank or empty value:
In the majority of use cases, SSH will be configured with public key authentication as the only in-use authentication method. However, OpenSSH server also supports many other authentication methods, some of which are enabled by default. If these are not required, you can disable them to further reduce the attack surface of your SSH server:
If you’d like to know more about some of the additional authentication methods available within SSH, you may wish to review these resources:
X11 forwarding allows for the display of remote graphical applications over an SSH connection, but this is rarely used in practice. It is recommended to disable it if it isn’t needed on your server:
OpenSSH server allows connecting clients to pass custom environment variables, that is, to set a $PATH
or to configure terminal settings. However, like X11 forwarding, these are not commonly used, so can be disabled in most cases:
If you decide to configure this option, you should also make sure to comment out any references to AcceptEnv
by adding a hash (#
) to the beginning of the line.
Next, you can disable several miscellaneous options related to tunneling and forwarding if you won’t be using these on your server:
Finally, you can disable the verbose SSH banner that is enabled by default, as it shows various information about your system, such as the operating system version:
Note that this option most likely won’t already be present in the configuration file, so you may need to add it manually.
Save and exit the file once you’re done. You can now validate the syntax of your new configuration by running sshd
in test mode:
If your configuration file has a valid syntax, there will be no output. In the event of a syntax error, there will be an output describing the issue.
Once you’re satisfied with your configuration file, you can reload sshd
to apply the new settings:
In this step, you completed some general hardening of your OpenSSH server configuration file. Next, you’ll implement an IP address allowlist to further restrict who can log in to your server.
You can use IP address allowlists to limit the users who are authorized to log in to your server on a per-IP address basis. In this step, you will configure an IP allowlist for your OpenSSH server.
In many cases, you will only be logging on to your server from a small number of known, trusted IP addresses. For example, your home internet connection, a corporate VPN appliance, or a static jump box or bastion host in a data center.
By implementing an IP address allowlist, you can ensure that people will only be able to log in from one of the pre-approved IP addresses, greatly reducing the risk of a breach in the event that your private keys and/or passwords are leaked.
Note: Please take care in identifying the correct IP addresses to add to your allowlist, and ensure that these are not reserved or dynamic addresses that may regularly change, for example as is often seen with consumer internet service providers.
You can identify the IP address that you’re currently connecting to your server with by using the w
command:
This should output something similar to the following:
Locate your user account in the list and take a note of the connecting IP address. Here we use the example IP of 203.0.113.1
In order to begin implementing your IP address allowlist, open the OpenSSH server configuration file in your favorite text editor:
You can implement IP address allowlists using the AllowUsers
configuration directive, which restricts user authentications based on username and/or IP address.
Your own system setup and requirements will determine which specific configuration is the most appropriate. The following examples will help you to identify the most suitable one:
When you have found your appropriate use case, add it to the bottom of your OpenSSH server configuration file.
Restrict all users to a specific IP address:
Restrict all users to a specific IP address range using Classless Inter-Domain Routing (CIDR) notation:
Restrict all users to a specific IP address range (using wildcards):
Restrict all users to multiple specific IP addresses and ranges:
Disallow all users except for named users from specific IP addresses:
Restrict a specific user to a specific IP address, while continuing to allow all other users to log in without restrictions:
Warning: Within an OpenSSH configuration file, all configurations under a Match
block will only apply to connections that match the criteria, regardless of indentation or line breaks. This means that you must be careful and ensure that configurations intended to apply globally are not accidentally put within a Match
block. It is recommended to put all Match
blocks at the bottom/end of your configuration file to help avoid this.
Once you have finalized your configuration, add it to the bottom of your OpenSSH server configuration file as seen above.
Save and close the file, and then proceed to test your configuration syntax:
If no errors are reported, you can reload OpenSSH server to apply your configuration:
In this step, you implemented an IP address allowlist on your OpenSSH server. Next, you will restrict the shell of a user to limit the commands that they are allowed to use.
In this step, you’ll look at the various options for restricting the shell of an SSH user.
In addition to providing remote shell access, SSH is also great for transferring files and other data, for example, via SFTP. However, you may not always want to grant full shell access to users when they only need to be able to carry out file transfers.
There are multiple configurations within OpenSSH server that you can use to restrict the shell environment of particular users. For instance, in this tutorial, we will use these to create SFTP-only users.
Firstly, you can use the /usr/sbin/nologin
shell to disable interactive logins for certain user accounts, while still allowing non-interactive sessions to function, like file transfers, tunneling, and so on.
To create a new user with the nologin
shell, use the following command:
Alternatively, you can change the shell of an existing user to be nologin
:
If you then attempt to interactively log in as one of these users, the request will be rejected:
This will output something similar to the following message:
Despite the rejection message on interactive logins, other actions such as file transfers will still be allowed.
Next, you should combine your usage of the nologin
shell with some additional configuration options to further restrict the relevant user accounts.
Begin by opening the OpenSSH server configuration file in your favorite text editor again:
There are two configuration options that you can implement together to create a tightly restricted SFTP-only user account: ForceCommand internal-sftp
and ChrootDirectory
.
The ForceCommand
option within OpenSSH server forces a user to execute a specific command upon login. This can be useful for certain machine-to-machine communications, or to forcefully launch a particular program.
However, in this case, the internal-sftp
command is particularly useful. This is a special function of OpenSSH server that launches a basic in-place SFTP daemon that doesn’t require any supporting system files or configuration.
This should ideally be combined with the ChrootDirectory
option, which will override/change the perceived root directory for a particular user, essentially restricting them to a specific directory on the system.
Add the following configuration section to your OpenSSH server configuration file for this:
Warning: As noted in Step 2, within an OpenSSH configuration file, all configurations under a Match
block will only apply to connections that match the criteria, regardless of indentation or line breaks. This means that you must be careful and ensure that configurations intended to apply globally are not accidentally put within a Match
block. It is recommended to put all Match
blocks at the bottom/end of your configuration file to help avoid this.
Save and close your configuration file, and then test your configuration again:
If there are no errors, you can then apply your configuration:
This has created a robust configuration for the alex
user, where interactive logins are disabled, and all SFTP activity is restricted to the home directory of the user. From the perspective of the user, the root of the system, that is, /
, is their home directory, and they will not be able to traverse up the file system to access other areas.
You’ve implemented the nologin
shell for a user and then created a configuration to restrict SFTP access to a specific directory.
In this article, you reviewed your OpenSSH server configuration and implemented various hardening measures to help secure your server.
This will have reduced the overall attack surface of your server by disabling unused features and locking down the access of specific users.
You may wish to review the manual pages for OpenSSH server and its associated configuration file, to identify any potential further tweaks that you want to make.