SSH Tunneling

Security has long been an important computer issue, but it's become increasingly relevant as the number and severity of threats has risen.

Security has long been an important computer issue, but it’s become increasingly relevant as the number and severity of threats has risen.

One security risk of great concern is network data sniffing. When data is passed over a local network wire or when it’s passed between networks, the potential exists for parties other than the sender or recipient to intercept the data. Sniffing can give miscreants access to your passwords, sensitive documents, or even just a peek into your link.

Some network protocols protect against sniffing by encrypting data. Some protocols encrypt just the most sensitive data — typically, passwords — while others encrypt all data. One of the more popular protocols for encryption is the Secure Shell (SSH). It’s most commonly used for secure, remote text-mode logins and remote file transfers. but can also be used to safeguard other types of data transfer, such as remote mail retrieval via POP or IMAP, file sharing via SMB/CIFS or NFS, and remote GUI logins with VNC.

Using SSH tunneling, SSH serves as an extra layer, encrypting the data transferred via other TCP protocols. (SSH tunneling by itself won’t handle UDP or other protocols, though.) SSH tunneling is fairly simple to set up, but does require explicit support on both the client and the server, and has some limitations. In a basic configuration, you’ll tunnel only those protocols you want to encrypt between two systems.

An Overview of SSH Tunneling

In an ordinary TCP/IP connection, the server computer runs a server program that listens for connections on a particular port from client programs running on client computers. The client makes a connection to the server in response to a user action or occasionally because of an automated process, such as a cron job.

In an SSH tunnel, this arrangement is modified. The server computer runs two servers: the target server (say, an IMAP server) and an SSH server. The client computer runs two clients: the originating client (say, an IMAP client such as a mail reader) and an SSH client. The SSH client connects to the SSH server on the remote system, as you might expect of a client; however, the SSH client also configures itself to listen for connections on a port that you specify, as a server normally does. The SSH client can then encrypt the traffic from that port and pass it to the SSH server on the remote system, which decrypts the traffic and passes it on to the local port for the target server.

In effect, the SSH client and server take on the client and server roles, respectively, for SSH, but the SSH client becomes a proxy server for the tunneled protocol and the SSH server becomes a proxy client for that protocol. This arrangement is illustrated in Figure One, using IMAP as the sample protocol.

Figure One: In SSH tunneling, the SSH client takes on the role of a proxy server for the tunneled protocol

On the desktop computer (shown on the left), the IMAP client connects to the local SSH client, which acts as a proxy server for IMAP. The SSH server (on the machine on the right) accepts connections from the SSH client and forwards the traffic to the local IMAP server. All traffic is then secure, since all intramachine traffic is assumed to be safe.

There are a few implications of using an SSH tunneling scheme like that in Figure One: first, you cannot directly connect client applications to remote servers. So, for example, if you want you IMAP client to pick up email from another server, you must close the SSH connection and make a new SSH connection to the new server. Of course, you can also launch multiple SSH clients — say, one for IMAP on one server and another for IMAP on a different server — but the two local SSH clients must use different local ports, at least one of which must be a non-standard port for the protocol in question.

Variants of this configuration are possible. For instance, you can have a single SSH client accept connections from multiple computers, or have an SSH server forward encrypted traffic it receives on to other computers in an unencrypted manner. Both configurations are useful when you trust your local network security but when the link between networks is untrustworthy.

Instead of adding special SSH configurations to many systems on one or both sides of that untrusted link, you can configure just one SSH client and one SSH server and then reconfigure the clients to link up with the SSH proxy server rather than the ultimate target system.

Configuring the Server

Setting up the SSH server is normally quite simple and is limited to the SSH utilities. Thus, you may be able to use SSH tunneling even if you don’t administer the ultimate target system, so long as it runs SSH in addition to the protocol you want to tunnel. If you try a configuration like this and it doesn’t work, though, you should check the /etc/ssh/sshd_config file (note that the filename is sshd_config, not ssh_config) for a line like this:

AllowTcpForwarding yes

If this line is set to no rather than yes, the SSH server will refuse tunneled traffic. The default setting is yes, so this isn’t usually a problem, but it’s worth checking if you can’t get SSH tunneling to work.

