Setup the SSH server to use keys for authentication

20 Comments

In this article I describe how to configure the SSH server, so that users authenticate using keys, how to generate DSA keys using ssh-keygen, how to configure ssh-agent and finally how to use ssh-add to manage cached passphrases.

"Secure Shell or SSH is both a computer program and an associated network protocol designed for logging into and executing commands on a networked computer." -WikiPedia-

An SSH server can be set up in various ways, but in this document I’ll describe how it can be configured to:

  • only support connections through the 2nd version of the SSH protocol (SSH-2)
  • use DSA keys for user authentication, without permitting authentication with passwords
  • allow only a specific group of users to connect


The SSH-2 protocol, apart from many other useful features, provides stronger security than SSH-1. It’s a bit more cpu hungry than the latter, but this should not be a problem. Using the above configuration, someone must be extremely lucky to manage to break into our system.

But, let me say a few words about how the authentication is done. The user creates a keypair, which consists of a private key, that can be protected with a passphrase, and a public key. The public key is transfered to the server and the private key is kept in our workstation. We assume that the user has accounts in both the server machine and his workstation. Everytime he tries to connect to the server, the keys are validated and the user is granted access.

Prerequisites

A user account in the SSH server machine.

You need to install the following packages to the SSH server machine:

  • openssh
  • openssh-server

The client machines should have the following:

  • openssh
  • openssh-clients

First things first…

I assume that our server machine (server.example.com) is a headless one and that the SSH server is up and running with the default configuration. This permits users, including root, to login with their username/password combination. I also assume that we have already set up a user account on the server with the username "leopard". From a client machine (pc1.example.com) we connect like this:

# ssh leopard@server.example.com

Keypair generation

The default key directory is "~/.ssh". Create this directory in both the user leopard’s home on the server and in your current home directory on the client machine and chmod it so that only the users have access to it.

# mkdir ~/.ssh
# chmod 0700 ~/.ssh

Now, we will create our keypair on our client machine. The following command creates a standard 1024-bit DSA keypair:

# ssh-keygen -t dsa -f ~/.ssh/id_dsa

You will be asked for a passphrase for the private key. You can type any phrase here or leave it blank. Keep in mind that if you do not set a passphrase for you private key and someone else gets access to it, then it will take him only a few seconds to connect to your user account on the server. Anyway, this is up to you. After the key generation is finished, the files id_dsa (private key) and id_dsa.pub (public key) are created in the ~/.ssh/ directory.

Now, we will copy the public key to the /home/leopard/.ssh/ directory on the server saving it with the name authorized_keys and delete id_dsa.pub from our client machine, just because it’s not needed to be there.

# scp ~/.ssh/id_dsa.pub leopard@server.example.com:~/.ssh/authorized_keys
# rm -f ~/.ssh/id_dsa.pub

Make sure that you chmod both keys so that only the respective users have access to them. Issue the following command on both the server and the client machine:

# chmod 0600 ~/.ssh/*

A limited group of SSH users

As an extra security measure, we will create a new group on the server machine and configure the SSH server to only allow this group’s members to authenticate. So, we create a group named "sshusers" and add user "leopard" to it. This has to be done as root:

# groupadd sshusers
# usermod -a -G sshusers leopard

The SSH Server configuration

The SSH server’s configuration file is /etc/ssh/sshd_config. Most of the default options do not need to be modified. What we’ll do is to set it up so that only the members of the "sshusers" group can authenticate using keys instead of passwords. So, as root, fire up your favourite text editor and edit the server configuration file.
NOTE: It’s a good habit to create backups before editing system files.
The options that need to be modified are shown below:

Port 22
Protocol 2
AddressFamily inet
ListenAddress 192.168.0.1

With these we configure the server to listen on port 22, accept connections only over the SSH-2 protocol, use the IPv4 address family and bind on the 192.168.0.1 IP address. Only the "protocol" option is really critical. You can set the others as you like or leave the defaults.

HostKey /etc/ssh/ssh_host_dsa_key

Uncomment or add this line. This is exactly the same as the default option, but needs to be uncommented in the server configuration file, so that the server shows its DSA key’s fingerprint when the client tries to authenticate the server during the connection process. If this is not set, then the server shows its RSA key’s fingerprint (the reason is unknown to me).

LoginGraceTime 2m
PermitRootLogin no
MaxAuthTries 1

The LoginGraceTime option sets a time limit for the user authentication process. If this time passes and the user has not yet authenticated succesfully, then the server closes the connection. Leave this value to the default "2m" until everything is set up properly, so that you have enough time to read any server messages. After that, you can lower it to a reasonable value. I have set it to "20s".
Setting the "PermitRootLogin" option to "no" the server does not allow root to login directly. You can still use "su" after you have succesfully logged in as a normal user.
The "MaxAuthTries" option sets the maximum login attempts per connection. Since we use keys and key validation never fails, we set it to "1".

PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

These are the default options. I just add them here so that you make sure they are set up properly in your config.

