May 14, 2017 Roberto Puzzanghera79 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).
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.
First of all download the script (thanks to Joerg Backschues) which we'll use to create the keys and print the DNS record.
cd /usr/local/bin wget http://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 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`"
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 /var/qmail/supervise/qmail-smtpd/run script:
export QMAILQUEUE=/var/qmail/bin/qmail-dkim export DKIMVERIFY="FGHKLMNOQRTVWjpu" # This is to avoid verification of outgoing messages export RELAYCLIENT_NODKIMVERIFY=1
Declaring 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: email@example.com From: firstname.lastname@example.org 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 _SENDERemail@example.com # /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: firstname.lastname@example.org From: email@example.com 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=domain.net; 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 qmail-smtpd.
# su vpopmail # declare -x DKIMQUEUE=/bin/cat # unset RELAYCLIENT # /var/qmail/bin/qmail-dkim < /tmp/testmail.txt DKIM-Status: no signatures To: firstname.lastname@example.org From: email@example.com 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:
Finally, send an email to firstname.lastname@example.org 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.
[If you didn't install simscan yet skip this and come here later. qmail won't work with this directive without simscan installed.]
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 qmail-dkim to QMAILQUEUE and assing simscan to the DKIMQUEUE variable. In this case qmail-dkim will call simscan when 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
You can skip this paragraph if you have already decided to sign at qmail-remote level.
Insert the two following environment variables in your /var/qmail/supervise/qmail-smtpd/run script:
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 /usr/local/etc/domainkeys/default
# cat > /tmp/testmail.txt To: email@example.com From: firstname.lastname@example.org 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=/usr/local/etc/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: email@example.com From: firstname.lastname@example.org Subject: Test Message Test message FOLLOWING A BLANK LINE THAT YOU DON'T HAVE TO FORGET
If RELAYCLIENT is defined and both DKIMSIGN and 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
If RELAYCLIENT is not defined, then qmail-dkim uses DKIMSIGN for the key
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 RELAYCLIENT, DKIMSIGN, DKIMVERIFY are defined, qmail-dkim does verification
apache clamav dkim dovecot ezmlm fail2ban hacks lamp linux linux-vserver mariadb mediawiki mozilla mysql owncloud patches php proftpd qmail qmailadmin rbl roundcube rsync sieve simscan slackware spamassassin ssh surbl tcprules tex ucspi-tcp vpopmail vqadmin