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 '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 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 '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 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
#!/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 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 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.

 

 


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.

-madduck

[ 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.

Advantages:
* 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

asd

[ 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 smarthost.com, 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 yarikoptic (165.230.xx.xx) on Thu 18 Jan 2007 at 21:23
Nice idea on inetd In my case I've decided to use running ssh-agent from my very own user account ;-) This way, nobody could "authenticate" as myself if they get access to machine's other user account. Just a hint: To make your ssh connection more persistent from your Debian client (patch could have not been applied in other distros), ie to don't die after a while, just add a config line
ServerAliveInterval 60
Here 60 sec - is the interval when your client pings the server so firewalls don't shut it down due to inactivity.

[ 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:

1.

This:

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

should be:

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

2.

'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)

3.

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

--
|/usr/games/fortune

[ 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 http://www.asheesh.org/note/sysop/mail-tunnel.html .


--
|/usr/games/fortune

[ Parent | Reply to this comment ]

Posted by Anonymous (87.96.xx.xx) on Fri 26 Jan 2007 at 11:51
Please always use "example.com" or "example.org" 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 "192.0.2.0/24". http://www.rfc-editor.org/rfc/rfc3330.txt says:
192.0.2.0/24 - This block is assigned as "TEST-NET" for use in documentation and example code. It is often used in conjunction with domain names example.com or example.net 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 "example.com" or "example.org" in examples
/MBL

[ 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, "127.0.0.22 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

Username:

Password:

[Register|Advanced]

 

Flattr

 

Current Poll

What do you use for configuration management?








( 52 votes ~ 0 comments )