Email is a pain

I don’t know anyone who’s spent any amount of time working with email and really enjoyed it. There’s a lot to tweak and get wrong with email when you’re interested in not operating an open relay, and not interesting in all of your mail being junked at the receiver’s end. I needed to setup some sort of mail transport for the yttrx mastodon server, and my first thoughts were paid SMTP services like sendgrid etc. I ended up deciding against that as they require a lot of personal information to be shared with the recipients for anti spam reasons, and I wasn’t interested in divulging that type of information to anyone who was interested in signing up for my mastodon instance. So postfix it was.

postfix

Postfix is the defactor homebrew email platform on the internet. It supplanted sendmail back in the early 2000s, and is a very easy to use and extendible piece of email software. I decided that I would need the following:

  • a new server dedicated to email, to reduce configuration complexity
  • some dns records to help with mail server authentication
  • some local accounts

Setting up postfix

apt-get install postfix will do its thing and install & start a postfix service. I added a local user, and updated /etc/aliases to point some admin accounts to the local account, and ensured that the postfix service could accept mail for the correct domains by updating /etc/postfix/main.cf:

myhostname = mail.yttrx.com
mydestination = $myhostname, yttrx.com, mail.yttrx.com, masto.yttrx.com, localhost.localdomain, , localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 138.68.5.70/32

The mynetworks entry allows the mastodon host to be able to relay mail through this server without having to worry about authentication. This isn’t great, but it’ll be fine for now. I didn’t want to setup authentication with saslauthd etc., so this’ll have to do.

Setting up DKIM

DKIM is used to authenticate that mail has originated from a certain set of servers. It uses a private/public keypair to sign and then validate the contents of an email message. The public key is placed in DNS, and the sending server uses the locally hosted private key to sign the email and place a checksum into the header. On the receiving side, the receiving pulls the public key from DNS and verifies the contents of the email to ensure that it matches the header checksums. This gives the receiver confidence that someone else isn’t pretending to be sending mail from whomever is in the From: address of the email.

The /etc/opendkim.conf file looks like:

# This is a basic configuration for signing and verifying. It can easily be
# adapted to suit a basic installation. See opendkim.conf(5) and
# /usr/share/doc/opendkim/examples/opendkim.conf.sample for complete
# documentation of available configuration parameters.

Syslog                  yes
SyslogSuccess           yes
#LogWhy                 no

# Common signing and verification parameters. In Debian, the "From" header is
# oversigned, because it is often the identity key used by reputation systems
# and thus somewhat security sensitive.
Canonicalization        relaxed/simple
Mode                    sv
#SubDomains             no
OversignHeaders         From

# Signing domain, selector, and key (required). For example, perform signing
# for domain "example.com" with selector "2020" (2020._domainkey.example.com),
# using the private key stored in /etc/dkimkeys/example.private. More granular
# setup options can be found in /usr/share/doc/opendkim/README.opendkim.
Domain                  yttrx.com
Selector                2022
KeyFile                 /etc/dkimkeys/2022.private

# In Debian, opendkim runs as user "opendkim". A umask of 007 is required when
# using a local socket with MTAs that access the socket as a non-privileged
# user (for example, Postfix). You may need to add user "postfix" to group
# "opendkim" in that case.
UserID                  opendkim
UMask                   007

# Socket for the MTA connection (required). If the MTA is inside a chroot jail,
# it must be ensured that the socket is accessible. In Debian, Postfix runs in
# a chroot in /var/spool/postfix, therefore a Unix socket would have to be
# configured as shown on the last line below.
#Socket                 local:/run/opendkim/opendkim.sock
Socket                  inet:8891@localhost
#Socket                 inet:8891
#Socket                 local:/var/spool/postfix/opendkim/opendkim.sock

PidFile                 /run/opendkim/opendkim.pid

# Hosts for which to sign rather than verify, default is 127.0.0.1. See the
# OPERATION section of opendkim(8) for more information.
#InternalHosts          192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

# The trust anchor enables DNSSEC. In Debian, the trust anchor file is provided
# by the package dns-root-data.
TrustAnchorFile         /usr/share/dns/root.key
#Nameservers            127.0.0.1

The parts that we are interested in are the Socket inet:8891@localhost and the Domain, Selector and KeyFile entries. Ensure that they match your domain and selectors.

In addition, we’ll have to add a milter for postfix to interface with opendkim with. Under /etc/postfix/main.cf, add:

smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters

and we’re good to go.

DNS Records

I selected mail.yttrx.com as a hostname and ensured that my A & PTR records matched. In addition the MX record for masto.yttrx.com was pointed to mail.yttrx.com, and the SPF record was configured to reference the IP address

dig txt yttrx.com +short
"v=spf1 ip4:146.190.41.218/32 ~all"

I created the dkim records

opendkim opendkim-genkey -D /etc/dkimkeys -d yttrx.com -s 2022

and published the public record to dns

dig txt 2022._domainkey.yttrx.com +short
"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArarhJoSEEYgaQyzUwlC8brtQ0eZPXvg1M6wz/VcuX3GjQVJMNbxBHWzIhvNdFazCnnXyT7/8VNbMTpc9V++F1wCXjajhG4cqVdQ2XymbhRTZ6DCUriUkXubCmEOBORPNttDLAWqXKiYOW6HQGKtYriJmcY790HXD9Q4XxhSlphqauj88KneZCT3" "PyTAQgfs82anGX9vzctQLomAP6EIjd7sxvIczjBbBiwg7hRs7xISErt0koWJ9mpbjtOViTjbuwbs1QObS7qlajQj9UZFPgjmp4VZyxfNESTi88EFZUz+EWuVvUBtG26iRGITsgFudPaDuKnF/uN4mo13lAUUH3QIDAQAB"

Reading mail

As mentioned above, I didn’t want to deal with imap or any other service like that, so I simply use mutt to read mail on the host, which is fine by me.

Configuring mastodon

Update the .env.production file accordingly:

mastodon@tusky:~/live$ grep -i smtp .env.production
SMTP_SERVER=mail.yttrx.com
SMTP_PORT=25
SMTP_AUTH_METHOD=none
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS=auto
[email protected]
mastodon@tusky:~/live$

and then update the services with docker-compose up -d

Afterthoughts

There’s no inbound anti spam measures on the server. I’m sure that I’ll regret this in the future, but for the sake of time I’m getting the server into a mastodon ready state so that it can send out system related emails. Once my admin email gets scraped, I’ll start getting a bunch of spam, and I’ll revisit the setup.