RSAAuthentication no
PasswordAuthentication no
# (UPDATE: Jan 11, 2015: 'UsePAM no' is not supported in
# Red Hat Enterprise Linux and may cause several problems.
UsePAM yes
KerberosAuthentication no
GSSAPIAuthentication no

We do not want the server to let users authenticate using passwords or use SSH-1 based authentication methods. You should comment out any Kerberos or GSSAPI options too.

AllowGroups sshusers

Only users that belong to the "sshusers" group can authenticate. Any other user will be rejected without even being given the oportunity to authenticate.

MaxStartups 2

This option specifies the maximum number of concurrent unauthenticated connections to the SSH Server. It has nothing to do with the number of authenticated connections. The default value is "10". We lower this value in order to limit the connections from third parties which do not have an account on our server machine.

Banner /etc/ssh/banner

Finaly, you can set a text file that will be displayed as a banner when someone connects to the server. Just remember that it is displayed before the authentication takes place, so do not be very descriptive. The banner is not really needed.

This is all we have to do. The rest of the configuration options should be left to their default values, unless you need something different. This is up to you.

Restarting the server

Now, that we have finished editing the config file, we need to restart the server, so that our changes take effect. Before that, I would recommend deleting any existing server keys. Don’t worry, they will be recreated as soon as the service is restarted. A quick way to delete all the keys is to:

# rm -f ssh_host*key*

Then restart the server:

# service sshd restart

Note that the key creation time may vary from machine to machine, so it may take a few minutes if the CPU is slow.

The server logging is done through syslog and authentication information is sent to /var/log/secure. This file should not be world-readable.

A last thing is to take a note of the server’s DSA public key fingerprint, so that we can compare it with the fingerprint the server sends to our client when we connect. This is important for connections to the server from locations other than our LAN in order to be sure that we actually connect to our server. On the server console type:

# ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub

Take a note of the fingerprint.

Connect to the server

To connect to our SSH server from our client machine (pc1.example.com), we type:

# ssh leopard@server.example.com

I suggest that the first time you connect you should add the -v option to the above command for verbose output.

Before the user authentication takes place, the ssh client will try to authenticate the SSH server. Since, there is no stored information about your server it will present you the server’s public DSA key fingerprint so you can compare it with the fingerprint you had previously taken a note of during the server configuration. If the fingerprints are identical, you can answer positively to the question. At this time the file ~/.ssh/known_hosts is created on your client machine and it contains the trusted SSH server’s information. You will never be asked again if you trust this server. If the fingerprint comparison took you longer than the server’s LoginGraceTime, the user authentication does not take place. Just try to reconnect. This time you will eventually log in succesfully using key authentication.

Hashing the known_hosts file

Because the servers’ hostnames and addresses are stored in plain text in the known_hosts file, hashing it is a good habit. This can be done using the ssh-keygen utility. Type:

# ssh-keygen -H -f ~/.ssh/known_hosts

This process makes it unreadable, but the ssh programs can still read the contents. Make sure you permanently delete the known_hosts.old backup file.

Change your private key’s passphrase

If you ever need to change the private key’s passphrase you can use ssh-keygen:

# ssh-keygen -p -f ~/.ssh/id_dsa

The ssh-agent

Although key authentication has many advantages over the authentication with passwords, it has one significant drawback: we have to type the passphrase every time we make a connection to the SSH server. One solution would be not to use a passphrase for our private key. But, this is unacceptable. If someone else gets access to our key and finds out to which servers we connect, things get really bad. A second solution is to use the ssh-agent (part of the openssh package) which caches our passphrase in the memory and then it’s automatically used when we make the connection to the SSH server. This way, we only need to type the passphrase once. This is by far more secure than not using a passphrase.

The ssh-agent is a small daemon that runs in the background. When it is run, it exports some environment variables (SSH_AUTH_SOCK, SSH_AGENT_PID) which can be used by programs like ssh-add in order to manage the agent’s cached info or by other programs like the ssh client in order to use this cached info for user authentication. These environment variables must be available to these programs, so the ssh-agent needs to be started in our login shell. There are many different ways to start the agent. Here I’ll describe a rather simple, but very efficient one.

The ssh-agent’s configuration

What we need is to start the agent when we login to our client machine’s shell and stop it when we log out. So, we add the following line to ~/.bash_profile:

eval `ssh-agent`

Why do we use eval? When the ssh-agent is started, it just prints some commands to the stdout. These commands set and export the environment variables we talked about earlier. We use eval, so that these commands are actually executed, or better, evaluated by the shell, so the environment variables are made available to all applications that can use them.

We add the following line to ~/.bash_logout

eval `ssh-agent -k`

This "unsets" the environment variables and kills the agent every time we logout.

Management of cached passphrases

A small utility called ssh-add is used to manage the cached passphrases.

To add a key to the ssh-agent’s cache, we issue the command:

# ssh-add ~/.ssh/id_dsa

We are prompted for the passphrase. After typing it succesfully, it gets cached. From now on, the cached passphrase will be automatically used for every connection we make to the SSH server. Convenient!
If we store our key to the standard location ~/.ssh/ and name it with the standard filename id_dsa, then ssh-add can be run without arguments. Our key will be used.

