Using ssh Port Forwarding to Print at Remote Locations

Rory Krause

Issue #94, February 2002

Rory shows you how to connect the printing systems on different networks across the Internet in a secure manner.

In 1997, I was talking to a professional investor who thought that the value of office space in urban high rises was about to take a big dive. His rationale was that telecommuting was going to get big real soon. Well, here we are five years down the road, and I don't think telecommuting really has exploded. I've done it a couple of times. Some people here at SSC telecommute a couple of days a week. But in downtown Seattle, high rises are going up all over the place. On any given day there are 10-20 construction cranes on the skyline.

I wonder if my professional investor friend also was selling interests in pulp and paper companies. Tomorrow's office will be paperless, right?

Whether you telecommute or not, you probably use some form of electronic connectivity to the office when you are not there. Can you check your work e-mail at home? Do machines send e-mail to your cell phone? Can you access your company's intranet from the Web? Or, maybe you have never even been to the corporate headquarters, and you rove the globe packing a laptop that connects to your company's headquarters via a virtual private network (VPN).

If you don't have your VPN set up yet by your IT staff with its unlimited budget, you might be interested in ssh.

ssh--oh yeah, that's a secure Telnet program, right? Yes, it is, and it's much, much more. You're not still using Telnet, are you? Previous issues of Linux Journal have talked about the “much, much more” of ssh (see Resources). In this article I show a specific example of using ssh to do “much, much more”. I also demonstrate how to use ssh's port-forwarding feature to connect the printing systems on different networks across the Internet, at the same time securing the data while in transit.

Imagine that you work at Example Company. Example Company has a traditional office with a network of computers that contain all the data and programs that you and the company use to produce widgets. You are at home and use ssh to connect to Example's network over the hostile-worm-infested-jungle known as the Internet. ssh allows you to log in, read mail and start X11 programs. You also can use scp to pass files back and forth securely. If you have a fast connection to the Internet and through to your company's network, things can be very similar to working at your desk inside the traditional office—up until the time you want to burn a hard copy. What can you do? Well you can print to a file, scp the file to your home computer and submit the job to the local printer with lpr. This method works, but it takes three steps and does not work if you cannot print to file.

Hmmm...well you could turn all three steps into one big command-line juggernaut. On your home computer, typing

ssh -f rory@example.com cat secretdata | lpr

fires up an ssh session over to example.com and concatenates the file called secretdata in your remote home directory. The f option makes ssh go into the background after password authentication but before the cat command is run. Piping to lpr takes the standard out of ssh and directs it to standard in for lpr on the local computer. As long as your home lpr system is working and knows how to handle the file format of secretdata, out pops a hard copy on your home printer. Now, where did you put that ream of paper?

It would be nice just to be working away in your shell at Example Company and type lpr secretdata. That would save 30 typed characters and be easier to remember. What about printing from a mail reader or printing a report from a database application? An integrated print system would be even more useful for these activities.

One way to make the printing system seamless is to set up a relay system that gets the print job from the lpr system at example.com and passes it over to the lpr system at home.

Let's do this by using the port-forwarding features of ssh. What this does is allow you to connect an arbitrary port on your local system to a port on the remote system or vice versa. Then you can use programs that talk to a port to connect to the remote machine. Let's look at an example. Say you wanted to run your browser on your local machine but to look at your company's intranet, which is hosted on the company server at example.com but is not available to the public. Using ssh you forward the local port 8080 to example.com's port 80 with the command:

ssh -L 8080:example.com:80 example.com

After authentication you get a shell on example.com. Then, on your home computer open up your favorite browser, and enter the URL http://localhost:8080/.

Your browser sends its requests to the local port 8080, which gets passed over to port 80 at example.com, which processes the request and sends the answer back over the same channel to be displayed in your browser. Similarly, you could do a MySQL query or talk to an SMTP server by forwarding a local port to the appropriate port on a remote host.

Now that we have a handle on forwarding ports, let's talk about printing. First, we need a relay program that runs between the two different lpr systems.

Let's use a client/server system. The idea is that when we ssh to example.com, we fire up a local client program that connects to a server over at example.com using a forwarded port. If the server has a print job waiting, it sends it back to the client, which passes it off to the local lpr.

Examples of a simple server and client are shown in Listing 1 and Listing 2 [Listing 2 is available at ftp.linuxjournal.com/pub/lj/listings/issue94/5462.tgz].

