OpenSSH Power Tutorial
From Sfvlug
You may already use ssh on a daily basis. We'll take an in-depth look at the ssh tools and see ways they can make your life even easier.
The purpose of this page is to provide a short guide to doing really cool things with the OpenSSH suite of utilities.
Contents |
The Berkeley R Tools
A long time ago, when computers were very expensive and hard to come by, operators needed a way to manage machines which might not be located within walking distance. Telnet and FTP were introduced to connect to remote machines and transfer files between them. Then the "R" utilities were introduced to make remote command execution even easier.
Telnet And FTP
The common authentication method for both telnet and FTP is to type a username and password when prompted. There are two security concerns with this scenario. First, it is possible for anybody who can read packets in the route between the client and server to pick up the client user's username and password packets. Thus an attacker armed with both of these can now use them to log in to the remote system. The other concern is the authenticity of the server; the client has no way to know if the server connection has been falsified. Someone could simply set up a listening server on the remote end to collect usernames and passwords.
SSH provides protection against both these vulnerabilities by encrypting the communication, so the username and password are not readable, and also verifying the public key of the server. This way a spoofing listener should raise an alarm since the key won't match.
rsh, rlogin, and rcp
These are known as the evil "R" commands. They still operate by sending all their text in the clear, but do not require a password to be sent.
When a user invokes one of these commands, the server consults /etc/hosts.equiv and the user's ~/.rhosts for a valid host entry (and optionally username in the .rhosts file). Access is granted if the remote host is listed and the username is the same on both ends or the client username matches a line from .rhosts.
This may appear to be an improvement because now we're not sending passwords in the clear, but it's not that much better. An attacker now only needs to spoof the client address and use the correct username. The username is still sent in the clear (and everything else you send, which can include su and the root password), and there is still no way to verify the server is who you expect it to be.
Working With Keys
When you attempt to log in to a remote host via ssh, the process starts by establishing a secure connection. The user is asked if he or she accepts the server's public key, and if so, stores it in ~/.ssh/known_hosts. Once accepted, the user is not prompted again, but if this key ever changes, the client will break off the login process. Once the secure channel is achieved, the server attempts to authenticate the user using host-based authentication (same as .rhosts as described previously), public key (which we'll cover shortly), challenge-response (similar to one-time-passwords), and finally username and password. Once the user is authenticated, sshd and the PAM stack checks to see if the user is authorized to execute a command. There are lots of ways to control this, and we'll look at a few later.
Generating A Keypair
The first time sshd is executed, the startup script usually generates a key pair for the host. Usually, it generates three key pairs, one for SSH protocol version 1, one for protocol version 2 using RSA, and the other for DSA. This means the following files will be generated:
File Name | Version | Function |
---|---|---|
/etc/ssh/ssh_host_key | 1 | private |
/etc/ssh/ssh_host_key.pub | 1 | public |
/etc/ssh/ssh_host_rsa_key | 2 | private |
/etc/ssh/ssh_host_rsa_key.pub | 2 | public |
/etc/ssh/ssh_host_dsa_key | 2 | private |
/etc/ssh/ssh_host_dsa_key.pub | 2 | public |
It is critical that permissions remain correct on these files. All directories leading to the keys must be owned by root, and writable by root alone. Each of the key files should only be writable by root, and the private keys should not be readable by any group or other user. If any of these conditions is not met, then sshd will refuse to start.
Did you know you can use passwordless login with SSH just like with the Berkeley R tools? Host based authentication is disabled by default -- it also only works with version 1 of the SSH protocol -- and with good reason, but there is another method. You can generate a user based key pair which you can use to login. This method is also potentially more secure than using a password. Run the following from a shell prompt:
$ ssh-keygen Generating public/private rsa key pair. Enter passphrase (empty for no passphrase): EnterAGoodPassPhraseHere Enter same passphrase again: EnterAGoodPassPhraseHere Your identification has been saved in /home/user/.ssh/id_rsa. Your public key has been saved in /home/user/.ssh/id_rsa.pub. The key fingerprint is: bf:43:17:37:f9:56:76:3d:ca:f6:dd:a1:80:cf:9f:b1 user@host.example.com The key's randomart image is: +--[ RSA 2048]----+ | . . | | . o | | . . . E | | . o o + | | o . . S + | | o . . . | |. . . B . | | . . =.+ . o . | | . .o..o o | +-----------------+
Notice, the "randomart" looks kind of like a high heeled shoe. This is a helpful mnemonic.
Also, you see it says "empty for no passphrase?" Why do you need a passphrase if the purpose of the key pair is passwordless login? I'll explain how you only need to enter the passphrase infrequently to use your key below. You really should enter a very good passphrase, one which is significantly long and very hard to guess.
You may want to keep your private key on a thumb drive or your laptop's hard drive, so you can log in from remote locations. That is fine, but if it is ever lost or stolen, aren't you going to be very glad you have such a difficult passpharase?
The applications of a passphraseless key are usually when you need a special purpose user to run ssh from cron or other automated processes. In that case, you would want to make sure the key in question is only of limited value to an attacker, so the user it belongs to has restricted ability. Also, you wouldn't want to travel with such a key, to avoid exposure to risk.
ssh-keygen can do more than generate a new key pair. It can change or add (or remove) the passphrase of a private key. It can regenerate the public key from a private key (but not the reverse). If you want to verify the fingerprint of a key, you can display it with the following:
$ ssh-keygen -l -v [-f /path/to/file] Enter file in which the key is (/home/username/.ssh/id_rsa): 2048 03:d0:df:b8:3a:17:e0:f0:50:70:f6:01:f0:d0:fc:24 /home/username/.ssh/id_rsa.pub (RSA) +--[ RSA 2048]----+ | =*+.. | | *E... | | .o=.o | | o ..+ . | | = .S. | | o o. | | . . | | o . | | o | +-----------------+
The -v flag causes it to include the "randomart." I think that one looks kind of like the peninsula of Florida.
Another useful option is to publish a host's fingerprints in DNS. First, use ssh-keygen to spit out the key records and enter them into the domain's forward zone file:
$ ssh-keygen -r `hostname`. box.example.com. IN SSHFP 1 1 3e745a6c2021894055273276238df4fbf36b9985 box.example.com. IN SSHFP 2 1 7f7ae4de671152a307e70c3b3cb4b824c381c1eb
On the first line, the first "1" is for the SSHv2 RSA key, and on the second line, the corresponding "2" is for the SSHv2 DSA key. There is no fingerprint record for SSHv1 keys.
We can use a command line option to verify a fingerprint record when logging in.
$ ssh -o 'VerifyHostKeyDNS yes' square.example.com The authenticity of host 'square.example.com (192.0.2.99)' can't be established. RSA key fingerprint is 7b:63:39:21:e8:7a:46:8b:3a:73:44:71:06:08:4f:4f. Matching host key fingerprint found in DNS. Are you sure you want to continue connecting (yes/no)?
I will demonstrate how to make that option permanent below.
Installing Your Public Key
The simplest way to start using your private key is to use the ssh-copy-id program to install your public key to the remote host. It has very few options.
ssh-copy-id [-i [identity_file]] [user@]machine
If you don't have the option of using this program, you must copy the public key manually. You can do the following:
ssh square.example.com mkdir -m 0700 ~/.ssh ssh square.example.com 'cat - >>~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub ssh square.example.com chmod 0600 ~/.ssh/authorized_keys
Again, permissions are critical. sshd won't allow a login for a username using keys if they are compromisable. The user's HOME directory, .ssh subdirectory, and authorized_keys file must not be writable by any other users (including members of the user's group). Likewise, ssh will not allow the key to be used if the private key is writable by others. You are best off if private keys and the authorized_keys file are not readable by anyone but their owner.
You may see references to a file named authorized_keys2 in various documents and on the web. Its use is pretty well deprecated at this point, and OpenSSH handles all of its data being in authorized_keys, so you don't really need to bother maintaining it.
Using Your Private Key
Now that you have generated a keypair and installed the public key on a remote server, let's put it to use. On some distros, dbus will have automatically started ssh-agent for you. In a terminal check if it is running by checking if SSH_AGENT_PID is set.
echo $SSH_AGENT_PID 3134
If it's not running, you might want to just add the following to your ~/.bash_profile.
if [ -z "$SSH_AGENT_PID" ] ; then eval `ssh-agent` fi
Why do we need to wrap the call to ssh-agent in eval and back ticks? Because it outputs something like the following, which needs to be incorporated into the current running environment:
SSH_AUTH_SOCK=/tmp/ssh-OsjUy11690/agent.11690; export SSH_AUTH_SOCK; SSH_AGENT_PID=11691; export SSH_AGENT_PID; echo Agent pid 11691;
Now add your private key to the agent. Just run ssh-add. You will now be prompted for your passphrase. This is the only time you will need to type it in, until you restart the agent or remove your key.
You can also have your desktop prompt you for your passphrase when you log in by adding /usr/libexec/openssh/gnome-ssh-askpass to your startup items.
More Than Just Another Remote Login
As it turns out, OpenSSH is incredibly versatile. It not only allows you to run commands and a shell via a secure link, but it also allows you to send all kinds of traffic over that secure link.
Stupid Pet Tricks
I am often surprised by the number of people who are unaware that X11 is first and foremost a network protocol. Your local screen is a server, and all GUI programs you run are clients to that server. It is possible to use SSH to connect remote clients to your local server. As a matter of fact, OpenSSH has this built in. Simply specify either -X or -Y on the command line when connecting to the remote host. Your session will then have a DISPLAY variable set. Various remote X11 clients can then connect to this display and appear on your local server.
Sometimes you may not be fortunate enough to be connecting to a Linux or Unix host at the other end. Believe it or not, Windows can run OpenSSH and this is actually a pretty effective way to deal with it. If you have VNC installed on a Windows host, you can configure it to only listen to localhost. Then you can use SSH to tunnel to it.
ssh -f -N -L 5900:localhost:5900 remotehost
The -L flag configures a local forward. That means OpenSSH will open the first port listed (the first 5900 here) locally and send any connections to it over the tunnel. Once the packets get to the other end, a new connection will be made to the host named in the middle (localhost, which actually refers to the remote host's 127.0.0.1 address) on the last port listed. So, to connect to VNC on the remote Windows host, you actually point your vncviewer application to localhost, which defaults to port 5900 which was opened by ssh, gets tunneled to the remote host, and connects to 127.0.0.1:5900 on the remote end.
The -f flag tells ssh to fork into the background before executing a command. This makes it non-interactive. If all you want to do is run a remote command or establish a tunnel, you don't need a prompt on the other end.
The -N flag tells ssh not to execute any commands. This is also useful for port forwarding. The primary benefit, though, is that ssh won't start a shell on the remote end, and won't immediately exit because of this.
Another option to forwarding a single connection is to proxy all traffic going into a forwarded port out another host. OpenSSH provides the -D flag to specify a port for use as a SOCKS proxy. The default port for SOCKS is 1080/tcp.
ssh -f -N -D 1080 remotehost
Now configure FireFox to use a SOCKS5 proxy, 127.0.0.1 as the address, port 1080 as the port. Now everything you do in that instance of FireFox will proxy out your remote host. Try http://www.whatismyip.org.
Modify Client Behavior
When you start ssh as a client from the command line, first it reads /etc/ssh/ssh_config. Second, it reads ~/.ssh/config and anything presented therein overrides previous settings. Finally it parses command line options which override either of the config files.
Stanzas in the client configuration files begin with a Host directive. Each stanza ends when the next Host declaration begins. The indentation is not necessary but helps human legibility. Here are some examples.
Host office.example.com Port 8022 IdentityFile ~/.ssh/officekey
This first example is fairly straight forward. It demonstrates how to change the default port on a per-host example. It also illustrates the use of a different private key.
Host isilon User root
Although I don't personally like the situation, I have to log in to my Isilon storage cluster as root. This ensures I always pick the proper user.
Host netapp* ForwardAgent no ForwardX11 no ForwardX11Trusted no
I discovered I can't log in to NetApp storage devices if any of these forwarding options are enabled, so for the NetApps, I turn them off.
Host square DynamicForward 1080 LocalForward 8025 localhost:25 LocalForward 8119 localhost:8118 LocalForward 8143 imap.example.net:143 User alternate
When I log in to my remote workstation, I automatically set up a dynamic proxy, as well as a tunnel to the remote Privoxy session. I can also forward all outbound email to the remote sendmail process, and connect to yet another IMAP server. Here, again, I also need to use an alternate user name.
Host * ControlMaster auto ControlPath ~/.ssh/%r@%h:%p ForwardAgent yes ForwardX11 yes ForwardX11Trusted yes ServerAliveCountMax 5 ServerAliveInterval 600 VerifyHostKeyDNS yes
The preceding apply to all hosts, unless otherwise overridden by specific Host stanzas. The ControlMaster automatically opens a socket(7). So long as the connection remains, any new connections to the remote server use the socket rather than opening a new tunnel. This saves on network resources, as well as decreases the number of times I have to go through an authentication process, and establish a new encrypted connection.
ForwardAgent means I can continue to use my private key accross multiple hops.
ForwardX11 simply turns on the X-forwarding I mentioned earlier.
The client will send a keep-alive packet to the server once every ServerAliveInterval seconds. In this case, it's ten minutes. However, if it fails to recieve ServerAliveCountMax keep-alives in a row, ssh will consider the connection dead and give up. And in this case it means the connection hasn't responded in 50 minutes. This has the benefit of keeping my connection alive even if an overzealous firewall would attempt to sever it.
Finally, I can always verify host keys in DNS if they are available. I find this useful if I have to log in to a host which has changed names for some reason. The key may be unverified by me, but since I posted the fingerprint in DNS, I trust that I have connected to the right server.