dmo.ca/ blog/ nullmailer, ssh and systemd

Something broke on my laptop recently, and it stopped forwarding system emails to my server. While trying to fix it -- an out-of-the-box Exim setup that "just worked" after answering Debian's install questions -- I realized that my tolerance for dealing with SMTP crap has disappeared. So, out goes Exim, and in comes nullmailer.

There were a few hoops to jump through along the way, though.

Step 1 - fix nullmailer sender address

$ sudo apt-get install nullmailer

Then, fix nullmailer to do forced-sender by default:

$ echo "dmo+$(hostname -s)@dmo.ca" | sudo tee /etc/nullmailer/forced-from
$ sudo mv /usr/sbin/sendmail /usr/sbin/sendmail.bin
$ sudo -e /usr/sbin/sendmail

and paste in:

#!/bin/bash
exec /usr/sbin/sendmail.bin -f $(cat /etc/nullmailer/forced-from) $@ 

We now have a nullmailer that will force a (real, internet-routable) sender address for every message. But, it can't go anywhere....

Step 2 - set up SSH tunnel to mailserver

On the laptop:

$ ssh-keygen -t ecdsa -N '' -f ./nullmailer-tunnel-key
$ sudo install -m 0600 -o root -g root -t /etc/nullmailer ./nullmailer-tunnel-key* 
$ scp ./nullmailer-tunnel-key.pub $MYSERVER:

On the server:

$ sudo adduser --disabled-password --gecos "Nullmailer tunnel account,,," nullmailer
$ (echo -n 'command="nc localhost 25",no-X11-forwarding,no-agent-forwarding,no-port-forwarding '; cat nullmailer-tunnel-key.pub) | sudo tee -a ~nullmailer/.ssh/authorized_keys

And back on the laptop:

$ ssh -i ./nullmailer-tunnel-key nullmailer@$MYSERVER
220 dmo.ca ESMTP Mail Server
QUIT

Now, we have a way to tunnel to the remote SMTP service

Step 3 - Automatic SSH to remote SMTP with systemd

The next step is to have systemd start our SSH tunnel on-demand whenever nullmailer needs to send something

$ sudo -e /etc/systemd/system/nullmailer-tunnel.socket

and add:

[Unit]
Description=SSH Socket for nullmailer tunnel

[Socket]
<span class="createlink">ListenStream</span>=2525
Accept=yes

[Install]
<span class="createlink">WantedBy</span>=sockets.target

$ sudo -e /etc/systemd/system/nullmailer-tunnel@.service

and add:

[Unit]
Description=SSH Socket for nullmailer tunnel

[Service]
<span class="createlink">ExecStart</span>=/usr/bin/ssh -T -i /etc/nullmailer/nullmailer-tunnel-key nullmailer@(insert server name here)
<span class="createlink">StandardInput</span>=socket

This gives us a service that will invoke a new SSH tunnel to the remote system when port 2525 is connected to. Now, we can enable it:

$ sudo systemctl enable nullmailer-tunnel.socket
$ sudo systemctl start nullmailer-tunnel.socket

And then test it:

$ nc localhost 2525
220 ESMTP Mail Server
QUIT

Step 4 - Point Nullmailer in the right direction

Now, we just need to tell nullmailer to use this new port:

$ echo "localhost smtp --port=2525" | sudo tee /etc/nullmailer/remotes

and all should be working. Verify with a commandline email:

$ echo "Testing" | mail -s "Test via tunnel" you@yourdomain.tld