How to set up and use SSH


Jump to one of the sections of this page:


Part 1 — Administrative Work

Configure the service by modifying /etc/ssh/ssh_config and /etc/ssh/sshd_config on all hosts as follows:

/etc/ssh/ssh_config is used by local users connecting from this host to some remote server. Put the following in that configuration file:

# Only the more secure SSH2 protocol
Protocol 2 
# Allow the user to tunnel X applications through SSH.
# Read "man ssh_config" first.  Yes, there is a security
# issue.  However, very carefully read that section.  It
# is described as an additional problem if the attacker
# is able to subvert Unix file system security on one
# of the hosts.  If the attacker is able to do that,
# I have far worse problems that must be fixed NOW.
ForwardX11 yes 

/etc/ssh/sshd_config controls how this server handles connection requests from remote users. Put the following in that configuration file:

# Only the more secure SSH2 protocol
Protocol 2 
# If you list users allowed to connect via SSH, then
# they will be the only users allowed to authenticate
# to SSH.
# Also see "AllowGroups", "DenyUsers",
# and "DenyGroups" in "man sshd_config".
AllowUsers joe jane ....
# Only accept authentication via cryptographic
# challenge-response.  The remote client must
# have cryptographic keys.
PasswordAuthentication no
# Do not allow root login -- first login
# as ordinary user, then su to root
PermitRootLogin no
# Specify how to run sftp
Subsystem sftp /usr/libexec/sftp-server 

Collect host public keys with the following command, where the parameters list every SSH server as both simple hostname and fully-qualified domain name, as well as their IP addresses:

# ssh-keyscan host1 host1.example.com 10.0.0.1 \
              host2 host2.example.com 10.0.0.2 \
              [....] \
              hostN hostN.example.com 10.0.0.N > /etc/ssh/ssh_known_hosts 

Alternatively, create a file /tmp/hostlist containing all hostnames, all fully-qualified domain names, and all IP addresses, one item per line, for all of your SSH servers. Then run this command:

# ssh-keyscan -f /tmp/hostlist > /etc/ssh/ssh_known_hosts 

Once you have generated /etc/ssh/ssh_known_hosts, securely copy it to every host on which SSH will be used, even the workstations that will only be SSH clients.

You will generate new (and different!) SSH keys if you reinstall the operating system. That means that clients will refuse to connect to that freshly built SSH server, because it will look like it is a different host trying to masquerade. You will need to at least update that host's key. A simple yet safe procedure would be:

  1. On some host, edit /etc/ssh/ssh_known_hosts and remove the entry for the freshly rebuilt server.
  2. Re-run the above ssh-keyscan command to create an updated ssh_known_hosts file with the public key for the newly rebuilt server.
  3. Securely copy the new /etc/ssh/ssh_known_hosts file into place. Note — since we have disabled root login over SSH, we have also disabled root use of scp. You must copy the files to all servers as a user, and then su to root on each server to install them.
  4. At this point you may wish that you had thought to save a backup copy of those key files before starting... If you did, be careful about file permissions! The public keys are in files named *key.pub and must be mode 644 or 444. The private keys are in files named *key and must be mode 600 or 400.

Start an SSH agent at login time if your display manager does not already do this. Some, like Gnome's gdm display manager, tend to do this. Others, like KDE's kdm display manager, tend not to.

To test if this is needed, login to the graphical desktop as a user and run this command:

$ ssh-add -l  

If an SSH agent is running, you will see the following and you don't need to do anything additional to start an agent for the user:

The agent has no identities. 

However, if you see the following, you need to make a change to get an agent started for the users:

Could not open a connection to your authentication agent. 

If you need to make a change to get an SSH agent running at login time, modify your display manager configuration.

For example, on Linux with KDE's display manager, the configuration is stored in /usr/share/config/kdm/. On OpenBSD, it is /usr/local/share/config/kdm/. The file Xsession originally contains a block reading as follows:

