Introduction to SSH

Submitted by Syscrusher on Tue, 2005/06/07 - 08:36.

This article was originally published in 2001, but SSH is still among the most popular Open Source communication tools.

Why Secure Shell?

For years, people have connected from one computer to another with simple, reliable tools such as telnet, ftp, and so on. They work, they work well, and they are compatible with every operating system you have ever seen. So why change?

In a word: Security. The problem with all those simple tools is that they send user authentication information (login name and password) across the Internet in a plain-text format that can be read by anyone who has physical access to the wiring that carries the data. It can also be read by anyone who can crack the security of any system along the way, because that person could insert a "sniffer" program to capture passwords.

The odds of this happening may seem small, and with reliable ISPs perhaps they are small. The consequences of it happening even once are serious, though, especially since most people use the same password on multiple remote systems.

The primitive tools of yesterday's Internet also send the application data without encryption. Whether or not that is a problem depends, of course, on the nature of the data.

Enter Secure Shell. Secure Shell uses strong encryption methods to establish a secure connection between client and server. From the very first moments when the connection is made -- and before the authentication happens -- SSH encrypts all of the traffic between the two machines, transparently. As long as you trust your local machine and the machine to which you are connecting, you need not trust (very much) anything in the middle.

Secure Shell, though, has a lot more capabilities than just the encryption of the data and credentials. The later sections of this article will cover just a few of these advanced features, in more detail.

Obtaining Secure Shell

SSH is available primarily from two sources:

Secure Shell (the company) provides SSH as an open source product, but it must be licensed (and a fee paid) for commercial use. SSH is free for nonprofit, personal use, for educational use, and for brief evaluations of commercial applications. There is no copy protection or other crippling feature in the code; it is distributed freely and the company relies on the honor system for commercial users.

The OpenSSH project is a clone of the commercial SSH, and it is based on the OpenSSL (Secure Sockets Layer) libraries found at http://www.openssl.org/. OpenSSL provides the underlying encryption services for the Apache Web Server's secure mode as well.

Which one you use is a personal choice. Both work extremely well, and they are compatible with one another as far as network connectivity is concerned. Even some of the configuration files and command options are the same. Due to licensing issues, most Linux distributions ship with OpenSSH included. The commercial version of Secure Shell appeals to companies which require technical support beyond what can be found on the Internet. If your business depends on heavy use of this tool, the cost of a support license with the Secure Shell company could easily be justified.

Building either version from source is easy, and uses the standard procedures found in other source packages. Remember that the OpenSSH version requires OpenSSL to be installed first. Since most people either got OpenSSH with their distribution, or know how to build applications from source, the details of this are omitted here.

SSH has been ported to many versions of Linux and UNIX, and also to Windows using the Cygwin libraries. There are graphical front-ends available for most platorms, for those not comfortable with the command line.

SSH Basics: Using the ssh Command

In its simplest mode, ssh (note lower case -- here we are talking about the specific command, not the entire SSH package) works like a Telnet client. The command takes the form:

ssh hostname
where hostname is either the name or the IP address of the machine to which you wish to connect. By default, ssh attempts to login to the remote machine using the same login name as on the local machine. You can use the -l remote_login_name option to change this, if needed.

The first thing that happens is that ssh will exchange encryption keys with the remote host. What this means is that the two machines are verifying one another's identity, entirely a separate process from your user login. Your local machine sends its public encryption key to the remote end, and vice versa, unless these keys have already been exchanged previously. The first time this happens, of course, there is no way for either machine to validate the other's key. At the server end, this is generally not a problem because it will accept keys from any client (the usual case) or because your server's administrator has already asked you to send the public key (more on that later). At your client, though, you will receive a warning message that looks like this:

Host key not found from database.
Key fingerprint:
xuter-sovoh-heziv-dyhyz-zugad-kutus-neket-filul-cahet-zoceb-myxyx
You can get a public key's fingerprint by running
% ssh-keygen -F publickey.pub
on the keyfile.

What this means is that the remote host's key is a new one, not previously known to your client. You are asked if you still want to connect.

If you tell ssh to go ahead, the key from the remote is permanently stored in your home directory, in a subdirectory called either .ssh or .ssh2 (depending on whether you have the commercial or the freeware version), in a file called known_hosts. When you told ssh to go ahead, what this implied is that you trust the machine to which you have connected. This means not only that you trust it not to have been cracked, but also that you trust the connection in between to the extent that no one is spoofing (pretending to be) the remote machine at the moment.

