Demons Seeking Dæmons—A Practical Approach to Hardening Your OpenSSH Configuration
Chances are, if you are the owner or administrator of a Linux machine, you access it remotely from time to time, if not constantly. Be it a workstation at home, a co-located server or a hobby machine, if you are accessing Linux remotely, there is a good chance that you are using the OpenSSH server on the remote machine with some type of SSH client locally. (If you are not, you probably should be.) Although it is true that the OpenSSH server and clients do a tremendous job at encrypting the traffic that passes between systems, it also is true that any dæmon listening for connections is a door handle waiting to be turned by the black hats, the evildoers or the crackers. The less-desirable folks in these situations are, as the title states, demons seeking dæmons, a person or group of people that usually are up to no good seeking listening dæmons that, depending on the configuration of the machine, may or may not be secure. Hackers can be defined as people who have relatively benign motives for breaking into a system. Crackers often are thought of as hackers with malicious intent. Neither the former nor latter is welcome on my machines. In this article, I expand on the basic OpenSSH configuration and cover ways to improve the security of the SSH dæmon to offer more protection to your machines. If you have a machine that suffers a compromise, your machine may be helping to spread your unpleasantness to others as well. The Internet is one of the largest insecure data routes on the face of our planet.
OpenSSH provides the means for secure communication over insecure channels. The file sshd_config is the ruling party in the game of secure shell configuration. The sshd_config file consists of multiple options that can be changed to help improve the security of the listening dæmon. Although it may seem that because it is a remote access tool it should be secure right out of the box, this is hardly the case. For the most part, a default installation of the OpenSSH server will provide you with a relatively secure default configuration but one that can be improved substantially by the machine's administrator.
When planning for remote access initially, it is suggested that you consider three major things for the access of your machines:
Who will be allowed access to the machines?
How will this access be provided?
From where will this access be allowed?
We are going to assume that we will be using the OpenSSH server to provide remote access. This leaves us with the questions of who will be allowed remote access and from where they will be allowed. For some, this may be simple; perhaps the machine supports only a single user and remote access is carried out from a single domain. For others, this can be quite a challenge when multiple users that travel frequently are the machine's users.
The first and foremost person on a Linux machine is the root user. This is universally known, and it also should be universally known that if you need to have remote root access to a machine, there are many better ways to access as root than simply using SSH and logging in as root. If you think about the fundamentals of a brute-force attack attempt, it is obvious that the most relevant account that will be attacked is the root account. One need not guess whether the account is present; it is there. The sshd_config file lets us specify that root is not allowed to log in remotely at all through the PermitRootLogin directive.
I fully believe that the saying “an ounce of prevention equals a pound of cure” is highly accurate when dealing with remote-use accounts. Two options allowed in the sshd_config file, UsersAllow and UsersDeny, are more than an ounce of prevention, and although it may be one extra step when adding an account, modifying the UsersAllow for each account added provides that pound of cure that you may (thankfully) never need to seek. To expand on the UsersAllow directive, you cannot specify only particular users, but you also can specify particular users at specific hosts. So, if in advance you know exactly who will need to log in and from where, the minimal time overhead associated with adding these directives to the sshd_config file provides the peace of mind for you to know you are allowing remote access for only specific accounts from specific machines. Valid users can be retrieved from the /etc/passwd file in standalone machines or from the corresponding files in NIS or LDAP environments. Listing 1 is an example of parsing the previous month's security files to verify which accounts have logged in successfully using SSH.
Listing 1. Parsing the Logs for Successful Login
cat secure* | grep Accepted | awk -F' ' '\ {print $1" "$2" "$9}' | uniq -u Aug 30 juser Aug 22 kuser Aug 23 user Aug 15 foo ... Aug 24 13:23:19 foohost sshd[16348]: Accepted password for phil from 127.0.0.1 port 47338 ssh2 Aug 24 13:23:25 foohost sshd[16398]: User root not allowed because not listed in AllowUsers
Keeping a watchful eye on the demons seeking dæmons is relatively easy and the (expected) default setup should be confirmed in the configuration file. With the SyslogFacility set to AUTH, your sshd will log (via syslog) to (paths and filenames may vary depending on distribution) /var/log/messages and /var/log/secure. It is highly recommended that a program such as Psionic's logwatch be used to monitor your system logs. Logwatch will take care of parsing the logs, and you will be able to decipher the sshd authorized logins as well as failures. There is a distinct difference between an invalid user and an authentication failure. An authentication failure is a failure that occurs on an account that is present on the machine, and an invalid user is exactly that. This can become relatively important when you are choosing account names as well as contemplating who will be on the AllowUser and DenyUser lists. For example, you may not have an actual person named amanda using your machine, but you are using the amanda open-source backup program. This said account, if not added to the DenyUser specification, will trigger an authentication failure rather than an invalid user flag. Although not necessarily required, I add the system accounts that are active to the DenyUsers list just to be on the safe side. The most recent log entries from sshd scans show that the system accounts have in fact been added to the probe list (Listings 2 and 3).
Listing 2. Log Entries from Failures Due to the Account Not Being Authorized in sshd_config AllowUsers
User mail not allowed because not listed in AllowUsers User adm not allowed because not listed in AllowUsers
Listing 3. Log Entries from Invalid Accounts
Aug 28 06:04:15 foo sshd[11602]: Failed password for illegal user a... from 10.0.0.1 port 35078 ssh2 Aug 28 06:04:17 foo sshd[11604]: Illegal user aaa from 10.0.0.1 Aug 28 06:04:19 foo sshd[11604]: Failed password for illegal user aaa from 10.0.0.1 port 35417 ssh2 Aug 28 06:04:21 foo sshd[11606]: Illegal user qqq from 10.0.0.1
Because user names have been mentioned, it is now appropriate to discuss the possibility of the demons (the evildoers) having one of the two credentials required for a login. In the event of a Web server hosting personal accounts or from e-mail headers, it is relatively easy to determine a user name on a particular machine. With this, the demon has half of the user name/password combination, and if the scanner goes beyond simply scanning common accounts and passwords, it is easy to take the sshd scan one step further and begin brute-force attempts at those accounts that already have been determined to exist on a machine.
There is no need to re-invent the protocol, only the need to improve the current configuration. OpenSSH provides a strong end-to-end encryption method for networked machines. Updates continuously are released to the code to address any insecurities that may come about. One of the most relevant releases is the change from SSH v1 to SSH v2. Both version 1 and 2 sshd servers have their own keys. In other words, with SSH1 and SSH2, neither private nor public keys intermix. These keys are independent of each other. Just as the keys are independent of each other, so are the protocols, and it is possible through the Protocol directive in the configuration to allow either protocol 1, protocol 2 or both. SSH1 is depreciated but is still in use in many organizations and applications. It is advisable to check your sshd_config file, and if you are not dependent on SSH1, disable this and run only protocol 2 on your server. This eliminates any chances of falling victim to an insecurity associated with version 1.
Earlier, while you were perusing your password file to build your user list, you probably noticed that there is an sshd user with a home directory of /var/empty and a name listed as Privilege-separated SSH. If this user does not exist, the following is of particular importance, and you will want to look further into running sshd in privilege-separated mode. Privilege separation in sshd is a multipart process that entails the sshd process of creating a privileged monitor process, which creates an sshd process with the privileges of the user. This user-owned process in turn spawns the shell process. The privilege separation process is run via chroot and is restricted to the /var/empty directory. For those of you familiar with running processes in a chroot environment, this privilege separation is the same. It allows protection to the listening dæmon if a buffer overflow or similar compromise is discovered. The /var/empty directory should be root-owned, empty and it should not be world- or group-writable. If an sshd user does not exist, privilege separation will not work, and systems that lack mmap or anonymous memory mapping compression must be disabled.
Consider a situation where a machine has a minimal amount of users that are accessing it through SSH. Quite possibly, you own a machine that is accessed only by a couple of accounts, and then su or sudo is used for administrative purposes. Consider also the scripts that often are run to seek the machines that are running an SSH server. Although it is possible that the scripts may do a full-system port scan to expedite the scanning and identify possible victims, a specific port (22 in the sshd case) is often the only port scanned, and if found open, it is expanded upon. The logical alternative to this, especially in machines with minimal users logging in, is to run sshd on an alternate port. Specifically, in the sshd_config file, one of the very first options is the port. By changing the port from 22 to an alternate port, you can write off a good amount of the scripts that are scanning for open port 22. This is such a trivial configuration change, but it is one that will result in a drastic lessening of brute-force attempts (Listing 4).
Listing 4. Changing the Port
# 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 change a default value Port 13 Protocol 2
Given even the slowest typist on the planet, what would be the expected time to allow them to enter a password? Assuming an eight-character password, would it be eight seconds? Give them two seconds a character, and they have a hearty 16 seconds. Allow them 20 seconds of network latency, and we are at just more than 30 seconds, which seems like a more than generous amount of grace time. This is often surpassed with the default sshd configuration, though with the LoginGraceTime sometimes set as high as 120 seconds. This is the maximum amount of time that the server allows for a successful login. Ask yourself, if one were to knock on your house door and you were behind it, what would the maximum amount of time for deciding whether to let this person or people in? Coupled with this, we have another unique login directive named MaxStartups. This MaxStartups directive can be a very powerful deterrent when fighting the SSH scanners. MaxStartups specifies the maximum number of concurrent unauthenticated connections to the SSH dæmon. Note that this means unauthenticated connections. Therefore, if a script were running, trying to brute-force its way into the machine, and the script was able to fork processes, it could launch multiple (maybe even hundreds) of login attempts almost simultaneously. A good rule of thumb is to use one-third the number of your total remote users with a maximum setting of 10.
There is little to be considered to implement the changes mentioned. The default configuration is merely a guide, and it is the administrator's responsibility to expand on the guide and harden the sshd configuration. Prior to making the changes, it is highly recommended that you temporarily start sshd on an alternate port to provide yourself with a back door should you misconfigure the primary sshd and lose the connection. When issuing a restart on sshd, it is possible to make a mistake in the configuration file, which will result in the dæmon failing to restart (if it is first stopped). Doing ssh -p <alternate port> will allow a second dæmon to run and provide you with a secondary secure connection should the first one fail due to a configuration error. We are after all, working remotely, and if we lose the SSH dæmon without an alternate, physical access to the console will be required to make further modifications. Even though it may seem obvious, it is worth mentioning that you must issue a SIGHUP or restart the SSH dæmon for any changes to take place. To expand further on the idea of a second port being opened, it is possible to add a startup script for a second sshd to run with an alternate sshd_config file that specifies no options other than a single-user account from a single source. This essentially allows you to guarantee yourself access from a (presumed) secure machine if the initial dæmon is ever shut down.
For Linux systems administrators, it really comes down to you or a group of people against the world. You are on one end, providing access to your machine, and on the other end is a world of network-connected people, many of whom would like to have access to your machine. Usually the tools that are used to exploit the SSH dæmon are built with the default configurations in mind. It is the low-hanging fruit that is often the target, and it is your job to know your local environment and needs to remove that low-hanging fruit before it gets picked by others. A compromised machine causes not only havoc within your environment, it also presents a risk for the other multimillion or billion computers connected worldwide. OpenSSH provides an outstanding tool for remote access. Comparing the tool to a crescent wrench is a fair analysis. Both the wrench and OpenSSH are delivered with a wide range of capabilities. Just as you will need to adjust a crescent wrench to make the most of it, adjusting the default configuration of OpenSSH will maximize your remote-access capabilities while also providing a more secure environment.
Phil Moses spends his days managing Linux systems for the Physical Oceanography Research Division at Scripps Institution of Oceanography and spends his time off contemplating access to those remote areas of the world that are less traveled. Phil can be reached at philmoses@cox.net.