case $session in
  "")
    exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
    ;;
  failsafe)
    exec xterm -geometry 80x24-0-0
    ;;
  custom)
    exec $HOME/.xsession
    ;;
  default)
    exec /usr/bin/startkde
    ;;
  *)
    eval exec "$session"
    ;;
esac 

Change that code to the following, with the change highlighted:

case $session in
  "")
    exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
    ;;
  failsafe)
    exec xterm -geometry 80x24-0-0
    ;;
  custom)
    exec $HOME/.xsession
    ;;
  default)
    exec ssh-agent /usr/bin/startkde 
    ;;
  *)
    eval exec "$session"
    ;;
esac 

If you are stuck with CDE and its dtlogin interface (decent technology for about 1990), you need to make a change within the large and complicated file /usr/dt/bin/Xsession. Look for a block like the below, and insert the highlighted code. Yes, you will probably have to specify the full path, and you very likely will have to do some research and experimentation to figure out just exactly where this goes. Hint: look for the reference(s) to dtsession

# 
# Session startup clients and args
# 
if [ "$SESSIONTYPE" = "altDt" ]; then
      dtstart_session[0]="/usr/bin/ssh-agent $SDT_ALT_SESSION"
      dtstart_hello[0]="$SDT_ALT_HELLO"
else
      dtstart_session[0]="/usr/bin/ssh-agent $DT_BINPATH/dtsession"
      dtstart_hello[0]="$DT_BINPATH/dthello &"
fi
dtstart_session[1]="$HOME/.xsession"
dtstart_session[2]="$HOME/.x11start"

Log out, log back in, and run this command:

$ ssh-add -l

You should see a message that the SSH agent has no identities. That's fine, that's what we were looking for. If you instead see a message that the SSH agent cannot be contacted, then you got something wrong. Either you didn't really log all the way out and back in again, or (more likely, I would hope) you didn't get things set up correctly.

Re-start the SSH service however your version of *nix does this. You may find that the following signals your SSH daemon to re-read its configuration:

# pkill -HUP sshd

If not, consult the below table or your system documentation. If you reconfigured your display manager, also restart that.

Linux # /etc/init.d/sshd restart
Solaris and similar # /etc/init.d/sshd stop
# /etc/init.d/sshd start
BSD # pkill -TERM sshd
# /usr/sbin/sshd

Now your system is considerably more secure.


Part 1.5 — Access Control

You might want to consider some access control. Be aware that it can only provide limited protection. SSH access control applies in the following order:

  1. Packet filtering (e.g., iptables, IP Filter, pf, etc)
  2. TCP Wrapper (/etc/hosts.allow and /etc/hosts.deny)
  3. PAM and Allow/Deny rules in /etc/ssh/sshd_config

Packet filtering — This is a big topic. For Linux, see the Netfilter/iptables documents at http://www.netfilter.org/

TCP Wrapper by example

Allow SSH access for one block of IP addresses only, allow all connection requests on all other network services:

# cat /etc/hosts.allow
sshd : 192.168. 
# cat /etc/hosts.deny
sshd : ALL  

Allow SSH access for one block of IP addresses only, allow no other connection requests for network services aware of TCP Wrapper access control:

# cat /etc/hosts.allow
sshd : 192.168.
# cat /etc/hosts.deny
ALL : ALL 

Allow only specific user@host SSH access, deny all other network services:

# cat /etc/hosts.allow
sshd : joe@192.168.12.0/24 , jane@host1.example.com , jane@host2.example.com
# cat /etc/hosts.deny
ALL : ALL 

PAM and Automatic black-list of remote password-guessing attackers — If you are reachable from the Internet, attackers using hosts all over the world will be trying to guess passwords through SSH connection.

First, see if you need to add this line to your /etc/ssh/sshd_config configuration file. You will have to experiment to verify whether your SSH daemon uses PAM by default (see if the syslog messages from sshd include references to PAM rules):

UsePAM 

The pam_abl PAM module can automatically add a password-guessing host to a black-list. Put something like this in the file /etc/pam.d/sshd before the existing auth lines:

auth   required   pam_abl.so config=/etc/security/pam_abl.conf 

