Setting up DKIMproxy with Exim for DKIM and DomainKeys signing
Was your mail ever rejected or tagged as spam because it didn’t have a DKIM signature? You could enable DKIM-signing in Exim, but then, some older mail systems may still use DomainKeys.
Currently, Exim does not support signing mails with both DomainKeys and DKIM. You have to pick one of them, unless you do some tricks with patching Exim or connecting back to itself.
Still though, such setup is not perfect: Exim does not wrap its DomainKeys signature, which may result in SpamAssassin installations tagging your mail with HEAD_LONG rule (“Message headers are very long”) and assigning it 2.5 spam points.
So how to sign mail with DKIM and DomainKeys without negative consequences of being tagged by SpamAssassin for very long headers? DKIMproxy to the rescue!
Exim configuration
Exim should listen on an additional port – make sure you have something similar in your exim.conf file:
local_interfaces = 0.0.0.0 : 127.0.0.1.10025
Exim should speak TLS only on port 25, otherwise, it won’t be able to communicate with DKIMproxy:
tls_advertise_hosts = 0.0.0.0.25
routers section in exim.conf:
dkimproxy:
driver = manualroute
condition = "${if eq {$interface_port}{10025} {0}{1}}"
transport = dkimproxy_smtp
route_list = "* localhost byname"
self = send
transports section in exim.conf:
dkimproxy_smtp:
driver = smtp
port = 10027
allow_localhost
DKIMproxy configuration
Download and install DKIMproxy (“./configure; make; make install” after you downloaded and extracted dkim-proxy package, unless your distribution provides it packaged).
Create a /etc/dkimproxy/dkimproxy_out.conf file with similar content:
# specify what address/port DKIMproxy should listen on
listen 127.0.0.1:10027
# specify what address/port DKIMproxy forwards mail to
relay 127.0.0.1:10025
# specify what domains DKIMproxy can sign for (comma-separated, no spaces)
domain host.example.com,example.com
# specify what signatures to add
signature dkim(c=relaxed)
signature domainkeys(c=nofws)
# specify location of the private key
keyfile /etc/dkimproxy/dkim.key.private
# specify the selector (i.e. the name of the key record put in DNS)
selector default
Make sure DKIMproxy is started when your server boots (add it to your startup scripts):
/usr/local/bin/dkimproxy.out --conf_file=/etc/dkimproxy/dkimproxy_out.conf --daemonize
Place the private key you want to use with DKIMproxy in /etc/dkimproxy/dkim.key.private. If you use cPanel, you may symlink it to a key in /var/cpanel/domain_keys/private/.
DNS configuration
Similar entries will be needed in your DNS zone configuration:
default._domainkey 14400 IN TXT "k=rsa; p=MH.........;"
_asp._domainkey.example.com. IN TXT "dkim=unknown"
_adsp._domainkey.example.com. IN TXT "dkim=unknown"
_domainkey.example.com. IN TXT "t=y; o=~;"
Key generation
# openssl genrsa -out private.key 1024
# openssl rsa -in private.key -pubout -out public.key
Testing
You can test if DKIM and DomainKeys signing works properly on these websites:
- http://www.myiptest.com/staticpages/index.php/DomainKeys-DKIM-SPF-Validator-test
- http://www.brandonchecketts.com/emailtest.php
You can also test it by sending an email to:
- check-auth@verifier.port25.com
Troubleshooting
If you use cPanel, you will see such entries in your /var/log/exim_mainlog:
socket bind() to port 10025 for address 127.0.0.1 failed: Address already in use: waiting 30s before trying again
This is because /etc/init.d/exim script shipped with cPanel starts two instances of Exim, one of them is listening on port 465 – SMTP over SSL (TLS), but reading the same config file. This conflicts with “local_interfaces = 0.0.0.0 : 127.0.0.1.10025“. Second instance of Exim will try to bind to port 10025 as well, but will give up after 10 tries.
To fix it, add this to your Exim config file:
daemon_smtp_ports = 25 : 465
tls_on_connect_ports = 465
And comment out these lines from /etc/init.d/exim:
echo -n "Starting exim-smtps: "
# TMPDIR=/tmp $DAEMONIZE /usr/sbin/exim -tls-on-connect -bd -oX 465
# echo $RESULT
Nice writeup! You might want to put this into the Exim router section (dkimproxy) to keep it from signing incoming and local deliveries:
domains = ! +local_domains
Hi, You did a really nice work. I think there is a mistake in DNS Configuration, you should write _adsp._domainkey.example.com. IN TXT “dkim=unknown” instead of _asdp._domainkey.example.com. IN TXT “dkim=unknown”
@pascall: thanks, I corrected this typo (ADSP stands for “Author Domain Signing Practices”).
I have my mail going out of port 26. Do I need to change all instances of port 25 for port 26 in your examples or does it not matter?
@Jeff: Normally, it shouldn’t matter.
Thank you for this excellent article which helped me a lot !
I have added the config part in:
router/01_exim4-config_dkim_route
transport/30_exim4-config_dkim_transport
is that correct or do you see a better way to implement dkimproxy with exim ?
One more time, thanks !
Cool, thanks for the quick response. I get the error you mentioned in troubleshooting but its for port 10027 instead of 10025. Does this make sense?
Thanks for the walk through, but something is going wrong on our server. After doing all the steps, all mail simply sits in the mail queue. When I attempt to deliver a message I get the following …
Message 1M7VER-0007d0-1O is not frozen
delivering 1M7VER-0007d0-1O
Connecting to localhost [127.0.0.1]:10027 … connected
and then it eventually times out. I have to remove the exim.conf entries before mail can start being delivered again … but of course it doesn’t then pass DKIM
Any help would be greatly appreciated
@Allan – see troubleshooting section for:
To fix it, add this to your Exim config file:
daemon_smtp_ports = 25 : 465
tls_on_connect_ports = 465
If it doesn’t help you, try using strace against Exim, Dkimproxy or both (strace -o -f -p ; then try to send an email, and try to analyze strace output – solves 99% of problems).
Well I am not sure what was the problem before (I had also tried the extra troubleshooting lines) but I repeated everything and it is now working for our main domain.
Thank you, thank you, thank you
Now, a quick question …
To make this work for multiple domains, do I just follow these 2 steps for each one?
1. Add the extra domains to dkimproxy_out.conf and restart dkimproxy
2. Add the same 4 dns records on each domain (exactly the same, including the same public key?)
Yes, add these domains to dkimproxy configuration (+ restart it), add same DNS records – it should work just fine.
Excellent. That is what I thought and yes it does work
After trying for so long to get DKIM working, it finally is thanks to you.
So here is now a challenge for all Linux buffs …
What we need now is a script that will run as part of cPanel’s postwwwacct script that will automatically update the dkimproxy_out.conf file with the new domain.
NB: cPanel passes the domain name as $OPTS{‘domain’}
Hi,
Thank you very much for this great HowTo, it works!!
But there is one question open for me: In this setting I think when dkimproxy is not started, the message is queued and later tried again, isn’t it?
For a better error handling I would like the messages just to be not signed if dkim proxy is down, not installed or not working for some other reason. I would be solve this with: “If there is an error with the dkimproxy-router, just continue with the next router!”
If I am right, the “self = send” just tells exim to send the message to the DKIM proxy although the destination is localhost. Without it, exim would believe that this must be an error as the destination is the localhost; is this true?
I played around with this setting and also tried to add “more = true” but it does not seem to work. If dkimproxy is down, then the messages are either frozen or queued. But – as said – I want them to be passed to the next router (that is, send them out without signing).
Is there a way to accomplish this?
Regards,
divB
@Rod (and all others using exim4 with Debian splitted config): The best location to put the router is in the file 199_exim4-config_dkim, right before 200_exim4-config_primary which is responsible for all external domains. Otherwise you may get in trouble if you have a hubbed domain setup (mails which are sent to another host behind are passed to dkimproxy too).
And another suggestion: I have setup dkimproxy with a sender map file now and use the following condition:
condition = “${lookup{$sender_address_domain}lsearch{/etc/dkimproxy/sender.map}{${if eq {$interface_port}{10029}{0}{1}}}{0}}”
This way only domains who should actually be signed are passed to dkimproxy. Works nice for me!
Regards,
Niki
When I follow your how-to, I do run into the issue noted for cPanel. Unfortunately, when I try the last portion for /etc/init.d/exim file, to do the following:
And comment out these lines from /etc/init.d/exim:
# echo -n “Starting exim-smtps: ”
# TMPDIR=/tmp $DAEMONIZE /usr/sbin/exim -tls-on-connect -bd -oX 465
# echo $RESULT
I end up with this error on restarting exim at that point:
# /etc/init.d/exim restart
/etc/init.d/exim: line 117: syntax error near unexpected token `fi’
/etc/init.d/exim: line 117: ` fi’
I’m not a perl expert, so I really have no idea how to resolve this one for the error and I cannot find anyone else online reporting the same issue on this set script change.
Thanks
Please don’t comment out this line (I corrected the text, thanks for the tip):
echo -n “Starting exim-smtps: ”
It’s bash, not Perl – and you can’t have empty “if / fi” in bash.
Please also note that if you have lfd/csf running on your system (if you have WHM/cPanel, you almost certainly have it) – it will complain that integrity of /etc/init.d/exim has changed.
Thanks, that change did work to get /etc/init.d/exim to function. The error is still in exim_mainlog about starting, however. The error is the following (similar to what you’ve noted but I chose to keep 10028 that dkimproxy already had selected rather than 10025):
2009-09-10 14:17:45 exim 4.69 daemon started: pid=21215, no queue runs, listening for SMTP on port 587 (IPv4) [127.0.0.1]:10028
2009-09-10 14:17:45 socket bind() to port 10028 for address 127.0.0.1 failed: Address already in use: waiting 30s before trying again (9 more tries)
2009-09-10 14:18:15 socket bind() to port 10028 for address 127.0.0.1 failed: Address already in use: waiting 30s before trying again (8 more tries)
And so on. I do have all of the entries in exim.conf that have been noted in this how-to. I’d be more than willing to provide my entire exim.conf if necessary, although that would be a relatively long post. CSF/LFD did not complain about the integrity of /etc/init.d/exim however, and I did allow 10027 and 10028 in the csf.conf file and restart CSF.
Thanks again.
Try to stop Exim once again:
/etc/init.d/exim stop
If needed, kill all exim processes manually before starting Exim again.
Let me know if it helped.
I did the following as suggested:
# /etc/init.d/exim stop
Shutting down exim: [ OK ]
Shutting down spamd: [ OK ]
# killall -9 exim
exim: no process killed
# ps aux|grep exim
root 23310 0.0 0.0 6696 644 pts/0 S+ 14:35 0:00 grep exim
# /etc/init.d/exim start
Starting exim-587: [ OK ]
Starting exim: [ OK ]
After that, it still isn’t working and returning the error as before. Of note, it usually shows a start of exim-smtps, which it isn’t now.
Does it matter in exim.conf where these lines are?
local_interfaces = 0.0.0.0 : 127.0.0.1.10028
daemon_smtp_ports = 25 : 465
tls_on_connect_ports = 465
I have them at the top after the first comment in the file.
If you didn’t solve your problem already – could you post your Exim config file and /etc/init.d/exim somewhere (i.e. upload it to some website)?
A problem with this kind of setup is that it doesn’t integrate well with cpanel because you have to specify domains and key files in dkimproxy config
Actually, with a few changes, it is now working. The issues were due to exim also running on port 587 for an alternate port (cPanel has the option to add an extra optional port). Since many ISPs block post 25, I have it also running on 587.
The changes that needed to be done were the following:
In exim.conf
daemon_smtp_ports = 25 : 465 : 587
In /etc/init.d/exim
if [ "$ALTPORT" != "" -a "`grep \"daemon_smtp_port.* $ALTPORT\" /etc/exim.conf`" = "" ]; then
echo -n “Starting exim-$ALTPORT: ”
# TMPDIR=/tmp $DAEMONIZE /usr/sbin/exim -bd -oX $ALTPORT
# echo $RESULT
fi
After doing those changes, restarting dkimproxy and exim worked to get it functioning.
I appreciate the assistance and hopefully this information helps anyone in the future using cPanel if they also do the alternate port option for exim. Great tutorial
@Mihai Secasiu: you can also specify (in dkimproxy config) the paths to the keys generated by cPanel, I don’t really see it being a problem.
I don’t see an issue either, especially since you could always create a script in /scripts/postwwwacct that would edit the dkimproxy_out.conf file to add the new domain and path to the key file for every new account created as well. Since cPanel will check for any scripts in postwwwacct after running wwwacct, this would effectively automate the additions after setting up such a script.
[...] [...]
Wow. Great write-up. Wonder if you had the same guide but written for dummies. I am running a VPS on wiredtree.com Using a VPS cause i like the freedom it allows. Not becuase i am a serious web guy.
Problem is tat all my emails sent to any yahoo account is sent to the spam folder. Now I have enabled domain keys and it shows up fine in the head. But DKIM shows as neutral. So I need to get DKIM installed and working so that I can email folks who use Yahoo.
Now I have putty and know how to follow directions really well. So when you say, “Exim should listen on an additional port – make sure you have something similar in your exim.conf file:”. What i need to know is how to look at that file. I am talking about real frickin noob here. Assume you are asking your mom to do this.
Is it smart to have someone with my knowledge do this? Probably not. But i don’t have a choice. Support has said they can not help me. I have escalated my request but i am not counting on it.
So can anyone help me here?