A previous version of this tutorial was written by Jamie Scaife.
Linux servers are often administered remotely using SSH by connecting to an OpenSSH server, which is the default SSH server software used within Ubuntu, Debian, CentOS, FreeBSD, and most other Linux/BSD-based systems.
OpenSSH server is the server side of SSH, also known as SSH daemon or sshd
. You can connect to an OpenSSH server using the OpenSSH client, specifically by running the ssh
command. You can learn more about the SSH client-server model in SSH Essentials: Working with SSH Servers, Clients, and Keys. Properly securing your OpenSSH server is very important, as it acts as the front door or entry-point into your server.
In this tutorial, you will harden your OpenSSH server by using different configuration options to ensure that remote access to your server is as secure as possible.
To complete this tutorial, you will need:
Once you have this ready, log in to your server as your non-root user to begin.
In this first step, you will implement some initial hardening configurations to improve the overall security of your SSH server.
The exact hardening configuration that is most suitable for your own server depends heavily on your own threat model and risk threshold. However, the configuration you’ll use in this step is a general secure configuration that will suit the majority of servers.
You will edit the main OpenSSH configuration file in /etc/ssh/sshd_config
to set the majority of the hardening options in this tutorial. Before continuing it is a good idea create a backup of your existing configuration file so that you can restore it in the unlikely event that something goes wrong.
Create a backup of the file using the following cp
command:
- sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
This will save a backup copy of the file to /etc/ssh/sshd_config.bak
.
Next, review the current default OpenSSH configuration options that correspond to the settings in /etc/ssh/sshd_config
. To do this, run the following command:
- sudo sshd -T
This will run 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 nano
or your favorite text editor to begin implementing the initial hardening measures:
- sudo nano /etc/ssh/sshd_config
Note: The OpenSSH server configuration file that is installed with Ubuntu 20.04 includes many default options and configurations. Depending on your existing server configuration, some of the recommended hardening options may already have been set.
When editing your configuration file, some options may be commented out by default using a single hash character (#
) at the start of the line. In order to edit these options, or enable the option, you’ll need to uncomment them by removing the hash.
The first hardening option is to disable logging in via SSH as the root user. Set the PermitRootLogin
option to no
by uncommenting or editing the line:
PermitRootLogin no
This option will prevent a potential attacker from logging into your server directly as root. It also encourages good operational security practices on your part, such as operating as a non-privileged user and using sudo
to escalate privileges only when absolutely needed.
Next, you can limit the maximum number of authentication attempts for a particular login session by configuring the MaxAuthTries
option:
MaxAuthTries 3
A standard value of 3
is acceptable for most setups, but you may wish to set this higher or lower depending on your own risk threshold.
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:
LoginGraceTime 20
The configuration file specifies this value in seconds.
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.
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:
PasswordAuthentication no
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:
PermitEmptyPasswords no
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:
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication no
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. Disable it if you are not running a graphical environment on your server:
X11Forwarding no
OpenSSH server allows connecting clients to pass custom environment variables. For example, a client can attempt to set its own $PATH
or to configure terminal settings. However, like X11 forwarding, these are not commonly used, so you can disable the option in most cases:
PermitUserEnvironment no
If you decide to restrict this option by setting it to no
, then you should also make sure to comment out any references in the configuration file to AcceptEnv
by adding a hash (#
) to the beginning of any line that specifies that option.
Next, you can disable several miscellaneous options related to tunneling and forwarding if you won’t be using these on your server:
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no
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:
DebianBanner no
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. If you are using nano
press CTRL+O
to save the file and press ENTER
when prompted with the file name. Then press CTRL+X
to exit the editor.
Now validate the syntax of your new configuration by running sshd
in test mode with the -t
flag:
- sudo sshd -t
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
using systemd
to apply the new settings:
- sudo systemctl reload sshd.service
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:
- w
This will output something similar to the following:
Output 14:11:48 up 2 days, 12:25, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
your_username pts/0 203.0.113.1 12:24 1.00s 0.20s 0.00s w
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 nano
or your preferred text editor:
- sudo nano /etc/ssh/sshd_config
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:
AllowUsers *@203.0.113.1
AllowUsers *@203.0.113.0/24
AllowUsers *@203.0.113.*
AllowUsers *@203.0.113.1 *@203.0.113.2 *@192.0.2.0/24 *@172.16.*.1
AllowUsers sammy@203.0.113.1 alex@203.0.113.2
Match User ashley
AllowUsers ashley@203.0.113.1
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:
AllowUsers *@203.0.113.1
Save and close the file, and then proceed to test your configuration syntax:
- sudo sshd -t
If no errors are reported, you can reload OpenSSH server to apply your configuration:
- sudo systemctl reload sshd.service
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 explore 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:
- sudo adduser --shell /usr/sbin/nologin alex
Alternatively, you can change the shell of an existing user to be nologin
:
- sudo usermod --shell /usr/sbin/nologin sammy
If you then attempt to interactively log in as one of these users, the request will be rejected:
- sudo su alex
This will output something similar to the following message:
OutputThis account is currently not available.
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 nano
or your preferred editor again:
- sudo nano /etc/ssh/sshd_config
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 an 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 home 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:
Match User alex
ForceCommand internal-sftp
ChrootDirectory /home/alex/
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:
- sudo sshd -t
If there are no errors, you can then apply your configuration:
- sudo systemctl reload sshd.service
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 final step, you will implement various additional hardening measures to make access to your SSH server as secure as possible.
OpenSSH server can impose restrictions on a per-key basis. Specifically, restrictions restrictions can be applied to any public keys that are present in the .ssh/authorized_keys
file. This ability is particularly useful to control access for machine-to-machine sessions, as well as providing the ability for non-sudo users to control the restrictions for their own user account.
While you can apply most of these restrictions at the system or user level using the /etc/ssh/sshd_configuration
file, it can be advantageous to implement them at the key-level as well, to provide defence-in-depth and an additional failsafe in the event of accidental system-wide configuration errors.
Note: You can only implement these additional security configurations if you’re using SSH public-key authentication. If you’re only using password authentication, or have a more complex setup such as an SSH certificate authority, these options will not be usable.
Begin by opening your .ssh/authorized_keys
file in nano
or your preferred editor:
- nano ~/.ssh/authorized_keys
Note: Since these configurations apply on a per-key basis, you’ll need to edit each individual key within each individual authorized_keys
file that you want them to apply to, for all users on your system. Usually you will only need to edit one key/file, but this is worth considering if you have a complex multi-user system.
Once you’ve opened your authorized_keys
file, you will see that each line contains an SSH public key, which will most likely begin with something like ssh-rsa AAAB...
. Additional configuration options can be added to the beginning of the line, and these will only apply to successful authentications against that specific public key.
The following restriction options are available:
no-agent-forwarding
: Disable SSH agent forwarding.no-port-forwarding
: Disable SSH port forwarding.no-pty
: Disable the ability to allocate a tty (i.e. start a shell).no-user-rc
: Prevent execution of the ~/.ssh/rc
file.no-X11-forwarding
: Disable X11 display forwarding.You can apply these to disable specific SSH features for specific keys. For example, to disable agent forwarding and X11 forwarding for a key, you would use the following configuration:
no-agent-forwarding,no-X11-forwarding ssh-rsa AAAB...
By default, these configurations work using an “allow by default, block by exception” methodology; however, it is also possible to use “block by default, allow by exception,” which is generally preferable for ensuring security.
You can do this by using the restrict
option, which will implicitly deny all SSH features for the specific key, requiring them to be explicitly re-enabled only where absolutely needed. You can re-enable features using the same configuration options described earlier in this tutorial, but without the no-
prefix.
For example, to disable all SSH features for a particular key, apart from X11 display forwarding, you can use the following configuration:
restrict,X11-forwarding ssh-rsa AAAB...
You may also wish to consider using the command
option, which is very similar to the ForceCommand
option described in Step 3. This doesn’t provide a direct benefit if you’re already using ForceCommand
, but it is good defense-in-depth to have it in place, just in the unlikely event that your main OpenSSH server configuration file is overwritten, edited, and so on.
For example, to force users authenticating using a specific key to execute a specific command upon login, you can add the following configuration:
command="top" ssh-rsa AAAB...
Warning: The command
configuration option acts purely as a defense-in-depth method, and should not be solely relied on to restrict the activities of an SSH user, as there are potentially ways to override or bypass it depending on your environment. Instead, you should use the configuration in tandem with the other controls described in this article.
Finally, to best use the per-key restrictions for the SFTP-only user that you created in Step 3, you can use the following configuration:
restrict,command="false" ssh-rsa AAAB...
The restrict
option will disable all interactive access, and the command="false"
option acts as a second line of defense in the event that the ForceCommand
option or nologin
shell were to fail.
Save and close the file to apply the configuration. This will take effect immediately for all new logins, so you don’t need to reload OpenSSH manually. Be sure to test the commands that you intend to use before disconnecting your current SSH session.
In this final step, you implemented some additional advanced hardening measures for OpenSSH server by using the custom options within your .ssh/authorized_keys
file(s).
In this article, you reviewed your OpenSSH server configuration and implemented various hardening measures to help secure your server.
The options that you configured 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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!