Listing 1. rlserver

The server will be started by lpr on the remote system and will run only if there is a print job waiting to be sent from the remote machine over to the home machine. The remote lpr system host is set up to pass the print job off to the rlserver program using this entry in /etc/printcap:

# Remote Printer for Rory
rory
        :lp=|/usr/local/scripts/rlserver 8888
        :sd=/var/spool/lpd/rory
        :lf=/var/spool/lpd/rory/log
        :mx#0
        :sh

Printcap entries vary slightly on lpr systems; this one is for LPRng. The 8888 is an argument to rlserver telling it which port should be used for Rory. Other users can be set up with different ports—just make sure the ports aren't in use by some other program. Ports 1-1024 usually require special privileges.

What the client does is poll the server once every five seconds. It connects to the local port that has been forwarded. If there is a job waiting, then there is a server to connect to. Otherwise the connection is refused and the client tries again in five seconds. When a connection is made, the server sends the print job data over to the client, which copies it down into a temporary file. After the client receives all the data, the temporary file is sent to the local lpr. Now there are a few other things we need to do to set this up to run smoothly.

If you actually work in the corporate office part of the time, you want to use the office printer while you are in the office. But when you log in from home, you want the lpr-forwarding printer to be selected. This can be set up automatically by using an ssh feature and the shell initialization files. In $HOME/.ssh/enviroment, add the line:

REMOTE=yes

Once you ssh to a remote host, this variable will be set in your remote environment.

Next, add this stanza to your .bashrc on the remote host:

if [ "$REMOTE" = "yes" ]; then
    PRINTER=printername
fi

This will set your printer to printername when ssh starts a bash session. Make sure printername is the same as what is set up in the printcap file. If you don't use bash, do whatever your shell needs in its startup file.

Next, let's make sure the port forwarding takes place behind the scenes. That way we can simply type ssh example.com without remembering a lot of options and port numbers. Edit the $HOME/.ssh/config file, and add

Host example.com
    LocalForward 8888 example.com:8888

This will forward the local port 8888 to example.com's port 8888.

If you have a slow internet connection you may want to bump up the compression level from the default of six. Add these two lines before the host-specific sections of $HOME/.ssh/config:

Compression yes
CompressionLevel 9

Copy the program rlclient (Listing 2) to your home computer. Make sure the first line has the correct Python path in it. Execute rlclient in a shell. In a separate shell ssh to example.com. Your port 8888 should be forwarded, compression turned on, and your printer variable all should be set up. Check the printer variable with:

echo $PRINTER
It should say whatever you set up as your printername.

Now make sure your local (home) lpr system is up and running, and send a file to print on the remote system (example.com):

lpr mytestfile.txt

Check on the status of the print job with lpq. Also, watch the shell where rlclient is running. It will display output of what is going on.

I had some problems with new versions of ssh that use Protocol 2 not forwarding ports or complaining each time rlclient tried to connect. The error messages were of this sort:

channel_request_forwarding: cannot listen to port: 8888
channel_open_failure: 3: reason 2 Connection refused

In these cases, it was necessary to force the protocol level to 1 by using -1 on the command line or adding Protocol 1 to the $HOME/.ssh/config file under the other entries for example.com.

In addition to the lpr client and server, I found two very simple programs useful in debugging port forwarding. These are called time.client.py and time.server.py (Listings 3 and 4, respectively). Both programs take one argument, which is the port number you want them to use. The server waits for connections, sends out the time of day when it receives a connection, and the client simply prints out the time. They can be used over ssh or on the same machine.

Listing 3. time.client.py

Listing 4. time.server.py

Once everything is debugged, it is much easier to use a script to start the rlclient in the background when sshing to example.com. Then use the script (or an alias to the script) to ssh:

#!/bin/sh
# Start up the rlclient and ssh to example.com.
$HOME/rlpr/rlclient > $HOME/rlpr/log &
echo "$!" > $HOME/rlpr/pid
ssh example.com
kill -9 $(cat $HOME/rlpr/pid)

In this example, we have looked at how to relay print jobs from one location to another over a port secured by ssh. Uses for this port-forwarding feature are limited only by your imagination. Go forth and ssh.

Resources

email: info@linuxjournal.com

Rory Krause is a system administrator at Specialized Systems Consultants. He holds a degree in Mechanical Engineering from the University of Washington. Hobbies include archery and gardening.