Top 20 OpenSSH Server Best Security Practices

OpenSSH is the implementation of the SSH protocol. OpenSSH is recommended for remote login, making backups, remote file transfer via scp or sftp, and much more. SSH is perfect to keep confidentiality and integrity for data exchanged between two networks and systems. However, the main advantage is server authentication, through the use of public key cryptography. From time to time there are rumors about OpenSSH zero day exploit. This page shows how to secure your OpenSSH server running on a Linux or Unix-like system to improve sshd security.

OpenSSH defaults

  • TCP port – 22
  • OpenSSH server config file – sshd_config (located in /etc/ssh/)
  • OpenSSH client config file – ssh_config (located in /etc/ssh/)

1. Use SSH public key based login

OpenSSH server supports various authentication. It is recommended that you use public key based authentication. First, create the key pair using following ssh-keygen command on your local desktop/laptop:

DSA and RSA 1024 bit or lower ssh keys are considered weak. Avoid them. RSA keys are chosen over ECDSA keys when backward compatibility is a concern with ssh clients. All ssh keys are either ED25519 or RSA. Do not use any other type.

$ ssh-keygen -t key_type -b bits -C "comment"
$ ssh-keygen -t ed25519 -C "Login to production cluster at xyz corp"
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_aws_$(date +%Y-%m-%d) -C "AWS key for abc corp clients"

Next, install the public key using ssh-copy-id command:
$ ssh-copy-id -i /path/to/public-key-file user@host
$ ssh-copy-id user@remote-server-ip-or-dns-name
$ ssh-copy-id vivek@rhel7-aws-server

When promoted supply user password. Verify that ssh key based login working for you:
$ ssh vivek@rhel7-aws-server

For more info on ssh public key auth see:

2. Disable root user login

Before we disable root user login, make sure regular user can log in as root. For example, allow vivek user to login as root using the sudo command.

How to add vivek user to sudo group on a Debian/Ubuntu

Allow members of group sudo to execute any command. Add user vivek to sudo group:
$ sudo adduser vivek sudo
Verify group membership with id command $ id vivek

How to add vivek user to sudo group on a CentOS/RHEL server

Allows people in group wheel to run all commands on a CentOS/RHEL and Fedora Linux server. Use the usermod command to add the user named vivek to the wheel group:
$ sudo usermod -aG wheel vivek
$ id vivek

Test sudo access and disable root login for ssh

Test it and make sure user vivek can log in as root or run the command as root:
$ sudo -i
$ sudo /etc/init.d/sshd status
$ sudo systemctl status httpd

Once confirmed disable root login by adding the following line to sshd_config:
PermitRootLogin no
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no

See “How to disable ssh password login on Linux to increase security” for more info.

3. Disable password based login

All password-based logins must be disabled. Only public key based logins are allowed. Add the following in your sshd_config file:
AuthenticationMethods publickey
PubkeyAuthentication yes

Older version of SSHD on CentOS 6.x/RHEL 6.x user should use the following setting:
PubkeyAuthentication yes

4. Limit Users’ ssh access

By default, all systems user can login via SSH using their password or public key. Sometimes you create UNIX / Linux user account for FTP or email purpose. However, those users can log in to the system using ssh. They will have full access to system tools including compilers and scripting languages such as Perl, Python which can open network ports and do many other fancy things. Only allow root, vivek and jerry user to use the system via SSH, add the following to sshd_config:
AllowUsers vivek jerry
Alternatively, you can allow all users to login via SSH but deny only a few users, with the following line in sshd_config:
DenyUsers root saroj anjali foo
You can also configure Linux PAM allows or deny login via the sshd server. You can allow list of group name to access or deny access to the ssh.

5. Disable Empty Passwords

You need to explicitly disallow remote login from accounts with empty passwords, update sshd_config with the following line:
PermitEmptyPasswords no

6. Use strong passwords and passphrase for ssh users/keys

It cannot be stressed enough how important it is to use strong user passwords and passphrase for your keys. Brute force attack works because user goes to dictionary based passwords. You can force users to avoid passwords against a dictionary attack and use john the ripper tool to find out existing weak passwords. Here is a sample random password generator (put in your ~/.bashrc):