Configuring the Client

The bulk of the required configuration for the SSH tunnel is performed on the SSH client computer. This is done by launching SSH with appropriate parameters to have it connect to a remote server and listen to local connections on an appropriate port. If you’re not sure what port number to use, consult the documentation for the protocol you want to forward. In many cases, you can find the port number in the /etc/services file.

Relevant options to the ssh client program are:

* -N tells the protocol to not execute a remote login. If you omit it, SSH will try to open an interactive login session (or with some other options, to run a remote program).

* -f tells the program to execute in the background after asking for a password.

* -L localport:server:serverport specifics a number of things: the SSH local port to listen to, the server to which the data should be forwarded, and the port on the server to which the data should be sent.

* ssh-server provides the hostname of the SSH server, optionally preceded by a username and an at sign (@). Normally, the name of the server will be the same server you specified with the -L parameter, but you can connect to a remote SSH server and have that server forward traffic to some other computer that doesn’t run an SSH server. If you include a username, the remote connection will be in that user’s name, although this username will not be used by the tunneled protocol; you can use another username for that.

As an example, the following command, when typed as root on a client, tunnels IMAP (port 143) traffic from the local computer to the IMAP port on mailserv.pangaea.edu, using the dale account on the remote system:

# ssh -N -f -L \
143:mailserv.pangaea.edu:143 \

When forwarding privileged ports (those numbered below 1024), you must run the ssh client program as root, although you do not need to specify root as the user on the remote system. In fact, some systems forbid remote root logins via SSH as a security measure, so you may need to specify some other user. And even if remote root logins are permitted, using them for tunneling is probably both unnecessary and a potential security risk.

If you don’t want to run ssh as root, you can bind to an unprivileged local port. However, this requires you to use that unprivileged port rather than the protocol’s normal port number when you try to use the SSH tunnel. This isn’t a problem for some clients, but for others it is.

For example, to set up an SSH tunnel in this way, using the local port 2000, you might type the following command as an ordinary user:

% ssh -N -f -L \
2000:mailserv.pangaea.edu:143 \

If you want the SSH client system to accept connections from other computers and forward those connections over the secure SSH link, be sure to add the -g parameter to the ssh command. This parameter tells the client to accept proxy connections from other computers, not just from the system on which ssh is running.

Configuring No-Password Operation

Normally, ssh asks for a password when you connect. This can make automatic use of an SSH tunnel tedious, because you must type the command to make the connection (possibly as root) and manually enter a password before you can use it. To work around this problem, though, you can configure SSH to connect without passwords. Instead, SSH will use private and public keys. To use this system, follow these steps:

1. On the SSH client system and as the user who’ll be initiating the connections, type the following command:

% ssh-keygen -q -t rsa -f ~/.ssh/id_rsa -C ” -N ”

2. Copy the ~/.ssh/id_rsa.pub file from the client to the account on the server that you’ll be using for the SSH tunnel.

3. On the SSH server and as the user whose account will be used for the tunnel, verify that the ~/.ssh directory exists. If necessary, type mkdir ~/.ssh followed by chmod 0700 ~/.ssh to create the directory and give it the proper permissions.

4. On the SSH server, type the following command to add the public key file you’ve just transferred to the authorized keys file. Adjust the public key filename as necessary, if it’s not in the user’s home directory. Also, some systems use authorized_keys2 rather than authorized_keys:

% -cat ~/id_rsa.pub ~/.ssh/authorized_keys

5. On the SSH server, verify that the keys file has appropriate permissions. They should be no more than 0600, so type chmod 0600 ~/.ssh/authorized_keys, if necessary.

After you make these changes, ssh should no longer ask for a password when you connect to the server, at least when you use the accounts you used in the preceding procedure. If SSH persists in asking for a password, try using the -2 parameter, which forces use of the SSH level 2 protocol. (Some older systems default to level 1, which uses a similar but distinct private/public key system.)

You can now create an SSH tunnel without having to type a password, which makes creation of a tunnel a scriptable task. You can do the job in a local startup script, a System V-like startup script, or a script that could be called by some other tool, such as a PPP dialer script or a script called as part of a fetchmail cron job.

