|
Softpanorama |
May the source be with you, but remember the KISS principle ;-)
|
SSH can be used to secure connections between systems. It allows application traffic such as that generated by TELNET, FTP POP3, or even X Windows to be both encrypted and compressed. Compression is useful over slow modem links. Implementations can allow the user a choice of encryption methods. Client software often offers both SSH1 and SSH2 support. The user is authenticated by password or public/private key.
The SSH suite includes the ssh program which replaces rlogin and telnet, scp which replaces rcp, and sftp which replaces ftp. Also included is sshd which is the server side of the package, and the other basic utilities like ssh-add, ssh-agent, ssh-keysign, ssh-keyscan, ssh-keygen and sftp-server. OpenSSH supports SSH protocol versions 1.3, 1.5, and 2.0.OpenSSH is primarily developed by the OpenBSD Project, and its first inclusion into an operating system was in OpenBSD 2.6. The software is developed outside the USA, using code from roughly 10 countries, and is freely useable and re-useable by everyone under a BSD license.
Managing the distribution of OpenSSH is split into two teams. One team does strictly OpenBSD-based development, aiming to produce code that is as clean, simple, and secure as possible. We believe that simplicity without the portability "goop" allows for better code quality control and easier review. The other team then takes the clean version and makes it portable, by adding the portability "goop" so that it will run on many operating systems (these are known as the p releases, and named like "OpenSSH 4.0p1"). Please click on the provided link for your operating system.
ssh (Secure Shell) is a program for logging into a remote machine and for executing commands on a remote machine. It is intended to replace rlogin and rsh, and to provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel.
ssh connects and logs into the specified hostname. The user must prove his or her identity to the remote machine using one of several methods depending on the protocol version used:
First, if the machine the user logs in from is listed in /etc/hosts.equiv or /etc/shosts.equiv on the remote machine, and the user names are the same on both sides, the user is immediately permitted to log in. Second, if .rhosts or .shosts exists in the user's home directory on the remote machine and contains a line containing the name of the client machine and the name of the user on that machine, the user is permitted to log in. This form of authentication alone is normally not allowed by the server because it is not secure.
The second (and primary) authentication method is the rhosts or hosts.equiv method combined with RSA-based host authentication. It means that if the login would be permitted by $HOME/.rhosts, $HOME/.shosts, /etc/hosts.equiv, or /etc/shosts.equiv, and if additionally the server can verify the client's host key (see /etc/ssh_known_hosts in the FILES section), only then is login permitted. This authentication method closes security holes due to IP spoofing, DNS spoofing, and routing spoofing.
Note to the administrator: /etc/hosts.equiv, $HOME/.rhosts, and the rlogin/rsh protocol in general, are inherently insecure and should be disabled if security is desired.
As a third authentication method, ssh supports RSA-based authentication. The scheme is based on public-key cryptography. There are cryptosystems where encryption and decryption are done using separate keys, and it is not possible to derive the decryption key from the encryption key. RSA is one such system. The idea is that each user creates a public/private key pair for authentication purposes. The server knows the public key, and only the user knows the private key. The file $HOME/.ssh/authorized_keys lists the public keys that are permitted for logging in. When the user logs in, the ssh program tells the server which key pair it would like to use for authentication. The server checks if this key is permitted, and if so, sends the user (actually the ssh program running on behalf of the user) a challenge in the form of a random number, encrypted by the user's public key. The challenge can only be decrypted using the proper private key. The user's client then decrypts the challenge using the private key, proving that he or she knows the private key but without disclosing it to the server.
ssh implements the RSA authentication protocol automatically. The user creates his or her RSA key pair by running ssh-keygen(1). This stores the private key in $HOME/.ssh/identity and the public key in $HOME/.ssh/identity.pub in the user's home directory. The user should then copy the identity.pub to $HOME/.ssh/authorized_keys in his or her home directory on the remote machine (the authorized_keys file corresponds to the conventional $HOME/.rhosts file, and has one key per line, though the lines can be very long). After this, the user can log in without giving the password. RSA authentication is much more secure than rhosts authentication.
The most convenient way to use RSA authentication may be with an authentication agent. See ssh-agent(1) for more information.
If other authentication methods fail, ssh prompts the user for a password. The password is sent to the remote host for checking. However, since all communications are encrypted, the password cannot be seen by someone listening on the network.
When a user connects using the protocol version 2, different authentication methods are available. At first, the client attempts to authenticate using the public key method. If this method fails, password authentication is tried.
The public key method is similar to RSA authentication described in the previous section except that the DSA algorithm is used instead of the patented RSA algorithm. The client uses his private DSA key $HOME/.ssh/id_dsa to sign the session identifier and sends the result to the server. The server checks whether the matching public key is listed in $HOME/.ssh/authorized_keys and grants access if both the key is found and the signature is correct. The session identifier is derived from a shared Diffie-Hellman value and is known only to the client and the server.
If public key authentication fails or is not available, a password can be sent encrypted to the remote host for proving the user's identity. This protocol 2 implementation does not yet support Kerberos or S/Key authentication.
Protocol 2 provides additional mechanisms for confidentiality (the traffic is encrypted using 3DES, Blowfish, CAST128 or Arcfour) and integrity (hmac-sha1, hmac-md5). Notice that protocol 1 lacks a strong mechanism for ensuring the integrity of the connection.
When the user's identity has been accepted by the server, the server either executes the given command, or logs into the machine and gives the user a normal shell on the remote machine. All communication with the remote command or shell will be automatically encrypted.
If a pseudo-terminal has been allocated (normal login session), the user can disconnect with ~., and suspend ssh with ~^Z. All forwarded connections can be listed with ~#. If the session blocks waiting for forwarded X11 or TCP/IP connections to terminate, ssh can be backgrounded with ~&, although this should not be used while the user shell is active, as it can cause the shell to hang. All available escapes can be listed with ~?.
A single tilde character can be sent as ~~ (or by following the tilde by a character other than those described above). The escape character must always follow a newline to be interpreted as special. The escape character can be changed in configuration files or on the command line.
If no pseudo tty has been allocated, the session is transparent and can be used to reliably transfer binary data. On most systems, setting the escape character to “none” will also make the session transparent even if a tty is used.
The session terminates when the command or shell in the remote machine exits and all X11 and TCP/IP connections have been closed. The exit status of the remote program is returned as the exit status of ssh.
If the user is using X11 (the DISPLAY environment variable is set), the connection to the X11 display is automatically forwarded to the remote side in such a way that any X11 programs started from the shell (or command) will go through the encrypted channel, and the connection to the real X server will be made from the local machine. The user should not manually set DISPLAY. Forwarding of X11 connections can be configured on the command line or in configuration files.
The DISPLAY value set by ssh will point to the server machine, but with a display number greater than zero. This is normal behavior, because ssh creates a “proxy” X server on the server machine for forwarding the connections over the encrypted channel.
ssh will also automatically set up Xauthority data on the server machine. For this purpose, it will generate a random authorization cookie, store it in Xauthority on the server, and verify that any forwarded connections carry this cookie and replace it by the real cookie when the connection is opened. The real authentication cookie is never sent to the server machine (and no cookies are sent in the plain).
If the user is using an authentication agent, the connection to the agent is automatically forwarded to the remote side unless disabled on the command line or in a configuration file.
Forwarding of arbitrary TCP/IP connections over the secure channel can be specified either on the command line or in a configuration file. One possible application of TCP/IP forwarding is a secure connection to an electronic purse. Another possible application is going through firewalls.
ssh automatically maintains and checks a database containing identifications for all hosts it has ever been used with. RSA host keys are stored in $HOME/.ssh/known_hosts in the user's home directory. Additionally, the file /etc/ssh_known_hosts is automatically checked for known hosts. Any new hosts are automatically added to the user's file. If a host's identification ever changes, ssh warns about this and disables password authentication to prevent a trojan horse from getting the user's password. Another purpose of this mechanism is to prevent man-in-the-middle attacks which could otherwise be used to circumvent the encryption. The StrictHostKeyChecking option (see below) can be used to prevent logins to machines whose host key is not known or has changed.
The following options are supported:
| ssh -n shadows.cs.hut.fi emacs & |
will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically forwarded over an encrypted channel. The ssh program will be put in the background. This does not work if ssh needs to ask for a password or passphrase. See also the -f option.)
ssh will normally set the following environment variables:
Additionally, ssh reads $HOME/.ssh/environment and adds lines of the format VARNAME=value to the environment
The following exit values are returned:
The canonical system name (as returned by name servers)
is used by sshd(1M) to verify the client host when logging in. Other names
are needed because ssh does not convert the user-supplied name
to a canonical name before checking the key, to prevent someone with access
to the name servers from being able able to fool host authentication.
Notice that,
by default, sshd(1M) will be installed so that it requires successful RSA
host authentication before permitting .rhosts authentication. If
your server machine does not have the client's host key in /etc/ssh_known_hosts,
you can store it in $HOME/.ssh/known_hosts. The easiest way to
do this is to connect back to the client from the server machine using
ssh. This will automatically add the host key to $HOME/.ssh/known_hosts.
The scp utility copies files between hosts on a network. It uses ssh(1) for data transfer, and uses the same authentication and provides the same security as ssh(1). Unlike rcp(1), scp will ask for passwords or passphrases if they are needed for authentication.
Any file name may contain a host and user specification to indicate that the file is to be copied to/from that host. Copies between two remote hosts are permitted.
The following options are supported:
The following operands are supported:
The following exit values are returned:
See attributes(5) for descriptions of the following attributes:
| ATTRIBUTE TYPE | ATTRIBUTE VALUE |
|---|---|
| Availability | SUNWsshu |
rcp(1), ssh(1), ssh-add(1), ssh-agent(1), ssh-keygen(1), sshd(1M), attributes(5)
To view license terms, attribution, and copyright for OpenSSH, the default path is /var/sadm/pkg/SUNWsshdr/install/copyright. If the Solaris operating environment has been installed anywhere other than the default, modify the given path to access the file at the installed location.
scp is based on the rcp(1) program in the BSD source code from the Regents of the University of California. The authors are Timo Rinne and Tatu Ylonen.
The clients ssh and scp are quite configurable, with many settings that can be changed to suit your whim. If you want to modify the behavior of these clients, three general techniques are at your disposal:
We now present a general overview of these three methods.
Several client features are controlled by environment variables. For example, scp prints statistics about each file it transfers if the environment variable SSH_ALL_SCP_STATS is set. Environment variables may be set in your current shell by the standard methods:
# C shell family (csh, tcsh) $ setenv SSH_ALL_SCP_STATS 1 # Bourne shell family (sh, ksh, bash) $ SSH_ALL_SCP_STATS=1 $ export SSH_ALL_SCP_STATS
Alternatively, environment variables and values may be specified in a file. System administrators can set environment variables for all users in /etc/environment, and users can set them in ~/.ssh/environment (SSH1, OpenSSH) and ~/.ssh2/environment (SSH2). These files contain lines of the format:
NAME=VALUE
where NAME is the name of an environment variable, and VALUE is its value. The value is taken literally, read from the equals sign to the end of the line. Don't enclose the value in quotes, even if it contains whitespace, unless you want the quotes to be part of the value.
Command-line options let you change a client's behavior just once at invocation. For example, if you're using ssh1 over a slow modem connection, you can tell SSH1 to compress the data with the -C command-line option:
$ ssh1 -C server.example.com
ssh, scp, and most of their support programs, when invoked with the -h option, print a help message listing all of their command-line options. For example:
# SSH1, SSH2 $ ssh -h $ ssh-keygen2 -h
If you don't want to retype command-line options continually, configuration files let you change a client's behavior now and in the future, until you change the configuration file again. For example, you can enable compression for all clients you invoke by inserting this line into a client configuration file:
Compression yes
In a client configuration file, client settings are changed by specifying keywords and values.
In the example, the keyword is Compression and the value is yes. In SSH1 and OpenSSH, you may optionally separate the keyword and value with an equals sign:
Compression = yes
SSH2 doesn't support this syntax, however, so it can be easier always to use the "keyword <space> value" format to avoid confusion.
You may configure clients to behave differently for each remote host you visit. This can be done on the fly with command-line options, but for anything reasonably complex, you'll end up typing long, inconvenient command lines like:
$ ssh1 -a -p 220 -c blowfish -l sally -i myself server.example.com
Alternatively, you can set these options within a configuration file. The following entry duplicates the function of the command-line options above, collecting them under the name "myserver":
# SSH2, OpenSSH Host myserver ForwardAgent no Port 220 Cipher blowfish User sally IdentityFile myself HostName server.example.com
To run a client with these options enabled, simply type:
$ ssh1 myserver
Configuration files take some time to set up, but in the long run they are significant timesavers.
We've given you a peek at the structure of a configuration file: a Host specification, followed by a bunch of keyword/value pairs. In the coming sections, we continue this philosophy, defining the structure and general rules before explaining the meanings of keywords. Once the generalities are covered, we'll dive into specific keywords. Sound good? Let's go.
As we cover the many configuration keywords, note that all can be supplied on the command line if desired. The -o command-line option exists for this purpose. For any configuration line of the form:
Keyword Value
you may type:
SSH1 and OpenSSH allow use of the equals sign (=) between the keyword and value, which allows you to omit the quotes on the command line: ssh -o Keyword=Value.
# SSH1, SSH2, OpenSSH $ ssh -o "Keyword Value" ...
For example, the configuration lines:
User sally Port 220
# SSH1, SSH2, OpenSSH $ ssh -o "User sally" -o "Port 220" server.example.com
SSH1 additionally permits an equals sign between the keyword and the value:
$ ssh1 -o User=sally -o Port=220 server.example.com
This example shows that the -o option may be specified multiple times on the command line. The option also works for scp in SSH1 and OpenSSH:
# SSH1, OpenSSH $ scp -o "User sally" -o "Port 220" myfile server.example.com:
Another relationship between command-line options and configuration keywords is found in the -F option (SSH2 only). This option instructs an SSH2 client to use a different configuration file instead of ~/.ssh2/ssh2_config. For example:
$ ssh2 -F ~/.ssh2/other_config
Unfortunately there's no equivalent option for SSH1 or OpenSSH clients.
Client configuration files come in two flavors. A single, global client configuration file, usually created by a system administrator, governs client behavior for an entire computer. The file is traditionally /etc/ssh_config (SSH1, OpenSSH) or /etc/ssh2/ssh2_config (SSH2). (Don't confuse these with the server configuration files in the same directories.) Each user may also create a local client configuration file within his or her account, usually ~/.ssh/config (SSH1, OpenSSH) or ~/.ssh2/ssh2_config (SSH2). This file controls the behavior of clients run in the user's login session.
Note: The system administrator may change the locations of client configuration files, using the compile-time flag --with-etcdir or the serverwide keyword UserConfigDirectory.
Values in a user's local file take precedence over those in the global file. For instance, if the global file turns on data compression, and your local file turns it off, the local file wins for clients run in your account. We cover precedence in more detail soon.
Client configuration files are divided into sections. Each section contains settings for one remote host or for a set of related remote hosts, such as all hosts in a given domain.
The beginning of a section is marked differently in different SSH implementations. For SSH1 and OpenSSH, the keyword Host begins a new section, followed by a string called a host specification. The string may be a hostname:
Host server.example.com
an IP address:
Host 123.61.4.10
a nickname for a host:
Host my-nicknameor a wildcard pattern representing a set of hosts, where ? matches any single character and * any sequence of characters (just like filename wildcards in your favorite Unix shell):
Host *.example.com Host 128.220.19.*
Some further examples of wildcards:
Host *.edu Any hostname in the edu domain Host a* Any hostname whose name begins with "a" Host *1* Any hostname (or IP address!) with 1 in it Host * Any hostname or IP address
For SSH2, a new section is marked by a host specification string followed by a colon. The string, like the argument of Host, may be a computer name:
server.example.com:
an IP address:
123.61.4.10:
a nickname:
my-nickname:
or a wildcard pattern:
*.example.com: 128.220.19.*:
Following the host-specification line are one or more settings, i.e., configuration keywords and values, as in the example we saw earlier. The following table contrasts SSH1 and SSH2 configuration files:
|
SSH1, OpenSSH |
SSH2 |
|---|---|
Host myserver User sally IdentityFile myself ForwardAgent no Port 220 Cipher blowfish |
myserver: User sally IdentityFile myself ForwardAgent no Port 220 Ciphers blowfish |
The settings apply to the hosts named in the host specification. The section ends at the next host specification or the end of the file, whichever comes first.
Because wildcards are permitted in host specifications, a single hostname might match two or more sections in the configuration file. For example, if one section begins:
Host *.edu
and another begins:
Host *.harvard.edu
and you connect to server.harvard.edu, which section applies? Believe it or not, they both do. Every matching section applies, and if a keyword is set more than once with different values, the earliest value takes precedence.
Suppose your client configuration file contains two sections to control data compression, password authentication, and password prompting:
Host *.edu Compression yes PasswordAuthentication yes Host *.harvard.edu Compression no PasswordPromptLogin no
and you connect to server.harvard.edu:
$ ssh server.harvard.edu
Notice that the string server.harvard.edu matches both Host patterns, *.edu and *.harvard.edu. As we've said, the keywords in both sections apply to your connection. Therefore, the previous ssh command sets values for keywords Compression, PasswordAuthentication, and PasswordPromptLogin.
But notice, in the example, that the two sections set different values for Compression. What happens? The rule is that the first value prevails, in this case, yes. So in the previous example, the values used for server.harvard.edu are:
Compression yes The first of the Compression lines PasswordAuthentication yes Unique to first section PasswordPromptLogin no Unique to second section

While this feature might seem confusing, it has useful properties. Suppose you want some settings applied to all remote hosts. Simply create a section beginning with:
Host *
and place the common settings within it. This section should be either the first or the last in the file. If first, its settings take precedence over any others. This can be used to guard against your own errors. For example, if you want to make sure you never, ever, accidentally configure SSH sessions to fall back to the insecure rsh protocol, at the beginning of your configuration file put:
# First section of file Host * FallBackToRsh no
Alternatively, if you place Host * as the last section in the configuration file, its settings are used only if no other section overrides them. This is useful for changing SSH's default behavior, while still permitting overrides. For example, by default, data compression is disabled. You can make it enabled by default by ending your configuration file with:
# Last section of file Host * Compression yes
Voilá, you have changed the default behavior of ssh and scp for your account! Any other section, earlier in the configuration file, can override this default simply by setting Compression to no.
Suppose your client configuration file contains a section for the remote host myserver.example.com :
Host myserver.example.com ...
One day, while logged onto ourclient.example.com, you decide to establish an SSH connection to myserver.example.com. Since both computers are in the same domain, example.com, you can omit the domain name on the command line and simply type:
$ ssh myserver
You can get around this limitation by declaring myserver to be a nickname for myserver.example.com. In SSH1 and OpenSSH, this is done with the Host and HostName keywords. Simply use Host with the nickname and HostName with the fully qualified hostname:
# SSH1, OpenSSH Host myserver HostName myserver.example.com ...
ssh will now recognize that this section applies to your command ssh myserver. You may define any nickname you like for a given computer, even if it isn't related to the original hostname:
# SSH1, OpenSSH Host simple HostName myserver.example.com ...
Then you can use the nickname on the command line:
$ ssh1 simple
For SSH2, the syntax is different but the effect is the same. Use the nickname in the host specification, and provide the full name to the Host keyword:
# SSH2 only simple: Host myserver.example.com ...
Then type:
$ ssh2 simple
Host server.example.com ...
and you want to experiment with different settings. You can just modify the settings in place, but if they don't work, you have to waste time changing them back. The following steps demonstrate a more convenient way:
Within the configuration file, make a copy of the section you want to change:
# Original Host server.example.com ... # Copy for testing Host server.example.com ...
In the copy, change "Host" to "HostName":
# Original Host server.example.com ... # Copy for testing HostName server.example.com ...
Add a new Host line at the beginning of the copy, using a phony name, for example, "Host my-test":
# Original Host server.example.com ... # Copy for testing Host my-test HostName server.example.com ...
Setup is done. In the copy (my-test), make all the changes you want and connect using ssh my-test. You can conveniently compare the old and new behavior by running ssh server.example.com versus ssh my-test. If you decide against the changes, simply delete the my-test section. If you like the changes, copy them to the original section (or delete the original and keep the copy).
You can do the same with SSH2:
# Original server.example.com: ... # Copy for testing my-test: Host server.example.com ...
You probably noticed in the previous examples that we are using the # symbol to represent comments:
# This is a comment
In fact, any line beginning with # in the configuration file is treated as a comment and ignored. Likewise, blank lines (empty or containing only whitespace) are also ignored.
You might also have noticed that the lines following a host specification are indented:
# SSH1, OpenSSH Host server.example.com Keyword1 value1 Keyword2 value2 # SSH2 only server.example.com: Keyword1 value1 Keyword2 value2
Forwarding is a type of interaction with another network application, as shown in Figure below.

SSH intercepts a service request from some other program on one side of an SSH connection, sends it across the encrypted connection, and delivers it to the intended recipient on the other side. This process is mostly transparent to both sides of the connection: each believes it is talking directly to its partner and has no knowledge that forwarding is taking place. Even more powerfully, SSH forwarding can achieve certain types of communication that are impossible without it.
Forwarding isn't a new concept. The basic operation of a terminal connection over a network (say, using telnet) is also a kind of forwarding. In a telnet connection, you sit on one end, your remote shell is on the other, and both sides operate as if directly connected by a serial cable. Nevertheless, sitting in the middle is a cooperating telnet client and server, forwarding bytes back and forth. SSH forwarding is much the same, except SSH plays fancy tricks with the data to add security.
We have also seen another type of SSH forwarding, agent forwarding. This let us create SSH connections from one computer, through a second computer, and onto a third using public-key authentication, but without installing our private key on the second machine. To accomplish this, an SSH server pretended to be an SSH agent, while transparently forwarding data to and from a remote agent. This paradigm holds true for TCP port forwarding and X forwarding, as the SSH server transparently masquerades as another network application
SSH uses TCP/IP as its transport mechanism, usually TCP port 22 on the server machine, as it encrypts and decrypts the traffic passing over the connection. We will now discuss a cool feature that encrypts and decrypts TCP/IP traffic belonging to other applications, on other TCP ports, using SSH. This process, called port forwarding, is largely transparent and quite powerful. Telnet, SMTP, NNTP, IMAP, and other insecure protocols running over TCP can be made secure by forwarding. IMAP server machine must be running an SSH server for port forwarding to provide real protection.
Our port forwarding example protects your IMAP connection but doesn't truly protect your email messages. Before reaching your IMAP server, the messages pass through other mail servers and may be intercepted in transit. For end-to-end email security, you and your correspondent should use tools such as PGP or S/MIME to sign and/or encrypt the messages themselves.
In short, with minimal configuration changes to your programs, SSH port forwarding protects arbitrary TCP/IP connections by redirecting them through an SSH session. Port forwarding can even pass a connection safely through a firewall if you configure things properly. Once you start securing your communications with port forwarding, you'll wonder how you ever got along without it. Here are examples of what you can do:
Access various kinds of TCP servers (e.g., SMTP, IMAP, POP, LDAP, etc.) across a firewall that prevents direct access.
Provide protection for your sessions with these same TCP servers, preventing disclosure or alteration of passwords and other content that would otherwise be sent in the clear as part of the session.
Tunnel the control connection of an FTP session, to encrypt your username, password, and commands. (It isn't usually possible to protect the data channels that carry the file contents, though. )
Use your ISP's SMTP servers for sending mail, even if you're connected outside the ISP's network and the ISP forbids mail relaying from your current location.
SSH port forwarding is a general proxying mechanism for TCP only. (See Sidebar "TCP Connections" for an overview of TCP concepts.) Forwarding can't work with protocols not built on TCP, such as the UDP-based DNS, DHCP, NFS, and NetBIOS, or with non-IP-based protocols, such as AppleTalk or Novell's SPX/IPX. We're being a little imprecise here. DHCP is entirely based on UDP, so SSH port forwarding can't do anything with it. The others, however, either use both TCP and UDP for different purposes or can sometimes be configured to run over TCP, though they generally use UDP. Nevertheless, in most common situations, SSH can't forward them.
In our earlier example, we had an IMAP server running on machine S, and an email reader on home machine H, and we wanted to secure the IMAP connection using SSH. Let's delve into that example in more detail.
IMAP uses TCP port 143; this means that an IMAP server will be listening for connections on port 143 on the server machine. To tunnel the IMAP connection through SSH, you need to pick a local port on home machine H (between 1024 and 65535) and forward it to the remote socket (S,143). Suppose you randomly pick local port 2001. The following command then creates the tunnel:
[3] You can also use ssh -L2001:S:143 S, substituting "S" for localhost, but we will discuss later why localhost is the better alternative when possible.
$ ssh -L2001:localhost:143 S
The -L option specifies local forwarding, in which the TCP client is on the local machine with the SSH client. The option is followed by three values separated by colons: a local port to listen on (2001), the remote machine name or IP address (S), and the remote, target port number (143).
The previous command logs you into S, as it will if you just type ssh S. However, this SSH session has also forwarded TCP port 2001 on H to port 143 on S; the forwarding remains in effect until you log out of the session. To make use of the tunnel, the final step is to tell your email reader to use the forwarded port. Normally your email program connects to port 143 on the server machine, that is, the socket (S,143). Instead, it's configured to connect to port 2001 on home machine H itself, i.e., socket (localhost,2001). So the path of the connection is now as follows:
The email reader on home machine H sends data to local port 2001.
The local SSH client on H reads port 2001, encrypts the data, and sends it through the SSH connection to the SSH server on S.
The SSH server on S decrypts the data and sends it to the IMAP server listening on port 143 on S.
Data is sent back from the IMAP server to home machine H by the same process in reverse.
Port forwarding can be specified only when you create an SSH connection. You can't add a forwarding to an existing SSH connection with any SSH implementation we know of, though there's nothing intrinsic to the SSH protocol that would prevent it, and it would sometimes be a useful feature. Instead of using the -L option to establish a local forwarding, you can use the LocalForward keyword in your client configuration file:
# SSH1, OpenSSH LocalForward 2001 localhost:143 # SSH2 only LocalForward "2001:localhost:143"
Note the small syntactic differences. In SSH1 and OpenSSH, there are two arguments: the local port number, and the remote socket expressed as host:port. In SSH2, the expression is just as on the command line, except that it must be enclosed in double quotes. If you forget the quotes, ssh2 doesn't complain, but it doesn't forward the port, either.
Our example with home machine H and IMAP server S can be set up like this:
# SSH1, OpenSSH Host local-forwarding-example HostName S LocalForward 2001 localhost:143 # Run on home machine H $ ssh local-forwarding-example
In SSH1 and OpenSSH, by default, only the host running the SSH client can connect to locally forwarded ports. This is because ssh listens only on the machine's loopback interface for connections to the forwarded port; that is, it binds the socket (localhost,2001), a.k.a. (127.0.0.1,2001), and not (H,2001). So, in the preceding example, only machine H can use the forwarding; attempts by other machines to connect to (H,2001) get "connection refused." However, ssh for SSH1 and OpenSSH has a command-line option, -g, that disables this restriction, permitting any host to connect to locally forwarded ports:
# SSH1, OpenSSH $ ssh1 -g -L<localport>:<remotehost>:<remoteport> hostname
The client configuration keyword GatewayPorts also controls this feature; the default value is no, and giving GatewayPorts=yes does the same thing as -g:
# SSH1, OpenSSH GatewayPorts yes
There's a reason why GatewayPorts and -g are disabled by default: they represent a security risk.
A remotely forwarded port is just like a local one, but the directions are reversed. This time the TCP client is remote, its server is local, and a forwarded connection is initiated from the remote machine.
Continuing with our example, suppose instead that you are logged into server machine S to begin with, where the IMAP server is running. You can now create a secure tunnel for remote clients to reach the IMAP server on port 143. Once again, you select a random port number to forward (say, 2001 again) and create the tunnel:
$ ssh -R2001:localhost:143 H
The -R option specifies remote forwarding. It is followed by three values, separated by colons as before but interpreted slightly differently. The remote port to be forwarded (2001) is now first, followed by the machine name or IP address (localhost) and port number (143). SSH can now forward connections from (localhost,143) to (H,2001).
Once this command has run, a secure tunnel has been constructed from the port 2001 on the remote machine H, to port 143 on the server machine S. Now any program on H can use the secure tunnel by connecting to (localhost,2001). As before, the command also runs an SSH terminal session on remote machine H, just as ssh H does.
As with local forwarding, you may establish a remote forwarding using a keyword in your client configuration file. The RemoteForward keyword is analogous to LocalForward, with the same syntactic differences between SSH1 and SSH2:
# SSH1, OpenSSH RemoteForward 2001 S:143 # SSH2 only RemoteForward "2001:S:143"
For example, here's the preceding forwarding defined in an SSH2-format configuration file:
# SSH2 only remote-forwarding-example: Host H RemoteForward "2001:S:143" $ ssh2 remote-forwarding-example
You might think that the GatewayPorts feature discussed in the last section applies equally well to remote port forwardings. This would make sense as a feature, but as it happens, it isn't done. There would have to be a way for the client to communicate this parameter to the server for a given forwarding, and that feature hasn't been included in the SSH protocol. In SSH1 and SSH2, remotely forwarded ports always listen on all network interfaces and accept connections from anywhere. The OpenSSH server does accept the GatewayPorts configuration option, and it applies globally to all remote forwardings established by that server.
If you use LocalForward or RemoteForward in your configuration file, you might run into a subtle problem. Suppose you have set up a section in your configuration file to forward local port 2001 to an IMAP server:
# SSH1 syntax used for illustration Host server.example.com LocalForward 2001 server.example.com:143
This configuration works fine if you connect once:
$ ssh server.example.com
$ ssh server.example.com Local: bind: Address already in use
Why does this happen? Because your configuration file section tries to forward port 2001 again but finds that port is already in use ("bound" for listening) by the first instance of ssh. You need some way to make the connection but omit the port forwarding.
SSH1 (but not OpenSSH) provides a solution, the client configuration keyword ClearAllForwardings. From the name, you might think it terminates existing forwardings, but it doesn't. Rather, it nullifies any forwardings specified in the current ssh command. In the previous example, you can connect without forwardings to server.example.com with:
# SSH1 only $ ssh1 -o ClearAllForwardings=yes server.example.com
The original tunnel, set up by the first invocation, continues to exist, but ClearAllForwardings prevents the second invocation from attempting to recreate the tunnel. To illustrate the point further, here's a rather silly command:
$ ssh1 -L2001:localhost:143 -o ClearAllForwardings=yes mymachine
The -L option specifies a forwarding, but ClearAllForwardings cancels it. This silly command is identical in function to:
$ ssh1 mymachine
ClearAllForwardings may also be placed in your client configuration file, of course. It seems more useful on the command line, however, where it can be used on the fly without editing a file.
The differences between local and remote forwarding can be subtle. It can get a bit confusing to know which kind of forwarding to use in a given situation. The quick rule is look for the TCP client application.
|
The rest of this section is devoted to dissecting the forwarding process in detail and understanding where this rule comes from.
Local and remote forwarding can be confusing because of overloaded terminology. In a given port forwarding situation, there are two clients and two servers lying around. We have the SSH client and server programs (e.g., ssh and sshd ), plus the TCP application's client and server programs whose connection you want to protect by port forwarding.
An SSH session has a direction of establishment. That is, you run an SSH client on one machine, and it initiates a session with an SSH server on another. Likewise, a forwarded connection has a direction of establishment: you run an application client on one machine, and it initiates a session with a service on another. These two directions may or may not match. This is the difference between local and remote forwarding. Let's introduce some terminology and provide some diagrams to make sense of this.
To begin with, we have an application client and server running on two hosts, A and B. The application server is listening on a well-known port W for incoming client connections. Without SSH, you can tell the application client that its server is on host B, port W. The client makes a direct connection to the server, and all application protocol data go in the clear over the network:
To protect the application protocol data by forwarding, you establish an SSH session between these two hosts. When setting up the SSH session, you select an unused port number P on the application client side (host A), and request SSH port forwarding from the socket (A,P) to the socket (B,W). Once the session is established, the SSH process on A is listening for incoming TCP connection requests on port P. Tell the application client that its server is on (A,P) instead of (B,W), and the stage is now set for port forwarding.
There are now two cooperating SSH processes with an established, encrypted SSH session between them; you don't yet distinguish between the SSH client and server. Inside that session, SSH creates multiple channels, or logical streams for carrying data. It uses channels to carry and distinguish the input, output, and error streams for an interactive login or remote command run via SSH, and similarly creates a new channel for each use of a port forwarding, to carry the forwarded data inside the protected SSH session.
As Figure above shows that now, when the application client tries to connect to its server, it connects instead to the listening SSH process (1). The SSH listener notices this and accepts the connection. It then notifies its partner SSH process that a new instance of this port forwarding is starting up, and they cooperate to establish a new channel for carrying the data for this forwarding instance (2). Finally, the partner SSH process initiates a TCP connection to the target of the port forwarding: the application server listening on (B,W) (3). Once this connection succeeds, the port forwarding instance is in place. The SSH processes cooperate to pass back and forth any data transmitted by the application client and server, over the channel inside the SSH session. This allows them to communicate and secures the application's activities on the network.
With this general framework in place, you can distinguish between local and remote forwarding. First we introduce some terms. In the generic port forwarding description in the last section, you saw that one SSH process listens for connections, while the other is ready to initiate connections in response to connections accepted on the other side, to complete the forwarded path. We call the first side the listening side of the SSH session with respect to this forwarding, and the other, the connecting side. For example, in the Figure above host A is the listening side, while host B is the connecting side. Note that these terms aren't mutually exclusive. Since a single SSH session may have multiple forwardings in place, the same side of a session may be the listening side for some forwardings, and simultaneously the connecting side for others. But with respect to any particular forwarding, it is one or the other.
Now, recall that in the last section we didn't label the SSH processes according to which was the SSH client and which the SSH server, but simply referred to two cooperating SSH processes. We do so now, and can state succinctly the local versus remote distinction:
In a local forwarding the application client and hence the listening side are located with the SSH client. The application server and connecting side are located with the SSH server.
In a remote forwarding the situation is reversed: the application client and listening side are located with the SSH server, while the application server and connecting side are located with the SSH client.
So, as we said at the beginning of this section: use a local forwarding when the application client is on the local side of the SSH connection, and a remote forwarding when it's on the remote side.
In all our discussions of port forwarding so far, the application client and server have been located on the machines on the ends of the SSH session. This is reflected in our always using "localhost" in naming the target socket of a forwarding:
$ ssh -L2001:localhost:143 server.example.com
Since the application server is located on the same machine as the connecting side of the SSH port forwarding, the target host can be "localhost." But the connections between the application client and the SSH listening side, and between the application server and the SSH connecting side, are themselves TCP connections. For convenience, TCP implementations allow programs to make connections between two sockets on the same host. The connection data is simply transferred from one process to another without actually being transmitted on any real network interface. However, in principle, either the application client or server—or both—could be on different machines, potentially involving as many as four hosts in a single forwarding:
Although this situation is possible, you generally don't want to do it for security reasons, namely privacy and access control.
The complete path followed by forwarded data includes three TCP connections. But only the second connection, between the two SSH processes, is protected as a channel inside the SSH session. The other two connections are just simple TCP connections. Normally each of these is on a single host, and is therefore protected from network snooping or interference, so the entire forwarding path is secure. But if either of these two connections is between different hosts, its data will be vulnerable in transit.
The other security problem of off-host forwarding concerns the listening side. In short, the listening side of a forwarding has no access control, so intruders may gain access to it. To explain this problem, we must first discuss the loopback address of a host.
In addition to any physical network interfaces it may have, a host running IP has also has a virtual one called the loopback interface. This is a software construct, not corresponding to any network hardware. Nonetheless, the loopback appears and responds like a real interface. Under Unix, it is often named lo0 and is listed by ifconfig:
$ ifconfig -a
...
lo0: flags=849<UP,LOOPBACK,RUNNING,MULTICAST> mtu 8232
inet 127.0.0.1 netmask ff000000
The loopback interface is always assigned the same IP address: 127.0.0.1, the loopback address,[4] and the local naming service provides the name "localhost" for that address. This mechanism gives a reliable way for processes to communicate with one another on the local host via IP, regardless of what IP addresses the host may have on real connected networks, or indeed if the host has no real network connections at all. You can always refer to your local host using the well-known loopback address.
[4] Actually, the entire network 127.0.0.0/8—comprising 24 million addresses—is reserved for addresses that refer to the local host. Only the address 127.0.0.1 is commonly used, although we have seen devices use a handful of others for special purposes, such as "reject" interfaces on a terminal server or router.
By design, a loopback address is local to its host. One machine can't contact the loopback address of another. Since the loopback address 127.0.0.1 is standard on all IP hosts, any connection to 127.0.0.1 leads a machine to talk to itself. (Plus, the loopback network isn't routed on the Internet.)
When a host listens on a TCP port, it establishes a potential endpoint for a TCP connection. But the endpoints of a TCP connection are sockets, and a socket is an (address,port) pair, not a (host,port) pair. Listening must take place on a particular socket and thus be associated with a particular address, hence a particular interface on the host. This is called binding the interface. Unless otherwise specified, when asked to listen on a particular port, TCP binds all the host's interfaces and accepts connections on any of them. This is generally the right behavior for a server. It doesn't care how many network interfaces the local host has: it just accepts any connection made to its listening port, regardless of which host address was requested.
[5] Named after the Berkeley sockets library routine bind, commonly used to establish the association.
Consider, however, what this means in the case of SSH port forwarding. There is no authentication or access control at all applied to the listening side of a forwarding; it simply accepts any connection and forwards it. If the listening side binds all the host's interfaces for the forwarded port, this means that anyone at all with network connectivity to the listening host—possibly the whole Internet!—can use your forwarding. This is obviously not a good situation. To address it, SSH by default binds only the loopback address for the listening side of a forwarding. This means that only other programs on the same host may connect to the forwarded socket. This makes it reasonably safe to use port forwarding on a PC or other single-user machine but is still a security problem on multiuser hosts. On most Unix machines, for example, a knowledgeable user can connect to any listening sockets and see what's on them. Keep this in mind when using port forwarding on a Unix machine.
If you want to allow off-host connections to your forwarded ports, you can use the -g switch or GatewayPorts option to have the listening side bind all interfaces, as we did in an earlier example:
$ ssh1 -g -L P:S:W B
But be aware of the security implications! You may want to exercise more control over the use of forwarded ports in this situation by using TCP-wrappers, which we discuss later in this chapter.
Let's tackle a more complicated example of port forwarding. As shown in the Figure below your home machine H talks to work machine W via a bastion host, B, and you want to access your work email from home. Machine W runs an IMAP server, and your home machine H has an IMAP-capable email reader, but you can't hook them up. Your home IMAP client expects to make a TCP connection directly to the IMAP server on W, but unfortunately that connection is blocked by the firewall. Since host B is inside the firewall, and it's running an SSH server, there should be some way to put all the pieces together and make the IMAP connection from H to W.
Port forwarding can solve this problem. As before, the IMAP server is on port 143, and we select a random local port number, 2001. This time, however, we use a slightly different command to set up forwarding:
# Executed on home machine H $ ssh -L2001:W:143 B
This establishes an interactive SSH session from home machine H to bastion host B and also creates an SSH tunnel from local host H to the email server machine W. Specifically, in response to a connection on port 2001, the local SSH client directs the SSH server running on B to open a connection to port 143 on W, that is, socket W:143. The SSH server can do this because B is inside the firewall. If you configure your email reader to connect to local port 2001, as before, the communication path is now:
The email reader on home machine H sends data to local port 2001.
The local SSH client reads port 2001, encrypts the data, and sends it into the tunnel.
The tunnel passes through the firewall, because it is an SSH connection (port 22) that the firewall accepts.
The SSH server on bastion host B decrypts the data and sends it to port 143 on work machine W. This transmission isn't encrypted, but it's protected behind the firewall, so encryption isn't necessary. (Assuming you're not worried about snooping on your internal network.)
Data is sent back from the IMAP server to home machine H by the same process in reverse.
You have now bypassed the firewall by tunneling the IMAP traffic through SSH.
It may happen that you'd like to forward a port via SSH but don't want an SSH login session to the remote host. For example, if you're using the IMAP forwarding example we've been harping on, you may want only to read email, not open an unnecessary terminal connection at the same time. With SSH2, this is simple: just provide the -f option to ssh2 in your port forwarding command:
# SSH2 only $ ssh2 -f -L2001:localhost:143 server.example.com
or use the GoBackground keyword for the same effect:
# SSH2 only GoBackground yes
As a result, ssh2 puts itself into the background and handles connections to the forwarded port 2001, and that is all. It doesn't create an interactive terminal session with standard input, output, and error channels. The -S option also avoids starting a terminal session but unlike -f, it doesn't put the session in the background (in other words, the -f option implies -S ):
# SSH2 only $ ssh2 -S -L2001:localhost:143 server.example.com
The -f option is also supported by SSH1 and OpenSSH but its operation is different from that of SSH2. It is intended more for executing remote commands that don't require terminal interaction, such as graphical programs using X. Specifically:
It causes the backgrounded ssh to connect the local end of the terminal session to /dev/null (that is, -f implies the -n option).
It requires you to specify a remote command, ideally one that doesn't read from standard input, since the backgrounded ssh connects the local end of the session channel to /dev/null (that is, -f implies the -n option).
For example, if X forwarding is turned on (which we'll discuss later), the following command puts itself into the background, popping up a graphical clock on your local display, with the clock program running on the remote host zwei.uhr.org :
# SSH1, OpenSSH $ ssh -f zwei.uhr.org xclock
This is equivalent to the background command:
# SSH1, OpenSSH $ ssh -n zwei.uhr.org xclock &
In contrast, SSH2 doesn't require a remote command when using the -f option. You may provide one as earlier, and ssh2 behaves in the same way as its SSH1 or OpenSSH counterparts:
$ ssh2 -f zwei.uhr.org xclock
but the remote command isn't necessary; you can set up a forwarding and put ssh2 into the background conveniently:
$ ssh2 -f -L2001:localhost:143 server.example.com
If you tried this with SSH1 or OpenSSH, you see:
# SSH1, OpenSSH $ ssh -f -L2001:localhost:143 server.example.com Cannot fork into background without a command to execute.
To get around the nuisance of providing an unwanted remote command, use one that does nothing for a long time, such as sleep :
# SSH1, OpenSSH $ ssh -f -L2001:localhost:143 server.example.com sleep 1000000
When invoked with -f or GoBackground, ssh persists until you explicitly kill it with the Unix kill command. (You can find its pid with the ps command.) Alternatively, you can request one shot forwarding, which causes the client to exit when forwarding is over with. Specifically, the client waits indefinitely for the first forwarded connection. After that, when the number of forwarded connections drops to zero, the client exits.
One shot forwarding is accomplished easily in SSH2 with the -fo command-line option, a variation on -f (the "o" stands for "one shot "):
# SSH2 only $ ssh2 -fo -L2001:localhost:143 server
One shot forwarding isn't directly supported by SSH1 or OpenSSH, but you can get the same effect with the following method:
Set up the forwarding with ssh -f, and for the required remote command, use sleep with a short duration:
$ ssh -f -L2001:localhost:143 server sleep 10
Before the sleep interval expires, use the forwarded connection:
$ ssh -p2001 localhost
Once the sleep command finishes, the first ssh tries to exit, but it notices a forwarded connection is in use and refuses to exit, printing a warning you can ignore:
Waiting for forwarded connections to terminate... The following connections are open: port 2001, connection from localhost port 143
ssh waits until that connection ends and then terminates, providing the behavior of one shot forwarding.
Earlier, we suggested selecting any unused port for the listening side of a forwarding. Port numbers are encoded in a 16-bit field and can have any value from 1 to 65535 (port is reserved). On multiuser operating systems such as Unix, ports 1 through 1023 are called privileged and are reserved for processes run by the superuser (user ID zero). If a nonprivileged process tries to bind a privileged port for listening, it will fail with an error message such as "insufficient permission."[6]
[6] Microsoft Windows and MacOS have no privileged port restriction, so any user can listen on any free port.
When setting up the listening side of a tunnel, you generally must select a port number between 1024 and 65535, inclusive. This is because an SSH program running under your user ID, not the superuser's, is responsible for listening on that port. If SSH reports that your chosen port is in already in use, just choose another; it shouldn't be hard to find a free one.
For the target side of the tunnel, you can specify any port number, privileged or not. You are attempting to connect to the port, not listen on it. In fact, most of the time the target side is a privileged port, since the most common TCP services have ports in the privileged range.
If you are the superuser on a machine with SSH clients, you can perform local forwarding with a privileged port. Likewise, you can forward a remote privileged port if your remote account has superuser privileges.
Some TCP applications hardcode the server port numbers and don't permit them to be changed. These applications aren't usable with port forwarding if the operating system has a privileged port restriction. For example, suppose you have an FTP client that's hardwired to connect to the server on the standard FTP control port, 21. To set up port forwarding, you have to forward the local port 21 to the remote port 21. But since port 21 is privileged, you can't use it as a listening port number unless you are the superuser. Fortunately, most Unix TCP-based programs let you set the destination port number for connections, and on PCs and Macs, there's no privileged port restriction.
Suppose you want to forward a connection from your local machine to remote.host.net. The following two commands both work:
$ ssh -L2001:localhost:143 remote.host.net $ ssh -L2001:remote.host.net:143 remote.host.net
Most of the time this difference doesn't matter, but sometimes you must take it into account. The application server (e.g., the IMAP daemon) might be doing access control based on source address and not be configured to accept the loopback address. Or it might be running on a multihomed host and have bound only a subset of the addresses the host has, possibly not including the loopback address. Each of these situations is usually an oversight, but you might not be able to do anything about it. If you're getting "connection refused" from the connecting side of the forwarding, but you've verified that the server appears to be running and responding to normal clients, this might be the problem. If the server machine is running Unix, the command netstat -a -n should list all the network connections and listeners on that machine. Look for listeners on the relevant port, and the addresses on which they are listening.
Sometimes, the problem can be more acute if the server uses the source IP address itself as part of whatever protocol it's speaking. This problem crops up when trying to forward FTP over SSH. [Section 11.2]
In general, we recommend using localhost as the forwarding target whenever possible. This way, you are less likely to set up an insecure off-host forwarding by accident.
What happens to forwardings when an SSH connection terminates? The ports simply cease being forwarded; that is, SSH is no longer listening on them, and connection attempts to those ports get "connection refused."
What happens if you try to terminate an SSH session while it still has active forwarded connections? SSH will notice and wait for them to disconnect before stopping the session. The details of this behavior differ among implementations.
In SSH2, if you log out of a session that has an active forwarded connection, the session stays open but sends itself into the background:
remote$ logout warning: ssh2[7021]: number of forwarded channels still open, forkedto background to wait for completion. local$
The ssh2 process now waits in the background until the forwarded connections terminate, and then it exits. In contrast, with SSH1 and OpenSSH, if you disconnect a session with active forwardings, you get a warning, but the session stays in the foreground:
remote$ logout Waiting for forwarded connections to terminate... The following connections are open: port 2002, connection from localhost port 1465
To send it into the background and return to your local shell prompt, use the escape sequence return-tilde-ampersand:
~& [backgrounded] local$
Sometimes a forwarded port mysteriously hangs around after the forwarding SSH session has gone away. You try a command you've used successfully several times in a row and suddenly get an error message:
$ ssh1 -L2001:localhost:21 server.example.com Local: bind: Address already in use
(This happens commonly if you're experimenting with port forwarding, trying to get something to work.) You know that you have no active SSH command listening on port 2001, so what's going on? If you use the netstat command to look for other listeners on that port, you may see a connection hanging around in the TIME_WAIT state:
$ netstat -an | grep 2001 tcp 0 0 127.0.0.1:2001 127.0.0.1:1472 TIME_WAIT
The TIME_WAIT state is an artifact of the TCP protocol. In certain situations, the teardown of a TCP connection can leave one of its socket endpoints unusable for a short period of time, usually only a few minutes. As a result, you cannot reuse the port for TCP forwarding (or anything else) until the teardown completes. If you're impatient, choose another port for the time being (say, 2002 instead of 2001) and get on with your work, or wait a short time for the port to become usable again.
We've seen several keywords and command-line options for configuring SSH clients for port forwarding, such as -L and -R. In addition, the SSH server can be configured for port forwarding. We'll cover compile-time, serverwide, and per-account configuration.
You can enable or disable port forwarding at compile time with configure. It is enabled by default. For SSH1, the configure flags —disable-server-port-forwardings and —disable-client-port-forwardings turn off port forwarding capability for sshd1 and SSH1 clients, respectively. For SSH2, the single flag —disable-tcp-port-forwarding disables port forwarding for both clients and servers.
Port forwarding can be globally enabled or disabled in sshd. This is done with the serverwide configuration keyword AllowTcpForwarding in /etc/sshd_config. The keyword may have the value yes (the default, enabling forwarding) or no (disabling forwarding):
# SSH1, SSH2, OpenSSH AllowTcpForwarding no
In addition, SSH2 has the following options:
# SSH2 only AllowTcpForwardingForUsers AllowTcpForwardingForGroups
The syntax of these is the same as for the AllowUsers and AllowGroups options. They specify a list of users or groups that are allowed to use port forwarding; the server refuses to honor port forwarding requests for anyone else. Note that these refer to the target account of the SSH session, not the client username (which is often not known).
F-Secure SSH1 Server supports the additional keywords AllowForwardingPort, DenyForwardingPort, AllowForwardingTo, and DenyForwardingTo for finer-grained control over forwarding. The two ...Port keywords let you control remote forwardings for given TCP ports, with support for wildcards and numeric ranges. For example, to permit remote forwardings for ports 3000, 4000 through 4500 inclusive, 5000 and higher, and any port number ending in 7:
# F-Secure SSH1 only AllowForwardingPort 3000 4000..4050 >5000 *7
The ...To keywords are similar but control forwardings to particular hosts and ports (i.e., to particular sockets). Host and port specifications are separated by colons and use the same metacharacters as the ...Port keywords:
# F-Secure SSH1 only DenyForwardingTo server.example.com:80 other.net:* yoyodyne.com:<1024
The permissible metacharacters/wildcards are shown in the following table:
|
Metacharacter |
Meaning |
Example |
|---|---|---|
|
* |
Any digit |
300* |
|
< |
All values less than |
<200 |
|
> |
All values greater than |
>200 |
|
.. |
Range of values (inclusive) |
10..20 |
It's important to realize that the directives in this section don't actually prevent port forwarding, unless you also disable interactive logins and restrict what programs may be run on the remote side. Otherwise, knowledgeable users can simply run their own port-forwarding application over the SSH session. These settings alone might be a sufficient deterrent in a nontechnical community, but they won't stop someone who knows what she's doing.
In your account, you can disable port forwarding for any client that connects via a particular key. Locate the public key in your authorized_keys file and precede it with the option no-port-forwarding:
# SSH1, OpenSSH no-port-forwarding ...key...
(SSH2 doesn't currently have this feature.) Any SSH client that authenticates using this key can't perform port forwarding with your SSH server.
The same remarks we just made about serverwide port forwarding configuration apply here: the restriction isn't really meaningful unless you further restrict what this key is allowed to do
Now that you've seen general TCP port forwarding, we move to a new topic: forwarding of X protocol connections. X is a popular window system for Unix workstations, and one of its best features is its transparency. Using X, you can run remote X applications that open their windows on your local display (and vice versa, running local applications on remote displays). Unfortunately, the inter-machine communication is insecure and wide open to snoopers. But there's good news: SSH X forwarding makes the communication secure by tunneling the X protocol.
X forwarding also addresses some firewall-related difficulties. Suppose you're a system administrator with a set of exposed production machines on the other side of a firewall from you. You log into one of these machines using SSH, and want to run an graphical performance-monitoring tool, such as Solaris's perfmon, that uses the X Window System. You can't, though, because to do that, the external machine needs to make a TCP connection back to the internal machine you started on, and the firewall blocks it (as it should, since X is quite insecure). X forwarding solves this problem, permitting X protocol connections to pass through the firewall, securely tunneled via SSH.
Our discussion begins with a brief overview of X and then explains the details of X forwarding. In addition to explaining how to use X forwarding, we also expose the internals of X authentication and how it interacts with SSH, as well as other technical topics.
Sidebar 2. VNC Forwarding: An Alternative to X ForwardingX forwarding is problematic from a security point of view, for the same reason as X itself. As we will see, the design of X means that remote programs must make separate network connections back to the user; this requires yet another layer of authentication and authorization, complicating the situation and opening an avenue of attack. SSH X forwarding tries to secure this as much as possible, but it may still be unacceptable in some environments. An alternative technique is to use Virtual Network Computing (VNC) over SSH. VNC is free software developed by AT&T Laboratories in the United Kingdom, which provides remote GUI access for Unix and Windows platforms. With VNC, you can open a window on your Unix machine running X and have the desktop of a remote Windows machine appear there, so you can operate the Windows box remotely. Conversely, you can run the VNC client on a Windows machine and connect to a remote X display running on a Unix host. Since VNC involves only a single outbound connection, it is easier and safer to tunnel through SSH than X. You can find out more about VNC (and download the software) at: |
The X Window System, or X, is the most widely used graphical display system for Unix machines. Like SSH, X has clients and servers. X clients are windowing application programs, such as terminal emulators, paint programs, graphical clocks, and so forth. An X server is the underlying display engine that processes requests from X clients, communicating via a network protocol called the X protocol. A machine typically runs a single X server but possibly many X clients.
Most important to our discussion, X supports sophisticated window management over a network. X clients can open windows not only on their local machine but also on other computers on the network, whether they are down the hall or across the globe. To accomplish this, an X client makes a network connection to a remote X server and carries on a conversation, using the X protocol to draw on the remote screen, receive remote keyboard events, learn the remote mouse location, and so on. This obviously requires some type of security, which we discuss soon.
A central concept of X is the display, an abstraction for the screen managed by an X server. When an X client is invoked, it needs to know which display to use. Displays are named by strings of the form HOST:n.v, where:
HOST is the name of the machine running the X server controlling the display.
n is the display number, an integer, usually 0. X allows for multiple displays controlled by a single server; additional displays are numbered 1, 2, and so on.
v is the visual number, another integer. A visual is a virtual display. X supports multiple virtual displays on a single, physical display. If there's only one virtual display (which is the most common scenario), you omit the ".v", and the default is visual 0.
For example, on the machine server.example.com, display 0, visual 1 is represented by the display string "server.example.com:0.1".
Under Unix, most X client programs let you specify the display string in two ways: the -d or -display command-line option, or the environment variable DISPLAY. For example, to run the X client program xterm on the only X display of the workstation anacreon, use the command-line option:
$ xterm -d anacreon:0 &
or the environment variable:
$ setenv DISPLAY anacreon:0 $ xterm &
X is a large, deep software product whose documentation fills a dozen O'Reilly books. We've barely scratched the surface with our explanation, but you've now seen enough to understand X forwarding.
Although X clients can communicate with remote X servers, this communication isn't secure. All interactions between the X client and server, such as keystrokes and displayed text, can be easily monitored by network snooping because the connection isn't encrypted. In addition, most X environments use primitive authentication methods for connecting to a remote display. A knowledgeable attacker can get a connection to your display, monitor your keystrokes, and control other programs you're running.
Once again, SSH comes to the rescue. An X protocol connection can be routed through an SSH connection to provide security and stronger authentication. This feature is called X forwarding.
X forwarding works in the following way. An SSH client requests X forwarding when it connects to an SSH server (assuming X forwarding is enabled in the client). If the server allows X forwarding for this connection, your login proceeds normally, but the server takes some special steps behind the scenes. In addition to handling your terminal session, it sets itself up as a proxy X server running on the remote machine and sets the DISPLAY environment variable in your remote shell to point to the proxy X display:
syrinx$ ssh sys1 Last login: Sat Nov 13 01:10:37 1999 from blackberry Sun Microsystems Inc. SunOS 5.6 Generic August 1997 You have new mail. sys1$ echo $DISPLAY sys1:10.0 sys1$ xeyes The "xeyes" X client appears on the screen
The DISPLAY value appears to refer to X display #10 on sys1, but there's no such display. (In fact, there might be no true displays on sys1 at all.) Instead, the DISPLAY value points to the X proxy established by the SSH server, i.e., the SSH server is masquerading as an X server. If you now run an X client program, it connects to the proxy. The proxy behaves just like a "real" X server, and in turn instructs the SSH client to behave as a proxy X client, connecting to the X server on your local machine. The SSH client and server then cooperate to pass X protocol information back and forth over the SSH pipe between the two X sessions, and the X client program appears on your screen just as if it had connected directly to your display. That's the general idea of X forwarding.
X forwarding can even solve the firewall problem mentioned earlier, as long as the firewall permits SSH connections to pass through. If a firewall sits between your local and remote machines, and you run an X client on the remote machine, X forwarding tunnels the X connection through the firewall's SSH port to the local machine. Therefore, the X client's windows can open on your local display. If X forwarding isn't present, the firewall blocks the connection.
Some aspects of X forwarding probably sound familiar from our earlier explanation of port forwarding. In fact, X forwarding is just a special case of port forwarding for which SSH has special support.
X forwarding is on by default in SSH1 and SSH2, but off in OpenSSH. If you need to enable or disable X forwarding for your clients, here's how to do it. Unlike general port forwarding, which requires you to fiddle with TCP port numbers, X forwarding has only an on/off switch. In your SSH client configuration file, use the keyword ForwardX11 with a value yes (the default, to enable) or no (to disable):
# SSH1, SSH2, OpenSSH ForwardX11 yes
On the command line, you may also use -x to disable X forwarding:
# SSH1, SSH2, OpenSSH $ ssh -x server.example.com
# SSH2 only $ ssh2 +x server.example.com # OpenSSH only $ ssh -X server.example.com
The behavior of X forwarding can be modified through compile-time configuration, serverwide configuration, and per-account configuration.
SSH1 and SSH2 can be compiled with or without X support. The compile-time flags --with-x and —without-x make this determination:
# SSH1, SSH2 $ configure ... --without-x ...
In addition, if you compile with X support, you may set the default behavior for X forwarding. In SSH1, you can enable or disable forwarding by default in the client and the server separately, using the compile-time flags —enable-client-x11-forwarding (or —disable-client-x11-forwarding) and —enable-server-x11-forwarding (or —disable-server-x11-forwarding):
# SSH1 only $ configure ... --disable-server-x11-forwarding ...
In SSH2, you can enable or disable all X forwarding by default with —enable-X11-forwarding or —disable-X11-forwarding:
# SSH2 only $ configure ... --enable-X11-forwarding ...
Remember, enable/disable flags simply set the default behavior. You can override these defaults with serverwide and per-account configuration.
The serverwide configuration keywords X11Forwarding (SSH1, SSH2, OpenSSH) and its synonyms ForwardX11 (SSH2) and AllowX11Forwarding (SSH2) enable or disable X forwarding in the SSH server. By default, it is enabled:
# SSH1, SSH2, OpenSSH X11Forwarding no # SSH2 only: either will work ForwardX11 no AllowX11Forwarding no
The X11DisplayOffset keyword lets you reserve some X11 display numbers so sshd can't use them. This keyword specifies the lowest display number SSH may use, preventing sshd from clashing with real X servers on the lower-numbered displays. For example, if you normally run actual X servers on displays and 1, set:
# SSH1, OpenSSH X11DisplayOffset 2
The XAuthLocation keyword specifies the path to the xauth program, which manipulates authorization records for X. We describe this keyword later, after we discuss xauth.
# SSH1, OpenSSH XAuthLocation /usr/local/bin/xauth
In your SSH1 or OpenSSH authorized_keys file, you may disallow X forwarding for incoming SSH connections that use a particular key for authentication. This is done with the option no-X11-forwarding:
# SSH1, OpenSSH no-X11-forwarding ...rest of key...
We've mentioned in passing that X performs its own authentication when X clients connect to X servers. Now we're going to dive into technical detail on the inner workings of X authentication, why it's insecure, and how SSH X forwarding builds on it to create a secure solution.
In most cases, X forwarding simply works, and you don't have to think about it. The following material is to aid your understanding and satisfy any intense cravings for tech talk (both yours and ours).
When an X client requests a connection to an X server, the server authenticates the client. That is, the X server determines the client's identity to decide whether to allow a connection to the server's display. The current release of the X Window system (X11R6) provides two categories of authentication: host-based and key-based:
The simpler method. Using the program xhost, you indicate a list of hosts that may connect to your X display. Notice that connections are authenticated only by hostname, not by username. That is, any user on a listed host may connect to your display.
Uses the xauth program to maintain a list of X authentication keys, or display keys, for X clients. Keys are kept in a file, usually ~/.Xauthority, along with other data associated with the various displays the client wants to access. When a