To list the cached keys we type:

# ssh-add -l

To remove a cached key:

# ssh-add -d ~/.ssh/id_dsa

To empty the ssh-agent’s cache:

# ssh-add -D

Further Reading

There are numerous articles around the web about SSH. Just use google. Keep in mind though that all the necessary info is in the man pages. You should not just read them, but rather study them:

  1. The official openssh manuals
  2. The openssh FAQ

This article appeared in the digg.com homepage on November 16th, 2005. I thank all “diggers” by heart.

Setup the SSH server to use keys for authentication by George Notaras is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Copyright © 2005 - Some Rights Reserved

20 responses on “Setup the SSH server to use keys for authentication

  1. Phlegyas Permalink →

    Excellent explanation. Thank you for helping me understand some of the settings in sshd_config.

  2. Chris Jones Permalink →

    /var/log/secure is only applicable to RedHat and similar systems. Debian and friends will be writing such information into /var/log/auth.log
    Also it might be worth covering enabling the server debugging while doing the setup, so more information is logged to syslog (valuable if you are playing with keys for the first time and it’s not working properly)

  3. George Notaras Post authorPermalink →

    Thanks for the feedback guys.

    Chris, good thing to post this info. I’ll add it to the post as soon as I have some free time. I think there are some other things that should have been mentioned, eg ClientAliveInterval and ServerAliveInterval options, options about forwarding etc.

  4. Yesil Baris Permalink →

    Thanks for such a nice how-to. I’ll spurl this asap.

  5. Tdot Permalink →

    Excellent article. I wish we would have more articles of this quality and a good index or wiki for all this articles on the web. But this is still the strength of good books.

    If you like to block SSH attackers have a look at
    Securing SSH with DenyHosts (note: link is down)

  6. efutch Permalink →

    Great information. However, you mentioned that one of the goals was to let some users use passwords and others keys. I don’t see that explained in the article?

  7. George Notaras Post authorPermalink →

    Thanks for the comments.

    Tdot:
    There is excellent info in your article. I guess all should read it as brute force attacks on the SSH servers is a constant issue. Using keys makes it extremely difficult for someone to break into the system, but, on the other hand, all this traffic from scripts/bots trying to connect is annoying and it makes log files grow big. This is a good read!
    I also have in mind to search for some info about configuring iptables in a such way that someone would need to ping some ports in a certain order before the SSH port opens, but I didn’t have the time to do so.

    efutch:
    Hmm, I guess you are right. The meaning is not 100% clear. The proper phrase would be allow only a specific group of users to connect instead of authenticate. I’ll edit it as soon as possible. Thanks.

  8. Trevor Permalink →

    George:

    Below is a great article on portknocking for SSH.
    Since I implemented this, I have completely eliminated all SSH brute force attacks.
    I log connection attempts on port 22.
    If I want to allow people access to my webserver, ftp, or ssh, I just have to tell them to first go to:
    http://192.168.0.1:{secretport#}
    It will fail, but after that, their IP is free to hit said service.
    This is for my home box so I only turn on ftp or web service as necessary.

    http://www.dotancohen.com/howto/portknocking.php

  9. George Notaras Post authorPermalink →

    This is exactly what I was looking for. Trevor, thanks a lot for this one! Absolutely a "must-read" article.

  10. Nick Permalink →

    Great article, this had me up and going in a few minutes.

    However, I have one question.

    Can I take the keypair generated on clientone (my primary) and use it from different clients?

    What I would like to do is copy my keys to a usb drive and be able to connect to my server from any workstation, not just my primary one.

    Thanks again for your awesome article!

  11. George Notaras Post authorPermalink →

    Hi, Nick. Thanks for your comment.
    Yes, as long as you have your private key (for example id_dsa.pub) with you, you can connect to the SSH server from any client machine or user account you like.

  12. katalin Permalink →

    just a suggestion as I see you’ve mentioned in the article “Take a note of the fingerprint.” you might want to expand the article to include the support of dns servers (bind) for SSHFP record which allows you to add the fingerprint in DNS and ssh can check for this before connecting to a machine. relevant pages man ssh-keygen search for SSHFP.

  13. George Notaras Post authorPermalink →

    This is indeed very convenient functionality, which I have overlooked. I’ll look into it more thoroughly and update the guide as required. Thanks for your feedback.

  14. Tricky Permalink →

    A problem with deleting your .pub key is that you might need it on another occasion – such as if you get another server to which you want to configure access. To retrieve your public key from your private key, run the following command:

    ssh-keygen -y ~/.ssh/id_dsa > ~/.ssh/id_dsa.pub
  15. George Notaras Post authorPermalink →

    The article has been updated. Added note about the use of ‘UsePam No’.

  16. Jan Viktor Apel Permalink →

    Thank you very much for this very useful and well written tutorial on how to properly configure sshd!

    1. George Notaras Post authorPermalink →

      Hello, thanks for your kind words.

      Some minor improvements of the article are in my plans. I hope I find the time to make them soon.

      George