Making a Connection

Making a connection with a client works just like connecting to the remote system normally does, except that you connect to the local SSH proxy system.

For instance, Figure Two shows the KMail configuration dialog box for an IMAP session that uses an encrypted SSH tunnel. In this case, the SSH client runs on the local computer’s port 143, so you specify localhost in the Host data entry box. In all other respects, client configuration works precisely as it normally does.

Figure Two: Clients using an SSH tunnel connect to localhost rather than the remote system

In use, you’re likely to notice a few differences or be forced to make some changes to how you use an SSH-encrypted link:

* The SSH link must be established before you try to use an encrypted connection. This fact can be a problem if you don’t want to keep an SSH link open perpetually. In some cases, you may be able to write a script to open the SSH link, perform tasks with the tunneled protocol, and then close the SSH link. Other times, keeping the SSH link open at all times is the best approach.

* The fact that the SSH client binds to a local port means that you may not be able to use it for some peer-to-peer protocols. To encrypt an SMB/CIFS connection, for instance, you must bind the SSH client to the SMB/CIFS server port, which means Samba can’t do so. In some cases, you may be able to bind the SSH client only to the localhost version of the port, leaving the external ports free for the normal server. (This works for Samba, for instance.)

* Because an SSH tunnel only handles TCP traffic, you won’t be able to tunnel UDP or other types of traffic. This fact can limit an SSH tunnel’s utility. For instance, NFS uses UDP by default, although recent versions can be configured to use TCP. SMB/CIFS relies on TCP for file sharing operations, but uses UDP for name resolution and browsing.

The Special Case of X Connections

A special case of SSH tunneling is the protocol’s capacity to tunnel X connections. In this case, the X server runs on the SSH client computer, and the X clients (that is, the X programs you want to run) reside on the SSH server computer. In this case, the SSH server does double duty as an SSH server and an X proxy server, while the SSH client functions as an SSH client and an X proxy client. To make this configuration work, X programs must know to connect to the SSH server’s X server instance rather than the X server running on the local system. The SSH server therefore connects to an appropriate X port and sets the DISPLAY environment variable to point clients at this port.

To work in this way, you must set appropriate options in the X server and X client configuration files. In particular, check your /etc/ssh/ssh_config file for the following line:

ForwardX11 yes

If this line is set to no, change it to yes. Alternatively, you can pass the -X parameter to ssh when you call it to enable X forwarding. (That’s an uppercase -X; the lowercase -x option disables X forwarding!) On the SSH server, check the /etc/ssh/sshd_config file for the following line:

X11Forwarding yes

As with the equivalent client option, be sure this server option is set to yes. Also, be sure to check the correct files — ssh_config for the client and sshd_config for the server. The one-character difference in filenames can be easy to miss.

Once configured in this way, you should be able to connect to a remote Linux system via SSH and immediately launch X programs from the command line. They’ll run on your local desktop but be tunneled through SSH.

SSH Shortcuts

If you find yourself connecting to the same machines time and again, try setting commonly used options in your own SSH configuration. Your personal ssh configuration file (typically) lives in $HOME/.ssh/config, and can set global SSH parameters or per-machine parameters. Here’s an example file (line numbers have been added for reference only):

1 # Global parameters
2 Protocol2,1
4 Host web
5 Usermoe
6 HostNamewww.linuxmagazine.com
7 Protocol 1
8 ForwardX11 no

The first line is a comment. Line 2 specifies that the SSH utilities should first try protocol version 2 and then version 1. Since Protocol doesn’t appear in a Host section (like the one in lines 4-8), it is applied globally.

Lines 4-8 form a host section and create a host alias named web. Typing ssh web is the equivalent of typing ssh -1 -x -l moe www.linuxmagazine.com. Host-specific options override global options.

The SSH configuration file can be quite handy. You can also specify port numbers with Port, enable compression with Compression, among other handy options. See the ssh_config man page for more details.

Roderick W. Smith is the author or co-author of twelve books, including Linux Power Tools and Advanced Linux Networking. He can be reached at rodsmith@rodsbooks.com.

Comments are closed.