If you are like me, and you take your laptop (a Powerbook of course) with you everywhere you go, you find yourself connecting to the Internet over WiFi hotspots or through corporate networks. Everything works smoothly and seamlessly, until you try to send email only to find SMTP traffic is blocked; or you want to surf the web only to find a restrictive firewall limits your access. Using ssh you can tunnel through the local firewall and connect to a server under your control and regain not only fuller access to the Internet, but add a layer of protection to your activities through encryption.
ssh, or secure shell is a tool at allows you to access a remote server over an encrypted connection. Someone monitoring the connection will know it exists but in all likelihood they won't be able to read the actual data passing over the connection as it will be encrypted. Just using ssh is not terribly hard or exciting. Command line access is useful and very powerful, but rarely sexy.
Port forwarding adds a little sex to ssh. Using port forwarding it is possible to map ports on your machine to those of another, remote machine. For example, you could map your web access port (typically 8080) to a web proxy port of a machine that sits outside whatever firewall exists in your current location. Once the mapping is established you would be able to use the internet just as before, only now your activities would be masked by encryption, and it would appear as if you had just visited a single site, that of the remote server. Also, since your HTTP requests would be resolved by the remote server (hopefully one under your control) any restrictions imposed locally would not limit your activities.
Before I proceed, a word of caution. Corporate network administrators are humorless and potentially vindictive when they feel their network is threatened. Using these techniques to tunnel out through a corporate firewall where you work may result your being asked to pack up your marbles and leave abruptly. Just because the data stream is encrypted doesn't mean it is invisible. Use ssh at your own risk. Your mileage may vary. Void where prohibited by law, employee handbook, or policy.
ssh 101
First of all you need a destination to ssh into; in my case a good friend showed me how to setup a Linux server and configure it to accept incoming secure shell connections. If you have a spare Macintosh running OS X it would be even easier. Open up the Sharing panel in System Preferences, and enable "Remote Login" on the Services tab. (If you are behind a router or firewall, you will have to open port 22 on the firewall to allow the incoming traffic. A better idea is to open some obscure port, say 55522 on the firewall and forward inbound traffic to it to port 22 on your destination machine. An extra layer of obfuscation to keep out unwanted visitors.)
Let's say that the IP address of your destination server is 169.254.1.1. (Yes, I know it's a private IP address, this is an example, remember?) A basic ssh connection command would look something like this:
ssh -l mark 169.254.1.1
Here I am connection to 169.254.1.1 as 'mark'. Once the inital connection is made I'll be prompted for my password. If it is the first time I've connected to that remote host I'll be asked to accept a key for that machine. Once connected everything behaves just as if I had used telnet to make the connection, only my traffic between the two machines is now encrypted. You can try this for yourself, by opening a secure shell to your local machine, like so:
eeyore:~ mark$ ssh -lmark 127.0.0.1
mark@127.0.0.1's password:
Last login: Tue Oct 19 07:28:58 2004
Welcome to Darwin!
eeyore:~ mark$
To forward a port, for example the HTTP port (8080) you add the "-L" parameter to the command. Like this:
ssh -l mark -L8080:localhost:3128 169.254.1.1
In other words, securely connect to some remote host (169.254.1.1) as "mark", and create a port forwarding or tunnel, mapping the local (laptop) port 8080 to localhost:3128 on the remote machine. While this may seem counter-intuitive at first it makes sense when you look closely. Once the ssh connection is made to the remote server, the tunnel needs to access the remote machine, which is localhost. Put another way, 127.0.0.1 on the remote machine.
At this point I have a secure, encrypted connection between my laptop and a remote server, and a port forwarding tunnel through this connection that maps any request on my laptop for port 8080 to port 3128 on the remote server. Any HTTP traffic on my laptop requesing port 8080 will be resolved by the proxy running on port 3128 on the remote server. (Squid proxy uses 3128 as its port.)
Changing my proxy information in the Network panel of System Preferences to map all HTTP and Secure Web requests to localhost:8080 I am now able to access the Internet via a secure connection. All inter-machine communication is encrypted. Understand, that traffic between your remote host and the Internet itself, will not be encrypted. This method only slows down local eavesdroppers.
Some Parameters, and an alias
In order to use ssh and port forwarding for email, we need to work with two ports that are owned by root, namely ports 25 (SMTP) and 110 (POP) (I suppose IMAP would work this way as well, but since you most likely have a web interface to your IMAP accounts, and you already know how to secure HTTP traffic, I won't cover IMAP port forwarding here). Simply adding a "sudo" command to the beginning of our ssh command allows the ssh command to run as if you were logged in as root. We're also going to add a compression parameter to our command in hopes of gaining a little performance. Our ssh command now looks like this:
ssh -C -lmark -L8080:localhost:8080 169.254.1.1
This gem breaks down like this:
sudo allows the command that follows to be run as if you were logged in as root. You supply your password and, provided your ID has administrator access, the command runs.
ssh -C run the ssh command, and the first option is '-C' or compress all traffic. Should get some performance gain from this.
-luserid the id on the remote system you want to log in using, not your local id.
-L8080:localhost:8080 forward local port 8080 to the remote system and once there resolve it as localhost:8080.
169.254.1.16 the IP address of the remote system I wish to log in to using the userid specified in the '-l' parameter.
So that I don't have to type this command every time I wish to connect I created as alias in my .bash_profile file, like this:
alias 'remote=sudo ssh -C -luserid -L8080:localhost:8080 169.254.1.1'
Now I just open a terminal window and type 'remote' to connect. I'm prompted for my local machine administrator id first (from the sudo command) and then for my login password on the remote server.
Now I have full HTTP and HTTPS browsing via ssh from my laptop.
Secure Email
Now we need to forwarded the POP (110) and SMTP (25) ports on our laptop through a tunnel inside ssh connection. The ports are (hopefully) resolved at the remote server and any requests for ports 25 or 110 on my laptop pass-through the tunnel and "execute" on the remote server.
Being a good programmer, I.E. lazy, I created a script using perl to establish the ssh connection. Actually two scripts,
remote and
home. Remote is the script for connecting from any location, home resets the connection for when I am using my own broadband connection.
The ssh portion of the remote perl script looks like this:
ssh -L25:relay.hostname.com:25 -L110:mail.hostname.com:110 -L8080:localhost:8080 -luserid host -N -f
The port forwards, indicated by the '-L' parameter setup a listener on each port listed (25, 110, and 8080) and pass any requests to those ports indicated (relay.hostname.com, mail.hostname.com, and localhost, on the remote machine, named 'host'.
The
'-l' parameter indicates the user id I wish to use for this connection, and 'host' is the name or IP address of the remote server I wish to access.
The
'-N' parameter causes no remote command to be executed. From Eridius, in the comments below:
Usually when you ssh in to a remote machine it executes the shell (or rather, it probably executes login, which uses the shell). On a normal ssh execution you can specify an alternate command to execute if you want it to run that and then exit. Normally the ssh connection is shut down once the command is finished running (which is how typing exit to the remote shell shuts down the ssh connection). The -N switch means it doesn’t run any command, it just establishes a connection, and it shuts down when you send it the appropriate signal.
The
'-f' parameter cause the ssh process to goto background as it runs, freeing up the shell used to start it. More on this later.
I am forwarding the POP and SMTP ports to the hostname that is my mail server. Your name will obviously be different. HTTP (8080) is forwarded to a HTTP proxy running on the remote server itself. This gets me outside the firewall and my mail servers are now visible.
My mail account settings have to be changed to take advantage of these port forwards. Specifically I removed the existing POP and SMTP server settings and replaced them both with '127.0.0.1', which is the localhost address of my machine. Now when the e-mail client accesses either POP or SMTP the request is forwarded through the ssh connection to be resolved at the remote host. Viola, secure (encrypted) e-mail access from behind a firewall.
The second script, called "home" is used while I am at home, connecting though my broadband modem. I don't need to forward ports and secure shell from home but, since my mail accounts have all been setup to point to 127.0.0.1, not using a script would require changing them each time I came home. Using the script allows those settings to remain unchanged.
Since this script only needs to create a port forward, the remote host I connect to is myself. Like so,
ssh -L25:relay.hostname.com -L110:mail.hostname.com -luserid -N -f 127.0.0.1
Same as the remote script, but I don't need to forward HTTP requests, and I am connection back to myself. My machine has an open connection to the internet that allows for POP and SMTP, this script exists just to capture requests from my mail client and pass them on to the appropriate servers.
Because I used the '-f' parameter in my ssh command, the ssh process happens in the background, freeing up the shell used to start it. This is nice and tidy, but switching from home to work, or work to remote, requires that you first shut down any existing ssh processes. The actual perl script has two more lines to it that find and kill any running ssh connections. They look like this:
Home
#!/usr/bin/perl
open (PS, "ps auxw | grep \'ssh -L\' | grep -v grep | awk '{ print \$2 }'|");
while (){ chop; system ("sudo kill $_");}
system ("sudo ssh -L25:relay.hostname.com -L110:mail.hostname.com -luserid -N -f 127.0.0.1");
remote
#!/usr/bin/perl
open (PS, "ps auxw | grep \'ssh -L\' | grep -v grep | awk '{ print \$2 }'|");
while (){ chop; system ("sudo kill $_");}
system ("sudo ssh -L25:relay.hostname.com:25 -L110:mail.hostname.com:110 -L8080:localhost:8080 -luserid host -N -f");
I'm not a perl coder by any stretch, but I understand the first line to be collecting all the process ids of running ssh connections. The second line then kills these processes. The final line of each script is the ssh command itself, as described above. It is worth noting, that these scripts will shutdown any manually created ssh tunnels. As Eridius points out in the comment below, a better method would keep track of the PID for the opened ssh tunnel(s) in a temporary file, which could be used later to close them and only them. I'll leave that set of scripts for the reader to develop.
Multiple Email Accounts
You have more than one email account you'd like to access this way? No problem, simply forward more ports. Select a port number for each mail account you'd like to receive mail from, one one more for outbound (SMTP) traffic. Visit Mail Preferences and assign each account a unique POP port number (50110, 50111, 50112, et cetera.) Set all accounts to use the selected SMTP port (50025). Now alter the ssh command in the script to look something like this:
#!/usr/bin/perl
open (PS, "ps auxw | grep \'ssh -L\' | grep -v grep | awk '{ print \$2 }'|");
while (){ chop; system ("sudo kill $_");}
system ("sudo ssh -L25:relay.account1.com:25 -L50110:mail.account1.com:110 -L50111:mail.account2.com:110
-L50112:mail.account3.com:110 -L50113:mail.account4.com:110 -lmark -N 127.0.0.1 -f");
p(ednote). The last line was broken for display and should be entered on one line.
All outbound mail (SMTP, port 25) will be forwarded to the single relay account. All incoming mail will be gathered through as many reflections of port 110 as are needed.
I know this is complex, but complex in a good geeky way. I'm happy, and I get my e-mail (thru an encrypted link) regardless of where I connect from.
Update: 10.23.2004 I have updated several points in the original posting based on the excellent note from Eridius in the comments below. One point I didn't address was reconfiguring your mail client to use ports outside of the 0 - 1024 range to avoid needing to use
sudo at all. I think the comment sufficient explains the idea, so I leave it to the reader to modify his or her port settings.
I have a few notes on your post:
You don't need to use sudo to forward port 8080, yet you are. That sudo bit should be moved lower to where you forward ports 25 and 110.
You shouldn't use sudo at all. Just change the local ports to be >1024 and you can drop sudo entirely from this post (which is good, because the less root access the better). You can configure your mail client to access 127.0.0.1 on port 8025 and 8110 if you want instead of 25 and 110 and those don't need root access.
The -N means don't execute a remote command. Usually when you ssh in to a remote machine it executes the shell (or rather, it probably executes
login, which uses the shell). On a normal ssh execution you can specify an alternate command to execute if you want it to run that and then exit. Normally the ssh connection is shut down once the command is finished running (which is how typingexitto the remote shell shuts down the ssh connection). The -N switch means it doesn't run any command, it just establishes a connection, and it shuts down when you send it the appropriate signal.Why are you using
kill -9? The -9 is extremely unnecessary here. That means 'terminate with prejudice', and should only be used if the process will otherwise not quit. Simply usekillwithout any specified signal (so it passes the default one) and that should work fine.I recommend a nice piece of freeware called SSHKeychain. It puts a status item in the menubar and acts as an ssh agent (actually it's a front to ssh-agent). This lets you do passwordless connections, which is nice. And additionally it has a tunnel feature where you can set up tunnels and turn them on and off from the menuitem. I find it really useful.
You should mention that your perl script will shut down any manually-created ssh tunnels. Perhaps you could modify your script to save the pid of the opened SSH tunnel to a temporary file and then read back that file when shutting down the tunnel?
sudousage entirely. Learning is always a trial and error process, ssh and port forwarding were no exception in my experience. Trying to explain what you know (or think you know :)) forces you to see your understanding through the eyes of others. I have increased my understanding of ssh and port forwarding through this blog entry. Thanks again!kill -9does not let the process sync to disk and properly close any files or other resources that it is using. It’s like hard-rebooting your computer, but for one program. Always give a process the chance to quit properly and then finish it off if it’s difficult. The number of times a process has problems is rare and the benefit to letting it quit properly significantly outweighs it.—
cp
I tried the SMTP part of this great idea. But, even forwarding the local 2525 port to server:25 throught ssh, I get:
$ telnet localhost 2525
mail from: me@server
250 OK
rcpt to: myfriend@otherserver
550 relay not permitted
It seems that the remote server still sees my ip, and not think about me as a locally connected user.
Thanks!
This (http://www.stopdesign.com/log/2005/02/07/secure-email.html) article is the best I’ve seen about setting yourself up for secure email access. While a bit lengthly it is a very worthwhile read. I’ve actually implemented my wireless connection this way.
—
“I love my Mac. I feel about it what no man should feel about a computer.” – Jeffery Zeldman
—
“I love my Mac. I feel about it what no man should feel about a computer.” – Jeffery Zeldman
Post new comment