Why?

You’ve got a server and sometimes you need to send outgoing emails. Like me, you may have messed around using someone elses SMTP server. Maybe, you’ve had a go at setting up Postfix or Exim?

It’s a lot of hard work, isn’t it?

Well, I couldn’t be arsed with all that, and eventually came across the awesome opensmtpd.

If you’re on Debian 10 (Buster), make sure you’ve enabled Debian Backports in your sources.list and run:

sudo apt-get install opensmtpd/buster-backports

You need to install from backports because the configuration syntax changed. The standard buster package will give you version 6.0.3, but we want at least 6.6

Run # smtpd -h to check:

root@server:/home/simon# smtpd -h
version: OpenSMTPD 6.6.4p1
usage: smtpd [-dFhnv] [-D macro=value] [-f file] [-P system] [-T trace]

Below is the defaut config file “/etc/smtpd.conf”

OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $
# This is the smtpd server system-wide configuration file.
# See smtpd.conf(5) for more information.
table aliases file:/etc/aliases
# To accept external mail, replace with: listen on all
#
listen on localhost
action "local" maildir alias <aliases>
action "relay" relay
# Uncomment the following to accept external mail for domain "example.org"
#
# match from any for domain "example.org" action "local"
match for local action "local"
match from local for any action "relay" 

For outgoing mail only, we don’t need to touch this at all!

DNS Records

We need to tell our domain registrar that we’re handling our mail now.

First, we’ll set an A record with the IP address of our server and a hostname of mail I threw in smtp as well.

DNS A records

Next we’ll set our MX records. The 10 in the screenshot below is the priority. I usually see this as 10 so that’s what I’ve been using.

DNS MX record

Now we’ll set our SPF (Sender Policy Framework). As long as we’ve set our A and MX records, we can just put this as a TXT record:

v=spf1 a mx -all

DNS TXT Record

If you want to know more, here is a good article on the topic: mailtrap blog

Reverse DNS

Cloudflare explanation of a PTR record

The Domain Name System, or DNS, correlates domain names with IP addresses. A DNS pointer record (PTR for short) provides the domain name associated with an IP address. A DNS PTR record is exactly the opposite of the ‘A’ record, which provides the IP address associated with a domain name.
DNS PTR records are used in reverse DNS lookups. When a user attempts to reach a domain name in their browser, a DNS lookup occurs, matching the domain name to the IP address. A reverse DNS lookup is the opposite of this process: it is a query that starts with the IP address and looks up the domain name.

So, lastly, you need to set the reverse DNS or PTR record for you IP on your VPS settings page.

You’ll (usually) find this in your IP settings. All you need to do is put set RDNS or reverse DNS to your hostname. For this site, that’d be

simonh.uk

Let’s check opensmtpd is running correctly:

