Showing posts with label exim4. Show all posts
Showing posts with label exim4. Show all posts

Sunday, December 30, 2018

Making a Mailserver - Spam Blocking, Revisited

In an earlier post I described implementing spamassassin with exim4. The information there still holds true, but the technique of simply implementing "spamd" has not been enough to hold back spammers who have my email address. My email address was harvested in both the LinkedIn and Last.fm hacks. In the last years the targeted spam has increased noticeably.

I started to train my desktop email client to pick out spam and it does a decent job, so I weathered the deluge for some time. However, plenty of spam still gets through and when my desktop email client is not open I have plenty of junk to pick through on my mobile devices.

Finally, I've taken the time to sharpen up my exim4 defenses.

Challenges


In the rest of this post, I'll be answering these questions:
  1. Does spamassassin support Domain Name System Blacklists (DNSBL)?
  2. How do I integrate blocklist (DNSBL) checks in exim4?
  3. How do I block hosts that are not really mailservers?
  4. How do I block on reverse DNS failures?
  5. How do I allow specific hosts to skip checking by exim4 and spamd?
  6. How do I verify these measures are working properly?
The answers to these questions are straightforward, but took quite a bit of research time and verification. Point 6 isn't separately addressed; each section that follows will talk about the ways that I verified the spam mitigations were working.

spamassassin and exim4


It turns out that spamassassin (spamd) supports DNSBL by default. I actually discovered this after going through the process of integrating zen.spamhaus.org checking in exim4. The difference is that you can kill spam outright with exim4 integration, but spamd will use it as part of the point calculation when determining how 'spammy' an email is.

There's a downside then to spamd: while the blocklist from spamhaus has a very high accuracy, being on the blocklist doesn't guarantee that spamd will calculate enough points to junk the email.

It's possible to change the point value of addresses that are on DNSBLs by adding a "score URIBL_BLACK <value>" line to the spamassassin config file. You also need to ensure that perl's Net::DNS is installed. To check if that is installed, try "perl -MNet::DNS -e 1" and the command should execute with no errors.

One unanswered question I have is whether the perl module takes care of the DNS lookup and server to use, or whether your server needs to have a a spamhaus friendly DNS server in /etc/resolv.conf - see the spamassassin DNSBL discussion below.

Verify spamassassin is using DNSBL


To verify whether DNSBL is being used by spamassassin, check the log for the presence of URIBL_BLACK. This could be the syslog logfile depending on the system, not the exim4 logs:

Dec 29 16:22:02 mail spamd[2739]: spamd: result: Y 12 - AXB_XMAILER_MIMEOLE_OL_024C2,BAYES_00,FORGED_MUA_OUTLOOK,FORGED_OUTLOOK_HTML,FORGED_OUTLOOK_TAGS,FREEMAIL_FROM,FROM_MISSP_EH_MATCH,FROM_MISSP_FREEMAIL,FROM_MISSP_MSFT,FROM_MISSP_REPLYTO,FROM_MISSP_XPRIO,FSL_CTYPE_WIN1251,FSL_NEW_HELO_USER,HTML_MESSAGE,LOTS_OF_MONEY,MIME_HTML_ONLY,MISSING_HEADERS,MISSING_MID,MONEY_FROM_MISSP,NSL_RCVD_HELO_USER,RCVD_IN_DNSWL_NONE,RCVD_IN_SORBS_WEB,REPLYTO_WITHOUT_TO_CC,SPF_SOFTFAIL,TO_NO_BRKTS_FROM_MSSP,TO_NO_BRKTS_MSFT,T_COMPENSATION,URIBL_BLACK scantime=1.3,size=4689,user=Debian-exim,uid=104,required_score=5.0,rhost=127.0.0.1,raddr=127.0.0.1,rport=41221,mid=(unknown),bayes=0.000005,autolearn=no autolearn_force=no 

This means that this particular message was found in a blocklist. Of course you are not going to see that present on every email that is checked.

Integrate the spamhaus blocklist into exim4


The instructions that I'll provide here are not specific to spamhaus, it's just the service I decided to try. It's free to a point, someone like me with relatively low volumes of email will be able to use the service unimpeded. There is a performance hit on your own server while doing the DNS lookup on spamhaus though. The speed of the lookup will slow your mail delivery of legitimate email by milliseconds.

It's supposedly possible to download a blocklist and do local lookups on that, but setting that up is more complex and requires frequent downloads of large lists, so it seems of small reward for a lot of work if you are not handling much of email.

My exim4 config is broken into separate config elements, which is a fairly normal thing to do, but you may find the files to place this config will differ depending on your system.

Enable "DNSBLS" as exim4 refers to it, in your custom macro file (/etc/exim4/conf.d/main/00-custom_macros for example):

CHECK_RCPT_IP_DNSBLS = zen.spamhaus.org

Configure the deny option or leave it at a warning level (/etc/exim4/conf.d/acl/30_exim4-config_check_rcpt).

# Check against classic DNS "black" lists (DNSBLs) which list
# sender IP addresses
.ifdef CHECK_RCPT_IP_DNSBLS
#warn
# message = X-Warning: $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text)
deny
  message = Failed sender validation
  log_message = michael DENY - $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text)
  dnslists = CHECK_RCPT_IP_DNSBLS
.endif

Notice above that I've commented out the default "warn" and "message". I've also added a custom log_message so the "michael DENY" sticks out. I know when I see that log that it's taking action on something I did. Also I dumbed down the message to not be too helpful to spammers, not that I think they're reading the SMTP rejection reasons!

When I initially implemented this, I never saw the rule being triggered. The reason was because the spamhaus lookup always failed to return any record. If your mailserver is configured to do lookups via a major DNS servers like 8.8.8.8, 1.1.1.1 or 9.9.9.9, the spamhaus lookups don't work. I'm not going to go into why they don't work here, but suffice to say that the major DNS providers don't want to know about these queries.

Unfortunately, that means finding a DNS server that will help you with your inquiries, or running a DNS server on the local box (or in your local network). If you refer back to my previous post on setting up a nameserver, then you can simply add the following snippets to the existing setup (/etc/bind/named.conf.options):


acl "trusted" {
        localhost;
        <other trusted IPs>;
};
options {
        ...

        allow-query { any; };
        allow-recursion { trusted; };
        allow-query-cache { trusted; };
        ...
}


I sincerely encourage you to check the bind documentation yourself. Don't go adding random config from the internet to highly sensitive services without understanding what each setting means. Just a little explanation, the acl restricts DNS queries for domains other than in the local zones to a list of permitted hosts, including localhost, so that exim4 and other local services can use this server to resolve IPs.

Naturally, you need to ensure that /etc/resolv.conf has "nameserver 127.0.0.1" or another DNSBL friendly server configured.

Back to the exim config. Without customising, i.e. using the default settings, you'll get a new header on the email message. The email won't be dropped by this rule,  but you will see in the email (or in the exim4 rejectlog if dropped elsewhere) an X-Warning header. Note it below:

