SMTP via a SSH tunnel
Posted by Utumno on Thu 18 Jan 2007 at 12:53
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, your geographic location, evil admins in your local network ) 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 'email@example.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 sylpheed-claws-gtk2
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 'firstname.lastname@example.org
'. 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
with port forwarding
ssh -L 25:smarthost.com:25 email@example.com
and point Sylpheed 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
while [[ 1 ]];
ssh -N -L 25:smarthost.com:25 firstname.lastname@example.org
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
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 email@example.com
220 smarthost.com ESMTP Sendmail 8.13.8/8.13.4; Wed, 17 Jan 2007 11:31:55 +0100 (CET)
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 Sylpheed ) 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 firstname.lastname@example.org
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:
socket_type = stream
protocol = tcp
wait = no
user = root
disable = no
server = /usr/bin/ssh
server_args = -q -T -i /root/.ssh/tunnel_key email@example.com
groups = yes
bind = 127.0.0.1
Most of the info was shamelessly copied from here
Also, take a look at J. Franken's excellent SSH-tunneling HOWTO