Setting up a mail server (WIP)
1. Introduction
I use Postfix as a SMTP and Dovecot (with Pigeonhole (Sieve)) as an IMAP server. ClamAV for an antivirus. For anti-spam I use SpamAssassin. For DKIM and DMARC — OpenDKIM and OpenDMARC respectively. I could use rspamd instead of the latter three, but it doesn't work on Raspberry Pi.
It is vital to make the DKIM, DMARC and SPF DNS records. Also, if you want your mail server to be trusted by every other mail servers then you should get a static IP-address if you don't yet. And you have to ask your ISP to edit PTR DNS record for your static IP-address to point to your domain.
Unfortunately for me I don't have neither, and I'm afraid that even if I get the static IP-address, my ISP won't edit PTR record, because that's available only for bussiness customers.
Server is configured in a simple way using PAM (real system users) with user's passwords and with mail stored in ~/Maildir.
2. Installing
You need to install following packages: postfix
, dovecot
, pidgeonhole
(or could be dovecot-sieve
), clamav
, opendkim
, opendmarc
and spamassassin
.
3. Postfix SMTP server
Its configuration files are in directory /etc/postfix
. First we need to work with main.cf
file. Then configure services in master.cf
. Also I'll show you how to make aliases for users.
3.1. main.cf
Set myhostname
to a hostname of a server (e.g. mail.example.org
). Set mydomain
to your domain name (e.g. example.org
). Set myorigin
to $mydomain
to set origin of mail being sent from your server.
mydestination
is a list of domains that are delivered through a local transport. If server should go outside then this parameter must include $mydomain
alongside names for the local machine. E.g. $myhostname, localhost, $mydomain, mail.$mydomain
.
local_recipient_maps
are lookup tables with all names and/or addresses of local recipients. In my case it set to unix:passwd.byname $alias_maps
.
I have inet_interfaces = all
to listen on all the interfaces.
In mynetworks
, as stated in a Postfix's manual, we specify a list of “trusted” clients that have more privileges than “strangers”. In particular, such clients are allowed to relay mail through Postfix. I have it set to localhost and my LAN.
In alias_maps
we specify a list of lookup tables that contain aliases for existing users. And in alias_database
just add $alias_maps
. alias_database
is, as stated in a manual, separate because not all the tables specified with $alias_maps
have to be local files.
recipient_delimeter = +
. Here we set a delimeter to a plus sign (that's just a usual practice that I obeyed).
I use a Maildir-style mailboxes, so home_mailbox
is set to Maildir/
(slash is necessary).
We use Dovecot, so mailbox_transport
should be set to lmtp:unix:private/dovecot-lmtp
. Here we point to where Dovecot LMTP server listens, in our case it is a UNIX-socket.
Optionaly, you can set inet_protocols
to IP versions used by you, I set it just to ipv4
for a quite legitimate reason of not having IPv6 address. :)
Here are all the modified parameters listed:
myhostname = mail.example.org mydomain = example.org myorigin = $mydomain inet_interfaces = all mydestination = $myhostname, localhost, $mydomain, mail.$mydomain local_recipient_maps = unix:passwd.byname $alias_maps mynetworks = localhost, 192.168.0.0/24 alias_maps = hash:/etc/postfix/aliases alias_database = $alias_maps recipient_delimiter = + home_mailbox = Maildir/ mailbox_transport = lmtp:unix:private/dovecot-lmtp inet_protocols = ipv4
Next I'll cover how to make encryption working, set up milters (mail filters (i.e. OpenDKIM and OpenDMARC)), and restrictions.
3.2. master.cf
Here are all needed lines to be added or modified:
smtp inet n - n - - smtpd -o content_filter=spamassassin submission inet n - n - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes smtps inet n - n - - smtpd -o content_filter=spamassassin -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes spamassassin unix - n n - - pipe user=spamd argv=/bin/vendor_perl/spamc -e /sbin/sendmail -oi -f ${sender} ${recipient}
3.3. User aliases
User aliases are in aliases
file. They has a form "<alias>: <username>
", e.g. me: arav
. Where username
may be other alias. After modifications you need to run newaliases
program to update aliases.db
database file.
3.4. Starting Postfix
To start a Postfix service on systemd-based Linux distro run systemctl start postfix
. To make Postfix run on every boot run systemctl enable postfix
.
4. Dovecot POP3/IMAP server with Sieve mail filter
5. SpamAssassin spam filter
6. OpenDKIM signing and verifying filter
On ArchLinux OpenDKIM is unable to write in /run
, so I created /var/spool/opendkim
directory for it.
6.1. opendkim.conf
Well, that's main config file
KeyTable refile:/etc/opendkim/keytable SigningTable refile:/etc/opendkim/signingtable InternalHosts refile:/etc/opendkim/internal-hosts Socket local:/var/spool/opendkim/opendkim.sock PidFile /var/spool/opendkim/opendkim.pid UMask 000 UserID opendkim:opendkim Mode sv SubDomains yes Canonicalization relaxed/simple Syslog yes SyslogSuccess yes LogWhy yes SoftwareHeader yes
I myself set up a multi-domain variant just in case. So, here we have two main tables: KeyTable
and SigningTable
. Those files tells OpenDKIM where to find keys and what domains to sign. You may use one key for all domains or generate keys for each domain.
InternalHosts
tells OpenDKIM what hosts should be signed rather than verified.
Socket
tells where to listen to connections, in this case we use UNIX sockets.
Mode
selects operating mode(s). In our case we have two modes: (s)igner and (v)erifier.
SubDomains
set to yes tells that we allow subdomains of our domains to be signed and verified.
Canonicalization
selects the canonicalization method(s) to be used with signing. We set relaxed for header and simple for body. I don't fully understand it and just use what suggested.
Below are logging options that tells to write in syslog.
With SoftwareHeader
set to yes OpenDKIM will be always adding "DKIM-Filter" header field.
6.2. Generating keys
opendkim-genkey -r -s myselector -b 2048 -d example.com
This command will generate a key pair stored in files "myselector.private" and "myselector.txt" for a given domain.
-r
restricts the key to emails use only. -s
is a name of selector.-b
is the size of the key in bits. -d
is our domain.
Name of a selector is usually a mail
, but that's just what I use, you can choose whatever you want.
6.3. Populating KeyTable and SigningTable
KeyTable has following structure (a line per domain):
myselector._domainkey.example.com example.com:myselector:/etc/opendkim/myselector.privateAnd SigningTable this one:
*@example.com myselector._domainkey.example.com
6.4. internal-hosts file
As stated above in this file we put hosts whose mail should be signed rather than verified. And its structure is the following:
127.0.0.1 192.168.0.0/24
127.0.0.1
is necessary to be there according to a manual.
6.5. Starting OpenDKIM
systemctl start opendkim
and systemctl enable opendkim
to start and enable OpenDKIM service to run on OS start up if you got Poetteringed just like me. :)
7. OpenDMARC email policy filter
Its configuration lies in /etc/opendmarc/opendmarc.conf
and is fully documented. Here are the options I changed:
AuthservID OpenDMARC FailureReports true FailureReportsBcc admin@example.org FailureReportsSentBy admin@example.org IgnoreAuthenticatedClient yes RejectFailures true RequiredHeaders yes Socket unix:/var/spool/opendmarc/opendmarc.sock SoftwareHeader true SPFSelfValidate true Syslog true TrustedAuthservIDs mail.example.org,example.org UMask 002
What's in a Socket
option should be added to Postfix's smtpd_milters
and non_smtpd_milters
.
Creating DMARC DNS record covered in 7.4.
8. DNS records
8.1. MX and A/AAAA
It's good to have a dedicated A (IPv4 address) or AAAA (IPv6 address) record for a mail server's hostname instead of a CNAME record so other servers won't need to do two DNS requests. Hostname is usually mail.example.org if there's just one server, you can call it whatever you want. Remind you that we set it in Postfix in myhostname
parameter.
And A record looks like this:
mail IN 86400 A 203.0.113.4
Where mail
is a hostname, 86400 is a TTL of a record in seconds.
Next we need to add a MX (mail exchanger) record that looks like this:
MX 10 mail.example.org.
Here 10 is a priority of a record. The lower a number the higher a priority.
A period at the end of the hostnames is necessary in DNS records.
8.2. PTR
PTR is a reverse DNS record that stands for pointer and is used to “bind” a hostname to IP-address. Mail servers looks for this record and check so this name equals to a hostname provided in EHLO. Most servers will reject your mail if your PTR looks something like 1.2.3.4.pppoe.someisp.net or not set at all.
There are three ways to set this record: ask your hosting or internet-provider, or get your own Autonomous System (:^)).
Example of this record:
1 IN PTR mail.example.org.
8.3. SPF
SPF stands for Sender Policy Framework and in my case it looks exactly like this:
v=spf1 +a +mx -all
So, v
is a version of a protocol. +a +mx
means that only servers specified in the A and MX DNS records could send email, and -all
that no one else could do that.
8.4. DMARC
DMARC stands for Domain-based Message Authentication Reporting and Conformance. And its DNS record could be like this one that I use:
_dmarc IN TXT "v=DMARC1; p=reject; rua=mailto:admin@example.org; ruf=mailto:admin@example.org"
v
is a version of a protocol.
p
is a default policy that could be set to none
, quarantine
and reject
. I chose to reject
mail that comes from «me” if there's something wrong with a origin of a message. If you could get email from subdomains then you need to set sp
as well.
rua
is an address for the reports and ruf
is for the forensic reports.
8.5. DKIM
In 5.2 we generated a key pair for our domain and now we'll take what's inside a myselector.txt
file and add it to our DNS.
DKIM DNS record looks like this:
myselector._domainkey IN TXT ( "v=DKIMv1; k=rsa; s=email; p=<public key goes here>" )
By the way, brackets are used in case a content of a record doesn't fit on one line.
9. Setting up a ClamAV antivirus
All you need to make it work together with Postfix is to add /run/clamav/milter.sock
to smtpd_milters
and non_smtpd_milters
options in Postfix, also make some changes in configs of ClamAV.
In clamav-milter.conf
you need the following:
MilterSocket unix:/run/clamav/milter.sock ClamdSocket unix:/run/clamav/clamd.ctl
Also, in case you need ClamAV to add headers also in case a message is free of viruses add AddHeader Add
or AddHeader Replace
option. The difference between them is detaily described in config file itself.
Before starting ClamAV you need to update its virus definitions with freshclam
util. Also, enable and start clamav-freshclam
systemd service to keep definitions recent.
I don't know how it is in other distros, but, for whatever reason, an Arch Linux's package doesn't have a systemd service file for the ClamAV milter. So I just copy it here from ArchWiki:
[Unit] Description='ClamAV Milter' After=clamav-daemon.service [Service] Type=forking ExecStart=/usr/bin/clamav-milter --config-file /etc/clamav/clamav-milter.conf [Install] WantedBy=multi-user.target
Save it as /usr/lib/systemd/system/clamav-milter.service
and run systemctl daemon-reload
.
Next you need to enable and start clamav-daemon
and clamav-milter
.