Then add something like this to the reference configuration file /etc/security/pam_abl.conf

# Black-list any remote host with 10 consecutive authentication failures
# in one hour, or 30 in one day.  Keep them in the black-list for two days.
host_db=/var/lib/abl/hosts.db
host_purge=2d
host_rule=*:10/1h,30/1d
# Black-list any local user other than root for which there are
# 10 consecutive authentication failures in one hour, or 30 in
# one day.  Keep them in the black-list for two days.  Note that
# this means that non-root users may be subjected to denial of
# service attacks caused by remote password guessing.
user_db=/var/lib/abl/users.db
user_purge=2d
user_rule=!root:10/1h,30/1d 

Allow/Deny rules in /etc/ssh/sshd_config: — See the sshd_config manual page for the details on this. The short version is that you add lines like the following to sshd_config and then send a HUP signal to the daemon. Once you add one AllowUsers rule, then the only users allowed to login via SSH are the listed ones:

AllowUsers joe
AllowUsers jane 

You can also do this by client hosts:

AllowUsers *@host1.example.com
AllowUsers *@host2.example.com 

Or even by domains:

AllowUsers *@*.example.com 

Part 2 — User Work

The user needs to generate SSH public/private key pairs. Use the following command sequence. Provide a strong passphrase, since security rests on this being extremely difficult to guess. Unless you really want to greatly inconvenience and confuse yourself, use the same passphrase for both RSA and DSA key pairs! Accept the default locations for key storage:

$ ssh-keygen -t rsa
$ ssh-keygen -t dsa
$ cat ~/.ssh/*.pub > ~/.ssh/authorized_keys 

In some situations (for example, Red Hat Enterprise Linux 5), the umask of the unprivileged user's environment may be set to 002, so the permission of the resulting authorized_keys file is 664, world-writeable. And the SSH daemon may refuse to pay attention to those keys (again, as seen on RHEL5). So you may need to do this:

$ chmod 644 ~/.ssh/authorized_keys 

Once those keys are generated, the entire directory ~/.ssh must be copied into place on all systems. If you are using NFS and automounting, this is already done! If you are not, then the administrator might need to get involved briefly.

At every login, the user simply runs this command:

$ ssh-add 

Type your SSH key passphrase (what you typed back when you generated the keys). Now everything is automatic! Commands like ssh, scp, and even sftp work automatically without any password-typing by you!


How The Magic Happens

You can observe quite a bit of this by asking SSH to be verbose. For example:

$ ssh -v someserver date

If you are really interested in details, ask for very verbose narration:

$ ssh -vv someserver date

1 — Your client host and the remote server authenticate to each other using public-key cryptography. Each host is certain that it has the other's public key, since the administrator stored all those in /etc/ssh/ssh_authorized_keys. Host #1 picks a random number and sends it to host #2 as a challenge. The host #2 encrypts that challenge with its private key to create the response, and sends it back to host #1. Host #1 decrypts the response using the public key of host #2 (which host #1 is certain that it has). If the result is identical to the challenge, then that must really be host #2 over there!

2 — The client and server negotiate a cipher and a session key. The cipher will be the strongest encryption algorithm that both hosts know. The session key will be generated with the Diffie-Hellman algorithm — see my "Just enough cryptography" page for details.

3 — User authentication happens. Now that the two hosts are certain of each other's identity, and they have negotiated a secure communication channel, the user can be authenticated. Cryptographic challenge-response authentication is tried first, as described above for hosts. Since we disabled password authentication on the server, that method has to work. If the user forgot to run ssh-add, they get mysterious remote authentication failures. So either:
— Train your users to always run ssh-add
— Put something in a startup file to force them to do this


My page on hardening default installations of Linux and BSD

My general security page


Home Page Site Map Public Key E-Mail
Use /bin/vi! Hosted on OpenBSD
Hosted on Apache Valid XHTML 1.1! Valid CSS!
© Bob Cromwell Jul 2008. Created with /bin/vi, hosted on OpenBSD with Apache.    Root password available here