From 343a2d8f22da424b23cd7e2e47fe9a9abcfc417f Mon Sep 17 00:00:00 2001 From: revsuine Date: Sat, 23 Nov 2024 04:02:06 +0000 Subject: [PATCH 1/4] content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: stop double dkim signing --- .../blog/mail_server_alpine_postfix_dovecot_tutorial/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md index fe9e42e..cce1033 100644 --- a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md +++ b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md @@ -1283,6 +1283,9 @@ smtp-amavis unix - - n - 2 smtp -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_address_mappings + # avoid double dkim signing by setting smtpd_milters to empty + # otherwise will run all milters again after amavis + -o smtpd_milters= ``` The first block tells Postfix to send emails to Amavis, and the second block tells Postfix to run an extra smtpd daemon From 19c742b0b41dde1f5ec44671e736e6413bfd68cc Mon Sep 17 00:00:00 2001 From: revsuine Date: Sat, 23 Nov 2024 20:47:15 +0000 Subject: [PATCH 2/4] content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: postfix-policyd-spf-perl section --- .../index.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md index cce1033..7a67e99 100644 --- a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md +++ b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md @@ -94,10 +94,18 @@ The mail server will be composed of the following software: Mail delivery agent Dovecot + + SPF authentication + postfix-policyd-spf-perl + DKIM authentication and signing OpenDKIM + + DMARC authentication + OpenDMARC + Spam filter Amavis @@ -801,6 +809,8 @@ checks. ## Sender Policy Framework +### Set up your DNS record + Add a TXT record for your root domain with the contents `v=spf1 mx ~all`, like: ```dns @@ -863,6 +873,68 @@ Breaking down the TXT data: +### Get Postfix to validate SPF + +We're going to use a Postfix SMTPd policy server called postfix-policyd-spf-perl to check SPF of incoming emails. +postfix-policyd-spf-perl is very simple and requires almost no configuration. + +Install `postfix-policyd-spf-perl` and create a user, `policyd-spf` for it: + + # apk add postfix-policyd-spf-perl + # adduser -S -s /sbin/nologin -h /dev/null -H policyd-spf + +Explanation of `adduser` flags: + + + + + + + + + + + + + + + + + + + + + + +
OptionExplanation
-SCreate a system user
-s /sbin/nologinSet shell to /sbin/nologin so the user doesn't have a shell
-h /dev/nullSet home directory to /dev/null
-H + Don't create a home directory (if you try to create /dev/null and assign it to + policyd-spf there will be all sorts of permissions issues) +
+ +Now edit `/etc/postfix/master.cf` to tell Postfix to start up the postfix-policyd-spf-perl daemon: + +```conf +policyd-spf unix - n n - 0 spawn + user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl +``` + +Now get Postfix to use postfix-policyd-spf-perl in `/etc/postfix/main.cf` by adding the following lines: + +```conf +smtpd_recipient_restrictions = + permit_mynetworks, + reject_unauth_destination, + check_policy_service unix:private/policyd-spf +policyd-spf_time_limit = 3600 +``` + +postfix-policyd-spf-perl is now set up, and you can test it by sending yourself an email from a mainstream email +provider (which ought to have an SPF record) and checking for the presence of this header: + +``` +Received-SPF: pass (protonmail.com: Sender is authorized to use 'revsuine@protonmail.com' in 'mfrom' identity (mechanism 'include:_spf.protonmail.ch' matched)) +``` + ## DomainKeys Identified Mail ### Configure OpenDKIM @@ -1065,6 +1137,11 @@ non_smtpd_milters = $smtpd_milters This uses the Milter extension, which is something that can be used to process mail; in this case, to add headers to emails relating to DKIM. +You can, again, test this on both incoming and outgoing mail. On outgoing mail, there should be a `DKIM-Signature:` +header present. On incoming mail from domains implementing DKIM, there should be a +`Authentication-Results: master.revsuine.xyz;` header (obviously replacing `master.revsuine.xyz` with your hostname) +indicating whether or not the email has passed DKIM authentication. + ## Domain-based Message Authentication, Reporting, and Conformance ### Ensure your domains are aligned in email headers From 4d580d21e8142cc5d4764693d0a4035cecac0bc9 Mon Sep 17 00:00:00 2001 From: revsuine Date: Sat, 23 Nov 2024 23:07:02 +0000 Subject: [PATCH 3/4] content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: opendmarc section --- .../index.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md index 7a67e99..6a05ccb 100644 --- a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md +++ b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md @@ -1274,6 +1274,120 @@ The `fo` tag indicates when you would like to receive reports. The options are: +### OpenDMARC + +We can use software called OpenDMARC to enforce DMARC policies for incoming mail. OpenDMARC is another milter. Let's +install it and enable its service: + + # apk add opendmarc + # rc-update add opendmarc + # rc-service opendmarc start + +Edit the OpenDMARC config at `/etc/opendmarc/opendmarc.conf`. + +Change + +```conf +AuthservID HOSTNAME +``` + +to + +```conf +AuthservID OpenDMARC +``` + +This is so that the `Authentication-Results` header from OpenDKIM authentication. This will also make it clear which +program adds which `Authentication-Results` header. + +Add the following line, replacing `mail.domain.com` with your *hostname* (so in [my +instance](#a-note-on-my-dns-records), this is `master.revsuine.xyz`). + +```conf +TrustedAuthservIDs mail.domain.com +``` + +Enable `RejectFailures`, which means your server will comply with `p=reject` in DMARC DNS records. + +```conf +RejectFailures true +``` + +You also probably want to enable `RequiredHeaders`, which rejects emails that don't conform to RFC5322 standards, e.g. +are missing a `From:` header. + +```conf +RequiredHeaders true +``` + +In case external SPF validation fails (as in, no SPF results are placed in the message header), you probably want to +add + +```conf +SPFSelfValidate true +``` + +which tells OpenDMARC to perform the SPF check itself if it can't find SPF results in the message header. + +Now provide OpenDMARC with a socket to use for communication with sendmail. We will use a TCP socket on port 8893: + +```conf +Socket inet:8893@localhost +``` + +For a Unix socket, you'd use the following format: + +```conf +Socket local:/var/run/opendmarc/opendmarc.sock +``` + +By default, you will have the line + +```conf +IgnoreHosts /etc/opendmarc/ignore.hosts +``` + +in `/etc/opendmarc/opendmarc.conf`. This tells OpenDMARC to not authenticate the list of hosts in +`/etc/opendmarc/ignore.hosts`. An example `ignore.hosts` is + + 127.0.0.1 + 93.113.25.226 + +Keep in mind that if you have specified `IgnoreHosts`, this file needs to exist in order for OpenDMARC to run. If you +have the option set, make sure to `touch /etc/opendmarc/ignore.hosts` (or whatever filepath you've specified). +Alternatively, comment out this option in order to use the default, which is to not authenticate mail coming from +127.0.0.1. + +Restart OpenDMARC for these changes to take effect: + + # rc-service opendmarc restart + +To have Postfix use the OpenDMARC milter, it's simple as adding the socket to the `smptd_milters` and +`non_smtpd_milters` variable in `/etc/postfix/main.cf`: + +```conf +milter_default_action = accept +milter_protocol = 6 +smtpd_milters = inet:127.0.0.1:8891,inet:127.0.0.1:8893 +non_smtpd_milters = $smtpd_milters +``` + +Restart Postfix for the changes to take effect: + + # rc-service postfix restart + +And when you receive emails from a legitimate source that implements DMARC, you should see the following headers in +your emails: + +``` +Received-SPF: pass (protonmail.com: Sender is authorized to use 'revsuine@protonmail.com' in 'mfrom' identity (mechanism 'include:_spf.protonmail.ch' matched)) receiver=master.revsuine.xyz; identity=mailfrom; envelope-from="revsuine@protonmail.com"; helo=mail-40130.protonmail.ch; client-ip=185.70.40.130 +DMARC-Filter: OpenDMARC Filter v1.4.2 master.revsuine.xyz 88CFF1288D1 +Authentication-Results: OpenDMARC; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com +Authentication-Results: OpenDMARC; spf=pass smtp.mailfrom=protonmail.com +Authentication-Results: master.revsuine.xyz; + dkim=pass (2048-bit key; secure) header.d=protonmail.com header.i=@protonmail.com header.a=rsa-sha256 header.s=protonmail3 header.b=nc4YWVM/ +``` + ### Test SPF, DKIM, and DMARC You can use [mail-tester.com](https://www.mail-tester.com/) and send an email from your domain to check that SPF, DKIM, From 3b2361ef62f36b1bc3fa10fd58e4a72defbd0aa5 Mon Sep 17 00:00:00 2001 From: revsuine Date: Sun, 24 Nov 2024 00:50:18 +0000 Subject: [PATCH 4/4] content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: pigeonhole section --- .../index.md | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md index 6a05ccb..aa48f4a 100644 --- a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md +++ b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md @@ -224,6 +224,7 @@ following TCP ports are open on your firewall: | 465 | Email message submission over TLS | | 587 | Email message submission | | 993 | IMAPS (IMAP over TLS) | +| 4190 | ManageSieve | ## Obtain a TLS certificate @@ -1307,6 +1308,11 @@ instance](#a-note-on-my-dns-records), this is `master.revsuine.xyz`). TrustedAuthservIDs mail.domain.com ``` +This specifies that OpenDMARC should trust authentication results from `mail.domain.com`. Otherwise you would get the +following error message in your syslog: + + ignoring Authentication-Results at 1 from mail.domain.com + Enable `RejectFailures`, which means your server will comply with `p=reject` in DMARC DNS records. ```conf @@ -1388,6 +1394,10 @@ Authentication-Results: master.revsuine.xyz; dkim=pass (2048-bit key; secure) header.d=protonmail.com header.i=@protonmail.com header.a=rsa-sha256 header.s=protonmail3 header.b=nc4YWVM/ ``` + + ### Test SPF, DKIM, and DMARC You can use [mail-tester.com](https://www.mail-tester.com/) and send an email from your domain to check that SPF, DKIM, @@ -1619,6 +1629,136 @@ X-Spam-Status: Yes, score=999.802 tagged_above=2 required=6.2 URIBL_ZEN_BLOCKED_OPENDNS=0.001] autolearn=no autolearn_force=no ``` +# Pigeonhole + +Dovecot can do server-side mail filtering with sieve scripts. These are user scripts that can perform actions on mail +based on particular criteria, e.g. + +```sieve +require "fileinto"; + +if address :is "to" "postmaster@revsuine.xyz" { + fileinto "Postmaster"; +} +``` + +Places mail in the `Postmaster` folder if the `To:` field is `postmaster@revsuine.xyz`. You also can do things +unconditionally, like + +```sieve +redirect postmaster@revsuine.xyz; +``` + +unconditionally redirects all mail to `postmaster@revsuine.xyz`. + +Sieve scripts can be both per-user and system-wide. + +For more examples, [this page](https://doc.dovecot.org/main/howto/sieve.html) has some good examples. + +## Installing and setting up Pigeonhole + +To use Sieve, install `dovecot-pigeonhole-plugin`: + + # apk add dovecot-pigeonhole-plugin + +Then edit `/etc/dovecot/conf.d/20-lmtp.conf`, and add the `sieve` plugin like so: + +```conf +protocol lmtp { + # Space separated list of plugins to load (default is global mail_plugins). + mail_plugins = $mail_plugins sieve +} +``` + +To configure Pigeonhole and sieve, edit `/etc/dovecot/conf.d/90-sieve.conf`. Sieve's options will be configured in the +`plugin {}` block in this file. + +We can set the location of user sieve scripts with the `sieve` option. + +```conf +sieve = file:~/sieve;active=~/.dovecot.sieve +``` + +means that `~/sieve` is a directory of sieve scripts, whilst `~/.dovecot.sieve` is a symlink to the "active" one, e.g. + +``` +sieve +├── script1.sieve +├── script2.sieve +└── script3.sieve +``` + +could be your `~/sieve/` directory, and to make `script2.sieve` active, you would do + + $ ln -s ~/sieve/script2.sieve ~/.dovecot.sieve + +`sieve_before` defines a directory of sieve scripts which will be executed *prior* to any user scripts. e.g. + +```conf +sieve_before = /etc/dovecot/sieve +``` + +means that the sieve scripts in `/etc/dovecot/sieve` will be executed first, then the user's personal scripts at +`~/.dovecot.sieve`. + +You can specify multiple directories in order, like so: + +```conf +sieve_before = /var/lib/dovecot/sieve.d/ +sieve_before2 = ldap:/etc/sieve-ldap.conf;name=ldap-domain +sieve_before3 = /etc/dovecot/sieve +``` + +etc. The `sieve_after` option also exists, and works the same way. + +This is not the same as `sieve_default`, which is *overridden* by user sieve scripts and only executes when a user has +no sieve script. + +## ManageSieve + +Users can configure their own user sieve scripts using a protocol called ManageSieve. Like how IMAP allows users to +read their emails without having shell access to the mail server, ManageSieve allows users to write sieve scripts +without requiring shell access. + +To enable ManageSieve, edit `/etc/dovecot/conf.d/20-managesieve.conf`. Make sure the following line is uncommented: + +```conf +protocols = $protocols sieve +``` + +By default, ManageSieve will listen on port 4190. + +## Sieve scripts for spam filtering + +Let's use a system-wide sieve script to file SpamAssassin-marked spam into a Spam folder. Create an +`/etc/dovecot/sieve/` directory, and add it to your `sieve_before` settings: + +```conf +plugin { + ... + sieve_before = /etc/dovecot/sieve/ + ... +} +``` + +Now create a new sieve script, `/etc/dovecot/sieve/spam_folder.sieve`: + +```sieve +require ["fileinto", "mailbox"]; +if header :contains "X-Spam-Flag" "YES" { + fileinto :create "Spam"; +} +``` + +Replace `"Spam"` with the name of your spam folder. If it's a subfolder, e.g. + +``` +Inbox +└── Spam +``` + +you would write `"Inbox.Spam"` in that case. + # Miscellaneous suggestions You may want to get your domain whitelisted on [dnswl.org](https://www.dnswl.org/), an email whitelist service where