If you didn't trust the remote key, ssh will abort without saving it and without connecting. If security is paramount for your application, you should contact the remote server's owner to find out the fingerprint of its host key. This is public data, so there is no problem with it being repeated to anyone over the phone. The point is to compare the key fingerprint with the one ssh shows you, to verify by independent means that the Internet is connecting you with the correct machine, not a fake. For many applications, this entire process is overkill and you can just trust the key that ssh shows you. The point is, you have a choice depending on your needs.

Once connected, ssh acts just about like telnet, sending your keystrokes and receiving characters from the server to display on your screen. The difference is that everything flowing in both directions is fully encrypted. Suppose you use the su command to become root on the remote. Under telnet, the keystrokes you entered when typing the password were sent in the clear, but under ssh they are encrypted. You protect not only your own identity, but the identities you use on any additional connections or authentications made from the initial shell.

If the current login on the local machine isn't correct for the remote, just use a command like this:

ssh -l remote_login hostname
to specify the remote login name. You will be prompted for the password.

If all you wanted to do at the remote end is execute a single command and logout, you can simply specify that command (in quotes) on the ssh command line, like this:

ssh -l remote_login hostname "remote_command"
including any parameters for the remote command. Here's an example:
ssh -l mylogin yourhost.yourdomain.com "ls -l /tmp"
This example will retrieve a list of files in the /tmp directory on the remote machine.

More Fun with the ssh Command

For all you command-line jockeys, this may be enough. But what if you want to run a graphical application such as Netscape on the remote host? Under Windows, you're stuck unless you have an add-on product such as PC-Anywhere(tm) or VNC. With UNIX and Linux, though, the X11 graphics system allows keyboard, pointer, and display to be on a different machine than the place where the application is running. Unfortunately, using this feature over the Internet can be a dangerous security exposure. Among other things, you would have to allow a remote connection back to your local client (so that the display can be controlled from the other end of the link) and you might even have to open a "hole" in your firewall on the X11 ports (6000 through 6003).

Fortunately, the ssh command provides a way around this problem by creating what is called a "tunnel" for X11 within the existing secure link. X11 command streams from the remote end are encrypted, forwarded to the local client, decrypted, and sent to the local display, all automatically. To enable this feature, you need to enable X11 forwarding in the ssh command at the client end. How this is accomplished depends, unfortunately, on which version you are running.

  • For commercial SSH, add the +x option to the ssh command.
  • For OpenSSH, add the -X option to the ssh command.
Both versions have options in their client configuration files to make this behavior the default, probably something you will want to do on your personal machine.

For those interested in how this works under the covers, what ssh does is to set the DISPLAY environment variable at the remote end to a value like remotehost:11.0 where remotehost is the remote computer name. The 11.0 part is very different from the 0.0 you normally see. The number is a virtual video hardware adapter number, with zero implying the first such adapter on the system. The ssh daemon appears to the application as just another X11-capable terminal device. Back at the client end, the data stream is transparently routed to and from the local console, which is typically called just :0.0 or localhost:0.0.

Try logging onto a remote system to which you have access, and running the xeyes command to test. The eyes display on your local console, but the program is running on the remote server. All of the network traffic is flowing over the standard SSH encrypted channel, with no special setup needed on the client or on the firewalls at either end.

Copying Files with Secure Copy

If you know how to use the UNIX/Linux cp (copy) command, then you basically already know how to use the SSH Secure Copy command, scp. To copy a file or files from your client to the server, use this syntax:

scp localfile... [login@]remotehost:remote_path
The localfile is the list of one or more local filenames, with wildcards if you wish, to be copied. The remotehost is self-explanatory. If you provide the login parameter and @ symbol, you can connect as someone other than your current login name (if, of course, you know the password). The remote_path simply specifies where you want the copied file(s) to go.

If you want to insist that the destination be a directory, not a filename, leave a trailing slash on the end. This protects against typos that can cause you to end up creating a bogus filename at the remote end. For example, suppose you want to copy the file index.html to ~/pub_html (directory) on your web server. If you specify this:

scp index.html www.mydomain.com:pub-html
the results won't be as you expect. Note that there is a dash, rather than the intended underscore, in the destination -- clearly an error. But what results is a valid filename, so scp will dutifully create a file called pub-html in your home directory on the server. If the destination had been www.mydomain.com:pub-html/ (note trailing slash) instead, then scp would have issued an error message saying the directory was not found. An error message is almost always better than something that appears to work but didn't do what you wanted!

To go the other way, just reverse the syntax of the source and destination, thusly:

scp [login@]remotehost:remote_path localpath
You can repeat the remote host and path portions to specify multiple files, but of course there must be only one localpath because scp expects the last command line value to be the destination. There is one caveat: If you want to use wildcards at the remote end, you have to place them inside single-quotes so that your local command shell doesn't try to expand them. So use this, for example, to copy all HTML files from the remote server to the current directory on the client:
scp www.mydomain.com:pub_html/'*.html' .
Note the use of the period to represent the current directory at the local end.

When specifying the remote path, the default starting point is the remote login's home directory. So someone@www.yourhost.com:pub_html/zero.php places the zero.php file in ~someone/pub_html/ on the remote. If you put a slash (/) right after the colon, you will cause the path to begin at the root directory on the remote.

Secure File Transfer Protocol (sftp)

The sftp command works conceptually like regular ftp, though once you have connected some of the commands are different. For instance, get and put work as expected, but instead of having the option to rename files in transit, they offer the ability to list multiple filenames. Directories are automatically recursed. Thus, the mget and mput commands are obsoleted in sftp.

There are no ascii or binary commands. All files are copies as direct binary. Instead of dir from ftp, use ls in sftp just as you would in Linux itself. lls takes the place of !dir for obtaining a list of the files in the local machine's current directory. The directory commands, cd, mkdir, and lcd work as you would expect.

Personally, I find that sftp is less convenient than scp, and I therefore rarely use sftp at all. But if you want it, it is available.

Advanced Authentication with Public Key Cryptography

SSH is able to authenticate machines to one another using cryptographic means, and does so in a way that is basically transparent to the user. It can, however, use cryptography to authenticate the user as well. This can either supplement or supplant the use of passwords, depending on how the server is configured and on the user's choice at the client end. There is a continuum of options, some of which are:

  • The client can specify crypto-based login so that a password is not needed. The server trusts this method and does not request a password.
  • The client can specify crypto-based login. The server trusts this, but the private key at the client end is protected by a passphrase. The user is still prompted for a password, but it is used only to unlock the private key at the local end.
  • The server requires both crypto and password authentication, and the client protects the private key with a passphrase.
  • In an ultra-paranoid world, the server can allow initial connection only by client machines that prove their identity using cryptographic means. The client will allow itself to connect to the server only if the server's identity is similarly established. Users authenticate using both cryptographic and password means.
There are dozens of other permutations.

Luckily, the configuration of user login credentials is fairly easy in SSH. First, you need to know that public key cryptography relies on two binary values that are different but related to one another. One of these, the public key, is distributed freely and insecurely, to anyone who requests it. The private key is a closely-held secret, and is often itself encrypted using traditional symmetrical encryption, guarded by a difficult-to-guess passphrase.

Details of public-key cryptography are beyond the scope of this tutorial, but for now it is enough to know that the client retains the users' private keys, while the server gets a copy of the public keys. When a user authenticates, he or she gives the client command access to the private key, knowing that the server already has the public key.

Generating a key pair is very easy, using the ssh-keygen program:

ssh-keygen -f filename
There are other options depending on what form of encryption (DSA, RSA, and so forth) is desired and how strong (how many bits) the key should be. For now, the defaults are fine. The filename specifies where the private key will go; the public key goes to the same place but with an extension of .pub added to the end. Guard the private key carefully!

During key creation, you are prompted for an optional passphrase. This can be much more complex than the few characters used in a password; many people use an entire sentence if maximum security is needed. Don't pick something obvious, of course, or your efforts are for naught. Social engineering is still the easiest way to obtain passwords illicitly!

If you leave the passphrase blank, you have created a keypair that can be used for passwordless login to the remote host. In this situation, you are relying 100% on the security of the local client to protect that private key file. This may be okay if the keys are given access only to non-critical things, but is not a good choice if you need strong security. It is, however, a useful tool for automating certain system administration tasks on a distributed network. Be sure to protect the keys by using the command chmod 600 keyfiles to prevent others from reading them!

To use your new keys, just use scp to copy your public key to the remote host, placing it into a file called ~/.ssh/authorized_keys. Login with ssh first, and if this file already exists, copy the keys from your client to another filename and then concatenate them, rather than replacing the file. If you should need to login to this remote host from multiple clients with the same login name, you can copy the public and private keys to as many clients as required. Just remember to protect the private keys carefully and only copy them to trusted hosts. Alternatively, you can let each client host have its own keypair, and concatenate the public keys into a single authorized_keys file on the server. The same process works in reverse, of course, if you have one client that needs connectivity to multiple servers. It's just a matter of copying the keys around.

To actually login with the key method, just add a -i private_keyfile option to the ssh or scp command (it works the same on both). Specify the path to the correct private keyfile, and the SSH server at the remote end will attempt to match it with the public keys in the authorized_keys file for your account.

Virtual Private Networks with SSH Tunneling

One of the most interesting features of SSH is its ability to tunnel network traffic inside the standard SSH encrypted channel. We have already seen how this works for X11 connectivity, but the developers of SSH extended the concept to support any arbitrary port at the server and at the client.

