Introduction

Though a loathed ubiquity, email can perversely forment greater concern in its absence than its reluctantly accepted presence. Or so I found out recently, when my ability to access my email servers was blocked due to a firewall.

I like to use a terminal for everything, and mail is no exception. To read email, I use offlineimap, which synchronises a local copy of my IMAP server for fast access. So when I found myself in this predicament, I wondered if a judicious application of SSH tunnels might ameliorate my worries.

SSH Tunnels

Like their analogue counterparts, SSH tunnels allow obstacles — in this case, firewalls — to be bypassed, as long as there is a good enough way to ‘tunnel’ around them. In my case, I had a laptop which was ostensibly behind a firewall; a ‘jumphost’, in the parlance: a middle-man to where I want to end up; and a mail server, accessible on the internet.

I wanted to use this ‘jumphost’ to talk to the internet for me, and relay all of my messages.

Scripts

The Easy Way

The easy way to do this would be to set up constantly forwarding port on each server along the trip, each ready to accept connections at any time. The problem with this is that:

  • This would leave port open, possibly raising suspicion;
  • I didn’t want my internet-accessible server with ports streaming straight to my IMAP server authentication page, and;
  • That’s too easy

The Hard Way:

The solution I wanted would quickly establish a tunnel all of the way through, fetch/send all email, and then close the connection.

I have two computers:

  1. l, my jumphost
  2. j, my sendmail server

exchange.iinet.net.au is my IMAP server, where all of my emails to read wait for me.

Local Computer

To do this, I wrote the following in port_forward.sh:

#!/bin/bash
nohup ssh -N -L 8000:localhost:4430 -L 8001:localhost:8001 l > /dev/null 2>&1 &
pid=$! # ssh PID
echo $pid

This makes local ports (8000, 8001) listen on server l’s ports (4430, 8001) respectively. It also returns the process ID, so that we know how to stop the port forwarding when appropriate.

I have a script which runs my instance of syncmail:

#!/bin/bash
HOME=/Users/joel
MAIL=$HOME/imap_mail
PROCMAILD=$HOME/.procmail.d
#PATH=./:/Users/joel/perl5/bin:./:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/go/bin:/Users/joel/bin:/usr/local/go/bin:/usr/local/go/bin

CONFIG_FILE=${1:-$HOME/.offlineimaprc}
`/usr/local/bin/offlineimap -c "$CONFIG_FILE" 2>&1 | cat >> "$PROCMAILD"/log`

results=`find $MAIL/joelbuckley/INBOX/new -type f -newer "$PROCMAILD"/last_run -print0`

And the script that invokes it, syncmail_wrapper.sh:

#!/bin/bash
CONFIG_FILE=${1:-$HOME/.offlineimaprc}

REMOTE_PID=$(ssh -n -f l '~/bin/port_forward_remote.sh')
LOCAL_PID=$(/Users/joel/bin/port_forward.sh)
sleep 2

~/bin/syncmail.sh "$CONFIG_FILE"

ssh l "kill -9 $REMOTE_PID"
kill $LOCAL_PID

And then sendmail equivalent, sendmail_mutt_wrapper.sh:

#!/bin/bash

IN=$(cat -)

REMOTE_PID=$(ssh -n -f l '~/bin/port_forward_remote.sh')
LOCAL_PID=$(port_forward.sh)

sleep 2

echo "$IN" | /usr/local/bin/msmtp --read-envelope--from "$@"

ssh l "kill -9 $REMOTE_PID"
kill $LOCAL_PID

Remote Computer

#!/bin/bash

nohup ssh -N -g -L 4430:exchange.iinet.net.au:993 -L 8001:localhost:465 j > /dev/null 2>&1 &
pid=$! # ssh PID
rc=$? # ssh return code

[ $rc -eq 0 ] && echo $pid

Pulling It All Together

Now, whenever I would like to synchronise email, I run

$ syncmail_wrapper.sh ~/.offlineimaprc.

My ~/.offlineimaprc contains:

remotehost = localhost
remoteport = 8000

And, to send email (I use msmtp), my ~/.msmtprc looks like this:

host localhost
port 8001

My ~/.muttrc file contains:

set sendmail = "~/bin/sendmail_mutt_wrapper.sh"