Postfix server setup
Server setup | |
← Previous | Next → |
MDA (Dovecot) | Desktop mail forwarding |
This step of the howto assumes you've already set up Dovecot to provide authentication and local delivery services. This page describes the setup for Postfix, TLS, and Amavis + SpamAssassin + ClamAV. Dovecot, and Sieve setup are on the Dovecot server setup page. Squirrelmail setup is described in the Web server setup page, along with the HTTPS configuration.
The basic sources for setting up Postfix and Dovecot are the basic Postfix howto and the Postfix virtual user howto on the Ubuntu wiki.
Basic installation
- Install postfix
root@server:~# aptitude install postfix mailx
- Update
/etc/postfix/main.cf
compatibility_level = 2 mydomain = domain1.com # Change this line for your domain myorigin = $mydomain myhostname = mail.$mydomain # Change this for your mailserver's name mydestination = $myhostname localhost.$mydomain localhost relaydomains = $mydomain relayhost = mynetworks = 127.0.0.0/8 192.168.1.0/24 smtpd_recipient_restrictions = permit_mynetworks reject_unauth_destination ## Anti-spammer measures # Don't talk to mail systems that don't know their own hostname. smtpd_helo_restrictions = reject_unknown_helo_hostname # Don't accept mail from domains that don't exist. smtpd_sender_restrictions = reject_unknown_sender_domain # Block clients that speak too early. smtpd_data_restrictions = reject_unauth_pipelining proxy_interfaces = 1.2.3.4 # My external IP smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) biff = no # appending .domain is the MUA's job. append_dot_mydomain = no # Uncomment the next line to generate "delayed mail" warnings #delay_warning_time = 4h alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mailbox_size_limit = 0 recipient_delimiter = .+ # Make sure you change this from a '+' inet_interfaces = all home_mailbox = Maildir/ virtual_mailbox_domains = /etc/postfix/vhosts virtual_mailbox_base = /var/vmail virtual_mailbox_maps = hash:/etc/postfix/vmaps virtual_minimum_uid = 1000 virtual_uid_maps = static:5000 virtual_gid_maps = static:5000 virtual_alias_maps = hash:/etc/postfix/valiases
- Tell Postfix which IPv6 address it should use when sending messages. Add the line
-o smtp_bind_address6
below thesmtp
statement inmaster.cf
smtp unix - - y - - smtp -o smtp_bind_address6=1:2:3::25
- Tell Postfix about the domains it should service by creating
/etc/postfix/vhosts
domain1.com domain2.com
- Tell Postfix about the users in those domains by creating
/etc/postfix/vmaps
user1@domain1.com domain1.com/user1/mail/ user2@domain1.com domain1.com/user2/mail/ user1@domain2.com domain2.com/user1/mail/ user3@domain2.com domain2.com/user3/mail/
- Then create the hash file:
root@server:~# postmap /etc/postfix/vmaps
- (this command needs to be repeated every time
/etc/postfix/vmaps
is changed)
- Create aliases for the users in
/etc/postfix/valiases
root@domain1.com user1@domain1.com postmaster@domain1.com user1@domain1.com postmaster@domain2.com user1@domain1.com user2@domain1.com user2@domain2.com root@server.domain1.com user1@domain1.com root@desktop.domain1.com user1@domain1.com
- Then create the hash file:
root@server:~# postmap /etc/postfix/valiases
- (this command needs to be repeated every time
/etc/postfix/valiases
is changed)
- Create
/etc/aliases
root: user1 clamav: root amavis: root postmaster: root
- and then create the database
root@server:~# newaliases
- The virtual mailbox directory structure should be created for each user when they receive their first email.
- Get Postfix to reload the updated configuration files
root@server:~#postfix reload
- Test that Postfix is running properly. First, use
telnet
to connect to postfix
root@server:~# telnet localhost 25
- Postfix will respond
Trying 127.0.0.1... Connected to mail.server.org. Escape character is '^]'. 220 localhost.localdomain ESMTP Postfix (Ubuntu)
- then type the following
ehlo localhost mail from: root@localhost rcpt to: user1@domain1.com data Subject: My first mail on Postfix Hi, Are you there? regards, Admin . (Type the .[dot] in a new Line and press Enter ) quit
- then check that the message is delivered properly into the correct mailbox
root@server:~# ls /home/vmail/domain1/user1
- Then test that all the mail transport is working, using the
mail
command line utility
root@server:~# mail user2@domain1.com
- and again, check that the mail appears in the filesystem.
Adding TLS
Now to enable TLS, which encrypts communication between MTAs, preventing eavesdropping. We'll use the certificates generated previously. All that's needed is to modify the /etc/postfix/main.cf
file to point to the certificates.
# TLS parameters smtpd_tls_cert_file=/etc/letsencrypt/live/webmail.domain.tld/fullchain.pem smtpd_tls_key_file=/etc/letsencrypt/live/webmail.domain.tld/privkey.pem # smtpd_use_tls=yes smtpd_tls_security_level=may smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache
and then reload Postfix
root@server:~# postfix reload
Check it's working by telnetting into Postfix
root@server:~# telnet localhost 25 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 mail.domain.tld ESMTP Postfix (Ubuntu) ehlo localhost
and check that the line
250-STARTTLS
appears in the output. Use quit
to end the session.
Clients should connect on port 25, using TLS security.
Allowing off-site users with SASL
As it stands, the server will only forward mail sent from the LAN. That's fine, but if you want to send mail from other devices via this SMTP server, you need to put some authentication on it. SASL is the general term for authentication on mail servers.
Assuming you have Dovecot set up with some users (up to the 'Testing Dovecot' stage, you can use Dovecot's authentication engine for authenticating users of the SMTP server.
First, extend the Dovecot configuration file /etc/dovecot/conf.d/10-master.conf
with the unix_listener
section below so that it accepts authentication requests from Postfix.
service auth { unix_listener auth-userdb { mode = 0660 user = vmail group = vmail } # Postfix smtp-auth unix_listener /var/spool/postfix/private/auth { group = postfix user = postfix mode = 0666 } # Auth process is run as this user. #user = $default_internal_user } service auth-worker { # Auth worker process is run as root by default, so that it can access # /etc/shadow. If this isn't necessary, the user should be changed to # $default_internal_user. #user = root user = $default_internal_user }
Next, make Postfix authenticate users. Modify /etc/postfix/main.cf
to include the lines
smtpd_sasl_auth_enable = yes smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_recipient_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
Restart both Dovecot and Postfix:
root@server:~# service dovecot reload root@server:~# postfix reload
Mail clients can now post messages through the SMTP server, so long as they give a valid username/password combination that's recognised by Dovecot.
Amavis + ClamAV + SpamAssassin
Both ClamAV and SpamAssassin need to be introduced into Postfix's message-handlng queue to do their things with messages. The idea is that Postfix will take the message out of it's queue and hand it over the filter program (listening on the loopback interface), who will return it the same way after doing its thing. The trouble is, Postfix can only have one such filter, so Amavis is needed to act as a wrapper around them both. Here is how to do it. For instructions, I basically followed the ones on the Ubuntu wiki.
- Create the file
/etc/mailname
containing
mydomain.com
- Install the packages:
root@server:~# aptitude install amavisd-new spamassassin clamav-daemon pyzor razor libnet-dns-perl libmail-spf-perl root@server:~# aptitude install arj bzip2 cabextract cpio file gzip lhasa nomarch pax rar unrar unzip zip zoo root@server:~# aptitude install lrzip liblz4-tool rpm2cpio unrar-free ripole p7zip-full p7zip-rar lzop
ClamAV
- Add the ClamAV user to the Amavis group, and vice versa
root@server:~# adduser clamav amavis root@server:~# adduser amavis clamav
- Change
/etc/clamav/clamd.conf
to contain
# AllowSupplementaryGroups false AllowSupplementaryGroups true
SpamAssassin
- Edit
/etc/default/spamassassin
to activate the Spamassassin daemon. ChangeENABLED=0
to:
ENABLED=1
- and, to allow automatic rule updates, change
CRON=1
CRON=1
- Tell SystemD to keep Spamassassin running. Add these lines to the
[Service]
section of/lib/systemd/system/spamassassin.service
:
Restart=on-failure RestartSec=3
Then start Spamassassin:
root@server:~# systemctl restart spamassassin.service
Amavis
- Edit
/etc/amavis/conf.d/15-content_filter_mode
to enable spam filtering
@bypass_virus_checks_maps = ( \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re); @bypass_spam_checks_maps = ( \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
- Edit both
/etc/amavis/conf.d/50-user
to determine the fate of spam and virus-laden mails
$final_virus_destiny = D_PASS; $final_banned_destiny = D_PASS; $final_spam_destiny = D_PASS; $final_bad_header_destiny = D_PASS;
- (in other words, don't discard any mail for the moment)
- Restart Amavis
root@server:~# systemctl restart amavis.service
- Once Amavis is working and correctly identifying spam and malware, you can adjust the settings in
/etc/amavis/conf.d/50-user
to classify more spam, and reject the most spammy. (Rejected messages are quarantined, with a notification going topostmaster@domain.tld
.)
$enable_dkim_verification = 1; # was disabled to prevent warning $sa_tag_level_deflt = 0.0; # add spam info headers if at, or above that level $sa_tag2_level_deflt = 1.5; # add 'spam detected' headers at that level $sa_kill_level_deflt = 2.5; # triggers spam evasive actions $sa_dsn_cutoff_level = 6.0; # spam level beyond which a DSN is not sent, was 10 $final_bad_header_destiny = D_PASS; # False-positive prone (for spam) $final_virus_destiny = D_DISCARD; # (data not lost, see virus quarantine) $final_banned_destiny = D_REJECT; # D_REJECT when front-end MTA $final_spam_destiny = D_DISCARD; # Where quarantine notifications go $virus_admin = "postmaster\@$mydomain"; # due to D_DISCARD default $spam_admin = "postmaster\@$mydomain"; # due to D_DISCARD default # Where quarantine notifications appear to come from $mailfrom_notify_admin = "contentfilter\@$mydomain"; $mailfrom_notify_recip = $mailfrom_notify_admin; $mailfrom_notify_spamadmin = $mailfrom_notify_admin; $hdrfrom_notify_sender = $mailfrom_notify_admin; $hdrfrom_notify_admin = $mailfrom_notify_admin;
Spam will be quarantined and notifications sent to postmaster@domain.tld
. The notification will contain lines like:
Content type: Spam Internal reference code for the message is 26829-01/ZKBMjq6S2Dsi ... The message has been quarantined as: Z/spam-ZKBMjq6S2Dsi.gz The message WAS NOT relayed to: <someone@domain.tld>: 250 2.7.0 Ok, discarded, id=26829-01 - spam
You can release the quarantined message with amavisd-release
, like so:
root@server:~# amavisd-release Z/spam-ZKBMjq6S2Dsi.gz
or send it message to another user by giving their address:
root@server:~# amavisd-release Z/spam-ZKBMjq6S2Dsi.gz admin@domain.tld
Delete old spam by creating the file /etc/cron.daily/delete_old_spam
containing
#!/bin/sh find /var/lib/amavis/virusmails/ -type f -mtime +31 -delete
and make it executable
root@server:~# chmod u+x /etc/cron.daily/delete_old_spam
Postfix
- Now, include Amavis in Postfix's message queue. Modify
/etc/postfix/main.cf
to use Amavis
# Spam and virus filtering content_filter=smtp-amavis:[127.0.0.1]:10024
- Modify
/etc/postfix/master.cf
to handle the return from Amavis
# # Amavis spam and virus filtering # # Sending to Amavis # smtp-amavis unix - - - - 2 smtp smtp-amavis unix - - n - 2 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes -o max_use=20 # Returning from Amavis # 127.0.0.1:10025 inet n - - - - smtpd 127.0.0.1:10025 inet n - n - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_delay_reject=no -o smtpd_client_restrictions=permit_mynetworks,reject -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions=reject_unauth_pipelining -o smtpd_end_of_data_restrictions= -o mynetworks=127.0.0.0/8 -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -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_milters,no_address_mappings
- Add the two
-o …
lines tomaster.cf
, to prevent Spamassassin's reports being checked for being spam.
pickup unix n - y 60 1 pickup -o content_filter= -o receive_override_options=no_header_body_checks
- Reload the Postfix configuration:
root@server:~# postfix reload
Enable Dovecot LDA
We now want Postfix to use the Dovecot LDA for local mail delivery and filtering.
- Modify
/etc/postfix/master.cf
to include the line
# # dovecot as a local delivery agent # dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -a ${recipient} -d ${user}@${nexthop}
- Modify
/etc/postfix/main.cf
to include the line
## Local Delivery dovecot_destination_recipient_limit = 1 mailbox_transport = dovecot virtual_transport = dovecot
- Reload the Postfix configuration:
root@server:~# postfix reload
Block addresses that are known to spammers
The recipient_delimiter
option on Postfix is rather neat. Given a recipient_delimiter
of '.
', messages to user.ext1@domain1.com
are handled in an interesting way. If user.ext1@domain1.com
is a valid user, the message is put in their mailbox. If user.ext1@domain1.com
isn't a valid user, Postfix will drop everything in the username after the delimiter and retry the delivery to user@domain1.com
. This means that you can create specialised-use email addresses and have them all come to the same mailbox. It also means you can create 'disposable' email addresses. That also requires some method of disposing of those addresses when the time comes.
The disposal is done by checking both the headers of incoming emails the RCPT TO
addresses, and discarding or rejecting those that are to a disposable address.
- Add the following to
/etc/postfix/main.cf
# block addresses that are known to spammers header_checks = regexp:/etc/postfix/header_checks smtpd_recipient_restrictions = check_recipient_access hash:/etc/postfix/recipient_checks # Add this directive permit_sasl_authenticated permit_mynetworks reject_unauth_destination
- Create the file
/etc/postfix/header_checks
if /^To:/ /^To:.*(user1\.ext1@domain1\.com)/ reject Recipient address rejected: User unknown in virtual mailbox table /^To:.*(user1\.ext2@domain1\.com)/ reject Recipient address rejected: User unknown in virtual mailbox table endif
- Create the file
/etc/postfix/recipient_checks
# Reject messages with these recipient addresses user.banned2@example.com REJECT user.banned@example.com REJECT user.banned3@example.com REJECT user.banned4@example.com REJECT person.banned@example.com REJECT
- Reload the configuration
root@server:~# postmap /etc/postfix/recipient_checks root@server:~# postfix reload
Configure Postfix as a backup MX server for other domains
Having backup mail servers is always good, so it's only fair to offer the same service to others in exchange. This is how you do it.
- In
/etc/postfix/main.cf
, modifysmtpd_recipient_restrictions
and add therelay_domains
andsmtpd_relay_restrictions
smtpd_recipient_restrictions = check_recipient_access hash:/etc/postfix/recipient_checks # $mydomain included as a virtual domain below relay_domains = otherdomain1.com otherdomain2.com # Include the relay_recipients to only relay certain addresses at the relayed domains # relay_recipient_maps = hash:/etc/postfix/relay_recipients smtpd_relay_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination
- Reload the configuration
root@server:~# postfix reload
However, there is a problem with queue lifetimes. With this setup, messages held in the backup role will only be stored on this server for the standard three days. If you're acting as backup for a privately-run mail server, this may not be long enough (the other site's owner could be away on holiday, for example, and may not get back within three days). Simply increasing the queue lifetime means that undeliverable messages to third parties are not reported back for a long time, which isn't that great.
The way to fix is, apparently, is to have two Postfix instances. One instance handles the normal traffic, with a short queue time, while the other handles traffic for the backed-up mail host; the latter Postfix instance has a long queue time and so can keep the mail for a while. I've not attempted to do this yet, as the above settings work OK for me.
Open submission port (587)
Some ISPs prevent access to port 25, the normal mail transfer port. To allow clients on these networks to still connext to this server, we open port 587 for SMTP mail submission.
Modify /etc/postfix/master.cf
to include:
submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes # -o smtpd_client_restrictions=permit_sasl_authenticated,permit_mx_backup,reject -o milter_macro_daemon_name=ORIGINATING
You may also need to modify /etc/postfix/main.cf
to include
data_directory = /var/lib/postfix smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache tls_random_exchange_name = ${data_directory}/prng_exch
And don't forget to open the port in the firewall and on the router.
Authenication and verification
There are a number of ways to authenticate your email servers with others. This should prevent spam messages.
SPF
This is a mechanism for the receiving MTA to check that messages it receives came from an MTA that was authorised to send them. It uses DNS records to list the allowed domains. You simply set a DNS record to allow other MTAs to check mail that purports to come from you. To check mail that comes from other domains to your own MTA, you need to install an SPF checking tool and integrate it into Postfix.
- Add the following as a
TXT
record to the DNS entry for each domain you send mail.
v=spf1 mx a mx:backup.com ~all
- This tells other mail servers that mail servers listed the MX portion of your DNS entry can send email for your domain. It also tells those other servers to accept email sent from your IP number and servers listed in the MX records of backup.com (where backup.com is a domain that acts as a backup MX server for your domain).
- Install the SPF checking tool in Postfix
root@server:~# aptitude install postfix-policyd-spf-python
- Add the SPF checker to
/etc/postfix/master.cf
policy-spf unix - n n - - spawn user=nobody argv=/usr/bin/policyd-spf
- Add the SPF timeout line, and modify
smtpd_relay_restrictions
in/etc/postfix/main.cf
smtpd_relay_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination check_policy_service unix:private/policy-spf policy-spf_time_limit = 3600s
- Modify
/etc/postfix-policyd-spf-python/policyd-spf.conf
to whitelist any mail servers that act as secondary for this domain. Add this line:
Domain_Whitelist = mybackup.domain.com
- Test the setup by sending a message from a domain that uses SPF to the server you control. The mail log for your MTA should contain a line like
Nov 20 10:26:05 server policyd-spf[18300]: Pass; identity=helo; client-ip=157.56.112.83; helo=blah.senderdomain.com; envelope-from=user@senderdomain.com; receiver=user@domain.com
- and the headers of the message should contain a line like
Received-SPF: Pass (sender SPF authorized) identity=helo; client-ip=x.x.x.x; helo=blah.senderdomain.com; envelope-from=user@senderdomain.com; receiver=user@domain.com
- Test the setup by sending a message to a domain that uses SPF from your own server. The headers of that message should contain a line like
Received-SPF: Pass
- followed by a bunch of stuff that shows how the receiving MTA checks SPF records.
(Taken from Ubuntu community docs for Postfix/SPF).
DKIM
With DKIM, the originating MTA takes a hash of some message headers and the message body, signs it with the server's private key, and embeds the signed hash as an additional header in the message. The public key is published as a DNS TXT record. The receiving MTA recalculates the hash and compares it to the signed version in the message. If they match, the message passes the DKIM authentication.
In this example, I have several domains that can send mail. Each has a separate DKIM key. The details are stored in the /etc/opendkim/key_table
and /etc/opendkim/signing_table
files, rather than in the /etc/opendkim.conf
file (the latter is fine if you only have one domain).
- Install the packages
root@server:~# aptitude install opendkim opendkim-tools
- Edit
/etc/opendkim.conf
# Log to syslog Syslog yes # Required to use local socket with MTAs that access the socket as a non- # privileged user (e.g. Postfix) UMask 002 Socket inet:8891@localhost PiDFile /var/run/opendkim/opendkim.pid # Sign for example.com with key in /etc/mail/dkim.key using # selector '2007' (e.g. 2007._domainkey.example.com) #Domain example.com #KeyFile /etc/mail/dkim.key #Selector mail KeyTable /etc/opendkim/key_table SigningTable /etc/opendkim/signing_table ExternalIgnoreList /etc/opendkim/trusted_hosts InternalHosts /etc/opendkim/trusted_hosts # Commonly-used options; the commented-out versions show the defaults. #Canonicalization simple #Mode sv #SubDomains no #ADSPAction continue AutoRestart yes AutoRestartRate 10/1h Background yes UserID opendkim:opendkim Canonicalization relaxed/relaxed DNSTimeout 5 Mode sv SignatureAlgorithm rsa-sha256 SubDomains no #UseASPDiscard no #Version rfc4871 X-Header no
- Modify
/etc/default/opendkim
to include only these non-comment lines.
RUNDIR=/var/run/opendkim USER=opendkim GROUP=opendkim PIDFILE=$RUNDIR/$NAME.pid EXTRAAFTER=
- Create the directories for the keys (one for each domain)
$ root@server:~# mkdir /etc/opendkim $ root@server:~# mkdir /etc/opendkim/example.com $ root@server:~# mkdir /etc/opendkim/example2.com
- Create
/etc/opendkim/trusted_hosts
to show which hosts don't need DKIM authentication. In this case, it's just the hosts my LAN.
# local host 127.0.0.1 # local subnets that are trusted and do not need to be verified 192.168.1.0/24 aaaa:bbbb:cccc:dddd/64
- Create the keys for each domain
cd /etc/opendkim/example.com opendkim-genkey -s mail -d example.com chown opendkim:opendkim mail.private
- Create
/etc/opendkim/signing_table
example.com mail._domainkey.example.com example1.com mail._domainkey.example1.com example2.com mail._domainkey.example2.com
- Place the keys in your domains' DNS records. Create a new subdomain
mail._domainkey
for each domain (e.g.mail._domainkey.example.com
). Create a new TXT record for that new subdomain that contains the core of the relevant part of/etc/opendkim/example.com/mail.txt
. If the file contains
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; " "p=MIGfMA0GCSqGSIb3DQEBAQU...blah...blah...QIDAQAB" ) ; ----- DKIM key mail for example.com
the DNS TXT record for mail._domainkey.example.com
should contain
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQU...blah...blah...QIDAQAB
- Check the entry is in the domain
user@desktop:~$ host -t TXT mail._domainkey.example.com ns1.isp.net Using domain server: Name: ns1.isp.net Address: 100.100.100.1006#53 Aliases: mail._domainkey.example.com descriptive text "v=DKIM1\; k=rsa\; p=MIGfMA0GCSqGSIb3DQEB...blah...blah...uY/wIDAQAB"
- If needed, add the entry to
/etc/opendkim/key_table
:
mail._domainkey.domain.tld domain.tld:mail:/etc/opendkim/domain.tld/mail.private
- Modify
/etc/postfix/main.cf
to include
# DKIM Milter milter_protocol = 6 milter_default_action = accept smtpd_milters = inet:localhost:8891 non_smtpd_milters = inet:localhost:8891
- (If you already have milters operating, add the DKIM milter settings to the existing ones
milter_default_action = accept milter_protocol = 2 smtpd_milters = inet:localhost:8891,inet:localhost:8892 non_smtpd_milters = inet:localhost:8891,inet:localhost:8892
- Modify
/etc/postfix/master.cf
to prevent messages being signed when going both to and from Amavis. In the section for Amavis, extend thereceive_override_options
withno_milters
:
# Returning from Amavis 127.0.0.1:10025 inet n - - - - smtpd -o content_filter= ... -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
- Restart everything, checking
/var/log/mail.log
for errors
root@server:~# systemctl restart opendkim root@server:~# postfix reload
- Test it all works by sending some messages to and from domains with working DKIM services. Outgoing messages should contain a header like
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=mail; t=1448108655; bh=Hciu/taJLWKuURd9G8XyD6VK2YpDwXAHXAfUrZpPfRo=; h=Date:Subject:From:To:From; b=IFbjcnrZcD0SQsL9N2zGaOjhR/j2opmjAmxBgp8+3QejtqPgbNqFjdUOeXkQlh0xo M1nX2VK73v+2D7Og41jZuXQgb8jYn2yUeKLkvJDWNnEOdpmXvRpspuhu2+uqwEk8cj uqkY5jTXUBmn9FKVRRn0Xg8Nzl0jG7HY9pYNPXYY=
- If it works, the receiving MTA should also include a header like
Authentication-Results: spf=pass (sender IP is 100.100.100.100) smtp.mailfrom=example.com; desitination.com; dkim=pass (signature was verified) header.d=example.com;destination.com; dmarc=bestguesspass action=none header.from=example.com;
- Check that the DKIM process works in both directions and for each domain you have.
- Once you're confident that DKIM is working, tell the world to reject messages that don't have the DKIM keys. Create a new domain
adsp._domainkey.example.com
and give it a DNS TXT record containing
dkim=all
Most of these instructions were taken from How to eliminate spam and protect your name with DMARC, with some from Postfix/DKIM Ubuntu community documentation.
DMARC
See also this page on fixing OpenDmarc configuration.
DMARC is a method for MTAs to publish a policy for how their messages should be processed, and allows them to report how well they're handling each others' mail according to those policies.
DMARC policy for outgoing mail
You create a DNS TXT record that tells other MTAs about how you want your outgoing messages to be processed. You set up a DMARC milter on your MTA to process incoming messages according to the published DMARC policy. The DMARC milter also logs how you're handling other MTAs' messages and can send reports to them as requested..
- Create a subdomain
_dmarc.example.com
and a TXT record specifying the DMARC reports you want.
v=DMARC1; p=none; rua=mailto:dmarc@example.com; fo=1; adkim=r; aspf=r
- (The KTS DMARC Record Assistant will create the records for you. Remove the
fo=1;
for fewer reports once you're confident things are working.)
- Create the dmarc user by modifying
/etc/postfix/valiases
to include lines
dmarc@example.com user@example1.com dmarc@example1.com user@example1.com dmarc@example1.com user@example1.com
- Refresh the Postfix config.
root@server:~# postmap /etc/postfix/valiases root@server:~# postfix reload
You should get messages arriving from other MTAs to say how well you've been doing.
Testing and logging incoming mail
Next is to log the details from incoming messages and tell those MTAs how well they've been doing.
- Install OpenDMARC
root@server:~# aptitude install opendmarc libauthen-sasl-perl python3-psycopg2
- (I need the
libauthen
package to allow the Opendmarc scripts to connect to my mailserver via SASL, and thepsycopg2
package to allow the importer to connect to Postgres.)
- Update
/etc/opendmarc.conf
AuthservID mail.domain1.com PidFile /var/run/opendmarc/opendmarc.pid # Change this line! Socket inet:8893@localhost RejectFailures false Syslog true TrustedAuthservIDs mail.domain1.com,mail.domain2.com,mail2.example.com UMask 0002 UserID opendmarc:opendmarc IgnoreHosts /etc/opendmarc/ignore.hosts HistoryFile /var/run/opendmarc/opendmarc.dat #for testing: SoftwareHeader true
- where
mail.domain1.com,mail.domain2.com,mail2.example.com
are the names of mail servers you trust, including your own. I included the names of the other domains this MTA serves.
- Create a directory for the OpenDMARC Pid file:
root@server:~# mkdir /var/run/opendmarc root@server:~# chown -R opendmarc:opendmarc /var/run/opendmarc/
- Create the directory
/etc/opendmarc/
and the file/etc/opendmarc/ignore.hosts
, containing
localhost 192.168.1.0/24 aaaa:bbbb:cccc:dddd::/64
- Update
/etc/default/opendmarc
to include the line
SOCKET="inet:8893@localhost"
- Exit
/etc/postfix/main.cf
to include the extra milter. Add the port specification to these lines:
smtpd_milters = inet:localhost:8891,inet:localhost:8893 non_smtpd_milters = inet:localhost:8891,inet:localhost:8893
- Start OpenDMARC and refresh Postfix
root@server:~# systemctl restart opendmarc.service root@server:~# postfix reload
Send a mail to yourself from a service that uses DMARC, such as GMail. Messages you receive should include headers like:
DMARC-Filter: OpenDMARC Filter v1.2.0 mail.domain.org 821E7A4 Authentication-Results: mail.domain.org; dmarc=pass header.from=gmail.com
- The first line is the flag from the SoftwareHeader line, and shows that OpenDMARC handled the message. The second line is the DMARC authentication result. If it's all working, you can remove the SoftwareHeader line from
/etc/opendmarc.conf
and restart OpenDMARC.
Reporting on incoming mail
Important note on versions |
---|
The instructions below won't generate any messages with Ubuntu 14.04 LTS. The supplied version of OpenDMARC in that version of Ubuntu (1.2.0) has a bug where it fails to record the DMARC rua address, which specifies where the DMARC reports should go. This is fixed in OpenDMARC 1.3.1, which is bundled in Ubuntu 15.04 and later. |
The final part is to log the results of DMARC testing incoming mail, and sending the results back to the originating MTAs.
- Edit
/usr/share/doc/opendmarc/schema.mysql
. Update the default timestamp forlastsent
on about line 31 to be
CREATE TABLE IF NOT EXISTS requests ( … lastsent TIMESTAMP NOT NULL DEFAULT '1970-01-01 01:01:01',
- and uncomment the lines
CREATE USER 'opendmarc'@'localhost' IDENTIFIED BY 'changeme'; GRANT ALL ON opendmarc.* to 'opendmarc'@'localhost';
- create a stong password to replace 'changeme'.
- Run the script to create the MySQL database to store the reports.
root@server:~# mysql -u root -p < /usr/share/doc/opendmarc/schema.sql
- Fix the missing database backend selectors in the OpenDMARC scripts. In
/usr/sbin/opendmarc-import
,/usr/sbin/opendmarc-params
, and/usr/sbin/opendmarc-reports
, change the DBD lines to use "mysql" not "yes" (you may have to hunt, and it seems already fixed in/usr/sbin/opendmarc-expire
),
# require DBD::yes; require DBD::mysql; # my $dbscheme = "yes"; my $dbscheme = "mysql";
- Create
/etc/cron.daily/opendmarc-report
to generate the reports once a day.
#!/bin/bash DB_SERVER='localhost' DB_USER='opendmarc' DB_PASS='changeme' DB_NAME='opendmarc' WORK_DIR='/var/run/opendmarc' REPORT_EMAIL='dmarc@domain.com' REPORT_ORG='domain.com' mv ${WORK_DIR}/opendmarc.dat ${WORK_DIR}/opendmarc_import.dat -f touch ${WORK_DIR}/opendmarc.dat chown opendmarc:opendmarc ${WORK_DIR}/opendmarc.dat /usr/sbin/opendmarc-import --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose < ${WORK_DIR}/opendmarc_import.dat /usr/sbin/opendmarc-reports --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose \ --interval=86400 --report-email=${REPORT_EMAIL} --report-org=${REPORT_ORG} /usr/sbin/opendmarc-expire --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose
- where you use the
DB_PASS
password you generated earlier.
- Make the cron script executable:
root@server:~# chmod +x /etc/cron.daily/opendmarc-report
- Ensure that, for testing at least, you get a copy of every outgoing DMARC report. Modify
/etc/postfix/main.cf
to include
sender_bcc_maps = hash:/etc/postfix/sender_bcc
- then create
/etc/postfix/sender_bcc
to contain the one line
/dmarc@domain1.com/ user@domain1.com
- and refresh the Postfix configuration
root@server:~# postmap /etc/postfix/sender_bcc root@server:~# postfix reload
Most of these instructions were taken from How to eliminate spam and protect your name with DMARC, with some from Christian Laußat's Linux Blog.
DANE
To add: see documents at http://www.postfix.org/TLS_README.html#client_tls_dane . Will require DNSSEC enabled on the sending domain.
Spamhaus block lists
Spamhaus maintains lists of known spammer address and allows people to use them to block spam. They provide a plugin for SpamAssassin that handles the lookups.
- Sign up for a Data Query Service (DQS) account.
- Find your DQS key on the customer portal.
- Clone the plugin source code to somewhere convenient on your server.
- Follow the instructions in the plugin's README file. Note that
hbltest.sh
is in the3.4.1+/
directory. - Insert your key into the config files.
root@server:~/spamassassin-dqs/3.4.1+# sed -i -e 's/your_DQS_key/aip7yig6sahg6ehsohn5shco3z/g' sh.cf root@server:~/spamassassin-dqs/3.4.1+# sed -i -e 's/your_DQS_key/aip7yig6sahg6ehsohn5shco3z/g' sh_hbl.cf
- Modify
sh.pre
with the location of SpamAssassin
loadplugin Mail::SpamAssassin::Plugin::SH /etc/mail/spamassassin/SH.pm
- Copy the plugin into the SpamAssassin directory
root@server:~/spamassassin-dqs/3.4.1+# cp SH.pm /etc/mail/spamassassin root@server:~/spamassassin-dqs/3.4.1+# cp sh.cf /etc/mail/spamassassin root@server:~/spamassassin-dqs/3.4.1+# cp sh_scores.cf /etc/mail/spamassassin root@server:~/spamassassin-dqs/3.4.1+# cp sh.pre /etc/mail/spamassassin
- Restart SpamAssassin, Amavis, and Postfix
root@server:~# systemctl restart spamassassin root@server:~# systemctl restart amavis root@server:~# systemctl restart postfix
- Give it about 15 minutes to cache what it needs to, then test it with the Spamhaus blocklist tester.
See also
Here are a few pages that are useful guides or provide background and context.
- Secure Virtual Mailserver HOWTO: Postfix + OpenLDAP + Dovecot + Jamm + SASL + SquirrelMail
- Virtual Domains, Postfix, Dovecot LDA, and LDAP
- Postfix and Dovecot on Ubuntu Dapper Drake
- How To Install Postfix, Amavis, ClamAV, and Spamassassin on Debian Linux
- Single-user SpamAssassin installation
- Setting Up Email: A Postfix/Dovecot HOWTO
- Gentoo Wiki article on Postfix and Dovecot 2