How to eliminate spam and protect your name with DMARC DKIM and SPF
If you are reading this you are probably making my life harder
E-Mail sucks! Your users just keep clicking the links in those damn phishing mails. And you can’t do anything about it. Hell somebody might be sending spam in your name and you have no idea about it. Let me blow your mind: You can solve these problems, for free. And I will introduce you the tools you need.The cure to your E-Mail headaches hides behind three small acronyms:
- SPF: Sender Policy framework, tells others which mail servers are authorized to send E-Mail for your domain.
- DKIM: Domain Keys Identified Mail, uses encryption and DNS to verify an E-Mail sender and that it was not altered in transit
- DMARC: Domain based Message Authentication, Reporting and Conformance. Builds on SPF and DKIM and implements a policy and reporting system around them
Well guess what? Mine is as well. In this post I will not just give you a bunch of configuration examples and send you on your way. I will give you a small overview of my environment as a reference and then actually explain what you are doing and why. Armed with this knowledge, adapting this tutorial to your own infrastructure will be a breeze.
If you already have some of the systems in place feel free to jump to the point you are interested in.
Let’s get on the same page –overview of my mail environment
I promised you a look at my mail environment. Let me warn you it is “Historically Grown”. I hear you groan, well you are probably right. But If I can get my system to stop spam and phishing, so can you!A quick overview of the building blocks used for this:
- Mail server: Debian 7, Postfix, Dovecot , responsible for one mailbox domain and one virtual alias domain
- Antivirus and Spam check: Debian 7, Amavis-New, ClamAV, Spamassassin
- Webmail: Debian 7, Apache, Horde
- Database: Debian 7 MySQL
- Domain Controller: Debian 7, Samba 4(Backport).
The other thing you might notice, is that every service has it’s own (virtual) machine. This is due to two things:
- Linux Virtual containers with OpenVZ have close to 0 overhead
- When I started out and ran a bunch of services on one box, I quickly learned that the failure of one service can effect others in unexpected ways.
Wait you are not the mailman – Validate E-Mail Senders with SPF
The first cornerstone that will help you get rid of phishing is SPF. SPF is an acronym for Sender Policy Framework and is in the simplest terms a way to ensure, that a Server is authorized to send an E-Mail for a domain. You probably wouldn’t open the door to a guy in a ski mask with crowbar in hand only because he tells you has your latest Amazon order. In the online world SPF is a way to recognize the guy with ski mask.This is achieved with a set of special DNS records for your domain. These records contain information about which servers are allowed to send E-Mail for your domain and how certain you are about that information. This diagram shows you how SPF works:
As shown in the diagram your server does not need any special program to get your E-Mail SPF verified. Your Domain simply needs to have a SPF DNS record. On the receiving side you will however need a little more work. You have to implement a program that can check the SPF records of the domain you are receiving E-Mail from. With postfix the 3 easiest options are:
- Spamassassin: You probably have this running already. But in my opinion you will want to fine tune the score for the SPF tests a little. Spamassassin helps you to mark failed SPF checks as spam, but not in a way that is useful for DMARC verification.
- postfix-policyd-spf-python: This is the tool I recommend for the job. This allows you to rewrite the header to include the results of the SPF check or even outright reject E-Mail that fails the SPF check.
- opendmarc: As the name suggest this is mainly a tool to implement DMARC. Opendmarcs capabilities include running SPF checks, but due to its limitations I still recommend using postfix-policyd-spf-python.
Not everyone should be allowed to speak for you – Create your own SPF Record
And setting a SPF record for your domain empowers you to tell the world which mail servers are allowed to send your E-Mails. Setting this record will take you less than five minutes and potentially saves everyone you are corresponding with a lot of headaches. Some of you might say: “Why would I care about that?” Well that is easy. Because you are responsible! You are the mail admin and if unwanted E-Mail is sent in your name, warning everyone about the imposters is your duty.Now that you are convinced that you need to set a SPF record, let me get all technical on you. A SPF record is aDNS TXT record attached to your entire domain (or subdomain). This record starts with the SPF version that you are using. At the time of this writing there is only version one. The start of the record will look like this:
1 |
v =spf1 |
- Pass: expressed by “+” this qualifier is assumed if no other qualifier is used. The Server(s) listed with a pass qualifier are allowed to send mail for your domain.
- Fail: expressed by a “-“ The servers listed here are NOT allowed to send mail for your domain. The plus usually also implies that mail should be rejected if SPF fails. I personally am not a friend of dropping or rejecting mail unless I am 110% certain that I do not want it, but unlike many other people I still recommend using the fail qualifier instead of soft fail. You should know which hosts can conceivably send E-Mail for your domain and you should disallow all other hosts from sending messages in your name.
- Softfail: expressed by “~” basically states that those hosts are probably not allowed to send mail for your domain but you aren’t certain. As a result mail servers should accept but tag mail from those servers. I believe this is ok for testing SPF records while reducing the impact of mistakes.This qualifier can also be used during server transfers. (set the new server to pass and the old server to soft fail for a little while)
- Neutral: expressed by “?” you are telling everyone, that you have no idea whether these servers are allowed to send mail for your server. Mail from those server will usually be accepted, but unless you are just testing new additions to your SPF record, setting this is not much use for anyone.
- “all” you will only want to use this one with a “-“ qualifier in front of it, after you listed your servers. As the name suggests this mechanism encompasses all possible senders.
- ipv4” You can use this mechanism to allow Ipv4 hosts and networks to send E-Mail for your domain. The network has to be specified in CIDR Notation. An entry could look like this:
12
ipv4:127.0.0.1
ipv4:127.0.0.0
/24
- “ipv6” same as above but for ipv6. an entry could look like this:
1
ipv6:fc00::
/7
- “a” you can use this to specify the a record of the allowed mail server(s). This can be combined with CIDR notation to allow the entire subnet to which the returned address belongs. Here are a couple of examples
12345678
# use the a record of the current domain to determine valid sender IPs
a
# all IPs in the /24 subnet of this a record are valid senders
a
/24
# the next two work in the same way as above but they use the a record of example.com
# no matter what domain the SPF record is on
a:example.com
a:example.com
/24
- “mx” this mechanism works like the “a” mechanism, except that it uses all mx records of the current or specified domain as valid senders. Adding a subnet is possible as well.
- “redirect” refers to the SPF record of another domain. If you include a domain, all hosts considered valid senders for that domain, will also be valid senders for your domain. All changes to their SPF will also affect your domain. You might need this if you are using outbound gateways or hosted E-Mail. Another use for this would be if you are using multiple Mail domains on your own server(s). With this mechanism in place you would only have to keep one SPF record updated. A redirect is specified like this:
1
redirect=google.com
- The public record for my main domain allows only one host, defined by an a record, to send mail. All other hosts are explicitly forbidden to send mail for my domain:
1
v
=spf1 a:mail.skelleton.net –all
- This is actually the record I use in my local network. This record allows two different subnets to send E-Mail in my name:
1
v
=spf1 a:mail.skelleton.net
/24
ip4:10.0.0.0
/24
–all
- And on top of that I have a virtual alias domain that is hosted on my mail server. I set it up to redirect to my main domain:
1
v
=spf1 redirect=skelleton.net
Place a bouncer – Validate SPF within Postfix
Now that everyone knows your mail servers are the real deal, you might want to check the mail servers of all your incoming E-Mail. The tool of choice for this task is “postfix-policyd-spf-python”. You can use this policy service to just rewrite the headers of your incoming mail (useful for further processing) or you can outright reject E-Mails that fail the SPF checks.Let’s start:
- The first step is obviously the installation of the policy service. This is a simple one liner in Debian Wheezy
1
apt-get
install
postfix-policyd-spf-python
- Next you will have to edit the configuration file with the editor of your choice:
1
nano
/etc/postfix-policyd-spf-python/policyd-spf
.conf
- As I explained earlier, I only want to add the SPF check headers to my E-Mails and I do not want to reject any messages for SPF failures. To achieve this I had to change following two lines of my default configuration:
12
HELO_reject = False
Mail_From_reject = False
- Next you need to add the policy service to your Postfix configuration. This requires two changes to your Postfix main.cf file. Open this file with you editor:
1
nano
/etc/postfix/main
.cf
- You need to add the following option to your main.cf:
1
policy-spf_time_limit = 3600s
1smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination,check_policy_service unix:private
/policy-spf
- After those two changes you need to edit the master.cf configuration file of your Postfix installation:
1
nano
/etc/postfix/master
.cf
12policy-spf unix - n n - - spawn
user=nobody argv=
/usr/bin/policyd-spf
- Now all you need to do is restart Postfix, so that your configuration changes can take effect:
1
/etc/init
.d
/postfix
restart
But does it actually work? – Confirming your successful SPF implementation
After this SPF should work for your domain. You can check this by testing with an external mail account. Gmail is a good candidate for this since they also have DKIM and DMARC set up correctly. First send an E-Mail from your own domain to an external mailbox you control and check the message source. The header should contain a field named “Received-SPF” and this should contain pass. If this field has a fail or softfail, you need to look at your SPF Record again. If this header field is not present at all, the DNS record may not have propagated everywhere yet. In this case you have to wait a little. Your provider may not add SPF results to the header of incoming mails. In that case use another mail provider for this.In order to check if SPF is validated for your incoming mail, simply check the headers of a few E-Mails that have arrived since the change. The received SPF field has to be present in the header now.
A Virtual signature for your company – Authenticate E-Mails with DKIM
In essence DKIM is a way to sign E-Mails coming from your domain and telling the world how your signature should look like. It achieves that outcome by encrypting the message body and certain header fields with asymmetric encryption and then hashing the result. The process is slightly more complicated than SPF. But don’t worry, just have a look at the diagram to get an overview of the entire DKIM process.:- The Outgoing Email’s body and a few of the header fields are hashed and then encrypted with your domains private key . The content of this field could look like this:
1234567
v
=1; a=rsa-sha256; c=relaxed
/simple
; d=skelleton.net;
s=mail; t=1425776684;
bh=g3zLYH4xKxcPrHOD18z9YfpQcnk
/GaJedfustWU5uGs
=;
h=Date:From:To:Subject:From;
b=YK3uKQweSaxXF5h0SkoTeHvgvVuR3yjkkFT1XEGIbwoP9ht9PTo0+0qvtKB
/QhhaU
HCakgk2QC
/eE9R5RS4wqF0G/jDxGAFU5ZM2ULrNoVGCBkHTw3IfBBPVBR24tFjXKV2
pN5UPbMBMiyS3AdtujqTma4STHOxHyYVf+L8PSfg=
- v: DKIM version
- a: the used encryption and hash algorithms
- c: the canonicalization algorithms that should be used for header and body
- d: domain name that issued this signature.
- s: selector that is necessary to find the public key. In this case you would have to query mail._domainkey.skelleton.net in order to get the public key.
- t: Time the signature was created on
- bh: encrypted hash of the message body
- h: a list of signed header fields. Even if it is not listed here, the DKIM header field is always included in this list
- b: encrypted signature data
- The receiving mail server gets the mail and retrieves both the public key and the hash algorithm via DNS.
- Then the server takes all the information that was signed and hashes them in the same way the sender did
- These hashes are compared to the result of decrypting the signatures.
- If the two match DKIM will pass, else it will fail. The result of this DKIM test is added to the header of the E-Mail in a new “Authentication-Results” field.
- Processing of the E-Mail continues
Teach your mail server to sign E-Mail – Implement DKIM with Postfix
Now that you are properly hyped about DKIM, I have even more good news: all the tools you need are available in the official Debian Wheezy repository. Those tools are called opendkim and opendkim-tools and I will walk you through their installation and configuration in easy to follow steps.- Installing opendkim is a one liner on the shell:
1
apt-get
install
opendkim opendkim-tools
- Next you will need to edit the default configuration of opendkim:
1
nano
/etc/opendkim
.conf
- I will not list the entire configuration file here, but rather only the lines you need to edit. With this configuration opendkim is capable of signing E-Mails for multiple domains. But it also works for single domain scenarios:
1234567891011
KeyTable
/etc/opendkim/key_table
SigningTable
/etc/opendkim/signing_table
ExternalIgnoreList
/etc/opendkim/trusted_hosts
InternalHosts
/etc/opendkim/trusted_hosts
AutoRestart Yes
AutoRestartRate 10
/1h
Mode sv
PidFile
/var/run/opendkim/opendkim
.pid
SignatureAlgorithm rsa-sha256
Canonicalization relaxed
/simple
UserID opendkim:opendkim
- Next you need to create the directories that will be used for the keys and the additional configuration files:
123
mkdir
/etc/opendkim
mkdir
/etc/opendkim/example
.com
mkdir
/etc/opendkim/example2
.com
- Now you need to create the list of trusted hosts:
1
nano
/etc/opendkim/trusted_hosts
12345#local host
127.0.0.1
# local subnets that are trusted and do not need to be verified
10.0.0.0
/24
10.0.1.0
/24
- Now is the time to create the signing keys for your domain. The ‘–s’ option in opendkim-genkey specifies the selector or name of the key. I suggest using mail here, as that is the keys purpose. This step has to be repeated for every domain that you wish to sign E-Mail for
123
cd
/etc/opendkim/example
.com
opendkim-genkey -s mail -d example.com
chown
opendkim:opendkim mail.private
- Now that you have the key pairs, you can create the key table file. This file tells opendkim, about all the domains you want to sign and where to find their keys:
1
nano
/etc/opendkim/key_table
1mail._domainkey.example.com example.com:mail:
/etc/opendkim/example
.com
/mail
.private
- mail._domainkeyexample.com is the KeyID. The KeyID is build as follows: selector._domainkey.domain.tld
- example.com: the domain this entry is for
- mail: is the selector
- /etc/opendkim/example.com/mail.private: the path to the private key
- Next you have to create the signing table:
1
nano
/etc/opendkim/signing_table
1example.com mail._domainkey.example.com
- With this done, you have to ensure, that opendkim starts at boot time and has a socket. Simply edit the default file for opendkim to get this done:
1
nano
/etc/default/opendkim
1SOCKET=
"inet:12345@localhost"
- Now you have to change the configuration of postfix.
1
nano
/etc/postfix/main
.cf
1234milter_protocol = 6
milter_default_action = accept
smtpd_milters = inet:localhost:12345
non_smtpd_milters = inet:localhost:12345
- If you are running a filtering solution like amavis-new, you should consider excluding the milter from running when it returns your E-mails to postfix. This will not change the success or validity of your DKIM tests, but it will reduce system load:
1
nano
/etc/postfix/master
.cf
123127.0.0.1:25025 inet n - - - - smtpd
[....]
-o smtpd_milters=
- Before you can take your configuration live, you need to update the DNS records for your domain. This is simple since the DNS record you need has already been created for you during key creation. You simply need to copy the contents of following file:
1
/etc/opendkim/example
.com
/mail
.txt
- Once you are reasonably certain that your new record has propagated, you should restart opendkim and Postfix, so that your changes take effect:
12
/etc/init
.d
/opendkim
restart
/etc/init
.d
/postfix
restart
Validate your DKIM configuration
The validation is similar to what you did for SPF, E-mail an external web mail account that you have access to and then write an E-Mail back. You will need a mailbox at an provider that implements DKIM like Google Gmail.Once you have done that look into the headers of both e-mails.You should be able to find a header field called “Authentication-Results”. In this field you will find a string containing “dkim=pass”. If not, check you configuration again.
In the setup that I am describing you will ultimately find two authentication results headers field. One for DKIM and one for DMARC. Other implementations like the one G-Mail uses create only one field for all tests.
How do I know if a message should be signed? – Enhancing DKIM with ADSP
While DKIM is a way to determine if a signed E-Mail is valid. It does not contain any way to deal with E-Mails that are not DKIM signed. You can solve this problem by implementing a DKIM extension called ADSP or Author Domain Signing Practices. ADSP is a DNS record that tells other mail servers what to do with unsigned E-Mail. You can also achieve this with DMARC, but I urge you to implement both. Simply because DMARC is not anywhere near as widely implemented as DKIM. Since opendkim does already check ADSP on the receiving side, you only have to set a DNS record to implement it fully.You need to create a txt record for following host:
1 |
_adsp._domainkey.example.com |
1 |
dkim=all |
- unknown: both signed and unsigned mail is valid. If this is the case for your domain, you do not need to publish an adsp record as this is the default behavior in DKIM anyway
- all: All mail from this domain is supposed to be signed. If you can manage to get all your mail signed this is a good setting. But check on your newsletters and such before setting this.
- discardable: all mail from this domain is supposed to be signed and any mail that is not should be discarded. This is usually not needed. But if your organization deals with financial data or any other sensitive information do everyone a favor and set this.
Enforce your rules with DMARC
The last technology I would like to introduce you to is DMARC: Domain based Message Authentication, Reporting and Conformance. DMARC builds on both of the things that you just implemented in your mail environment. In the simplest terms DMARC allows you to create policies as to what should happen to an E-Mail if either one or both of the other checks fail. And it provides a standard for reporting that allows postmasters to be aware of phishing or spam in their name.As the previous two technologies DMARC relies on a specially formatted DNS TXT record. I will help you to create this record for your domain and after that I will show you how to implement opendmarc in a step by step guide. This will allow you to check incoming mail for DMARC compliance and to report aggregated statistics back to domains who want them. Here is a rough overview of how the everything will work once you implemented DMARC:
Publish your rules – Create your DMARC DNS record
The DMARC record is a txt record for the host “_dmarc.example.com”. Your DMARC record tells the outside world how they should handle E-mail that is coming from your domain. In addition it tells everyone how you would like to receive DMARC reports for your domain. With that being said, DMARC does not require other mail servers to follow all the reporting guidelines you set. In addition none of these settings have any effect on how your mail server processes incoming messages. Creating a valid DMARC record is somewhat complex, due to all the information that supposed to go into it. I will use my own DMARC record as an example to explain all the pieces of a valid DMARC record. If you don’t care for this much in depth knowledge, you can use this DMARC record generator.
1 |
v =DMARC1; p=quarantine; rua=mailto:dmarc [at] skelleton [dot] net; ruf=mailto:dmarc [at] skelleton [dot] net; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400 |
- v: DMARC version. This is a required field. For now it will have the value of ‘DMARC1’
- p: DMARC policy. This is an required field as well, you can set it to one of three values:
- none: Means don’t do anything if the DMARC verification fails. This is a good setting while you are still testing your DMARC implementation as it will not disrupt your outgoing mail if you made a mistake in your configuration.
- quarantine: Mail that fails DMARC checks should be treated as suspicious. This is good middle ground setting if you want to ensure that none of your mail gets lost.
- reject: Mail should be rejected If the DMARC verification fails. This is a good setting if your domain is used for phishing or if trust in your E-Mails is more important than occasional lost messages.
- rua: This is an optional parameter and contains the address to which the DMARC aggregate report should be submitted. You can specify web addresses here, but I have not done so so far. If you do not set this option at all, you will not receive DMARC reports.
- ruf: This is similar to the rua field, but these are the addresses for DMARC forensic reports. These reports contain detailed information about failed DMARC verifications of E-mail claiming to be from your domain.
- fo: These are reporting options for the failure reports. This can have four possible values:
- 0: generate a report if both SPF and DKIM tests failed
- 1: generate a report if either the SPF or the DKIM test failed
- s: generate a report if the SPF test failed
- d: generate a report if the DKIM test failed
- adkim: This option is optional and controls how strict the result of the DKIM verification should be interpreted. It defaults to relaxed if it is not present. Possible values are:
- s: strict
- r: relaxed
- aspf: This option is optional and controls how strict the result of the SPF check should be interpreted. It defaults to relaxed if no value is set. Possible values are
- s: strict
- r: relaxed
- pct: This is also optional and determines how many percent of the messages from your domain should have the DMARC verification done by other mail providers. The possible values here are integers between 0 and 100. It defaults to 100 if it is not set.
- rf: This optional field lets you specify your preferred reporting format for forensic reports. It defaults to “afrf”. These are the possible values:
- afrf: Authentication Failure Reporting Format
- aodef: Accident Object Description Exchange Format
- ri: This optional field is the interval at which you want to receive DMARC reports in seconds. It defaults 86400 seconds (one day). According to the DMARC specification every participating organization should be able to send reports at least once every day. Intervals as small as one hour are within the specification. But those smaller intervals are generally served on a best effort basis.
- sp: The last field is also optional (and not present in my example). It is the subdomain policy. If you do not set this, the policy you set in the beginning will apply to your subdomains. If you set this you can use the same values as in the policy field. This can be useful if you know, that you never send mails from one of your subdomains. In that case you can set the subdomain policy to reject without any risk of your legitimate E-Mails being discarded.
Know the law – Integrate opendmarc into Postfix on Debian
Now that your DMARC record is set, you need to integrate some kind of DMARC verification into your Postfix server. I choose the milter opendmarc for this task. Unfortunately it is not in the Debian Wheezy default repository. But you can use Wheezy-Backports to get an installation package. Which makes it easy to upgrade later on. I will walk you through the installation step by step:- Since opendmarc is currently only available as a backport, you will have to add the Debian backports repository. To do that you need to edit your sources.list file:
1
nano
/etc/apt/sources
.list
1deb http:
//ftp
.debian.org
/debian
wheezy-backports main contrib
- Now you need to update the list of available packages and then you can install opendmarc using the backports repository:
12
aptitude update
aptitude -t wheezy-backports
install
opendmarc
- Once the installation is done, you need to edit a few things in the opendmarc configuration file:
1
nano
/etc/opendmarc
.conf
1234567891011AuthservID mail.example.com
PidFile
/var/run/opendmarc
.pid
#Debian default
RejectFailures
false
Syslog
true
TrustedAuthservIDs mail.example.com,mail2.example.com
UMask 0002
UserID opendmarc:opendmarc
IgnoreHosts
/etc/opendmarc/ignore
.hosts
HistoryFile
/var/run/opendmarc/opendmarc
.dat
#for testing:
SoftwareHeader
true
- AuthservID: Sets what is used as AuthservID when processing E-Mails. This should be the hostname of your mail server or another unique string
- PidFile: Path to the PID file
- RejectFailures: This is a Boolean, if this is true E-Mails that fail DMARC verification will be rejected by your mail server. I prefer simply tagging the mail so I set this to false.
- Syslog: true or false. Tells opendmarc, whether it should log to syslog or not
- TrustedAuthservIDs: these AuthservIDs are assumed to be valid inputs for DMARC assessment. This can prevent the DMARC tests from running several times if you have multiple mail servers in your organization
- UMask: the PID file and the socket file are created with this umask
- UserID: the user and group running the opendmarc service separated by a colon.
- IgnoreHosts: The path to the Ignored Hosts list
- HistoryFile: The path under which the History file should be created. This file is necessary if you want to be able to create aggregate reports to send out to other organizations
- SoftwareHeader: adds a “Dmarc-Filter” header with the opendmarc version in every processed mail. This is good to have during testing
- ForensicReporting options seem to be broken in the version of opendmarc that I used. When I tried to uncomment them, opendmarc would not start because of unrecognized parameters.
- Now you need to create the Ignore hosts file that you specified in the configuration:
12
mkdir
/etc/opendmarc/
nano
/etc/opendmarc/ignore
.hosts
12localhost
10.0.0.0
/24
- With this part of the configuration complete, you just need to make a little change to the default file:
1
nano
/etc/default/opendmarc
1SOCKET=
"inet:54321@localhost"
- Now you can start opendmarc
1
/etc/init
.d
/opendmarc
start
- Next you have to add opendmarc to the milers in postfix. To do that edit your main.cf configuration file:
1
nano
/etc/postfix/main
.cf
12smtpd_milters=inet:localhost:12345,inet:localhost:54321
non_smtpd_milters=inet:localhost:12345,inet:localhost:54321
- Finally you need to reload your postfix configuration:
1
/etc/init
.d
/postfix/reload
You are not perfect – Verify your work
The easiest way to verify your setup is the same as before. Use an external mailbox write an E-Mail to it and then write one to you from that mailbox. Check the headers for the Authentication-Results field, that contains dmarc=pass. Remember that you should find two “Authentication-Results” headers if you followed this guide.If you are in Germany and you are using Gmail for your verification, make sure that you write from a @gmail.com address. Google’s German mail domain googlemail.com does not seem to have DMARC set up. This took me an embarrassing amount of time to figure out since everyone said that Google has DMARC implemented.
Help out your fellow admins – DMARC reporting
While your server now performs DMARC checks and has a DMARC record set, it is not DMARC compliant yet. For that you are missing reporting capabilities. I will show you how to send aggregate DMARC reports to get you compliant with the reporting part of DMARC. Trying to send forensic reports is a bad idea for two reasons:- There are privacy concerns with forensic reporting if your users subscribe to mailing lists. You can read a little more on this here.
- As mentioned above the configuration options for forensic reporting were not recognized in the version of opendmarc that I used. So this would require spending on a lot of time on something that is potentially a huge risk to the privacy of your users.
- Copy the DMARC database schema SQL script to your database server(if that is not the same as your mail server). You can find the SQL script under following path:
1
/usr/share/doc/opendmarc/schema
.mysql
- Edit the script to fit your needs. The default is mostly fine. But if you do not wish to create your database users by hand, you should uncomment and edit these two lines in the script (to uncomment them remove the leading –):
12
-- CREATE USER
'opendmarc'
@
'localhost'
IDENTIFIED BY
'changeme'
;
-- GRANT ALL ON opendmarc.* to
'opendmarc'
@
'localhost'
;
- ‘opendmarc’@: This is the username, you can choose whatever you like as username. But make sure that you put it into quotes. (The @ is not part of the username, but simply here to differentiate the username from the database name)
- ‘localhost’: this the host from which the database user is allowed to connect. If the MySQL database runs on your mail server, you can leave localhost here. If your MySQL database runs on a different server, put the IP of your mail server here.
- ‘changeme’: This is the user password. I suggest a nice strong randomly generated password here.
- opendmarc.*: This is the database name. if you did not change anything in the upper part of the script, leave this alone.
- connect to the database with an account with sufficient privileges to create a new database and run the script. One of the simpler ways of doing this would be the following commands on the database server:
12
cd
/path/to/schema
.mysql/
mysql -u root -p < schema.sql
- Once the database exists go back to your mail server and create a new script to read the history file into the database and send out the reports.
1
/etc/opendmarc/report_script
12345678910111213141516#!/bin/bash
DB_SERVER=
'database.example.com'
DB_USER=
'opendmarc'
DB_PASS='password
DB_NAME=
'opendmarc'
WORK_DIR=
'/var/run/opendmarc'
REPORT_EMAIL=
'dmarc [at] example [dot] com'
REPORT_ORG=example.com'
mv
${WORK_DIR}
/opendmarc
.dat ${WORK_DIR}
/opendmarc_import
.dat -f
cat
/dev/null
> ${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
Make the script executable:
1chmod
+x
/etc/opendmarc/report_script
1su
-c
"/etc/opendmarc/report-script"
-s
/bin/bash
opendmarc
- When the script worked as expected you can add it to your cron jobs:
1
nano
/etc/crontab
11 0 * * * opendmarc
/etc/opendmarc/report-script
- During testing you probably want to receive a copy of every outgoing DMARC report. You can simply add one of your Mailboxes as bcc for every Message sent by the DMARC address. Add following line to your postfix main.cf:
1
sender_bcc_maps =
hash
:
/etc/postfix/bcc_map
1<a href=
"javascript:DeCryptX('3g0m2c1s2e1A3h2z0a0m1q1m3h1/1d0o3p')"
>dmarc [at] example [dot] com<
/a
> <a href=
"javascript:DeCryptX('3p0a3l2n1c1p1y2h1p0r1c1d1d3C0e1y1b2o0p2n1f201d2q1n')"
>mailboxforbcc [at] example [dot] com<
/a
>
1postmap
/etc/postfix/bcc_map
1/etc/init
.d
/postfix
restart
Know your Spam – get the most out of Spamassassin
You have to recognize spam messages before you can redirect them to the Junk folders. My tool of choice for this task is amavis-new with Spamassassin. You might have noticed earlier, that I do not actually reject any incoming E-Mails, because I do not want to accidentally loose any important messages. Detecting malicious E-Mails is entirely up to amavis-new in my setup, as you can see in this diagram :I will assume, that you already have Spamassassin running in some form. I am going to just suggest a few score tweaks and show you the basics of writing your own rules. This will enable you to use the changes you made earlier to filter spam more effectively. Both the score changes and the custom rules should be added to the end of the Spamassassin configuration file.
1 |
nano /etc/spamassassin/local .cf |
1 |
score RULE_NAME 1.0 |
- score: tells the configuration that you wish to change the score of a rule
- RULE_NAME: is the name of the Spamassassin rule, this can be any default or custom rule
- 1.0: This is the score that any messages matching the rule get. A positive score, means that it is more likely to be spam. A negative Score means that the message is more likely to be ham. And a score of 0 means, that the test will not be run.
1
2
3
4
5
6
7
8
9
10 |
#Adjust scores for SPF FAIL score SPF_FAIL 4.0 score SPF_HELO_FAIL 4.0 score SPF_HELO_SOFTFAIL 3.0 score SPF_SOFTFAIL 3.0 #adjust DKIM scores score DKIM_ADSP_ALL 3.0 score DKIM_ADSP_DISCARD 10.0 score DKIM_ADSP_NXDOMAIN 3.0 |
I found however that not all tests that I whished to do were included Spamassassin. So I had to write my own rules. This is actually fairly simple. The most important thing to remember is, that you always have to escape special characters in your regex. If you don’t you might end up searching for mistakes in the wrong place for hours. Trust me….
Here are a few rule examples that I have written for further DKIM and DMARC checks:
1
2
3
4
5
6
7
8
9
10 |
#dmarc fail header CUST_DMARC_FAIL Authentication-Results =~ /mail \.example\.com; dmarc=fail/ score CUST_DMARC_FAIL 5.0 #dmarc pass header CUST_DMARC_PASS Authentication-Results =~ /mail \.example\.com; dmarc=pass/ score CUST_DMARC_PASS -1.0 meta CUST_DKIM_SIGNED_INVALID DKIM_SIGNED && !(DKIM_VALID || DKIM_VALID_AU) score CUST_DKIM_SIGNED_INVALID 6.0 |
- Type of the rule: header, meta, body, rawbody: The type of the rule describes what part of the E-Mail the rule matches. Meta rules are special. They let you link multiple rules with logical ‘and’ or ‘or’ operators and will be true if your entire construct returns a true. In order to deal with SPF, DKIM and DMARC, you will only need header and meta rules.
- Rule name: CUST_DMARC_FAIL: You need to specify a name for the rule. I recommend using a fixed prefix for your custom rules. This will make debugging the rules easier if something does not do what you want it to. You should watch out for this special naming convention: If you prefix your rule with ‘__’ it will only be evaluated as part of a meta rule.
- The rule itself is different for header rules and meta rules.
- Header rules: usually consists of two parts. The header field that should be searched and a regex that matches the desired content in the header field. For example: ‘Authentication-Results =~ /mail\.example\.com; dmarc=fail/ ’ Matches the header field ‘Authentication-Results’ if it contains the string ‘mail.example.com; dmarc=fail’. The header field that you match is separated from the regex by ‘=~’. The regex that describes your search text is started and ended by ‘/’. All special characters like a dot or an ‘@’ have to be escaped with a ‘\’.
- Meta rules: consist of rules linked by logical operators. For example: ‘DKIM_SIGNED && !(DKIM_VALID || DKIM_VALID_AU) ’, The Operator ‘&&’ signals a logical ‘and’ and the operator ‘||’ signals a logical ‘or’. Expressions in parentheses will be evaluated first.
The finishing Touch – 6 Easy Steps to use Dovecot and Sieve to redirect spam to your users junk folder
As I said a bunch of times already I don’t like loosing E-Mail. But I don’t want anybody to be annoyed by inboxes full of spam. While many E-Mail programs are able to to automatically move tagged spam, many smartphone clients do not have that ability. That is why I use Sieve server side filters as a plugin for Dovecot. Sieve allows you to write simple scripts, that change the local mail delivery. In this last part of the I will walk you through installing Sieve and setting up a global script that always gets executed first.Sieve allows you to do many other cool things including letting your users write their own scripts through a mail client, but that is out of scope for this article.
- Sieve is packaged in Debian, so it is fairly easy to install:
1
apt-get
install
dovecot-sieve
- Once the installation is done you need to activate sieve in your configuration:
1
nano
/etc/dovecot/conf
.d
/15-lda
.conf
1234protocol lda {
# Space separated list of plugins to load (default is global mail_plugins).
mail_plugins = $mail_plugins sieve
}
- Next you need to change the default sieve configuration of dovecot to run always run a script before running user scripts:
1
nano
/etc/dovecot/conf
.d
/90-sieve
.conf
123plugin {
sieve_before =
/var/mail/SpamToJunk
.sieve
}
- Now that you configured dovecot to use the Script, you still have to create it. But that is fairly easy:
1
nano
/var/mail/SpamToJunk
.sieve
12345require
"fileinto"
;
if
header :comparator
"i;ascii-casemap"
:contains
"X-Spam-Flag"
"YES"
{
fileinto
"Junk"
;
stop;
}
- Since this script will be run on every incoming E-Mail, it is a good idea to compile it:
1
sievec
/var/mail/SpamToJunk
.sieve
- Restart dovecot to enable your new configuration:
1
/etc/init
.d
/dovecot
restart