Sometimes, an SMTP server is only accessible from a local LAN. If you have a Linux computer and an SSH account inside of this LAN, you can nevertheless tunnel through and send e-mails from anywhere. There are several ways to do this and I found one that is transparent to my current setup.
SSH port forwarding
If you only need this once in a while, you can use SSH port forwarding:
ssh -N -L localhost:8587:the.smtp.server.com:587 email@example.com
This will connect to the SSH server and forward the local port 8587 to
the port 587 on the SMTP server. Here, I assume the the server listens
on the “submit” port 587. In your case it might also be 25, 465, or
something weird. This works even if the SMTP server is not the SSH
server, as long as the SSH server can access the SMTP server. You
should then configure your e-mail program to use
the SMTP server. You might have to convince it to accept a bogus SSL
-N option disables the execution of a command or shell: the
SSH client will simply forward the port. So stop the connection with
Ctrl-C when you’re done.
Automatic SSH tunnel with systemd
This works, but gets annoying. And you have to remember it when you need it, so I decided to use another trick. Systemd supports (x)inetd style on-demand services. This means that systemd itself listens on a network socket until a connection comes in. Then it spawns some process to handle this connection. Luckily for us, the handler process can communicate with systemd via stdin/stdout. So, our handler is
/usr/bin/ssh firstname.lastname@example.org -- nc the.smtp.server.com 587
This will spawn
nc on the SSH server, which is a program used to
communicate with a network socket via stdin/stdout—just what we
need. Careful: The connection must be made without user input,
otherwise it will not be automatic. So set up public key
authentication for your SSH
For systemd, we need two unit files. The first one will tell systemd
to open a network socket on demand. It should go into
/etc/systemd/system/emailtunnel.socket (replace the name
emailtunnel if you want):
[Unit] Description=Open an SSH connection in order to send an email. [Socket] ListenStream=127.0.0.1:8587 # Start one SSH connection per TCP connection to 8587. Accept=true [Install] WantedBy=sockets.target
Then we need a unit that describes the program that will be started to
handle the connection. This goes into
/etc/systemd/system/emailtunnel@.service (if you changed the name
before, you must also change it here):
[Unit] Description=An SSH tunnel [Service] Type=simple ExecStart=/usr/bin/ssh email@example.com -- nc the.smtp.server.com 587 StandardInput=socket StandardOutput=socket StandardError=journal
That’s it. Now type the following commands to enable and start the setup:
systemctl daemon-reload systemctl enable emailtunnel.socket systemctl start emailtunnel.socket
This will automatically open the SSH tunnel if someone connects to port 8587 and closes it when they disconnect. Several SSH tunnels can be opened in parallel.
Warning: This will enable anyone with access to the computer to use this SSH tunnel, not only specific users! Sadly, this is unavoidable. Be aware of the dangers if the computer has more than one user, especially with shell access.
Note 1: This part will work with any software that sends e-mail. So
if you do not use postfix, stop here and configure your software to
localhost:8587 to send e-mail.
Note 2: If you do not like systemd, I am sure you can replicate this setup using (x)inetd. I have no interest in this, though, so you have to do your own research.
Teach postfix to use this relay
The nice thing about running postfix yourself is that you only need to configure all your e-mail accounts once. After that, you can point Thunderbird and company at your central server. I’ll leave it up to you to secure this one, see the excellent manual for all details.
Access to your postfix server is secured, right? Good, now we can configure it to act as an SMTP client. First, we control which user can use which e-mail address. In postfix’s main config file, set
smtpd_sender_login_maps = hash:/etc/postfix/controlled_envelope_senders
/etc/postfix/controlled_envelope_senders to contain something
user1 firstname.lastname@example.org user1 email@example.com user2 firstname.lastname@example.org
These users on the left are usually the Unix users on your system. You can set up virtual users just for postfix, refer to the manual for that.
Then, we control which server is used for which e-mail address. Let’s
take our setup from above and say that
email@example.com should go
through the SSH tunnel. In postfix’s main config file, set
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relay
/etc/postfix/sender_relay should look like this:
Now we need to set up the user/password. First in the main config:
smtp_sender_dependent_authentication = yes smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
/etc/postfix/sasl_passwd with permissions
0600 and the
This almost works, but it may be that postfix rejects the SSL
certificate of the server as invalid, since we “renamed” it to
localhost. So, let’s disable this check. (Be sure that the LAN you
connect to via SSH is safe from DNS spoofing and eavesdropping!) Add
the following to the main config:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy_per_server, static:secure
This means that anything that is not listed in
/etc/postfix/tls_policy_per_server will be subjected to SSL
certificate checks. We can now disable this for our case in
/etc/postfix/tls_policy_per_server by adding:
Now we need to update all the maps for postfix by executing:
postmap /etc/postfix/controlled_envelope_senders postmap /etc/postfix/sender_relay postmap /etc/postfix/sasl_passwd postmap /etc/postfix/tls_policy_per_server
Finally, restart postfix and enjoy!