This is sub-part of my series on renovating my homelabs to ring in the roaring ’20s.

I previously wrote about setting up ZFS and, more importantly, ZED, the ZFS Event Daemon. ZED will notify me about ZFS events (like the completion of a daily scrub) via email - but we need to configure a relay so those emails make it off of my server and in to my inbox.

Sections:


Getting Gmail access credentials

Rather than use your email and password directly, it’s better security practice to generate a single use, app-specific password for things like Postfix.

If you have MFA set up on your Google account (and you should!), then you must generate an app password for this to work.

To generate an app password:

  1. Follow this link to myaccount.google.com/apppasswords (type it in if you don’t trust me).

    If you get a “not available for your account” message, you don’t have 2FA enabled. You can enable that here, or just use your password whenever you see “app password” in the rest of this document.

  2. Name the app password whatever you like - I picked “postfix@host”

  3. Note down the app password. It’s 16 characters and looks like AAAA AAAA AAAA AAAA, but we’ll remove the spaces whenever we use it.

This documentation from Linode was helpful for troubleshooting any issues that come up.

Installing and configuring Postfix

Installing Postfix on Fedora is easy:

$ sudo dnf install -y postfix 

Now we need to configure Postfix.

We add our Gmail credentials to /etc/postfix/sasl/sasl_passwd like so:

$ sudo mkdir -p /etc/postfix/sasl
$ sudo vi /etc/postfix/sasl/sasl_passwd
[smtp.gmail.com]:587 [email protected]:app-password
$ sudo postmap /etc/postfix/sasl/sasl_passwd

The files we just created are owned by root, but we should change their access just to be safe:

$ sudo chown -R root:root /etc/postfix/sasl
$ sudo chmod -R 0600 /etc/postfix/sasl

Now we need to tell Postfix to use SASL. We edit /etc/postfix/main.cf and add the following to the end of the file:

$ sudo vi /etc/postfix/main.cf
# Use Gmail to relay
relayhost = [smtp.gmail.com]:587
# Enable SASL
smtp_sasl_auth_enable = yes
# Disable anonymous auth
smtp_sasl_security_options = noanonymous
# Location of sasl_passwd
smtp_sasl_password_maps = hash:/etc/postfix/sasl/sasl_passwd
# Enable STARTTLS encryption
smtp_tls_security_level = encrypt

At this point we should be able to restart Postfix and test it, but I need to configure a few more things to get ZED and Mailx to work perfectly.

Gmail DOES NOT ALLOW the Sender address to be different from your email address. When you relay mail to Gmail, if the Sender does not match Google will rewrite it before relaying it. This may be fine, but if you have aliases or “send mail as” addresses set up in Gmail you will get indeterminate behavior - my mail consistently came from different addresses than the primary one I wanted.

To fix this, we can have Postfix apply some regex and rewrite the headers before relaying them to Gmail.

We write a header_check file:

$ sudo vi /etc/postfix/header_check
/(From:\s*\S*)\s*(<.+>)/ REPLACE $1@host<[email protected]>
$ sudo postmap /etc/postfix/header_check

The special sauce in this regex is the capture groups. By capturing the (From:\s*\S*)(From: .*) and (<.+>), we can preserve the sender name and append the hostname, while also rewriting the sender address. This results in mail going out that transforms like:

Sender that Postfix sees Sender that Gmail sees (after Postfix rewrite)
zed <root@host> zed@host <[email protected]>

Now any mail relayed will have the Sender address rewritten. We can also use sender_canonical_maps to tell postfix to remap all Sender addresses to our Gmail address for mail that it sends directly:

$ sudo vi /etc/postfix/sender_canonical_maps
/.+/    [email protected]
$ sudo postmap /etc/postfix/sender_canonical_maps

This may not be necessary if you only relay through Gmail and never send mail directly.

To enabled these rewrites in Postfix, we add a few lines to main.cf and restart it:

$ sudo vi /etc/postfix/main.cf
sender_canonical_maps = regexp:/etc/postfix/sender_canonical_maps
smtp_header_checks = regexp:/etc/postfix/header_check
$ sudo systemctl restart postfix

Now we can test sending mail!

$ sendmail [email protected]  <<< "test"

If the mail doesn’t show up, check the Postfix logs:

$ sudo journalctl -u postfix

I saw a spool related error, and needed to create the FIFO queue for mail:

$ sudo mkfifo /var/spool/postfix/public/pickup

And then I got a “ding” from Gmail that a new message had arrived :)

Mailx and ZED

This whole endeavor was because I wanted to get emails when my ZFS scrub finished, so I should finish wiring that up now that the server is capable of relaying to Gmail.

ZED is configured via the /etc/zfs/zed.d/zed.rc file, and we just need to make sure a couple of lines are correct:

$ sudo vi /etc/zfs/zed.d/zed.rc
...
ZED_EMAIL_ADDR="[email protected]"
ZED_EMAIL_PROG="mail"
ZED_NOTIFY_VERBOSE=1
...

ZED_NOTIFY_VERBOSE is the only line here that isn’t self-explanatory - it allows us to toggle whether we want notifications even when the pool is healthy, or if we should only be notified when something is wrong. I set it always notify me.

Finally, ZED is invoking mail to create its mail, and trying to pass a Subject on the commandline with -s subject, so we need to install a compatible wrapper to sendmail (Postfix won’t accept that incantation as-is). I found success with mailx, and it’s easily installed.

$ sudo dnf install -y mailx

Mailx doesn’t need any further configuration. At this point, we can test the whole pipeline by triggering a ZFS scrub and watching for the email when it completes!

$ sudo zpool scrub tank

# OR, if the service is installed from my ZFS post -
$ sudo systemctl start [email protected]

Troubleshooting

If the email doesn’t arrive, the logs for one of the services in the chain should indicate the problem:

$ sudo zpool status tank
$ sudo journalctl -u zfs-zed
$ sudo journalctl -u postfix

Read the other articles in this series here: