SMTP via a SSH tunnel

September 17th, 2008

Suppose you have an email account and a shell account on a Unix server. Furthermore, suppose that you yourself use a laptop and download your mail from the server by POP3 or IMAP, and send it via SMTP using the server as a smarthost. Now imagine that for some reason ( your dynamic IP, or your geographic location ) SMTP access is denied. What can you do?


I’ve got an account on a FreeBSD machine on which I’ve got 12 years’ worth of email, and a beautiful spamassassin / procmail setup. All my friends know the account ( let’s call it ‘utumno@smarthost.com’ and the server ‘smarthost.com’ ) so I’d really hate having to change it. I use fetchmail/procmail combo to download mail through IMAP, and Claws-Mail to read it.

However, now I am working overseas, and recently smarthost.com decided to deny all SMTP relay ( including SMTP AUTH ) from abroad.

Way out #1 – use local smarthost

This solution is actually quite tricky because I really want to keep sending mails from ‘utumno@smarthost.com’. If I simply try using ‘smtp.my.isp.com’ with that ‘from’ address, Sender Policy Framework fails. This makes some receving servers move my mails directly to the ‘spam’ folder, others – deny the mail outright.

Fixing that issue would require adding ‘smtp.my.isp.com’ to the SPF record of permitted senders on smarthost.com – something that I have no control of.

Way out #2 – simple SSH tunnel

Set up a SSH tunnel from localhost:25 to smarthost.com:25 with port forwarding

ssh -L 25:smarthost.com:25 utumno@smarthost.com

and point Claws-Mail at localhost:25. That works, however:

– the tunnel is sitting idle for 99% of the time, which is a waste of resources
– it will sometimes timeout or otherwise collapse.

The following script can help with the second issue

#!/bin/bash

while [[ 1 ]];
ssh -N -L 25:smarthost.com:25 utumno@smarthost.com
sleep 5
done

Add this to your initscripts and the tunnel should work all the time. However, to take care of the first issue we really need to approach the problem from a different side.

Way out #3 – use inetd to manage the tunnel

Our ssh tunnel is essentially a service offered to email client software and occasionally this service needs to be restarted. Under Linux, the tool that handles these kinds of services is inetd, or xinetd. These are daemons that listen on a port and when a connection arrives at that port they start a server process to handle that connection.

Before we tackle inetd however, let’s take care of creating a special-purpose public/private key pair to be used by our tunnel. Type

ssh-keygen -t dsa -f ~/.ssh/tunnel_key

and enter an empty passphrase when prompted. This will create the files ~/.ssh/tunnel_key (your private key) and ~/.ssh/tunnel_key.pub (your public key). Leave the first file where it is. From the second file (~/.ssh/tunnel_key.pub )we will make a new special authorized key on our smarthost. It contains some text of the form

ssh-dss AAAAB3NzaC1kc3MAAAC………

Copy this text and on the smarthost add a line to the file ~/.ssh/authorized_keys2 :

utumno@smarthost:~/.ssh$ cat tunnel_key.pub >> authorized_keys2

then edit this file and add command ‘command=”nc localhost 25″, no-X11-forwarding, no-agent-forwarding, no-port-forwarding’ in front if the ‘ssh-dss’ stanza so that the line looks like

command=”nc localhost 25″,no-X11-forwarding,no-agent-forwarding,no-port-forwarding ssh-dss AAAAB3NzaC1kc3MAAAC………

This makes ssh execute the command ‘nc localhost 25′ (25=SMTP port) whenever we ssh to the smarthost using the ‘tunnel_key’ key. This requires that the netcat (nc) program be installed on this machine, and be in the user’s path.

Now you should be able to connect to smarthost’ SMTP daemon with

utumno@laptop$ ssh -i ~/.ssh/tunnel_key utumno@smarthost.com
220 smarthost.com ESMTP Sendmail 8.13.8/8.13.4; Wed, 17 Jan 2007 11:31:55 +0100 (CET)
QUIT
221 2.0.0 smarthost.com closing connection
Connection to smarthost.com closed.

The final step is to use inetd to listen on local port 25 and create the tunnel whenever something ( like our Claws-Mail ) tries to connect to it. Add the following line to ‘/etc/inetd.conf’ :

# ssh tunnel to smarthost.com’s SMTP server
127.0.0.1:smtp stream tcp nowait root /usr/bin/ssh -q -T -i /root/.ssh/tunnel_key utumno@smarthost.com

and reload inetd with

utumno@laptop$ /etc/init.d/openbsd-inetd reload

Voilla! Transparent SMTP relay via an SSH tunnel.

If you use xinetd ( which is by the way hard to do on Etch because of bug #403355 ) enter the following instead:

service smtp
{
socket_type = stream
protocol = tcp
wait = no
user = root
disable = no
server = /usr/bin/ssh
server_args = -q -T -i /root/.ssh/tunnel_key utumno@smarthost.com
groups = yes
bind = 127.0.0.1
}

References

Most of the info was shamelessly copied from here.
Also, take a look at J. Franken’s excellent SSH-tunneling HOWTO.

Leave a Reply