Skip to main content

Why

SSH is a door into your server. This is especially true if you are opening ports on your router so you can SSH to your server from outside your home network. If it is not secured properly, a bad-actor could use it to gain unauthorized access to your system.

How It Works

/etc/ssh/sshd_config is the default configuration file that the SSH server uses. We will use this file to tell what options the SSH server should use.

Goals

  • A secure SSH configuration following industry best practices
Make sure you’ve completed Create SSH Group For AllowGroups first.

Steps

1

Backup the SSH configuration

Make a backup of OpenSSH server’s configuration file /etc/ssh/sshd_config and remove comments to make it easier to read:
sudo cp --archive /etc/ssh/sshd_config /etc/ssh/sshd_config-COPY-$(date +"%Y%m%d%H%M%S")
sudo sed -i -r -e '/^#|^$/ d' /etc/ssh/sshd_config
2

Add base security settings

Edit /etc/ssh/sshd_config and add these settings that should be applied regardless of your configuration/setup:
SSH does not like duplicate contradicting settings. For example, if you have ChallengeResponseAuthentication no and then ChallengeResponseAuthentication yes, SSH will respect the first one and ignore the second.Your /etc/ssh/sshd_config file may already have some of the settings/lines below. You will need to manually go through your /etc/ssh/sshd_config file and address any duplicate contradicting settings.
########################################################################################################
# start settings from https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67 as of 2019-01-01
########################################################################################################

# Supported HostKey algorithms by order of preference.
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key

KexAlgorithms [email protected],ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256

Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr

MACs [email protected],[email protected],hmac-sha2-512,hmac-sha2-256,[email protected]

# LogLevel VERBOSE logs user's key fingerprint on login. Needed to have a clear audit track of which key was using to log in.
LogLevel VERBOSE

# Use kernel sandbox mechanisms where possible in unprivileged processes
# Systrace on OpenBSD, Seccomp on Linux, seatbelt on MacOSX/Darwin, rlimit elsewhere.
# Note: This setting is deprecated in OpenSSH 7.5 (https://www.openssh.com/txt/release-7.5)
# UsePrivilegeSeparation sandbox

########################################################################################################
# end settings from https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67 as of 2019-01-01
########################################################################################################

# don't let users set environment variables
PermitUserEnvironment no

# Log sftp level file access (read/write/etc.) that would not be easily logged otherwise.
Subsystem sftp  internal-sftp -f AUTHPRIV -l INFO

# disable X11 forwarding as X11 is very insecure
# you really shouldn't be running X on a server anyway
X11Forwarding no

# disable port forwarding
AllowTcpForwarding no
AllowStreamLocalForwarding no
GatewayPorts no
PermitTunnel no

# don't allow login if the account has an empty password
PermitEmptyPasswords no

# ignore .rhosts and .shosts
IgnoreRhosts yes

# verify hostname matches IP
UseDNS yes

Compression no

# TCP keepalive is spoofable (runs outside the encrypted channel)
# Use ClientAlive instead (runs inside the encrypted channel)
TCPKeepAlive no

AllowAgentForwarding no
PermitRootLogin no

# don't allow .rhosts or /etc/hosts.equiv
HostbasedAuthentication no

# OpenSSH 9.1 and later
# Enforce a minimum RSA key size of 3072 bits
# https://www.keylength.com/en/compare/
# RequiredRSASize 3072

# https://github.com/imthenachoman/How-To-Secure-A-Linux-Server/issues/115
HashKnownHosts yes
Note on OpenSSH 9.1+: If you are running OpenSSH 9.1 or later, uncomment the RequiredRSASize 3072 line. This enforces a minimum RSA key size of 3072 bits and will reject smaller RSA keys during authentication. This only affects RSA keys. If you use ED25519 or ECDSA keys, you are not affected. You can check your key type and size with ssh-keygen -l -f ~/.ssh/id_rsa. On older OpenSSH versions, leave the line commented out as it will prevent sshd from starting.
3

Add environment-specific settings

Add these settings and set values as per your requirements:
SettingValid ValuesExampleDescription
AllowGroupslocal UNIX group nameAllowGroups sshusersGroup to allow SSH access to
ClientAliveCountMaxnumberClientAliveCountMax 3Maximum number of client alive messages sent without response
ClientAliveIntervalnumber of secondsClientAliveInterval 15Timeout in seconds before a response request
ListenAddresslocal addressesListenAddress 0.0.0.0 or ListenAddress 192.168.1.100Local addresses sshd should listen on
LoginGraceTimenumber of secondsLoginGraceTime 30Time in seconds before login times-out
MaxAuthTriesnumberMaxAuthTries 2Maximum allowed attempts to login
MaxSessionsnumberMaxSessions 2Maximum number of open sessions
MaxStartupsnumberMaxStartups 2Maximum number of login sessions
PasswordAuthenticationyes or noPasswordAuthentication noIf login with a password is allowed
Portport numberPort 22Port that sshd should listen on
Check man sshd_config for more details on what these settings mean.
If you set PasswordAuthentication no, make sure you have SSH key authentication working first, or you’ll lock yourself out!
4

Check for duplicate settings

Make sure there are no duplicate settings that contradict each other. The below command should not have any output:
awk 'NF && $1!~/^(#|HostKey)/{print $1}' /etc/ssh/sshd_config | sort | uniq -c | grep -v ' 1 '
If there is output, you have duplicate settings that need to be resolved.
5

Restart SSH service

Restart ssh to apply the changes:
sudo service sshd restart
Remember to keep that 2nd terminal session open until you verify the configuration works!
6

Verify the configuration

You can verify the configurations worked with sshd -T:
sudo sshd -T
You should see output like:
port 22
addressfamily any
listenaddress [::]:22
listenaddress 0.0.0.0:22
usepam yes
logingracetime 30
x11displayoffset 10
maxauthtries 2
maxsessions 2
clientaliveinterval 15
clientalivecountmax 3
streamlocalbindmask 0177
permitrootlogin no
ignorerhosts yes
ignoreuserknownhosts no
hostbasedauthentication no
...
subsystem sftp internal-sftp -f AUTHPRIV -l INFO
maxstartups 2:30:2
permittunnel no
ipqos lowdelay throughput
rekeylimit 0 0
permitopen any
7

Test SSH access

From a new terminal, try connecting to your server:
ssh user@server
If you can connect successfully, your configuration is working correctly!

Key Security Settings Explained

Authentication & Access Control

  • AllowGroups: Only users in the specified group can SSH in
  • PermitRootLogin no: Root cannot login directly via SSH
  • PasswordAuthentication: Controls if password login is allowed (recommend setting to no after setting up key-based auth)
  • PermitEmptyPasswords no: Accounts without passwords cannot login

Cryptography

  • HostKey: Specifies which host key algorithms to use (Ed25519 is preferred)
  • KexAlgorithms: Key exchange algorithms to use
  • Ciphers: Encryption algorithms to use
  • MACs: Message authentication code algorithms to use

Logging & Monitoring

  • LogLevel VERBOSE: Logs key fingerprints for better audit tracking

Feature Restrictions

  • X11Forwarding no: Disables X11 forwarding (security risk)
  • AllowTcpForwarding no: Disables TCP port forwarding
  • PermitTunnel no: Disables tunnel device forwarding
  • AllowAgentForwarding no: Disables SSH agent forwarding

References

Build docs developers (and LLMs) love