Envelope-from: <ass3@binkmail.com>
Envelope-to: <michael@moff.tech>
P Received: from 79-103-16-190.fibertel.com.ar ([190.16.103.79])
by mail.moff.tech with esmtp (Exim 4.84_2)
(envelope-from <ass3@binkmail.com>)
id 1gdFOD-00008q-90
for michael@moff.tech; Sat, 29 Dec 2018 15:14:57 +0100
I Message-ID: <4EAF76134D97CA28C910752BF1AC4EAF@KSP94W150>
F From: "michael@moff.tech" <ass3@binkmail.com>
T To: <michael@moff.tech>
Subject: ***wonderful spam***
Date: 29 Dec 2018 07:01:51 -0400
MIME-Version: 1.0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.5512
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.5512
X-Warning: 190.16.103.79 is listed at zen.spamhaus.org (127.0.0.11, 127.0.0.4: https://www.spamhaus.org/query/ip/190.16.103.79)
X-Spam-Score: 20.0 (++++++++++++++++++++)

You'll likely see the same warning in the exim4 mainlog:

2018-12-29 15:14:57 H=79-103-16-190.fibertel.com.ar [190.16.103.79] Warning: 190.16.103.79 is listed at zen.spamhaus.org (127.0.0.11, 127.0.0.4: https://www.spamhaus.org/query/ip/190.16.103.79)

Once you see this log or the custom deny log I suggested, you know that the lookup is working.

In the end, I ditched doing the spamhaus lookup when I realised that spamassassin was also doing it. I commented out the "CHECK_RCPT_IP_DNSBLS = zen.spamhaus.org" entry and left the 30_exim4-config_check_rcpt config just in case I wanted to switch it on again.

Blocking mailservers that aren't


Mail delivery is entirely dependent on DNS records. A mailserver without forward and reverse DNS entries is of doubtful reputation. It's a safe bet that any host sending you email that doesn't have complete DNS records is not a legitimate mailserver and should be ignored.

My default exim4 install does not hold mailservers or the sender addresses to strict standards. Organisations that deal with large volumes of emails, for large numbers of users, will receive legitimate email from such badly configured mail clients and mailservers.

I can say with a high degree of certainty that I should not be receiving email from weird hosts or senders with domains that don't exist or accept email. If I discard emails from such servers and senders, I might have a handful of people over some number of years that have a problem emailing me. The upsides to configuring my mailserver to be strict on these points outweigh the potential downsides.

There are three useful options. In the custom macros file (/etc/exim4/conf.d/main/00-custom_macros) you may elect to enable the following ACLs.
# Denied in /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt
CHECK_RCPT_VERIFY_SENDER = yes
#
# Denied in /etc/exim4/conf.d/acl/40_exim4-config_check_data
CHECK_DATA_VERIFY_HEADER_SENDER = yes
#
# Denied in /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt
CHECK_RCPT_REVERSE_DNS = yes

In the configuration files, you'll see that two of the methods are already deny, once the option is enabled (as above).

There's a helpful overview of many exim4 ACL options here and here. My descriptions below paraphrase them.

CHECK_RCPT_VERIFY_SENDER verifies that the sender of the message (RCPT TO) has a DNS entry. This is disabled by default, but when enabled will deny by default. Note that I added a custom log log message:

/etc/exim4/conf.d/acl/30_exim4-config_check_rcpt

# Deny unless the sender address can be verified.
#
# This is disabled by default so that DNSless systems don't break. If
# your system can do DNS lookups without delay or cost, you might want
# to enable this feature.
#
# This feature does not work in smarthost and satellite setups as
# with these setups all domains pass verification. See spec.txt chapter
# 39.31 with the added information that a smarthost/satellite setup
# routes all non-local e-mail to the smarthost.
.ifdef CHECK_RCPT_VERIFY_SENDER
deny
  message = Sender verification failed
  log_message = michael DENY - Sender verification failed
  !acl = acl_local_deny_exceptions
  !verify = sender
.endif

To date I have not seen this logged, so I can't verify that it's doing anything. It is possible that one of the other ACLs denies the email first.

CHECK_RCPT_REVERSE_DNS is the ACL that actually checks whether the mailserver has a reverse DNS entry.

/etc/exim4/conf.d/acl/30_exim4-config_check_rcpt

# Warn if the sender host does not have valid reverse DNS.
#
# If your system can do DNS lookups without delay or cost, you might want
# to enable this.
# If sender_host_address is defined, it's a remote call. If
# sender_host_name is not defined, then reverse lookup failed. Use
# this instead of !verify = reverse_host_lookup to catch deferrals
# as well as outright failures.
.ifdef CHECK_RCPT_REVERSE_DNS
#warn
# message = X-Host-Lookup-Failed: Reverse DNS lookup failed for $sender_host_address (${if eq{$host_lookup_failed}{1}{failed}{deferred}})
deny
  message = Sender validation failure
  log_message = michael DENY - Reverse DNS check failed
condition = ${if and{{def:sender_host_address}{!def:sender_host_name}}\
{yes}{no}}
.endif

Reverse check logs will now appear as so:

2018-12-30 07:17:37 H=([182.177.52.180]) [182.177.52.180] F=<ezambrano@maecabogados.com> rejected RCPT <michael@moff.tech>: michael DENY - Reverse DNS check failed


CHECK_DATA_VERIFY_HEADER_SENDER verifies that the sender is valid in at least one of the "Sender:", "Reply-To:", or "From:" header lines.

/etc/exim4/conf.d/acl/40_exim4-config_check_data


# require that there is a verifiable sender address in at least
# one of the "Sender:", "Reply-To:", or "From:" header lines.
.ifdef CHECK_DATA_VERIFY_HEADER_SENDER
deny
  message = No verifiable sender address in message headers
  log_message = michael DENY - No verifiable sender address in message headers
  !acl = acl_local_deny_exceptions
  !verify = header_sender
.endif


This condition is generally rare to see in the logs, but will look as so:

2018-12-30 09:00:31 1gdW1P-0003gC-Fx H=somelinuxhost.net (gentoo.somelinuxhost.net) [x.x.x.x] X=TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256 F=<noreply@somelinuxhost.net> rejected after DATA: michael DENY - No verifiable sender address in message headers: syntax error in 'From:' header when scanning for sender: malformed address: <noreply@somelinuxhost.net> may not follow noreply@somelinuxhost.net  in "noreply@somelinuxhost.net <noreply@somelinuxhost.net>"


After a day of monitoring logs I found that the only instance was against a message that I wanted to receive. A friend was sending me emails directly from a host that sent an automated daily digest. I decided to whitelist the domain:

# cat /etc/exim4/sender_local_deny_exceptions
somelinuxhost.net

Skip checking friendly hosts


My mailserver relays email for a couple of other servers I have on the internet. These host websites with contact forms that can change the "From" header to use the email address of the person who filled out the form.

In this case there will be various validation checks that fail.

018-12-29 17:51:59 no IP address found for host ip-x-x-x-x.eu-west-1.compute.internal (during SMTP connection from ec2-x-x-x-x.eu-west-1.compute.amazonaws.com (ip-x-x-x-x.eu-west-1.compute.internal) [x.x.x.x])
2018-12-29 17:51:59 H=ec2-x-x-x-x.eu-west-1.compute.amazonaws.com (ip-x-x-x-x.eu-west-1.compute.internal) [x.x.x.x] sender verify fail for <www-data@ip-x.x.x.x.eu-west-1.compute.internal>: Unrouteable address
2018-12-29 17:51:59 H=ec2-x-x-x-x.eu-west-1.compute.amazonaws.com (ip-x-x-x-x.eu-west-1.compute.internal) [x.x.x.x] X=TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128 F=<www-data@ip-x-x-x-x.eu-west-1.compute.internal> rejected RCPT <info-today@moff.tech>: michael-today UNKNOWN - Sender verification failed: Sender verify failed

You also need to give local server applications the ability to send email through exim4. For example, if you had a python script that generated an email at the of the script, you might see something like this in mainlog:

2018-12-30 23:44:56 1gdjpI-0005VU-Dg H=localhost (mail.moff.tech) [::1] F=<michael@moff.tech> rejected after DATA: michael DENY - No verifiable sender address in message headers: there is no valid sender in any header line

To add trusted hosts, including localhost, the solution is simple:

# cat /etc/exim4/host_local_deny_exceptions
127.0.0.1
x.x.x.x
y.y.y.y

In Conclusion


Don't waste your time integrating DNSBL into exim4 unless you do not want spamassassin to check for you.

So far so good. After a day of monitoring, not one piece of spam slipped through. I did discover that one email that I wanted was denied but due to the fact that the sender was someone firing email out from a machine not correctly setup to be a mailserver (I don't believe the sender cares enough to set that up).

This result was stunning when considering I could reduce spam not totally eliminate it, as appears to be the case after just 24 hours. One should expect to lose the odd email but careful review of the exim4 mainlog and rejectlog will help identify and whitelist desired 'special case' email senders.

Wednesday, July 6, 2016

Making a Mailserver (Part 5) - Wonderful Spam

This is an instalment in my series on setting up a Linux based mailserver. See these posts:

In this post we setup Spamassassin with exim4 to stomp on as much spam as possible. We won't give anything that looks like spam a chance to be delivered, we'll dump such messages before they even complete the delivery process.


And yet it spams

Why Spamassassin?

There are alternatives, a good friend of mine recently recommended dspam to me, so that's on my list to investigate. Spamassassin doesn't catch everything, mostly because some spammers are pretty damn good at their job. It does not do a great job of spotting messages delivering Malware and the tediously regular emails from bearing manufacturers.

If there was such a thing as a perfect solution, it wouldn't be by implementing just one technology. Nothing's perfect. I chose Spamassassin because it is mature, well understood, backed by Apache and easy to setup.

This is Kinda Interesting

For a good number of years I went without receiving a lot of spam. I didn't intentionally publish my email address on the web and many websites take care to obscure your email address, for example, when publishing mailing list archives.

I was intrigued by Steve Gibson's assertion in Security Now #557 that it takes multiple years before your email address really gets on Spammers' radar. He changes his email address once a year:
And something as simple as changing your email address loses spam. That is, it's just gone. And you might think that, oh, it's going to find you again within a week or two. No. It takes, I can attest to this, years, multiple years.
I'd naively held the belief that Spammers found your domain name and then worked through a list of common names to mail to (michael@, john@, chris@, etc). Perhaps they do do that, but it seems that scraping websites and database dumps is the most common and less time-wasting way of building a list of recipients.

In 2015 the level of spam quickly started to get out of control for me. Malware especially was really flooding in and even though I am largely Linux and Android focussed, constantly deleting spam made checking my email a tedious, rather than fun, task.

Plus one for Thunderbird however. I took the time to train Thunderbird's junk mail handling and it is really good. However, as I check email on my phone (most of the time), Thunderbird's junk handling wasn't going to help unless I always had it running in the background on some workstation, somewhere.

The spam was pouring in and since I was redirecting (aliasing via /etc/aliases) some mailboxes to Gmail accounts, I was ending up with a large queue of frozen messages because Gmail was not happy to handle redirected Spam. If you want a Spam free mailbox, there's arguably nothing better than Gmail. I was worried about the potential damage I was doing to my mail server's reputation with Gmail by reflecting Spam straight to Gmail.

Enter Spamassassin.

Exim and Spamassassin Integration

I recommend you first review the debian exim wiki. I did find it to be wrong in places when I referred to it. There was some Exim documentation about acl actions that also informed my config. I'll be providing a multi mail-domain example because I do handle mail for multiple domains. The following example will work for one mail domain or many.

I use exim4 split config files because ... that's what everyone else does.

In brief, the debian exim wiki tells you to do the following:
#apt-get install spamassassin
If you are using Debian Jessie or later (with systemd enabled by default), enable and start the service using systemctl;
#systemctl enable spamassassin.service
On earlier Debian releases, edit /etc/default/spamassassin ...
ENABLED=1 
...and then start the daemon.
#/etc/init.d/spamassassin start
At this point I found divergences between what the documentation tells you to do and what works in reality. The "add_header" did not work for me, following the wiki instructions. Here's how I set it up:

/etc/exim4/conf.d/acl/40_exim4-config_check_data
# warn
#   spam = Debian-exim:true
#   message = X-Spam_score: $spam_score\n\
#             X-Spam_score_int: $spam_score_int\n\
#             X-Spam_bar: $spam_bar\n\
#             X-Spam_report: $spam_report
#
# put headers in all messages (no matter if spam or not)
  warn  spam = Debian-exim:true
      add_header = X-Spam-Score: $spam_score ($spam_bar)
# add second subject line with *SPAM* marker when message
# is over threshold
  drop  spam = Debian-exim
#      add_header = Subject: ***SPAM (score:$spam_score)*** $h_Subject:
Important Points:
  • The debian docs "Subject" manipulation simply did not work for me. Refer to the "Rewriting Subject" section further below.
  • The debian docs used "nobody" as the user, I changed this to Debian-exim. Using "nobody" gets you all kinds of painful log messages.
  • The debian docs used "add_header = X-Spam-Report: $spam_report" on all  messages, this resulted in a message in the header of every email saying that the email had been detected as spam, regardless of the score. 
  • I do still insert the X-Spam_Score in every message.
  • I'm dropping anything over the Spamassassin threshold (required_score). The incoming message will be "rejected after DATA".
You will want to tinker with the threshold on dropped messages. 8 is too high, but it's better to start high and then inspect the score on the spam that makes it through. The bulk of spam appears to get very high scores, but between 4 to 5 there is a crossover between legitimate email and spam.

You also receive spam that gets scores as low as 1 and it's impossible to filter at that level without losing a lot of legitimate email.

You can tinker with the required score in /etc/spamassassin/local.cf and the default at the time of writing is 5, which I think is about right.
required_score 8.0
It's important to remember that the delivery agent is going to get a hard fail when a message scores over the required_score. It probably won't come back for a second try. A slighted mailing list server, for example, may mark your address as a hard fail and remove your subscription.

The rewrite_header in the Spamassassin config is meaningless because Exim is handling the mail and just asking Spamassassin for its opinion on the spam score. Other elements in the Spamassassin file are relevant to scoring the message.

That's it! That's all you need to do.

Rewriting Subject

I didn't implement this because I elected to dump the high scoring messages and write (for every message) the X-Spam-Score to the headers.

Reviewing the Efficacy

You really must spend days or weeks checking in with your Exim logs in addition to reviewing the Spam messages that are slip through.
  • When looking at the spam that hits your inbox, take a look at the X-Spam-Score header that was written in by Spamassassin. View the message source to see the headers. 
  • Don't be confused by  fake headers added by the spammer, such as fake Spam Score information.
  • There is often false information in the headers about being checked by this or that antivirus software. 
  • Message headers should be read from bottom to top. Each mail agent prepends its headers to the top of the message as the message bounces around mailserver to mailserver.
Review the Exim reject log. Here's what you should see when things are working:
# tail -vf /var/log/exim4/rejectlog 
2016-07-06 18:41:41 1bKptM-0006PS-7x H=208-180-142-165.chstcmtk01.com.sta.suddenlink.net [208.180.142.165] F=<xxx@swisslens.com> rejected after DATA
Envelope-from: <xxx@swisslens.com>
Envelope-to: <xxx@moff.tech>
P Received: from 208-180-142-165.chstcmtk01.com.sta.suddenlink.net ([208.180.142.165])
        by moff.tech with smtp (Exim 4.84_2)
        (envelope-from <xxx@swisslens.com>)
        id 1bKptM-0006PS-7x
        for
xxx@moff.tech; Wed, 06 Jul 2016 18:41:40 +0200
  Date: Wed, 06 Jul 2016 14:35:35 -0300
F From: "CamilleHot" <xxx@mndistaog.org>
R Reply-To: "CamilleHot" <xxx@mndistaog.org>
  X-Priority: 3 (Normal)
I Message-ID: <75461.14671320@mndistaog.org>
T To: xxx@moff.tech
  Subject: Come here! I want to make love to you
  MIME-Version: 1.0
  Content-Type: multipart/alternative;
        boundary="605311998876970"
  X-Spam-Score: 18.3 (++++++++++++++++++)

Notice the X-Spam-Score of 18.3 - high but not off the charts. Let's review the kind of scores we've recently seen:
# grep X-Spam-Score /var/log/exim4/rejectlog
  X-Spam-Score: 7.5 (+++++++)
  X-Spam-Score: 7.2 (+++++++)
  X-Spam-Score: 11.8 (+++++++++++)
  X-Spam-Score: 8.2 (++++++++)
  X-Spam-Score: 20.0 (++++++++++++++++++++)
  X-Spam-Score: 14.2 (++++++++++++++)
  X-Spam-Score: 18.4 (++++++++++++++++++)
  X-Spam-Score: 15.4 (+++++++++++++++)
  X-Spam-Score: 5.1 (+++++)
  X-Spam-Score: 16.8 (++++++++++++++++)
  X-Spam-Score: 13.6 (+++++++++++++)
  X-Spam-Score: 6.5 (++++++)
  X-Spam-Score: 14.2 (++++++++++++++)
  X-Spam-Score: 9.5 (+++++++++)
  X-Spam-Score: 18.5 (++++++++++++++++++)
  X-Spam-Score: 18.3 (++++++++++++++++++)
  X-Spam-Score: 6.1 (++++++)
In the period where you're still finding the right score level, I would recommend going back and looking at the headers on the 5.1 message: use "less" to simply view the file and search for the score string.

In fact I discovered, as I wrote this, that the 5.1 in the example above was a legitimate email about a delivery I was waiting on. Oh dear, perhaps I'll nudge the required_score up to 5.1. In my experience, 5.5 is too high.

It's easy to review the scores on the messages in your mailboxes. Scores can actually be negative in number, the lowest I've noticed is -11.89:

Your current inbox:
# grep X-Spam-Score /var/mail/you
Other folders:
# grep X-Spam-Score /home/you/mail/Trash

Keep looking at the Exim logs. Be curious. Learn what the headers mean. Tinker. It's fascinating stuff.

References

http://michaelfranzl.com/2015/02/11/exim-spamassassin-rewriting-subject-lines-adding-spam-score
https://spamassassin.apache.org/full/3.1.x/doc/Mail_SpamAssassin_Conf.html
http://www.exim.org/exim-html-current/doc/html/spec_html/ch-access_control_lists.html
http://www.exim.org/exim-html-current/doc/html/spec_html/ch-content_scanning_at_acl_time.html
https://www.maretmanu.org/homepage/inform/exim-spam.php

Wednesday, June 29, 2016

Making a Mailserver (Part 4) - Credibility is Critical

This is an instalment in my series on setting up a Linux based mailserver. See these posts:
Your mailserver has no reputation on the Internet. Even if it did, years of good reputation can be wiped away in a few 'open relay' minutes.

It's time to setup SPF and DKIM with bind and exim4.

DMARC

To ensure to the rest of the Internet that your server right at this moment is one of the good guys, set up everything you can within the DMARC framework.

The DMARC (Domain-based Message Authentication, Reporting & Conformance) concept is A Good Thing. The principles allow a mail server to express its validity. The DMARC methods allow sender validation through one or both of SPF and DKIM. That means, it is still possible for someone to spoof emails from your domain, but when your mailserver sends a message, SPF and DKIM validation is proof that that message at that moment is positively from your approved mail agent.

Some mail receivers will fail the DMARC validation lookup on messages and still deliver the email. However, if your mailserver starts out with a higher spam score (for whatever reason), the DMARC validation on your mailserver may be enough to mark down the spam score and get your email to its intended recipient.

In this example. we will set up:
  • SPF
  • DKIM
These standards try to solve the same problem (sender validity), but are often both checked, so we'll set up both. It's remarkably easy to configure them, particularly if you have unrestricted control of your DNS records.

SPF

This is dead simple to setup.

In part 1 we set up the minimum required DNS records, including the mail server (MX) record. Now we head back to bind9 to add a TXT record. Put this in the top level $ORIGIN section; i,e, where the NS records hang out.

/etc/bind/moff.tech.zone
TXT     "v=spf1 mx -all"
This record states that the SPF version is 1 and that all MX DNS records are entitled to deliver mail for moff.tech. MX is the record type, but could also include IP or IPv6 addresses. The "-all" says that anything in the list is authorised for the domain.

Visit The SPF Project website for detailed information on syntax.

In earlier times, this was an "SPF" record instead of "TXT", but "SPF" has been deprecated in favour of a "TXT" record.

DKIM

Review this debian-administration page before carrying out the steps, in order to make sense of the details. DKIM (DomainKeys Identified Mail) is easy to understand if your familiar with the concepts of PKI. If not, understanding what you're about to do next will be a steep but highly valuable learning curve.

Quoting a couple of points from the debian page mentioned above.
When a mail is sent a number of fields are hashed together, and that hash is then signed with the private key.
To validate an incoming message the receiver can see the from-address, perform the DNS-lookup necessary to find the public-key, and then repeat the hashing-process to compute the signature. If it matches then the result will be a "pass".
The intention is that only somebody who has control of the DNS for a domain can send mail from it - because all others will be missing the private key, and unable to sign the message.
We will create a private and public key so that your mailserver can put a signature, using the private key, to each email that can be verified by using the public key. The public key is retrieved with a query to your DNS server. The public key can be used to verify the signature, but cannot be used to forge your signature. That's the beauty of PKI.

In the next set of commands, we create the DKIM keys with openssl.
# sudo mkdir /etc/exim4/dkim
# cd /etc/exim4/dkim
# sudo openssl genrsa -out moff.tech.pem 1024 -outform PEM
# sudo openssl rsa -in moff.tech.pem -out moff.tech-public.pem -pubout -outform PEM
On my Debian system, I set the file permissions on the dkim directory (and files within) as so: 
drwxr-x---  2 root Debian-exim  4096 Jun  5 21:34 dkim
The Debian-exim group has permission to read the directory and files, while other (non root) users are permitted no access to these files whatsoever. This step may not be required, but it made sense to me in the context of how exim4 wants certificate file permissions to be set (see part 3).

Now tell exim4 that you want to sign messages with DKIM and where the key files are:

/etc/exim4/conf.d/main/00_my_macros
DKIM_CANON = relaxed
DKIM_SELECTOR = 20160629
DKIM_DOMAIN = ${lc:${domain:$h_from:}}
DKIM_PRIVATE_KEY=${if exists{/etc/exim4/dkim/${dkim_domain}.pem} {/etc/exim4/dkim/${dkim_domain}.pem}
}
  • The 20160629 is an arbitrary value, which for me indicates the date YYYYMMDD. There's nothing intrinsically important about the value here, but we reference it shortly when we create the DNS record.
  • I'm using a dynamic DKIM_DOMAIN and DKIM_PRIVATE_KEY configuration because I handle email for multiple domains, This configuration avoids repetitious configuration per domain. You don't need to think about it as long as the key names match the domain you are handling email for. Exim4 will (with a small computational overhead) dynamically work out the correct key to sign each email.
Now regenerate the configuration and restart exim4 :
# update-exim4.conf 
# service exim4 restart
The update-exim4.conf is essential. If email is going out, but no dkim signing is happening, then this is quite likely because the new configuration hasn't been picked up. Only restarting exim4 is not enough.

Put the public key file into a DNS TXT record, located within the correct $origin section.

/etc/bind/moff.tech.zone
$ORIGIN moff.tech.
_domainkey      IN TXT  "t=y;o=~;r=postmaster@example.com" 20160629._domainkey     IN TXT "k=rsa; p=XXX"
  • I have changed the email address to use example.com to stop bots scraping my email address from this blog post. You must use a real address.
  • p=XXX is the literal public key contained in the file moff.tech-public.pem, which you generated earlier. There's no problem from a security standpoint with me repeating the string in this post (it's publicly available by definition), but it is lengthy and it is useless to anyone other than me.
Of course, if you are handling email for multiple domains, you'll want to do this for each domain's zone file.

Surprisingly, you will get a daily summary email from other domains, primarily from hotmail, google and yahoo. I believe (but I'm not not absolutely sure) that this is standard when in 'testing mode'. The t=y element of the record means (according to RFC6376):
t= Flags, represented as a colon-separated list of names plain-
   text; OPTIONAL, default is no flags set).  Unrecognized flags MUST
   be ignored.  The defined flags are as follows:

  y  This domain is testing DKIM.  Verifiers MUST NOT treat messages
     from Signers in testing mode differently from unsigned email,
     even should the signature fail to verify.  Verifiers MAY wish
     to track testing mode results to assist the Signer.
That is to say, that "y" means "testing mode" and messages don't get dropped. During initial setup or server migration this is critical. Review the emails for SPF and DKIM 'fail' results as this will tell you if something is wrong. The results may tell you what other hosts are impersonating your mailserver!

My experience is that some mailservers are badly configured (or designed) and don't handle well messages that are relayed through a mailing list. There are different ways that this can go wrong, but I have personally seen SPF lookups fail because the sending list server is not (obviously enough) in my list of MX servers. This appears to be faulty logic on the recipient's behalf, because the receiver has looked up the SPF on my domain instead of the mailing list domain. [If you want to correct or fill this point in, please use the comments section, I really want to learn some more about this.]

Once you set up the DKIM record, you'll start to receive summary messages from a few places. The xml attachment will look similar to the following and gives you an indication on who's been sending email on your behalf as well as what passed and failed.
<feedback>
 <report_metadata>
  <org_name>google.com</org_name>
  <email>noreply-dmarc-support@google.com</email>
  <extra_contact_info>https://support.google.com/a/answer/2466580</extra_contact_info>
  <report_id>121341234</report_id>
  <date_range>
   <begin>1466812800</begin>
   <end>1466899199</end>
  </date_range>
 </report_metadata>
<policy_published>
 <domain>moff.tech</domain>
 <adkim>r</adkim>
 <aspf>r</aspf>
 <p>none</p>
 <sp>none</sp>
 <pct>100</pct>
</policy_published>
 <record>
  <row>
   <source_ip>64.x.x.x</source_ip>
   <count>1</count>
   <policy_evaluated>
    <disposition>none</disposition>
    <dkim>fail</dkim>
    <spf>pass</spf>
   </policy_evaluated>
  </row>
  <identifiers>
   <header_from>moff.tech</header_from>
  </identifiers>
  <auth_results>
   <spf>
    <domain>moff.tech</domain>
    <result>pass</result>
   </spf>
  </auth_results>
 </record>
</feedback>
In the above, you can see that for the sender 64.x.x.x DKIM failed while SPF passed. The message is probably legitimate, but DKIM has possibly been set up incorrectly [Editor's note: it was]. SPF passed so it appears that the final result was that 1 message was successful received.

Caution

The debian-administration website was at one point wrong. The page is great but used exim4 variable syntax that just did not work and was plainly wrong in the context of the wider example as they dropped "-private" from the DKIM_PRIVATE_KEY.

Further References

https://dmarc.org/overview/ 
I highly recommend this site to check your setup:
http://www.appmaildev.com/en/dkim/ 
This site does a basic check on whether your DKIM DNS is okay:
https://www.mail-tester.com/spf-dkim-check

Wednesday, June 22, 2016

Making a Mailserver (Part 3) - Let's Encrypt Webmail

This is an instalment in my series on setting up a Linux based mailserver. See these posts:

You May Not Want Webmail, But...

Consider webmail as an optional section, Let's Encrypt as mandatory. Webmail is not something that I recommend you set up on your mailserver, but webmail is handy as a when all else fails backup. When you're travelling, you can't be sure that you can SSH to your mailserver. Protocols outside of HTTP, HTTPS and DNS are often blocked, which makes mutt not an option. When visiting friends and family, I've found mail clients can be forbidden SMTP or IMAP protocol access to locations outside of the ISP.

On the downside, setting up a web server uses valuable server resources, increases complexity and widens your security exposure.

On the security point, I use squirrelmail and honestly I don't know that it's a very good option. I think squirrelmail is easy to set up and lightweight to run, but development appears to have stopped, which means that security updates won't be arriving. On the other hand, from the remote attacker standpoint if an attacker doesn't already have the username and password of a mail user, then they only have the squirrelmail login page to hack away at. Well hackers are inventive, there should be other avenues into it than just that page. An attacker could craft a malicious message, targeted to exploit squirrelmail, that a user might open via squirrelmail. It's an acceptable risk to me, for now.

If you're not interested in setting up a webmail front-end, what this section will additionally do is provide an easier path to set up Apache for the generation of Let's Encrypt certificates to use with your IMAP and SMTP server daemons.

Apache

Apache will host the squirrelmail install or, at the very least, host the method by which you can easily generate a certificate for your mailserver via letsencrypt. If only required for the latter option, Apache need only be enabled for the duration of the certificate generation and subsequent renewals, saving server resources and reducing your security exposure.

On a default install of Apache there is probably not a lot required to be configured. Apache comes with a default site for both SSL and HTTP. In the /etc/apache2/sites-available/ one should see a "default" and "default-ssl" file.

Check that mod_ssl is enabled:
$ sudo a2enmod mod_ssl
 Check whether the "default-ssl" and "default" sites are enabled:
$ ls -la /etc/apache2/sites-enabled/
-rw-r--r-- 1 root root   692 Dec  3  2015 default -> ../sites-available/default
-rw-r--r-- 1 root root  7251 Dec  3  2015 default-ssl -> ../sites-available/default-ssl
You don't need the default site, because you shouldn't be serving up other pages for sport, only webmail over SSL (HTTPS). 
$ sudo a2ensite default-ssl
$ sudo a2dissite default
Ideally you would copy those sites and customise your own sites based on those files, for example: 
$ ls -la /etc/apache2/sites-enabled/
lrwxrwxrwx 1 root root   47 Jun  1 16:45 ssl-mail.moff.tech.conf -> ../sites-available/ssl-mail.moff.tech.conf
Notice that when you use the a2ensite / a2dissite a symbolic link is created from sites-enabled to sites-available.

We'll discuss now the customisation of the ssl site file, including the stanza for squirrelmail. Please bear in mind the following:
  • This is an abridged version to highlight the things that need to be changed.
  • I'm not promising you the most secure or optimised configuration. 
  • It's really up to you to understand and research the settings here via Apache's useful, although sometimes dense, documentation. For security, you might want to start here.
<VirtualHost *:443>
        ServerName mail.moff.tech:443
        DocumentRoot "/usr/share/squirrelmail"
        ServerAdmin me@example.com
        SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
</VirtualHost>
I'm using example.com because I don't want spam robots harvesting my email address from this blog post.

We've altered the document root to be the squirrelmail install directory. If squirrelmail isn't installed yet, then this is going to cause Apache to complain when reading the config. At the very least, install squirrelmail (we'll customise it later) and set the document root accordingly. Note that this should not be set to the /etc/squirrelmail directory.

If you don't want to use squirrelmail, just leave DocumentRoot as default.

We'll revisit the SSL certs after sorting out letsencrypt. For now, leave them at the defaults (self signed certs), whatever they be called.

Don't move on to the letsencrypt stage until you can get Apache to start your ssl site successfully and have checked that your mail web address resolves and serves some web page - albeit with a self signed cert warning from your browser.

Let's Encrypt and Apache Integration

I don't need to go into a lot of detail here for setting up a Let's Encrypt certificate. Let's Encrypt have issued a million certificates and there's about as many user howtos and blogs to step you through. It works remarkably well as long as you do everything to the letter and have a simple Apache ssl site setup, like ours.

In truth you can use the letsencrypt standalone option and skip Apache altogether.

I've used the letsencrypt.sh client and it works well enough. I see that Let's Encrypt are now pushing something called certbot. I guess that's an experiment for me to do another time (and blog post).

Update: As of January, I notice that the letsencrypt-auto now presses certbot-auto. When running an update you receive "You are running with an old copy of letsencrypt-auto that does not receive updates, and is less reliable than more recent versions. We recommend upgrading to the latest certbot-auto script, or using native OS packages." However the cert is still succesfully issued.

Once you've stepped through the setup, check the location of the cert files. This is probably going to vary depending on the client you used to get the cert issued.
root@mail:/etc/apache2/ssl# ls -la /etc/letsencrypt/live/mail.moff.tech/
total 8
drwxr-xr-x 2 root root 4096 Jun 11 10:14 .
drwx------ 3 root root 4096 Jun 11 10:14 ..
lrwxrwxrwx 1 root root   43 Jun 11 10:14 cert.pem -> ../../archive/mail.moff.tech/cert1.pem
lrwxrwxrwx 1 root root   44 Jun 11 10:14 chain.pem -> ../../archive/mail.moff.tech/chain1.pem
lrwxrwxrwx 1 root root   48 Jun 11 10:14 fullchain.pem -> ../../archive/mail.moff.tech/fullchain1.pem
lrwxrwxrwx 1 root root   46 Jun 11 10:14 privkey.pem -> ../../archive/mail.moff.tech/privkey1.pem
That confirmed, you can head back to the Apache ssl site and verify the certificates. Where you had this:
SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
You should now have (or add) this:
SSLCertificateFile      /etc/letsencrypt/live/mail.moff.tech/cert.pem
SSLCertificateKeyFile   /etc/letsencrypt/live/mail.moff.tech/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/mail.moff.tech/chain.pem
Reload (you don't need to restart) Apache and check for errors at startup. Now you can disable Apache if you don't want to set up webmail.

The certificate you've been issued only lives for a few months. Consider a crontatb entry to renew the certificate. For example, this renews every three months (months 3, 6, 9, 12) on the 8th day:
1 0 29 JAN,APR,JUL,OCT * /root/letsencrypt/letsencrypt-auto renew
I have a script (tested and verified with crontab) to handle the renewal process because, as we will see shortly, when the certificates are required for multiple jobs the file permissions don't suit every daemon. Check out letsencrypt-handle-certs.sh on my gitlab repository.

Finally, when Apache is enabled, verify your SSL/TLS strength via SSL Labs.
 

Squirrelmail


The default install of sqm offers an apache.conf but I recommend that you ignore it. It doesn't show you the HTTPS context and you don't want to expose configtest.php anywhere.

There is a lovely script to configure Squirrelmail just as you want it:
$ sudo squirrelmail-configure
Some of the config points are these:
$imapServerAddress      = 'mail.moff.tech';
$imapPort               = 993;
$useSendmail            = false;
$smtpServerAddress      = 'mail.moff.tech';
$smtpPort               = 25;

Troubleshooting squirrelmail 

When sending an email via Squirrelmail, it may fail with a 550 message about relay not being permitted. This is because Squirrelmail didn't pass AUTH so the server doesn't trust the client and won't relay for it. The quick resolution is to add the server IP into the list of "Machines to relay mail for" in the Exim config as I mentioned in part 2.

I found that Squirrelmail will try an AUTH before Exim offers it. In other words, if you set this in /etc/squirrelmail/config.php:

$smtp_auth_mech = 'login';
Then the result may be a new problem:
Bad sequence of commands
503 AUTH command used when not advertised
Exim wants to do AUTH after TLS but Squirrelmail tries to do AUTH before that. The workaround for this is to set $smtp_auth_mech = 'none'; and make sure that the IP of the server is set as a host to be a smarthost for. This setup of Squirrelmail doesn't use TLS to connect to Exim, I can't make that work with AUTH. I ended up with a message about being unable to open the TCP stream and this in the syslog:
SMTP protocol synchronization error (input sent without waiting for greeting): rejected connection from H=mail.moff.tech [64.x.x.x] input="\026\003\001"

Since packets from Squirrelmail never leave the box, from a security standpoint there's little to gain with a TLS connection - unless you distrust your server processes or users. Some web searching might reveal a solution to setting up Squirrelmail with TLS and AUTH. If you find one, let me know!


Squirrelmail will use the system user credentials by default. If you try to login as root, then squirrelmail will fail to get in. You'll see something like this in the syslog:
Jun 22 22:57:07 mail dovecot: imap-login: Login: user=<root>, method=PLAIN, rip=x.x.x.x, lip=y.y.y.y, mpid=25745, TLS, session=<N5s1LuQ1HQBAFlV3>
Jun 22 22:57:07 mail dovecot: imap: Error: user root: Invalid settings in userdb: userdb returned 0 as uid
Jun 22 22:57:07 mail dovecot: imap: Error: Invalid user settings. Refer to server log for more information.
Setting the servername to match the one on the certificate will avoid errors such as this:
Jun 11 12:06:05 mail dovecot: imap-login: Disconnected (no auth attempts in 0 secs): user=<>, rip=::1, lip=::1, TLS, session=<Tvayzf00ZQAAAAAAAAAAAAAAAAAAAAAB>
Above, the certificate servername validation was probably failing. You can't use the loopback address or you'll get that kind of error.

Additionally, an error log message like this one:
Jun 11 11:55:55 mail dovecot: imap-login: Disconnected (no auth attempts in 0 secs): user=<>, rip=x.x.x.x, lip=y.y.y.y, TLS: SSL_read() failed: error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca: SSL alert number 48, session=<qutPqf00mABSh18U>
Will likely happen if the CA is unknown. You might see this if you've copied a self signed cert from another server without copying the "Self CA" as well. Rather than faff about sorting out a self signed cert, use Let's Encrypt.

Dovecot Certificate Integration

To splice the new certs into the Dovecot configuration, you might get away with simply calling the Let's Encrypt cert files where they stand.

Edit /etc/dovecot/dovecot.conf:
ssl_cert = </etc/letsencrypt/live/mail.moff.tech/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.moff.tech/privkey.pem
Then reload and watch the logs for errors.

After my first attempt at adding the Let's Encrypt cert, Thunderbird reported that my cert was still essentially self signed. I hadn't used the "full chain" file so apparently that meant Thunderbird wasn't too sure who the root CA was. This is possibly because Thunderbird didn't have the intermediate CA certs stored. Mozilla applications will apparently tuck a received Intermediary CA cert away for later reference. Using the full chain file allows Thunderbird to iterate the chain of authority.

That's also why you set the chain file in the Apache config.

Exim4 Certificate Integration

The exim4 config file states categorically:
# Full paths to Certificate and Private Key. The Private Key file
# must be kept 'secret' and should be owned by root.Debian-exim mode
# 640 (-rw-r-----). exim-gencert takes care of these prerequisites.
As it's a bad idea to tinker with the cert file permissions and owner/group. Copy them into the /etc/exim4/ directory and set the permissions accordingly:
# ls -l /etc/exim4/ | grep 'pem'
-rw-r----- 1 root Debian-exim  3456 Jun 12 22:01 fullchain.pem
-rw-r----- 1 root Debian-exim  1704 Jun 12 22:01 privkey.pem
If you're copying over the old files, then you don't need to change the exim4 default configuration. If you're editing the exim config, here's how:

/etc/exim4/conf.d/main/03_exim4-config_tlsoptions:
.ifndef MAIN_TLS_CERTIFICATE
#MAIN_TLS_CERTIFICATE = CONFDIR/exim.crt
MAIN_TLS_CERTIFICATE = CONFDIR/fullchain.pem


.ifndef MAIN_TLS_PRIVATEKEY
#MAIN_TLS_PRIVATEKEY = CONFDIR/exim.key
MAIN_TLS_PRIVATEKEY = CONFDIR/privkey.pem
Notice that we're using fullchain again.

I'm handling these steps in a cert renewal script, but at the time of writing I'm not ready to publish that script due to it having had very little testing.

After updating the cert file location, reload exim.
# service exim4   reload
Watch /var/log/syslog, /var/log/exim4/mainlog and /var/log/exim4/rejectlog for unusual messages.

Wednesday, June 15, 2016

Making a Mailserver (Part 2) - My Personal Daemons: Exim and Dovecot

This is an instalment in my series on setting up a Linux based mailserver. See these posts:

The Boring Preamble

In this post, we set up exim4 as an mail receiver (and personal email relay) and dovecot as your secure IMAP service. Why use exim4 and dovecot? Because there's safety in numbers; these are tremendously popular daemons, have comprehensible config structures and are well documented. That documentation extends to a lot of user sites, like this blog, where people post their own experiences and suggestions.

Security is the primary concern. Dovecot provides secure IMAP, but I needed exim to allow me to relay my mail. If you blindly turn on mail relay then soon enough most of the population of Eastern Europe and various other countries will be using your server as a spam hub and malware pivot-point.
This image is deliberately vague, but it is definitely of mailboxes

exim4

A mail relay is a server that accepts mail for destinations that are not delivered locally to that server. A relay assumes the responsibility of sending that email on to its intended destination. Relay is what I needed, but I was reluctant to share my server bandwidth with spammers. It is possible to configure exim4 to be a mail relay only on behalf of authorised hosts and users.

But first, you need to get exim4 off the ground with the basic config. The exim4 setup wizard is really great. On debian you can invoke this with
dpkg-reconfigure exim4-config
The magic of this wizard is that you can run it over and over while you try to get the settings right. The wizard won't overwrite customisations you've made in the other config files, unless those customisations pertain specifically to questions in the wizard.

Setup Wizard Explanation

General type of mail configuration:
Select: internet site; mail is sent and received directly using SMTP
System mail name:
Input: example.com
Do not put something like mail.example.com or hostname.example.com!
IP-addresses to listen on for incoming SMTP connections:
Input nothing.
This is an Internet facing mail server, you can't reduce your risk here.
Other destinations for which mail is accepted:
Input: anotherexample.com; yetanotherexample.com
Any other domains that you plan to receive mail for is added here. At this stage you probably want to leave this empty.
Domains to relay mail for:
It's really a good idea to leave this blank. If you wanted to accept and deliver mail for another domain and not deliver locally, then here is where you configure that. This may be like playing with fire if you are not in control of the domains that you list here. Don't confuse it with the "other destinations" question earlier, which is about local delivery.
Machines to relay mail for:
Input any host IP addresses that you control, for which you want to handle mail from. This is incredibly useful. You have several servers out on the Internet and you want them to be able to send email to the rest of the world, you can tell those servers to use this server as their "smarthost" mail relay. If you are going to set up Squirrelmail per my instructions then you must enter your own server's IP here. Squirrelmail in this setup will connect to Exim using the server's IP as its source.

Instead of trying to configure several servers with tight mail rules and dmarc policies, you need only configure this server and have your other servers relay their outgoing email here.
Keep number of DNS-queries minimal (Dial-on-Demand)?
Nobody cares, select "No".
Delivery method for local mail:
You probably want to select: "mbox format in /var/mail/"
Split configuration into small files?
Select "Yes". You end up with a mess of files, but in the long run I think it's better. The rest of this guide is based on this setting being enabled..

That's the last question. Now the basic framework of your mailserver is setup. The tricky secure lockdown is next.

With exim4, you can enable relay for the specific case where the sender is authenticated. In other words, if I can prove to my server that I am an authenticated person, then my server will relay my emails. When I connect to my mailserver to send email, I go through a username/password challenge.

You don't want to do the authentication over plain text, so encrypting the traffic is key to reducing the likelihood that someone will crack your username and password. That's what the STARTTLS method is for.

I'm not going into particular detail as to each setting, these you can look up for yourself in the exim4 documentation. Here's the step-by-step configuration. 

Adding the Security

Edit /etc/exim4/update-exim4.conf.conf and check that you can see this (if you can't, then you ignored my recommendation earlier and I can't help you anymore):
dc_use_split_config='true'
Now add these lines to the end of the file:
host_auth_accept_relay=*
auth_over_tls_hosts=*
auth_always_advertise=true
 Create a file called /etc/exim4/conf.d/main/00_my_macros and add this:
MAIN_TLS_ENABLE = true
AUTH_PLAINTEXT=yes
AUTH_CRAM_MD5=yes
You'll be back in this file later if you set up DKIM via my setup guide.

Now edit /etc/exim4/conf.d/auth/30_exim4-config_examples and uncomment/setup this section:
plain_server:
 driver = plaintext
 public_name = PLAIN
 server_condition = "${if crypteq{$auth3}{${extract{1}{:}{${lookup{$auth2}lsearch{CONFDIR/passwd}{$value}{*:*}}}}}{1}{0}}"
 server_set_id = $auth2
 server_prompts = :
 .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS
 server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}}
 .endif

Because you've made changes to the split config, run the config updater and then reload: 
update-exim4.conf
service exim4 reload
At the time of writing, I am not sure whether both steps are really required.

Fake Certificate

In later posts, you'll find out how to attach a letsencrypt certificate (a real certificate) to apache, exim and dovecot. For now though, let's just get something working.

Generate your own certificate for exim to use in STARTTLS (debian example):
 /usr/share/doc/exim4-base/examples/exim-gencert --force
You'll need to answer some questions, you can't get them wrong. It's an untrusted (self signed) certificate. We'll get a real certificate sorted out later, via letsencrypt. One thing at a time.

The certs will hopefully be dumped into the /etc/exim4/ as exim.crt and exim.key. Debian-exim will need to be able to read this and you can't have an all readable flag
chmod 640 /etc/exim4/exim.*
chown root.Debian-exim /etc/exim4/exim.*
ls -l /etc/exim4/
...
-rw-r----- 1 root Debian-exim   790 Sep 16 14:12 exim.crt
-rw-r----- 1 root Debian-exim   891 Sep 16 14:12 exim.key

Troubleshooting exim4

There is an excellent debug mode that you can run exim in. If you have problems, stop exim and start it in debug mode:
/etc/init.d/exim4 stop  
exim -bd -d -oX 25
Of course, when you want to start exim normally again, use the "start" option instead of "stop".

Tail the exim 'mainlog' and 'rejectlog' logfiles in /var/log/exim4 as you test email delivery to your server.

http://bradthemad.org/tech/notes/exim_cheatsheet.php

Dovecot

Configuring dovecot is relatively straightforward and doesn't need a lot of explaining. The dovecot help pages are very good. Don't randomly google problems, check those pages first.

I don't have any notes from the first time I set up dovecot as it is getting on for a decade ago and the config evolved over time. Over all these years, there appear to have been no configuration syntax changes that have affected me, until I jumped from Debian 6 to Debian 8 (Jessie).

When I did server migrations in the past, I lazily copied the old dovecot config files into the new /etc/dovecot directory and miraculously dovecot still started. The same was true when I migrated to Debian 8.5. However, I was confronted by these messages in /var/log/syslog:
doveconf: Warning: NOTE: You can get a new clean config file with: doveconf -n > dovecot-new.conf
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:25: 'imaps' protocol can no longer be specified (use protocols=imap). to disable non-ssl imap, use service imap-login { inet_listener imap { port=0 } }
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:100: ssl_cert_file has been replaced by ssl_cert = <file
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:101: ssl_key_file has been replaced by ssl_key = <file
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:718: protocol managesieve {} has been replaced by protocol sieve { }
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:890: add auth_ prefix to all settings inside auth {} and remove the auth {} section completely
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:928: passdb pam {} has been replaced by passdb { driver=pam }
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:1041: userdb passwd {} has been replaced by userdb { driver=passwd }
doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:1103: auth_user has been replaced by service auth { user }
I ran "doveconf -n > dovecot-new.conf" as suggested and the configuration was boiled down to this simple setup:
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_location = mbox:~/mail:INBOX=/var/mail/%u
mail_privileged_group = mail
passdb {
  driver = pam
}
protocols = imap
service auth {
  user = root
}
service imap-login {
  inet_listener imap {
    port = 0
  }
}
ssl_cert = </etc/ssl/certs/dovecot.pem
ssl_key = </etc/ssl/private/dovecot.pem
userdb {
  driver = passwd
}
#protocol imap {
#  mail_max_userip_connections = 20
#}
#protocol pop3 {
#  pop3_uidl_format = %08Xu%08Xv
#}
Notes on the above:
  • We'll setup letsencrypt certs later.
  • You should have certs generated automatically, if you want to learn more about handling them with correct file permissions and so on, refer to the dovecot wiki.
  • I commented out the last lines by hand.
A default install of dovecot sees many files placed inside the dovecot directory. Out of interest, I removed all of them except for the earlier generated dovecot-new.conf, which I renamed to dovecot.conf. Dovecot still started and remote connections to the server worked. Using "nmap localhost" I could verify that the listening ports were just the same. Therefore, I could verify that the other files were technically unnecessary for the pupose of this setup. However, to ensure your ease for future customisation, you'll want to leave those default files there.

In my version of the config, I am not using an include statement in dovecot.conf, so the conf.d files are not read. The dovecot documentation is really good, see http://wiki.dovecot.org/BasicConfiguration:
The default configuration starts from dovecot.conf, which contains an !include conf.d/*.conf statement to read the rest of the configuration. This split of configuration files isn't a requirement to use, and it doesn't really matter which .conf file you add any particular setting, just as long as it isn't overridden in another file. You can verify with doveconf -n that everything looks as you intended.
If you use systemd, you may encounter these messages:
# service dovecot start
Jun 14 22:09:56 mail dovecot[20409]: Error: systemd listens on port 143, but it's not configured in Dovecot. Closing.
Jun 14 22:09:56 mail dovecot: master: Error: systemd listens on port 143, but it's not configured in Dovecot. Closing.
Jun 14 22:09:56 mail dovecot[20409]: Error: systemd listens on port 143, but it's not configured in Dovecot. Closing.
Jun 14 22:09:56 mail dovecot: master: Error: systemd listens on port 143, but it's not configured in Dovecot. Closing.
Jun 14 22:09:56 mail dovecot: master: Dovecot v2.2.13 starting up for imap (core dumps disabled)
The messages are not actually something to worry about. Suggestions around the Internet say that they can be stopped as so:
# systemctl disable dovecot.socket
Removed symlink /etc/systemd/system/sockets.target.wants/dovecot.socket.
I'm not sure whether that works. You might want to do some googling to understand more about systemd. For now, it's not important.

Troubleshooting Dovecot

Use "tail -vf /var/log/syslog" to see dovecot reporting IMAP authentication and connections.

There are further troubleshooting notes in the Webmail post (part 3) of this series.

Once again, the dovecot docs are really good!
http://wiki.dovecot.org/RunningDovecot
http://wiki.dovecot.org/FrontPage#Troubleshooting