Distroname and release: Debian Squeeze
Postfix with DKIM (OpenDKIM) and SPF
DKIM, and SPF are great ways for securing that your domain is not being used for spoof mails.You should note, that this is only working as intended if the receiving server does verification of DKIM and SPF.
DKIM and SPF
DKIM and SPF are two completely different things. DKIM, is a way of signing the email itself. If the SMTP server at the recievers end then supports DKIM verifying, it can verify the public signature from the DNS TXT value with the value in the e-mail.SPF, is a way of telling the e-mail systems which hosts are allowed to send e-mails from a specific domain. If the SMTP server at the receivers end supports SPF, it can check if the e-mails come from one of the allowed servers, and take action.
There's some quite good info here.
http://www.socketlabs.com/articles/show/email-authentication-guide?page=1
Installing DKIM for use with postfix
apt-get install opendkim
Generating DKIM keys
This example, will setup DKIM keys for 2 domains. You will likely just need 1. At least to start with.Create signing table file. Multiple domains are allowed like so.
First part is the domain, with all emails, for which we should sign. Second part is the shortname.
We need the shortname when specifying which key to use in the key.table file
/etc/opendkim/signing.table
*@example.com example.com
*@example2.com example2.com
Generate keys. Notice that we use the dateformat YYYYMM as selector(naming).
mkdir -p /etc/opendkim/keys/{example.com,example2.com} opendkim-genkey -b 2048 -h sha256 -r -s$(date +%Y%m) -d example.com -D /etc/opendkim/keys/example.com/ -v opendkim-genkey -b 2048 -h sha256 -r -s$(date +%Y%m) -d example2.com -D /etc/opendkim/keys/example2.com/ -vRestrict permissions to files
chown opendkim:root /etc/opendkim/keys/ -R chmod 440 /etc/opendkim/keys/ -R cd /etc/opendkim/ chmod 755 keys/example.com/ chmod 755 keys/example2.com/Create an key.table file
The '202202' is the selector(-s) from the opendkim-genkey command earlier. Make sure you set the correct selector here. (todays YYYYMM).
/etc/opendkim/key.table
example.com example.com:202202:/etc/opendkim/keys/example.com/202202.private
example2.com example2.com:202202:/etc/opendkim/keys/example2.com/202202.private
Create an signing.table file. This file tells with domains/emails/subdomains to sign. Notice name reference as second pharagraph, must match domain defined.
*@example.com example.com *@example2.com example2.comCreate an trustedhosts.lst file.
We will use this to tell from which IPs we should sign mails with DKIM. And also which IP we should ignore DKIM checks from.
/etc/opendkim/trustedhosts.lst
127.0.0.1
::1
localhost
10.10.10.0/24
10.10.11.0/24
Test the key:
opendkim-testkey -d example.com -s 202202 -vvv opendkim-testkey: using default configfile /etc/opendkim.conf opendkim-testkey: checking key '202202._domainkey.example.com' opendkim-testkey: key secure opendkim-testkey: key OKSample confguration!
You should read up on the "ON-" settings, in the buttom, and adapt them to your needs.
/etc/opendkim.conf
Syslog yes
LogResults yes
LogWhy yes
SyslogSuccess yes
UMask 007
Mode sv
SubDomains yes
Socket inet:8892@localhost
PidFile /var/run/opendkim/opendkim.pid
OversignHeaders From
TrustAnchorFile /usr/share/dns/root.key
UserID opendkim
KeyTable refile:/etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
ExternalIgnoreList /etc/opendkim/trustedhosts.lst
InternalHosts /etc/opendkim/trustedhosts.lst
Nameservers 10.10.10.1
On-Default accept
On-BadSignature reject
On-DNSError tempfail
On-InternalError tempfail
On-KeyNotFound accept
On-NoSignature accept
On-Security tempfail
On-SignatureError reject
InternalHosts = Internal hosts, for which emails we sign with DKIMExternalIgnoreList = External hosts, for which we do NOT verify, but sign instead, since we use the same file as for InternalHosts!
systemctl restart opendkimSetup postfix to listen on the DKIM filter, by adding the following to the main.cf file.
/etc/postfix/main.cf
smtpd_milters =
inet:localhost:8892
non_smtpd_milters =
inet:localhost:8892
Now we have to setup the DKIM TXT record on our public DNS for our domain.
Inspect the generated file from "opendkim-genkey" command, and note the values for TXT and the key. (I have stripped quite a lot of the key)
/etc/opendkim/keys/example.com/202202.txt
202202._domainkey IN TXT "v=DKIM1; g=*; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC....AB" \
; ----- DKIM default for example.com
Now create a TXT DNS record at your DNS host, as this. (again, the key is stripped).
Note I have added t=y which makes DKIM run in test mode. This means that even if the verification fails, the mail will still be delivered. When eveything is OK, remove the t=y parameter.
host: 202202._domainkey string: v=DKIM1; g=*; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC....AB; t=yAll done, now restart opendkim and postfix.
systemctl restart opendkim systemctl restart postfix
Testing
The absolute easiest way to test the signing function, is to send an e-mail to an g-mail account.If it is successfull, you should get an "signed by example.com" in the detailed view of the e-mail.
The reason to do so, is that gmail supports DKIM verifying. If the SMTP reciever does not support DKIM, have no worries. The e-mail will be delivered, but not checked for DKIM.
If you have logging enabled you could check the mail.log file.
This does not verify if it is working as inteded, just if the signature is added to the e-mail!
If it is not working, it could be that the signature is wrong on the DNS host, but the header will still be added.
So until you are sure that everything is working, add the "t=y" parameter to the DKIM txt DNS entry, as mentioned earlier.
This shows, that signing is OK.
tail -f /var/log/mail.log |grep -i dkim Sep 23 17:13:50 myServer dkim-filter[3079]: C27E761E5A8 "DKIM-Signature" header addedFor verification, try to send an e-mail from the gmail account, and then check the mail.log for messages.
tail -f /var/log/mail.log |grep -i dkim Sep 23 17:05:18 myServer dkim-filter[3079]: 982BD39E5B9 DKIM verification successful
Rotating / Changing DKIM keys
Generate an new key, and set correct permissions.opendkim-genkey -s202202 -d example.com -D /etc/opendkim/keys/example.com/ chown opendkim:root /etc/opendkim/keys/example.com/202202* chmod 440 /etc/opendkim/keys/example.com/202202*Open the /etc/opendkim/keys/example.com/202202.txt file, and copy/paste the p= value to new new TXT record in your DNS configuration.
NOTE, there might be two entries like the ones below. If thats the case, copy the two lines to ONE line, and omitting the middle "" ones.
""p=MIIBIjA.............."" ""uFKAZV0cO......4QIDAQAB""So the full string will now be:
p="MIIBIjA.....4QIDAQAB"update the keytable file "/etc/opendkim/key.table", so the new selector used above matches, AND the filename:
Notice, there can be multiple entries here.
example.com example.com:202202:/etc/opendkim/keys/example.com/202202.private
Known issues and possible fixes with DKIM
DNS fail
Mar 2 13:13:56 server opendkim[617]: D312E5FCD1: signature=He2oEYVm domain=linkedin.com selector=d2048-201806-01 result="key DNS query failed"; signature=M7nqbPYW domain=maile.linkedin.com selector=proddkim1024 result="key DNS query failed" Mar 2 13:13:56 server opendkim[617]: D312E5FCD1: key retrieval failed (s=d2048-201806-01, d=linkedin.com): 'proddkim1024._domainkey.maile.linkedin.com' query timed out Mar 2 13:13:56 server postfix/qmgr[609]: D312E5FCD1: from=And/Or something like this., size=33878, nrcpt=1 (queue active)
Mar 2 14:15:26 mailobie opendkim[27325]: 279415FB5E: signature=r0rl9Qd3 domain=gmail.com selector=20161025 result="key DNS query failed" Mar 2 14:15:26 mailobie opendkim[27325]: 279415FB5E: key retrieval failed (s=20161025, d=gmail.com): '20161025._domainkey.gmail.com' query timed outAlso when we are testing the key, we get an timeout error.
opendkim-testkey -d gmail.com -s 202202 -vvv opendkim-testkey: using default configfile /etc/opendkim.conf opendkim-testkey: checking key '202202._domainkey.gmail.com' opendkim-testkey: '202202._domainkey.gmail.com' query timed outIf we test with dig, it works.
dig 20161025._domainkey.gmail.com TXTThe issue is likely because "unbound" is not running, and opendkm in Debian is compiled with unbound.
ref: https://wiki.debian.org/opendkim#DNS_resolution
We can fix this, by adding "Nameservers IP" to the config file, (or install and configure unbound)
/etc/opendkim.conf
Nameservers 123.123.123.123
If this does not work, disable the TrustAnchorFileBe aware that this might disable DNNSEC capabilities, and causing "opendkim-testkey: key not secure".
I've also seen the "opendkim-testkey: key not secure" error, when the domain used did not support DNSSEC. Dont worry about it, its a topic for another day.
/etc/opendkim.conf
#TrustAnchorFile /usr/share/dns/root.key
Remember to reload opendkim after.
dkim-filter / dkim-milter and ClamSMTP issue
Doing my tests, I discovered that dkim-filter, added the signature twice to the e-mail header.It also checked the verification twice. This was due to ClamSMTP, so I edited the /etc/postfix/master.cf file to not add the milter, when the e-mails is injected into postfix again.
Add "no_milters" to the recieve_overrride_options line to master.cf file.
/etc/postfix/master.cf
receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
Installing SPF for postfix
Installtion, and setup is actually quite straight forward.apt-get install postfix-policyd-spf-perl
Configuring SPF
First we will start by creating an TXT record. Below are the options.- ?all = neutreal, which is for testing purposes where we do not want other SMTPs to block us.
- ~all = softfail, will normally mark the SPF check as failed, but still be delivered.
- -all = hardfail, will mark the SPF check as failed, and reject the mail. After testing, this is want we want.
Create it as an TXT record on your DNS host.
An example could looke like this, meaning that all MX's configured for this domain is allowed to send. It is also possible to add IP's if we want a server which is not an MX to allow to send mail.
Note we are using an hardfail here, this should only normally be used after testing!
"v=spf1 mx -all"Now we will install the spfmilter/spffilter on the server running postfix.
All information here, are from the man page! "man postfix-policyd-spf-perl"
Add configuration to postfix master.cf file
/etc/postfix/master.cf
spfcheck unix - n n - 0 spawn
user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
Add configuration to main.cf. Notice, that you will hightly likely have other configuration parameters here!Be sure to add the check_policy_service under reject_unauth_destination, or you might become an open-relay!
/etc/postfix/main.cf
smtpd_recipient_restrictions =
reject_unauth_destination,
check_policy_service unix:private/spfcheck,
Set spfcheck timeout
postconf -e spfcheck_time_limit=3600Restart postfix
/etc/init.d/postfix restart
Testing
Testing can be done by using the postfix-policyd-spf-perl binary!/usr/sbin/postfix-policyd-spf-perl request=smtpd_access_policy protocol_state=RCPT protocol_name=SMTP helo_name=test.example.com queue_id= instance=71b0.45e2f5f1.d4da1.0 sender=foo@example.com recipient=foo@example.com client_address=[EXT. IP OF SERVER] client_name=[FQDN of Mailserver] [EMPTY LINE]Output would show something like this!(ips whiped)..
action=PREPEND Received-SPF: pass (example.com: xxx.xxx.xxx.xxx is authorized to use 'foo@example.com' in \ 'mfrom' identity (mechanism 'mx' matched)) receiver=mailobie; identity=mailfrom; \ envelope-from="foo@example.com"; helo=test.example.com; client-ip=xxx.xxx.xxx.xxxHere is an real-life log, from the mailserver, on an ingoing e-mail.
Nov 2 10:36:20 mailobie postfix/policy-spf[4441]: Policy action=PREPEND Received-SPF: pass \ (hotmail.com: Sender is authorized to use 'example@hotmail.com' in \ 'mfrom' identity (mechanism 'include:spf.protection.outlook.com' matched)) receiver=mailobie; identity=mailfrom; \ envelope-from="example@hotmail.com"; helo=EUR03-AM5-obe.outbound.protection.outlook.com; client-ip=40.92.70.19And/Or test by sending an spoofed e-mail from example http://www.deadfake.com.