Secure Shell: Part 2By Amy Rich In the last article we covered some basics of SSH and OpenSSH, including how to generate keys and use ssh-agent. In this article we'll draw on that knowledge and cover some tasks geared toward system administration such as role/task-based accounts, port forwarding, running ssh from cron, and debugging connections. Restricting Authenticated Sessions With the authorized_keys FileThe $HOME/.ssh/authorized_keys file on the client not only provides a means for public key authentication, but can also impose certain restrictions. The format of the file is:
options key-type base64-encoded-key commentThe public key on the local machine will already include all but the options section and should be copied over exactly to the $HOME/.ssh/authorized_keys file on the remote machine. The options section can be left blank in the $HOME/.ssh/authorized_keys file, or it can include one or more (comma separated) of the following keywords and arguments: (*) from="pattern list"In addition to authenticating via the public key, the connection must come from a host that matches the specified pattern. In this case, the pattern operators include: ? (one character), * (any characters), and ! (not). To limit the use of a key to my.domain, but exclude the host untrusted.my.domain, one could specify the following option: from="*.my.domain,!untrusted.my.domain" (*) command="command"Execute "command" whenever this key authenticates, ignoring whatever command the remote end supplied. This is one of the most powerful uses of OpenSSH's public key authentication, and can be used to create task-specific keys. The caveat to using restricted commands is similar to that of sudo; beware of specifying programs where the user can escape to a shell. This example limits the connecting machine to controller.my.domain and turns off the system: from="controller.my.domain",command="/sbin/init 5"This example runs lpstat -t, sending the output back to the user's local screen: command="/usr/bin/lpstat -t" (*) no-ptyThis prevents allocation of a pty, and is useful when running commands like ufsdump or when running things from cron. This example (from root's authorized_keys file) dumps the root filesystem to stdout:
from="dumphost.my.domain",command="ufsdump 0f - /",no-ptyA user on dumphost.my.domain connects to the above client machine with the following syntax to save the dump output to a file called /tmp/client-root: ssh root@client | dd of=/tmp/client-rootAn interactive restore on the local machine can happen at a later date with: ufsrestore -if /tmp/client-rootOr the user can just do a dump and an interactive restore on the fly. The dump needs to finish before any commands can be executed from within the restore, though: ssh root@client | ufsrestore -if - (*) environment="NAME=value"This option allows for the setting of any environment variables and can be useful for setting things like PATH and IFS for various commands. Multiple instances of this option in the same key declaration can be comma separated: environment="PATH=/usr/bin:/usr/sbin:/usr/local/bin",environment="IFS= ' '" (*) permitopen="host:port" or for IPv6: permitopen="host/port"This option restricts local 'ssh -L' port forwarding to the specified host and port. Unlike the from option, this option doesn't do pattern matching on the hostname, so the entries must be literal hostnames or IPs. This option can also be specified multiple times for the same key. Here's an example that allows port forwarding to my.host.domain:25 and to 192.168.1.1:143: permitopen="my.host.domain:25",permitopen="192.168.1.1:143" (*) no-port-forwardingThis options forbids TCP/IP forwarding and can be useful to limit users from using the remote host as a hopping point to other hosts. Users can bypass this restriction if they gain a shell on the remote host. (*) no-X11-forwardingThis option forbids the forwarding of X11 sessions. Users can bypass this restriction if they gain a shell on the remote host. (*) no-agent-forwardingThis option forbids agent forwarding. As a general rule, when creating any task based accounts, turn off all forwarding in the authorized_keys file. With this in mind, the dump example from above becomes: from="dumphost.my.domain",command="ufsdump 0f - /",no-pty,no-port forwarding,no-X11-forwarding,no-agent-forwarding Port ForwardingAnother extremely useful feature of ssh is port forwarding, sometimes called tunneling. The first type of port forwarding, local forwarding (- L), allows the local user to create a secure connection to an insecure port on the remote end. The connection is then referenced as being on the local machine. This example sets up a tunneled connection between port 1234 on the local machine and port 80 on the web server, www.my.domain. The -N flag indicates that an interactive ssh session is unwanted. When running port forwarding commands for an extended period of time, background the job so the callingshell is still usable. Authentication is required from the calling user if you're not using an agent: ssh -N -L 1234:localhost:80 www.my.domainA tunneled connection now exists from localhost on port 1234 to www.my.domainon port 22, encrypting all traffic. ssh on port 22 on the web server decrypts the data and sends it to port 80 on the web server. To actually access the web server over the encrypted connection, the web browser on the local machine opens the URL http://localhost:1234/. When doing port forwarding, OpenSSH only listens to the loopback interface (127.0.0.1) by default. Any connection to port 1234 from another machine in the above example would be refused because the tunnel is only listening on port 1234 of localhost. Using the -g flag specifies that the tunnel on the local machine can be used by a third party. Three machines are involved in this example, hostA.my.domain, hostB.my.domain, and www.my.domain. In the initial local port forwarding example, hostA would have been localhost and hostB would not have been involved. With local port forwarding that hostB can connect to, our command line on hostA looks like: ssh -N -g -L 1234:localhost:80 www.my.domainOn hostB, the user opens the URL http://hostA.my.domain:1234/ and receives data from the web server on www.my.domain. The data is only encrypted between www.my.domain and hostA.my.domain, and not between hostA.my.domain and hostB.my.domain. This sort of setup commonly occurs where hostA.my.domain is a gateway between a trusted and an untrusted network, e.g. hostA.my.domain has connections to both the internal network and the DMZ (or Internet). The second type of port forwarding, remote forwarding (-R), is similar to local port forwarding, but the directions are reversed. In this case, the connection is initiated from the remote machine to a local service. For this example, www.my.domain is the "local" machine, and many users on hostA want to make secure connections to the web server on www.my.domain. It's impractical for each of them to set up their own tunnel each time they log in, when one persistent tunnel can be specified from the server side (though root can set up a persistent tunnel from hostA.my.domain to www.my.domain using local port forwarding on hostA.my.domain, as well; it's a matter of preference). Run the following command on www.my.domain: ssh -N -R 1234:localhost:80 hostA.my.domainNow users on hostA.my.domain can open the URL http://localhost:1234/ and receive encrypted data from http://www.my.domain/. To make this even more seamless for the users, set up port 80 on hostA.my.domain to forward to port 80 on www.my.domain. To do this, root must be able to establish ssh connections from www.my.domain to hostA.my.domain because the connection port is below 1024. For this, PermitRootLogin must be set to yes in the sshd_config file on hostA.my.domain. From www.my.domain, run: ssh -l root -N -R 80:localhost:80 hostA.my.domainNow users can open the URL http://localhost/ without specifying the port number. The most common uses for port forwarding include SMTP, IMAP, web browsing, and any other unencrypted service that passes potentially sensitive data over an insecure connection. In addition, any tools that used to use rsh can now be wrapped to use ssh to connect between machines if they can't use direct ssh connections. SSH From CronExtensive automation indicates well-run systems, and much of that automation tends to happen via cron. Sys admins generally run into three issues when using ssh from cron: (1) ssh complains about not having a controlling tty, (2) sometimes a pipe is confused when ssh reports errors to STDOUT, and (3) there's no human to type in the passphrase. Here are some suggested solutions to those issues. Adding the following to the cron script usually eliminates the first two issues, controlling tty and STDOUT issues: ssh -q -o 'BatchMode yes'It may also be advantageous to specify no-pty in the authorized_keys file on the remote machine. The third issue, how to enter the passphrase, is a bit trickier and has two answers (both assume the use of public key encryption on the remote host). Keys can be set up with no passphrase, or an agent can be run at boot time and the resulting environment information can be saved off to a file for later use. Creating keys without passphrases is easy but not very secure. Anyone who gains access to the private key can log in to the remote machine with no additional information required. If ssh is invoked from cron with passphrase-less keys, at least restrict the remote end with the 'from' and 'command' keywords and all of the forwarding restricting keywords. The following creates the DSA key pair $HOME/.ssh/nopasskey and $HOME/.ssh/nopasskey.pub with no passphrase: ssh-keygen -t dsa -N '' -f $HOME/.ssh/nopasskeyThe second method, using an agent, is much more secure. If the machine reboots, though, the keys must be entered manually or the cron jobs will fail. To use an agent, the environment variables ssh-agent sets at invocation time must be stored in a file. Instead of using eval (as with interactive sessions) ssh-agent is called directly. This example assumes that root is running the agent and that /root/tmp exists and is mode 700. A special key called cron-key is the only one that the agent controls in this example, but multiple keys can be added if desired. For information on creating multiple keys, see the previous article in this series. This example also shows Bourne style syntax instead of csh style syntax since the root account generally has a Bourne style shell, and any shell scripts should be written in Bourne shell or one of its derivatives. ssh-agent -s | head -2 > /root/tmp/ssh-agent-info ssh-add /root/.ssh/cron-keyThe agent environment variables can now be read in by any scripts with the following Bourne shell code: Debugging When Things Go WrongWhen experimenting with options and configuration settings, something unexpected is bound to happen at some point. The first step to tracking down the issue is running the client side program in verbose mode using the -v switch. This prints out the client's side of the conversation with the server, and can often reveal things like incorrect permissions on files, missing files, or missing authentication types in the configuration file on the server. If there are no obvious errors, turning on debugging on the server end is the next logical course of action. Stop the running ssh daemon (the pid can usually be found in /var/run/sshd.pid), and start a new one with the -d flag: kill `cat /var/run/sshd.pid` && sshd -dThis will keep sshd from forking into the background, and only one new session will be able to connect. The verbosity of the debug mode can be increased by specifying multiple -d flags on the command line with a maximum of three. Watch the verbose output of both the client and the server during a failed connection. Comparing this output with a session that does work is also useful. If the problem is still not apparent, try using truss on one or both ends of the connection to see if there are issues such as missing libraries or stuck system calls. Even if the problem can't be immediately determined from the output of truss, posting that and the debugging information from the client and server to the OpenSSH mailing lists or newsgroup may result in a solution. ResourcesFor further information about OpenSSH and the secure shell provided with the Solaris OS, visit the Resources section at the end of Part One (Secure Shell: Part 1) of this series.
Unless otherwise licensed, code in all technical manuals herein (including articles, FAQs, samples) is provided under this License. |
|