March 31, 2020 Roberto Puzzanghera130 comments
This note concerns the DKIM patch embedded in my combined patch (more info here). This topic is advanced and you can skip it at the beginning.
DKIM provides a method for validating a domain name identity that is associated with a message through cryptographic authentication. The validation technique is based on public-key cryptography: Responsibility is claimed by the signer by adding a domain name to the message and then also affixing a digital signature of it and the message. The value is placed in the DKIM-Signature: header field. The verifier recovers the signer's public key using the DNS, and then verifies the signature.
You are invited to take a look to the man pages starting from dkim(8) and spawn-filter(8).
Mirko Buffoni did a slight modification here which allows to sign messages sent by authenticated users, and to verify all non authenticated ones.
I will show how to configure
qmail-remote to sign your outgoing messages and
qmail-smtpd to verify your incoming messages. Eventually, as an alternative, you can decide to set qmail-smtpd to do both things; this configuration is presented towards the bottom of this page.
In any case you have to create the domainkey signature.
Signatures are created using a private key on your system, and verified by a public key stored in the DNS for the email domain.
Before you can sign an email, you must create at least one public/private key pair. You may want to create key pairs for every domain you wish to sign, or use one single signature for all your hosted domains.
cd /usr/local/bin wget https://notes.sagredo.eu/files/qmail/domainkey chmod +x domainkey
Now create the folder which will store all the domainkeys.
Usage: /usr/local/bin/domainkey [-p] domain [selector] Create domainkey Print domainkey with -p
The script can create the key or print the existing key if used with the -p option.
When you create a key for a domain.net it will be stored in the
/usr/local/etc/domainkeys/domain.net folder. The folder will be owned by
qmailr (the user running
qmail-remote) if you sign at
qmail-remote level or
vpopmail, which is the user who runs
qmail-smtpd, if you decide to sign at
qmail-smtpd level. My script changes the owner to
qmailr by default
> domainkey domain.net Generating RSA private key, 1024 bit long modulus ......................++++++ ........++++++ e is 65537 (0x10001) writing RSA key TXT record for BIND: default._domainkey.domain.net. IN TXT "v=DKIM1\; k=rsa\; t=y\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyzJujXz9EiMat2eDzzLjWuSJ6g4i20FXGluNOmruuCFnVJP1OxurKdt57JZm+6QP2h9IOP0owBMmecXI9qx3CkFrlGSMbJsOEGqXwiTSSttgGmSTPZeNAureyo79spwPc44Ol2ZK2U9gBoDpSQEQdLdtXUreRvs/HyTkzalue2wIDAQAB"
qmail-remotefor signing and
Here is how to modify your
/var/qmail/rc script so that
qmail-remote will sign your outgoing messages:
#!/bin/sh # Using stdout for logging # Using control/defaultdelivery from qmail-local to deliver messages by default # DKIM 2048 key sign exec env - PATH="/var/qmail/bin:$PATH" \ QMAILREMOTE=/var/qmail/bin/spawn-filter \ DKIMSIGNOPTIONS="-z 2" \ FILTERARGS=/var/qmail/bin/dk-filter \ qmail-start "`cat /var/qmail/control/defaultdelivery`" # DKIM 1024 key sign #exec env - PATH="/var/qmail/bin:$PATH" \ #QMAILREMOTE=/var/qmail/bin/spawn-filter \ #FILTERARGS=/var/qmail/bin/dk-filter \ #qmail-start "`cat /var/qmail/control/defaultdelivery`" # Use this if you don't want to sign or you're signing at qmail-smtpd level #exec env - PATH="/var/qmail/bin:$PATH" \ #qmail-start "`cat /var/qmail/control/defaultdelivery`"
The variable QMAILREMOTE makes
qmail to call
spawn-filter before the message gets queued, which in turn will execute a filter of your choice declared by FILTERARGS. For individual domain level control it is best using the control file
spawn-filter). You can use QMAILLOCAL to sign local deliveries as well.
/var/qmail/control/domainkeys/%/default as the location of the key. The '%' symbol is automatically replaced with the sender's domain. Of course you can decide to sign all your domains with one single signature located in
If you need to store your domainkey elsewhere, define DKIMSIGN as follows (before the last
Insert the two following environment variables in your
export QMAILQUEUE=/var/qmail/bin/qmail-dkim export DKIMVERIFY="FGHKLMNOQRTVWp" # This is to allow msg without "subject" in the h= list # export UNSIGNED_SUBJECT=1 # This is to avoid verification of outgoing messages export RELAYCLIENT_NODKIMVERIFY=1
UNSIGNED_SUBJECT solve rare cases of email from providers which doesn't sign the subject (more info here). This would allow spammers to modify the subject without affecting the DKIM signature, so use at your own risk (I have it enabled since libero.it, a big provider here in Italy, would be blocked otherwise).
RELAYCLIENT_NODKIMVERIFY avoids that
qmail-dkim will verify outgoing messages.
You are invited to read the
qmail-dkim man page in order to choose the best configuration for you.
Create a test message to be used in the tests below (it's important to exit with
ctrl+d and that the first line is not empty):
# cat > /tmp/testmail.txt To: firstname.lastname@example.org From: email@example.com Subject: DKIM Test Message Test message FOLLOWING A BLANK LINE cntrl-D
We'll do the test as
qmailr, the user who runs
qmail-remote and which owns the domainkey.
# su qmailr # declare -x QMAILREMOTE=/var/qmail/bin/spawn-filter # declare -x DKIMSIGNOPTIONS="-z 2" # only if 2048 key sign # declare -x _SENDERfirstname.lastname@example.org # /var/qmail/bin/dk-filter < /tmp/testmail.txt DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=yourdomain.xy; s=default; h=To:From:Subject; bh=lrIChgTwMnmPKfGEgElIyJgL9jk=; b=LQPayl6VbbITdGjVC6vx2+bYF27jAjAkR6qm967GMd1L+0hb7szwP9cDfvy738 Y05llEnOqMrc1QbGGE9uLxqGQrkOAPhl0q+Hxt8yQz1B4BsVk8vED812K/178pIe f+4oGrnodNCCJwg97TLXJmVdecGbrmOVPiBfm51kl4nSI= To: email@example.com From: firstname.lastname@example.org Subject: DKIM Test Tessage Test message FOLLOWING A BLANK LINE
Send to yourself a message and look for the DKIM signature in the header:
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=yourdomain.xy; s=private; x=1298156019; h=to:from:subject; bh=tVrwKZaEzYO4qmI9mPfR04ZvZik=; b=r2VH/6rQdY6xcPjyg55ULnf7U+DEs +apecicwvygIZVvQwxU4NqiqAQGTF3Qkft2eBDu42TBzX4nV7FTs8wzn40iGkfhB mMZhQdcyOseg7DyOFKUK5D8Ji3ueF/xgYFUnffrxPdZUMpGN9+dA9fY0wkonp5ML dDSaNje9mmd34I=
I assume that
vpopmail is the user who runs
# su vpopmail # declare -x DKIMQUEUE=/bin/cat # unset RELAYCLIENT # /var/qmail/bin/qmail-dkim < /tmp/testmail.txt DKIM-Status: no signatures To: email@example.com From: firstname.lastname@example.org Subject: DKIM Test Message Test message FOLLOWING A BLANK LINE
Send a message to yourself from a gmail.com account (which is DKIM signed) and check the header. The message has been verified if you get a header like this:
This is how to test the signature of a mail that was prevently saved to disk as email.txt (do not cut&paste to avoid to alter the body with white spaces, just save it to disk):
> /var/qmail/bin/dkim -vS < /path/to/email.txt DKIM-Status: good
Finally, send an email to email@example.com with "test" as subject. They will send you back a reply with the test results and you will know if your DNS configuration is ok.
You can also check the validation of your dns record (not the signature verification) here https://mxtoolbox.com/dkim.aspx.
[If you didn't install
simscan yet skip this and come here later.
qmail won't work with this directive without
As you probably know, also
simscan needs to call
qmail-queue and must be included in the
QMAILQUEUE environment variable, just as
qmail-dkim. The same should happen with other qmail scanners.
The work around is to assign
QMAILQUEUE and assing
simscan to the
DKIMQUEUE variable. In this case
qmail-dkim will call
simscan when it has finished its work.
You have to modify like this your
/var/qmail/supervise/qmail-smtpd/run script (and
/var/qmail/supervise/qmail-submission/run as well)
export QMAILQUEUE=/var/qmail/bin/qmail-dkim export DKIMQUEUE=/var/qmail/bin/simscan
qmail-smtpdfor signing outgoing messages
You can skip this paragraph if you have already decided to sign at
Insert the two following environment variables in your
export QMAILQUEUE=/var/qmail/bin/qmail-dkim export DKIMKEY=/usr/local/etc/domainkeys/%/default
qmail-dkim will sign your messages if the variable
RELAYCLIENT is set (take a look at your
tcp.smtp) and will verify all messages from IPs where
RELAYCLIENT is not set.
DKIMKEY is the location of the key. The '%' symbol is automatically replaced with the sender's domain. Of course you can decide to sign all your domains with one single signature located in
# cat > /tmp/testmail.txt To: firstname.lastname@example.org From: email@example.com Subject: Test Message Test message FOLLOWING A BLANK LINE THAT YOU DON'T HAVE TO FORGET cntrl-D
In this test
qmail-dkim assumes that the domainkey is stored in the
/var/qmail/control/domainkeys folder, so if your domainkeys are stored elsewhere create a symbolic link.
# su vpopmail # declare -x DKIMQUEUE=/bin/cat # declare -x DKIMKEY=/var/qmail/control/domainkeys/%/default # declare -x RELAYCLIENT="" # /var/qmail/bin/qmail-dkim < /tmp/testmail.txt DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=yourdomain.xyz; s=default; x=1298483339; h=To:From:Subject; bh=6rJdFs2WVS8T72i61KhaAvC6O6s=; b=ffnXBB98Gg6iZFEIL6alk8c5davso ozyrunXM3C8qaejIrJOwYhnlWQwSdrV599WErmJcDYyMjFjVgLzRQ9OTeYHjEnHT X/aiIkvjcxRuOaTK7PcwrcJGdtF0c6LWoK9RgiPctCw7DMcHGmUcSyXWc6ayzHgX A7nopQYuHzTuIU= To: firstname.lastname@example.org From: email@example.com Subject: Test Message Test message FOLLOWING A BLANK LINE THAT YOU DON'T HAVE TO FORGET
qmail-dkim(8)for more info
RELAYCLIENT is defined and both
DKIMVERIFY are not defined then
qmail-dkim looks for
DKIMKEY for the private key to sign. If
DKIMKEY is not defined, then
control/domainkeys/%/default is used
RELAYCLIENT is not defined, then
DKIMSIGN for the key
RELAYCLIENTis not defined and both
DKIMVERIFYis defined, then
If the key has % in the filename, then it is replaced with the domain name from the From/Sender header. After substituting %, if the private key is not found, the
qmail-dkim removes the % and again checks for the key. e.g. strace shows this
access("control/domainkeys/mydomain.org/default", F_OK) = -1 ENOENT (No such file or directory) open("control/domainkeys/default", O_RDONLY|O_NONBLOCK) = 5
If the key has % in the filename and the private key does not exists, then
qmail-dkim exits without signing and without any failure. Hence messages will pass through. The reason for this behaviour is i have many clients who run multiple domains on a server and they need DKIM only for few domains.
If the key does not have % sign and the private key does not exist, then
qmail-dkim exits with 32 resulting in permanent failure
"Private key file does not exist (#5.3.5)"
If none of the variables
DKIMVERIFY are defined,
qmail-dkim does verification
apache clamav dkim dovecot ezmlm fail2ban hacks lamp letsencrypt linux linux-vserver lxc mariadb mediawiki mozilla mysql owncloud patches php proftpd qmail qmailadmin rbl roundcube rsync sieve simscan slackware spamassassin spf ssh ssl surbl tcprules tex ucspi-tcp vpopmail vqadmin