What does this mean, in practical terms? Basically, it means that your LAN can become an extension -- in a very secure way -- of the remote LAN, and vice versa. A complete explanation of the possibilities is beyond the scope of this article, but let's look at a couple of practical examples.

Suppose you have an account with an internet provider that offers SMTP for outbound mail and POP3 for inbound mail. Suppose you have a laptop and you travel a lot. Your ISP at home is a cable modem, DSL, or perhaps you are fortunate enough to have a dedicated T1 line. In any case, your home connection won't travel with you. Some of the cable and DSL providers offer remote dial-in, but many do not. Suppose, for the sake of argument, that yours does not. How can you check your e-mail while on the road?

On the surface, this is a simple problem. You sign up with Earthlink, AT&T, AOL, or any of dozens of other providers for a dialup account. Or you use the Ethernet connections that are now found in many of the larger hotels. Getting onto the Internet itself is usually not a problem in developed countries. But things get more complex when you try to manage your e-mail!

POP3 has password authentication, so most ISPs allow you to connect to your POP3 account to retrieve mail from anywhere. Of course, the connection is unsecured so you have to decide if you're willing to risk it. But sending mail is a bigger problem. Your ISP can't allow unrestricted SMTP access because SMTP has no authentication. That's right -- none! Instead, it relies on not allowing anyone to send mail who is not a user on the host where SMTP itself resides. To prevent spamming, SMTP is also restrictive about allowing relays from other hosts on its own domain. So if you simply set up SMTP on your Linux laptop, you haven't solved the problem because most of the SMTP servers on the Internet will reject your traffic, bouncing it back with a "relaying not allowed" message.

How do you solve this problem? SSH has the answer!

From the client, you run the ssh command, but tell it to create a tunnel between an unused port (there are thousands of these) on the client and a well-known server port (such as the POP3 port) on the server. At the server end, the SSH daemon (sshd) will allow this because you're really not doing anything you couldn't have done with the telnet command issued from your shell account on the server. Now your e-mail software can connect to the otherwise-unused port on your client machine (yes, you're connecting back to your own computer!) but SSH will transparently forward that data stream to the server's e-mail daemons. The last leg of that connection, however, is made from the server back to the same machine, so the restrictions on where the connection originated no longer apply. Now instead of "relaying" mail from a remote host, you are simply originating that mail using nothing but your ordinary user privileges on the server itself.

SMTP uses well-known port 25, and so to achieve the tunneling of SMTP we can use a command like this one:

ssh -L 9000:localhost:25 remotehost
What this does is to forward port 9000 on the client to port 25 on the server. So you set up your e-mail software to send mail by connecting to port 9000 on your own machine, which is always named "localhost" for network applications.

When you run the above command, you will actually get a regular command shell on the server, just as ssh always does. You can use this shell any way you like, including running X11 applications with the tunneling feature. SSH is smart enough to handle multiple tunnels at once. In fact, suppose we wanted to also tunnel the POP3 traffic, to avoid sending passwords in cleartext over an untrusted channel (what if a hotel employee puts a sniffer on the DSL network?). This can all be accomplished with a single ssh command!

ssh -L 9000:localhost:25 -L 9001:localhost:110 remotehost
Note that port 110 is the well-known POP3 port. If you look in the file /etc/services on your client, you will see where these numbers are obtained. 9000 and 9001, the local ports, were chosen more-or-less at random. Any number between 1024 and 65535 will work, as long as something else isn't already using it.

By the way, SSH itself generally uses port 22 for its own connections, so this does not conflict with POP3, SMTP, Telnet, X11, or other common services.

Each time you start this command (which you will probably want to put into a script, since it's so long), you will be prompted for a password. To avoid this, just set up the key-based authentication with a keypair that has an empty passphrase, and use the -i option as discussed in the previous section.

By the way, ports are global to each machine, not local to the user. So if you have this tunnel running, any user on the client machine can use it to reach the server. This isn't as big a security hole as it seems -- they can get to their POP3 mailboxes, but not to yours, because they don't know your password. And the SMTP server is shared by all users on the remote server anyway, so it is already insecure even when you're at home. The server-end ports are also global to all users on that machine, but you are not tying up the SMTP and POP3 ports continuously. Just as if you were at home, your client opens the connections (via SSH) only as needed and closes them when done. And most servers can support multiple concurrent connections on each of these ports anyway.

Conclusion

Secure Shell can do a lot more than what's documented here, but hopefully this has given you a feel for what can be accomplished. Take the time to read up on, and become familiar with, this extremely useful tool.

( categories: | )