simon@server:~ [ssh] $ systemctl status opensmtpd
● opensmtpd.service - OpenSMTPD SMTP server
   Loaded: loaded (/lib/systemd/system/opensmtpd.service; enabled; vendor preset
   Active: active (running) since Fri 2021-06-11 07:47:36 BST; 3 days ago
     Docs: man:smtpd(8)
  Process: 28100 ExecStart=/usr/sbin/smtpd (code=exited, status=0/SUCCESS)
 Main PID: 28101 (smtpd)
    Tasks: 7 (limit: 1166)
   Memory: 11.8M
   CGroup: /system.slice/opensmtpd.service
           ├─28101 /usr/sbin/smtpd
           ├─28102 smtpd: klondike
           ├─28103 smtpd: control
           ├─28104 smtpd: lookup
           ├─28105 smtpd: pony express
           ├─28106 smtpd: queue
           └─28107 smtpd: scheduler

Send a test email:

simon@server:~ [ssh] $ echo "hello!" | mail -s 'hi from server!' web@simonh.uk

And in my email client (Claws Mail):

Server Email

So, all went well.

Closing Notes

Ideally, we’d also set up DKIM, which along with SPF helps to improve the chances of your emails not being labelled as spam. But in my case, it wasn’t worth the trouble (and I did have trouble).

As I’m only sending mail to accounts I control, I can also label them as “NOT SPAM” if they get the dreaded [SPAM] header.

PKI Encryption (Added 2021-06-15)

Assuming you’re using Apache 2.4 as your web server:

$ sudo apt install certbot python3-certbot-apache
$ sudo certbot certonly -d yourdomain.com --apache

and then in your /etc/smtpd.conf add the following lines

pki yourdomain.com key "/etc/letsencrypt/live/yourdomain.com/privkey.pem"
pki yourdomain.com cert "/etc/letsencrypt/live/yourdomain.com/fullchain.pem"

listen on eth0 port 25 tls pki yourdomain.com

Then run

root@server:/home/simon# smtpd -n
configuration OK

Finally, restart opensmtpd, and I always check that there are no errors:

root@server:/home/simon# systemctl restart opensmtpd
root@server:/home/simon# systemctl status opensmtpd
● opensmtpd.service - OpenSMTPD SMTP server
   Loaded: loaded (/lib/systemd/system/opensmtpd.service; enabled; vendor preset
   Active: active (running) since Fri 2021-06-11 07:47:36 BST; 4 days ago
     Docs: man:smtpd(8)
  Process: 28100 ExecStart=/usr/sbin/smtpd (code=exited, status=0/SUCCESS)
 Main PID: 28101 (smtpd)
    Tasks: 7 (limit: 1166)
   Memory: 11.8M
   CGroup: /system.slice/opensmtpd.service
           ├─28101 /usr/sbin/smtpd
           ├─28102 smtpd: klondike
           ├─28103 smtpd: control
           ├─28104 smtpd: lookup
           ├─28105 smtpd: pony express
           ├─28106 smtpd: queue
           └─28107 smtpd: scheduler

The final thing to do is to connect from another machine to the server and check the output.

simon@computer:~$ openssl s_client -starttls smtp -connect yourdomain.com:25
CONNECTED(00000003)                                                                                                                                                                                                                           
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1                                                                                                                                                                       
verify return:1                                                                                                                                                                                                                               
depth=1 C = US, O = Let's Encrypt, CN = R3                                                                                                                                                                                                    
verify return:1                                                                                                                                                                                                                               
depth=0 CN = yourdomain.com
verify return:1  
---                                                        
Certificate chain                                          
 0 s:CN = yourdomain.com                                        
   i:C = US, O = Let's Encrypt, CN = R3                    
 1 s:C = US, O = Let's Encrypt, CN = R3                    
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1                                                   
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1                                                   
   i:O = Digital Signature Trust Co., CN = DST Root CA X3                                                              
---                                                        
Server certificate                                         
-----BEGIN CERTIFICATE-----                                
MIIFEjCCA/qgAwIBAgISA4O0G2Qpj6nXyJYMVicL2zLVMA0GCSqGSIb3DQEBCwUA                                                       
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD                                                       
EwJSMzAeFw0yMTA2MTUxNjQ5NDFaFw0yMTA5MTMxNjQ5NDBaMBExDzANBgNVBAMT                                                       
BmIxeC51azCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN6YKF+LQTPz                                                       
2FxMeho/DG5XoapUTlOcTVSGJf/P9v1ZW6qiOKMDv7y9LRXTQ7RdJWp8fd+XN0HT                                                       
j/gUNh5iOt3n7KEEa5inl47o7eE/KSKUgvwRkHc4HNmPEgdqQ2QrbvGaOFGzDj07                                                       
dA68TNmHk1r3sy1NeJWipTp4qVjyuEVOLShWVBsLl/RL/Esg8XvbSAYwq8LBems/                                                       
K4LFrJ4mZ1IVJYZZ+O6oyyCApjlWVy8fxvFNkeZz9Y5nMp7KQuLwRus/jWj/zRiK                                                       
KKmtnA0tGm/QydJy2ozGcsyr8inWd+7A7SDpe3KiquznM2nj/3xlg2swKVzBx9zr                                                       
Ymh4ibCJdbsCAwEAAaOCAkEwggI9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAU                                                       
BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUj1Dv                                                       
xS3CzTjZ7bRHAf4oT/bsoqYwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsU                                                       
wsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5j                                                       
ci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wEQYDVR0R                                                       
BAowCIIGYjF4LnVrMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEB                                                       
MCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIIBBAYK                                                       
KwYBBAHWeQIEAgSB9QSB8gDwAHYARJRlLrDuzq/EQAfYqP4owNrmgr7YyzG1P9Mz                                                       
lrW2gagAAAF6EMsQnwAABAMARzBFAiEA0wta1rOTMW7dz8zoJKN8vnPOn9vkyePd                                                       
jZt7nzKLk9gCIGV0zwsC1slAZSI48jdTh/QqgWRvh3wBBER/uepFtyJCAHYAfT7y                                                       
+I//iFVoJMLAyp5SiXkrxQ54CX8uapdomX4i8NcAAAF6EMsRKwAABAMARzBFAiAZ                                                       
SrWghf/uJDEMLg0N9K699d/0RmTr3L8uGFYp/9IBtQIhAMfeqE72W59iwARaf7jG                                                       
TmIvVKSbR3Xlls0ZhpJ2TuPoMA0GCSqGSIb3DQEBCwUAA4IBAQArgYBB2rE/PC98                                                       
TMOP/oNFRngEH0e5vVpc75r1CFy8urTbrfIW4NB7xevGK9FNQ2n7mUGmrtN1Gcra                                                       
WcRld3lzbEb/6jjbFO+X2DtAr8Xbn/MzfSCztEAW9P9iTaCjhHbqAiCsU18n13xb                                                       
f9GNkQoU+VSNOXRK6+aMWY/DAkvd0+IJ7qiZwZWFHhFlpPNO35VLSHslD+P02Fsz                                                       
BfNw0XhShOM5rBsilqQE3axr6EjTMQknTqWAj3xU/VEQj1H1VK5GonUyJzDVo7/c                                                       
X9QgI9F5nItsYFAEZLtRsctS/w8TM0JapAlfxVilgu53r1p97q4yjO94GCCB6tgL                                                       
KitlxSDz                                                   
-----END CERTIFICATE-----                                  
subject=CN = yourdomain.com                                      

issuer=C = US, O = Let's Encrypt, CN = R3                

That all looks good.