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.
- I wanted to force a real sender address by default, which nullmailer doesn't support
- I wanted an authenticated and encrypted connection to my mailserver without having to deal with configuring mail daemons for TLS or SMTP AUTH.
- I wanted it to handle network disconnects as gracefully as possible, given that it's on a laptop.
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