SMTP via a SSH tunnel

Posted by Utumno on Thu 18 Jan 2007 at 12:53

Tags: , , , ,
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 '' and the server '' ) 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 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 ''. If I simply try using '' 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 '' to the SPF record of permitted senders on - something that I have no control of.

Way out #2 - simple SSH tunnel

Set up a SSH tunnel from localhost:25 to with port forwarding
ssh -L
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
    sleep 5
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/ (your public key). Leave the first file where it is. From the second file (~/.ssh/ )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 >> 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
220 ESMTP Sendmail 8.13.8/8.13.4; Wed, 17 Jan 2007 11:31:55 +0100 (CET)
221 2.0.0 closing connection
Connection to 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's SMTP server  stream  tcp     nowait  root    /usr/bin/ssh    -q -T -i /root/.ssh/tunnel_key
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
        groups          = yes
        bind            =


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



Posted by Anonymous (130.60.xx.xx) on Thu 18 Jan 2007 at 13:19
An alternative would be a smarthost on port 443 with TLS session tunneling.

Yet another possibility are the mail-over-IMAP solutions offered e.g. by Courier and (I think Dovecot). These let you send your mail to /dev/null locally while your mailer stores a copy not in =sent, but in =to-be-sent. offlineimap then synchronises that with the server, where mail in =to-be-sent is periodically fed to sendmail and moved to =sent afterwards. This works quite well for me and has the added benefit that I don't produce any Received headers, no matter from where I send.


[ Parent | Reply to this comment ]

Posted by Anonymous (195.228.xx.xx) on Thu 18 Jan 2007 at 13:24
Although the last is a good solution, but the ssh+openvpn combo can do much more.
Get an OpenVPN on the server, set up the client on your laptop/computer, and use an ssh tunnel to connect to the server.

* ssh tunnels carrying OpenVPN never dies: OpenVPN's ttl signals keep ssh connection open
* you don't have to adapt your ssh command line and restart ssh each time you add/remove additional ports
* you can even put your default route on OpenVPN tunnel, if (and only if) you are smart enough to know how to keep out the ssh from that tunnel
* you can do this over and NSTX link and avoid paying for hotspots :) but this's an other stroy


[ Parent | Reply to this comment ]

Posted by niol (143.196.xx.xx) on Thu 18 Jan 2007 at 14:22
[ View Weblogs ]

Another very clean alternative to this is UUCP over SSH : secure, transparent and works both ways.UUCP over SSH howto

[ Parent | Reply to this comment ]

Posted by Utumno (211.72.xx.xx) on Thu 18 Jan 2007 at 16:10
[ View Utumno's Scratchpad | View Weblogs ]
I've read the article, very interesting! This approach however requires root on, and I dont have one...

[ Parent | Reply to this comment ]

Posted by Anonymous (195.28.xx.xx) on Thu 18 Jan 2007 at 17:28
use SMTP AUTH ... ssmtp port is 465 and it's universal solution

[ Parent | Reply to this comment ]

Posted by paulproteus (128.220.xx.xx) on Wed 24 Jan 2007 at 23:03
I'm using the info in here, thanks a lot! A few corrections for the record:



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

should be:

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


'command="nc localhost 25" ,no-X11-forwarding,no-agent-forwarding,no-port-forwarding' should be 'command="nc localhost 25",no-X11-forwarding,no-agent-forwarding,no-port-forwarding ' (the space must be removed)


A style note: I don't use root on the other end, I use a dedicated account on both sides.


[ Parent | Reply to this comment ]

Posted by Utumno (60.248.xx.xx) on Mon 29 Jan 2007 at 06:37
[ View Utumno's Scratchpad | View Weblogs ]
Thanks, I corrected the article.

[ Parent | Reply to this comment ]

Posted by paulproteus (72.226.xx.xx) on Tue 25 Dec 2007 at 22:55
As an update, I wrote my own version of this tutorial at .


[ Parent | Reply to this comment ]

Posted by Anonymous (87.96.xx.xx) on Fri 26 Jan 2007 at 11:51
Please always use "" or "" in examples. That way you are not hurting the real owner of the domain in question.
And while we're at it: When using example ip-addresses, please use "". says: - This block is assigned as "TEST-NET" for use in documentation and example code. It is often used in conjunction with domain names or in vendor and protocol documentation. Addresses within this block should not appear on the public Internet.

[ Parent | Reply to this comment ]

Posted by mbl (87.96.xx.xx) on Fri 26 Jan 2007 at 12:53
What I meant to say was of course:
Please always use "" or "" in examples

[ Parent | Reply to this comment ]

Posted by Anonymous (207.210.xx.xx) on Thu 24 Feb 2011 at 09:09
If you already have an ssh key setup to the server, you can simply add the "nc localhost 25" command, without the quotes, to the end of the server_args line in your xinetd configuration.

If you have exim4 installed on your mobile system for whatever reason, and listening on port 25, you need to have your tunnel listen on another local port, I used 2525. And you cannot use a port on localhost as your smarthost; exim4 is just too smart, and tells you "remote host address is the local host". So I made an entry in /etc/hosts, " smarthost", without the quotes, and used that as the "bind" host in my xinetd.d/smarthost configuration. I also had to add "smarthost" with port 2525 to /etc/services, and run dpkg-reconfigure exim4-config to set up the smarthost -- jc at unternet dot net

[ Parent | Reply to this comment ]

Sign In







Current Poll

Should this site stay open?

( 1603 votes ~ 24 comments )