-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
"Replacing OpenDKIM with dkimpy-milter"
by Colin Cogle
Published Thursday, August 29, 2019.
Updated September 2, 2019 with new build instructions.
Available online at
https://colincogle.name/dkim
ABSTRACT
OpenDKIM might be the gold standard, but the DKIM standard is evolving
without it, so it's time to retire it.
THE HISTORY OF DKIM
For almost a decade, DKIM (DomainKeys Identified Mail) has been one of
the most important anti-spam and anti-spoofing technologies available.
In that little window between someone sending an email and it going out
to the Internet, a domain's email server sticks a little hidden signa-
ture in each message that says that the sender address is real and the
message hasn't been modified. Receiving email servers can verify the
signature to prove this.
However, DKIM's age was becoming apparent. Its predecessor, DomainKeys,
which dated back as far as 2004, allowed RSA keys as small as 512 bits,
and signatures with SHA-1. Naturally, for compatibility reasons, both
of these glaring weaknesses made it into the final DKIM specification
in 2011 (as RFC 6376, though using either weakness was discouraged).
1024-bit RSA keys were already considered weak at the time, but some
people who tried to use 2048-bit (or larger) RSA keys found out that
the resulting TXT record was too big for their registrar's DNS zone
editor.
ALL OF THAT SUCKS. HOW DO WE FIX IT?
In 2017, the IETF created a working group to modernize DKIM, and they
did (released as RFC 8301): 512-bit RSA keys were finally banned, and
likewise, SHA-1 signatures were declared "historic" and "MUST NOT be
used for signing or verifying." 4096-bit RSA keys were also permitted,
but if you're one of the unlucky ones who can't fit a 2048-bit key into
your DNS zone, this doesn't help you at all.
More importantly, a new key type was added to DKIM shortly thereafter,
in RFC 8463: Curve25519. The main advantage is that elliptic curve
algorithms like this can provide the same or better security as RSA,
while using shorter keys; for example, a 256-bit EC key is as strong
as a 3072-bit RSA key, so everything is faster and uses less power.
To support the old while phasing in the new, emails will be dual-signed
with RSA and Ed25519, until RSA eventually falls out of style. Verifi-
ers only need to verify one signature, so old ones will check the RSA
one, and newer verifiers will check the Ed25519 one.
AND THAT BRINGS US TO OPENDKIM
Since 2010, OpenDKIM has been one of the most popular DKIM signers and
verifiers in the Linux world. However, it's laid dormant since 2015,
with bug reports and feature requests being largely ignored. I decided
it was time to make the switch to a new app that is being actively dev-
eloped, dkimpy-milter [
https://launchpad.net/dkimpy-milter].
INSTALLING dkimpy-milter
Due to a bug in the pip version of this, I recommend building directly
from source. Clone the Git repository to your server:
git clone -b master
https://git.launchpad.net/dkimpy-milter
cd dkimpy-milter
sudo python3 ./setup.py install --record=/dev/null \
--single-version-externally-managed
It installed under /usr/local, but that's fine. Then, I used the code
from their README to create a user account for it:
sudo adduser --system --no-create-home --quiet --disabled-login \
--disabled-password --shell /bin/false --group \
--home /run/dkimpy-milter dkimpy-milter
And now, set it to start up with your server:
sudo systemctl daemon-reload
sudo systemctl enable dkimpy-milter
sudo systemctl start dkimpy-milter
sudo systemctl status dkimpy-milter
(If you don't like systemd, I believe dkimpy-milter includes a classic
SysV init script.)
Stop, check for errors, and continue.
CONFIGURING dkimpy-milter
I was reading that dkimpy-milter was developed to be a drop-in replace-
ment for OpenDKIM. So, I copied opendkim.conf over dkimpy-milter.conf
and tried to start the service. I was greeted with plenty of warnings
about unknown or unsupported options. Well, it looks like dkimpy-milter
is still a work in progress on that front. I removed all of the "offen-
ding" lines.
I also adjusted permissions inside Postfix's chroot to make sure that
dkimpy-milter could put its socket there.
IMPORTING YOUR OLD KEYS? DON'T.
The creators of dkimpy-milter stuck to OpenDKIM syntax for their con-
figuration file. Instead of overloading existing options like "KeyTable"
and "SelectorTable", there are new ones, such as "KeyTableEd25519" and
"SelectorTableEd25519", that behave identically
I have multiple domains with multiple selectors, so I went ahead and
created new tables, started dkimpy-milter, and…
…I got an error saying none of those were valid options.
It turns out that OpenDKIM-style tables will be supported in the next
version. So, for now, it's back to the drawing board.
GENERATING NEW KEYS
I decided to create two keys for all of my domains to share, one RSA
key and one Ed25519 key. Go to whatever directory you keep your keys
in, and create and secure some new keys:
dknewkey -k rsa boring-old-selector
dknewkey -k ed25519 cool-new-selector
chown dkimpy-milter:dkimpy-milter boring-old-selector.key
chown dkimpy-milter:dkimpy-milter cool-new-selector.key
chmod 440 boring-old-selector.key
chmod 440 cool-new-selector.key
You'll get two files, a .dns file with your DKIM DNS record -- go
deploy those now (preferably with DNSSEC) -- and a .key file that
contains your secret, that should only be readable by dkimpy-milter.
dkimpy-milter.conf CONFIGURATION
Make your configuration file look something like mine.
# List all of your domains.
Domain colincogle.name, myotherdomain.com, another.site
# Provide your RSA key information.
KeyFile /etc/dkimkeys/boring-old-selector.key
Selector boring-old-selector
# Provide your Ed25519 key information.
KeyFileEd25519 /etc/dkimkeys/cool-new-selector.key
SelectorEd25519 cool-new-selector
##### POSTFIX CONFIGURATION #####
# Copy whatever setup you had with OpenDKIM.
UserID dkimpy-milter:postfix
UMask 007
PidFile /var/run/dkimpy-milter/dkimpy-milter.pid
Socket local:/var/spool/postfix/dkimpy-milter.sock
# Make dkimpy-milter sign as well as verify, so
# Postfix can also check incoming DKIM signatures.
Mode sv
# This Postfix variable will tell dkimpy-milter what to do.
MacroList daemon_name|ORIGINATING
MacroListVerify daemon_name|VERIFYING
Restart dkimpy-milter. It still won't do anything, though, until we
have our MTA send it some messages.
POSTFIX CONFIGURATION
Edit your master.cf file, and look for your SMTP and Submission
services. Obviously, we'll change the socket name. However, we need to
add a macro name that will tell dkimpy-milter whether it will sign a
message or verify one. This is new behavior in dkimpy-milter compared
to OpenDKIM. Fortunately, Postfix already has this functionality via
milter_macro_name:
Changes to your /etc/postfix/master.cf file.
smtp inet n - - - - smtpd
- -o smtpd_milters=sock:/opendmarc.sock
+ -o smtpd_milters=sock:/dkimpy-milter.sock
-o milter_macro_daemon_name=VERIFYING
submission inet n - - - - smtpd
- -o smtpd_milters=sock:/opendmarc.sock
+ -o smtpd_milters=sock:/dkimpy-milter.sock
-o milter_macro_daemon_name=ORIGINATING
You should check your main.cf file to see if you've referenced OpenDKIM
in there. If so, go ahead and change it accordingly. When you're all
done, run a `postfix reload`.
WE'RE DONE!
Send some email and read the source, or hop over to my favorite DKIM
testing site [
http://dkimvalidator.com/] and send it an email. When
it arrives, you should see two separate signatures, one RSA and one
Ed25519.
DKIM Information:
DKIM Signature
Message contains this DKIM Signature:
DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/simple;
d=colincogle.name;
[email protected]; q=dns/txt;
s=colin-ed25519; t=1567049647; h=from : content-type :
mime-version : subject : message-id : date : to : from;
bh=zrU0Ey8SLpTcRJGyugNEgYdAi2nzWHakc1f42rljNfg=;
b=7PieJLlpiMRV0DYkyMeTZWavJIRhjAF2irj2qRzjlQflkpluUvr3t97NDkjBh
Mq6JSH+Nz/DX1o2wVhQluJxAg==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
d=colincogle.name;
[email protected]; q=dns/txt; s=colin-rsa;
t=1567049647; h=from : content-type : mime-version : subject :
message-id : date : to : from;
bh=zrU0Ey8SLpTcRJGyugNEgYdAi2nzWHakc1f42rljNfg=;
b=wnrPT8C+JVBUVdoTC3YKQM6CRlH6E0PAyqaUi7PBqCJfQuUpTWkaog6zNe0dx
KF4AvY0YPXBGlGiGIp1jyizIIbA6hqdQ4IhzFuQ4JXB9k2vVTd5tqeSLUOIhWCt
8kiry2ShQmIi0tczBuVBLxq59ovfmQKSwZVg3rhUBDfsYtbydUESlcoOkn9fpT6
727HcMbGsfzf67itycfm8w/hbjt7esWd5VMJ33wg8NYvte+mI/WpqNapwV8Reg5
kQJ9FD3/vBOtKd8kM+DnzOciuqUZX0akQ2EwnujBEgHOVMqTKnYZUHT2Q1Pli31
mSaXaAVxI2oVWIjIw82XfIEG5jK6Q==
Signature Information:
v= Version: 1
a= Algorithm: ed25519-sha256
c= Method: relaxed/simple
d= Domain: colincogle.name
s= Selector: colin-ed25519
q= Protocol: dns/txt
bh= zrU0Ey8SLpTcRJGyugNEgYdAi2nzWHakc1f42rljNfg=
h= Signed Headers: from : content-type : mime-version : subject :
message-id : date : to : from
b= Data: 7PieJLlpiMRV0DYkyMeTZWavJIRhjAF2irj2qRzjlQflkpl
uUvr3t97NDkjBhMq6JSH+Nz/DX1o2wVhQluJxAg==
Public Key DNS Lookup
Building DNS Query for colin-ed25519._domainkey.colincogle.name
Retrieved this publickey from DNS: v=DKIM1; k=ed25519; s=email; t=s;
p=csY5YoFbP8dojeDjEIQwFmb88vdA8l6Ip7fESx39wNc=
Validating Signature
result = pass
Look! A dual-signed email! And this Ed25519-aware validator checked
the newer of the two signatures.
Congratulations! You're now helping to move the Internet forward, one
tiny step at a time. Hopefully your dual signatures will make another
postmaster somewhere Google to find out what you're doing, and why.
-----BEGIN PGP SIGNATURE-----
iHUEARYKAB0WIQQ7NZ6ap/Bjr/sGU4FSrfh98PoTfwUCYaKWRgAKCRBSrfh98PoT
f8oQAP98cApS7YJZp/neVVVcp1evKWJwLt8O5mQm4Eev9nt1lAD+KdjBz7zhkEZv
D/iCznamtn3lgda3QLKkdtXCLc9RoAI=
=ZWj/
-----END PGP SIGNATURE-----