genpasswd() {
	local l=$1
       	[ "$l" == "" ] && l=20
      	tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${l} | xargs

Run it:
genpasswd 16


7. Firewall SSH TCP port # 22

You need to firewall ssh TCP port # 22 by updating iptables/ufw/firewall-cmd or pf firewall configurations. Usually, OpenSSH server must only accept connections from your LAN or other remote WAN sites only.

Netfilter (Iptables) Configuration

Update /etc/sysconfig/iptables (Redhat and friends specific file) to accept connection only from and, enter:

-A RH-Firewall-1-INPUT -s -m state --state NEW -p tcp --dport 22 -j ACCEPT
-A RH-Firewall-1-INPUT -s -m state --state NEW -p tcp --dport 22 -j ACCEPT

If you’ve dual stacked sshd with IPv6, edit /etc/sysconfig/ip6tables (Redhat and friends specific file), enter:

 -A RH-Firewall-1-INPUT -s ipv6network::/ipv6mask -m tcp -p tcp --dport 22 -j ACCEPT

Replace ipv6network::/ipv6mask with actual IPv6 ranges.

UFW for Debian/Ubuntu Linux

UFW is an acronym for uncomplicated firewall. It is used for managing a Linux firewall and aims to provide an easy to use interface for the user. Use the following command to accept port 22 from only:
$ sudo ufw allow from to any port 22
Read “Linux: 25 Iptables Netfilter Firewall Examples For New SysAdmins” for more info.

*BSD PF Firewall Configuration

If you are using PF firewall update /etc/pf.conf as follows:

pass in on $ext_if inet proto tcp from {,} to $ssh_server_ip port ssh flags S/SA synproxy state

8. Change SSH Port and limit IP binding

By default, SSH listens to all available interfaces and IP address on the system. Limit ssh port binding and change ssh port (many brutes forcing scripts only try to connect to TCP port # 22). To bind to and IPs and port 300, add or correct the following line in sshd_config:

Port 300

A better approach to use proactive approaches scripts such as fail2ban or denyhosts when you want to accept connection from dynamic WAN IP address.

9. Use TCP wrappers (optional)

TCP Wrapper is a host-based Networking ACL system, used to filter network access to the Internet. OpenSSH does support TCP wrappers. Just update your /etc/hosts.allow file as follows to allow SSH only from and IP address:

sshd : 

See this FAQ about setting and using TCP wrappers under Linux / Mac OS X and UNIX like operating systems.

10. Thwart SSH crackers/brute force attacks

Brute force is a method of defeating a cryptographic scheme by trying a large number of possibilities (combination of users and passwords) using a single or distributed computer network. To prevents brute force attacks against SSH, use the following software:

  • DenyHosts is a Python based security tool for SSH servers. It is intended to prevent brute force attacks on SSH servers by monitoring invalid login attempts in the authentication log and blocking the originating IP addresses.
  • Explains how to setup DenyHosts under RHEL / Fedora and CentOS Linux.
  • Fail2ban is a similar program that prevents brute force attacks against SSH.
  • sshguard protect hosts from brute force attacks against ssh and other services using pf.
  • security/sshblock block abusive SSH login attempts.
  • IPQ BDB filter May be considered as a fail2ban lite.

11. Rate-limit incoming traffic at TCP port # 22 (optional)

Both netfilter and pf provides rate-limit option to perform simple throttling on incoming connections on port # 22.

Iptables Example

The following example will drop incoming connections which make more than 5 connection attempts upon port 22 within 60 seconds:

$IPT -I INPUT -p tcp --dport ${ssh_port} -i ${inet_if} -m state --state NEW -m recent  --set
$IPT -I INPUT -p tcp --dport ${ssh_port} -i ${inet_if} -m state --state NEW -m recent  --update --seconds 60 --hitcount 5 -j DROP

Call above script from your iptables scripts. Another config option:

$IPT -A INPUT  -i ${inet_if} -p tcp --dport ${ssh_port} -m state --state NEW -m limit --limit 3/min --limit-burst 3 -j ACCEPT
$IPT -A INPUT  -i ${inet_if} -p tcp --dport ${ssh_port} -m state --state ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o ${inet_if} -p tcp --sport ${ssh_port} -m state --state ESTABLISHED -j ACCEPT
# another one line example
# $IPT -A INPUT -i ${inet_if} -m state --state NEW,ESTABLISHED,RELATED -p tcp --dport 22 -m limit --limit 5/minute --limit-burst 5-j ACCEPT

See iptables man page for more details.

*BSD PF Example

The following will limits the maximum number of connections per source to 20 and rate limit the number of connections to 15 in a 5 second span. If anyone breaks our rules add them to our abusive_ips table and block them for making any further connections. Finally, flush keyword kills all states created by the matching rule which originate from the host which exceeds these limits.

table <abusive_ips> persist
block in quick from <abusive_ips>
pass in on $ext_if proto tcp to $sshd_server_ip port ssh flags S/SA keep state (max-src-conn 20, max-src-conn-rate 15/5, overload <abusive_ips> flush)

12. Use port knocking (optional)

Port knocking is a method of externally opening ports on a firewall by generating a connection attempt on a set of prespecified closed ports. Once a correct sequence of connection attempts is received, the firewall rules are dynamically modified to allow the host which sent the connection attempts to connect to the specific port(s). A sample port Knocking example for ssh using iptables:

$IPT -N stage1
$IPT -A stage1 -m recent --remove --name knock
$IPT -A stage1 -p tcp --dport 3456 -m recent --set --name knock2
$IPT -N stage2
$IPT -A stage2 -m recent --remove --name knock2
$IPT -A stage2 -p tcp --dport 2345 -m recent --set --name heaven
$IPT -N door
$IPT -A door -m recent --rcheck --seconds 5 --name knock2 -j stage2
$IPT -A door -m recent --rcheck --seconds 5 --name knock -j stage1
$IPT -A door -p tcp --dport 1234 -m recent --set --name knock
$IPT -A INPUT -p tcp --dport 22 -m recent --rcheck --seconds 5 --name heaven -j ACCEPT
$IPT -A INPUT -p tcp --syn -j door

For more info see:

13. Configure idle log out timeout interval

A user can log in to the server via ssh, and you can set an idle timeout interval to avoid unattended ssh session. Open sshd_config and make sure following values are configured:
ClientAliveInterval 300
ClientAliveCountMax 0

You are setting an idle timeout interval in seconds (300 secs == 5 minutes). After this interval has passed, the idle user will be automatically kicked out (read as logged out). See how to automatically log BASH / TCSH / SSH users out after a period of inactivity for more details.

14. Enable a warning banner for ssh users

Set a warning banner by updating sshd_config with the following line:
Banner /etc/issue
Sample /etc/issue file:

You are accessing a XYZ Government (XYZG) Information System (IS) that is provided for authorized use only.
By using this IS (which includes any device attached to this IS), you consent to the following conditions:

+ The XYZG routinely intercepts and monitors communications on this IS for purposes including, but not limited to,
penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM),
law enforcement (LE), and counterintelligence (CI) investigations.

+ At any time, the XYZG may inspect and seize data stored on this IS.

+ Communications using, or data stored on, this IS are not private, are subject to routine monitoring,
interception, and search, and may be disclosed or used for any XYZG authorized purpose.

+ This IS includes security measures (e.g., authentication and access controls) to protect XYZG interests--not
for your personal benefit or privacy.

+ Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching
or monitoring of the content of privileged communications, or work product, related to personal representation
or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work
product are private and confidential. See User Agreement for details.

Above is a standard sample, consult your legal team for specific user agreement and legal notice details.

15. Disable .rhosts files (verification)

Don’t read the user’s ~/.rhosts and ~/.shosts files. Update sshd_config with the following settings:
IgnoreRhosts yes
SSH can emulate the behavior of the obsolete rsh command, just disable insecure access via RSH.

16. Disable host-based authentication (verification)

To disable host-based authentication, update sshd_config with the following option:
HostbasedAuthentication no

17. Patch OpenSSH and operating systems

It is recommended that you use tools such as yum, apt-get, freebsd-update and others to keep systems up to date with the latest security patches:

18. Chroot OpenSSH (Lock down users to their home directories)

By default users are allowed to browse the server directories such as /etc/, /bin and so on. You can protect ssh, using os based chroot or use special tools such as rssh. With the release of OpenSSH 4.8p1 or 4.9p1, you no longer have to rely on third-party hacks such as rssh or complicated chroot(1) setups to lock users to their home directories. See this blog post about new ChrootDirectory directive to lock down users to their home directories.

19. Disable OpenSSH server on client computer

Workstations and laptop can work without OpenSSH server. If you do not provide the remote login and file transfer capabilities of SSH, disable and remove the SSHD server. CentOS / RHEL users can disable and remove openssh-server with the yum command:
$ sudo yum erase openssh-server
Debian / Ubuntu Linux user can disable and remove the same with the apt command/apt-get command:
$ sudo apt-get remove openssh-server
You may need to update your iptables script to remove ssh exception rule. Under CentOS / RHEL / Fedora edit the files /etc/sysconfig/iptables and /etc/sysconfig/ip6tables. Once done restart iptables service:
# service iptables restart
# service ip6tables restart

20. Bonus tips from Mozilla

If you are using OpenSSH version 6.7+ or newer try following settings:

#################[ WARNING ]########################
# Do not use any setting blindly. Read sshd_config #
# man page. You must understand cryptography to    #
# tweak following settings. Otherwise use defaults #
# 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
# Specifies the available KEX (Key Exchange) algorithms.
# Specifies the ciphers allowed
#Specifies the available MAC (message authentication code) algorithms
# LogLevel VERBOSE logs user's key fingerprint on login. Needed to have a clear audit track of which key was using to log in.
# Log sftp level file access (read/write/etc.) that would not be easily logged otherwise.
Subsystem sftp  /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO

You can grab list of cipher and alog supported by your OpenSSH server using the following commands:
$ ssh -Q cipher
$ ssh -Q cipher-auth
$ ssh -Q mac
$ ssh -Q kex
$ ssh -Q key

How do I test sshd_config file and restart/reload my SSH server?

To check the validity of the configuration file and sanity of the keys for any errors before restarting sshd, run:
$ sudo sshd -t
Extended test mode:
$ sudo sshd -T
Finally restart sshd on a Linux or Unix like systems as per your distro version:
$ sudo systemctl start ssh ## Debian/Ubunt Linux##
$ sudo systemctl restart sshd.service ## CentOS/RHEL/Fedora Linux##
$ doas /etc/rc.d/sshd restart ## OpenBSD##
$ sudo service sshd restart ## FreeBSD##

Other susggesions

  1. Tighter SSH security with 2FA – Multi-Factor authentication can be enabled with OATH Toolkit or DuoSecurity.
  2. Use keychain based authentication – keychain is a special bash script designed to make key-based authentication incredibly convenient and flexible. It offers various security benefits over passphrase-free keys

See also:

  • The official OpenSSH project.
  • Man pages: sshd(8),ssh(1),ssh-add(1),ssh-agent(1)

If you have a technique or handy software not mentioned here, please share in the comments below to help your fellow readers keep their OpenSSH based server secure.

🐧 If you liked this page, please support my work on Patreon or with a donation.
🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source & DevOps topics via:
CategoryList of Unix and Linux commands
File Managementcat
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
174 comments… add one
  • miphix Jul 25, 2017 @ 20:08

    I routinely choose to do a refresher of the basics and find nixCraft to have solid advice. I also find that the gets hammered for the advice he gives (and not gets hammered on the advice he could give.). With that said, I would like to point out, that sharing advice in general to contribute to someone else’s journey through their Linux adventure to be easier, more akin to the Linux spirit. A far cry from those that are so smart that they not only can find the time to pick his bones about details, and require the content enough to venture forth and find his guides. .

    I just wanted to pitch suggestions in addition to the guide. Understandably, a lot of those that read these guides are at home connecting to a Linux box on any particular VPS provider that fits their fancy (wallet, even. Yes, you! AWS hippies. 😛 ). I find it peculiar that a swath of ‘Linux’ nerds so aptly forget the capacity of their networking equipment. Cisco L2( <<— OSI model if you're lost) switches can mitigate spoofing visa vi MAC addressing, as well as limit multiple MACs to a specific port, and if the switch finds the MAC on its list. That or if it has too many on its list for that interface, it will elect to shut that port(<– interface, if you haven't read a book.) down.

    IP spoofing: — The primary issue with IP spoofing, is that unless the spoofing machine is on the same broadcast domain as the device that's being spoofed. The attack never receives a successful TCP handshake to keep going because the router knows where the actual IP is. Which is why it has to be on the same broadcast domain. Unless you can manipulate the ARP table of the opposite interface to return to you. UDP is mitigated by a small handful of functions embedded in the programs that are designed to converse with UDP (Go ahead, try to stream youtube to some poor fool. "derpa derp, DNS amplification!" *pets the duck-wit* "good job, you're learning").

    MiTM: — Unless you can manipulate a mid-stream router, configuring static arp to take effect after boot puts a dead stop to this being successful
    set -e
    [ "$IFACE" != "lo" ] || exit 0
    case "$ADDRFAM" in
    if [ "$IFACE" = "eth0" ]; then
    /usr/sbin/arp -s de:ad:be:ef:ca:fe
    Brute force: – fail2ban has always failed me. Personally, it has a weird habbit of bunging up the rest of my firewall rules. If you have this figured out, share in a comment (excluding comments containing, "it works for me." That's why you use it, right?). Instead of running an entire RAM chewing daemon that essentially does what can be done in a crude 5 lines of iptables (shares the same space as your network stack, anyways).

    (Input; denied: output; accepted. Established; maintained)
    # iptables -N IN_SSH
    # iptables -A INPUT -p tcp –dport ssh -m conntrack –ctstate NEW -j IN_SSH
    # iptables -A INPUT -p tcp –dport ssh -m conntrack –ctstate NEW -j IN_SSH
    # iptables -A IN_SSH -m recent –name sshbf –rttl –rcheck –hitcount 3 –seconds 10 -j DROP
    # iptables -A IN_SSH -m recent –name sshbf –rttl –rcheck –hitcount 4 –seconds 1800 -j DROP
    # iptables -A IN_SSH -m recent –name sshbf –set -j ACCEPT

    If you're crafty. You can configure a port knock that, instead of opening the inbound port, opens the outbound connection. You can cause any one without the knock sequence to initiate a TCP handshake with syn,ack. and cause the whole thing to hang without a word from sshd. just as one example that totally excludes the variety of ipset scripts out there to block known malicious IPs. And if you're sitting there with a script purring away at an SSH log-in. It's a good bet you're on that list. Just a shot in the dark, though. Why -use- one language for the keyboard while entering passwords. [host] + [space] allows you to switch between keyboards without interacting with the password mechanism.

  • L33tSp34k3rT04n1m41s Apr 19, 2017 @ 0:31

    Quite frankly, Bob is an ID10T. If I can continue to hammer away at your “root” user until I can guess the password, your pwnd.

    Root cannot be locked out and your simple SSH config isn’t going to be enough to stop me if I really want to get in. I can just sit back and let my bot hammer away until I get it.

  • Hugh Buntu Nov 5, 2015 @ 18:54

    I generally ignore advice from people who have such bad English writing skills that I have to spend an excessive amount of time decoding what they are trying to say.

    Just sayin’ – if you want to communicate, master your tool!

  • Fábio Aug 10, 2015 @ 23:49

    Very good practices, thank you for posting this!

  • Philippe Petrinko Jan 26, 2015 @ 15:22


    My Dear PenPal,
    We (I) enjoy your quirky ways.

    So, would you say Oxford English Dictionary instead of MW?

    ESLpod (English as a Second Language) is a wonderful resource to me. I download its Podcasts. (Yeah, Californian as far as I understand, so not UK, again).

    By the way, learning ESL is a tough goal, but then having to deal with _almost_ two different languages (UK vs US) is another fun part of the deal. 😉

    I have always wondered how english native speakers (UK and US) agreed on their Wikipedia single “English” vocabulary. Do you know if there has been an agreement? (for instance using UK-english only).

    — oooops — I may be quite out of scope of ssh right now…

    • Cody Jan 27, 2015 @ 15:33

      Very much so off topic. Normally I’d let it happen (that is, make sure it goes as far off topic as possible) out of the amusement it gives me, but… I’ll refrain from that here (this is a thread that should mostly remain on topic for its intended goal – it isn’t a discussion forum): I’ll just respond to two things without elaboration. Yes I like Oxford; Wiki has different authors just like this thread (am I back on topic ? 🙂 ) so it varies, I think. Now… ssh. I know I remarked on some of the points but here’s some other remarks:

      10: It cannot be stressed enough how important it is to use strong user passwords and passphrase for your keys.

      It should also be stressed that you shouldn’t reuse (reuse implies in more than one location!) passwords, passphrases or anything of the sort (of course that is beyond ssh I suppose but still relevant to the discussion).

      As for tcpwrappers – and I think I might have remarked here and same with others – it should be tcpwrappers or iptables, not both (although I have a very vague memory that they can go together it seems rather silly to configure one and then worry about the other).

      On 20: yes, you should use update managers to keep it up to date. No, you should not patch it yourself as long as (and Red Hat distributions do this as do others) the packages include backports (when not updated to most recent release). I should point out that this has multiple gains, not doing it manually, and frankly versions (and other information) can still be enumerated. But version is irrelevant: hiding version hardly stops brute force; scanners and the like really don’t care what version it is because they’ll try everything until blocked. So compiling it yourself is making things more difficult to track (and verify) and that includes available updates! If you don’t have a binary based distro, then that is another issue entirely. You might also (in any case) want to be on the security lists (to keep up to date with current issues). The latter bit goes especially if you compile it yourself but it can be of use otherwise (still not recommended to compile if you are receiving updates …).

      (Like how I did that, Philippe ?)

Leave a Reply

Your email address will not be published. Required fields are marked *

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.