
INTRODUCTION
============

This document will walk you through the process of configuring and activating
the OpenDKIM filter once it has been compiled and installed.  In doing so you
will:

o Generate a private and public key pair for use when signing your mail
  (skipped if you will only be verifying)
o Post your public key in your DNS space (also skipped if only verifying)
o Choose a local socket interface between the filter and your MTA
o Configure your filter
o Activate your filter
o Test your filter


COMPILING AND INSTALLING
========================

The INSTALL document in the root of the build directory covers the compilation
and software installation of opendkim and its prerequisites.  You should
complete that process before continuing with the next section.


SOCKET SELECTION
================

Your MTA and your opendkim filter will communicate over a socket connection.
You can choose to use a TCP (network) socket or a UNIX domain (filesystem)
socket.  They each have advantages: A UNIX domain socket can be secured
using the filesystem (i.e., with user or group permissions), but cannot be
reached from other machines that might want to share the service.

For a UNIX domain socket, you must pick a location within your filesystem
that the opendkim filter will be able to access in which the socket will be
created for MTAs to connect, and set file permissions accordingly.

For a TCP socket, you must select a port number not in use by other
applications, and the name or IP address of the interface on which opendkim
should listen for connections.  The IP address can be simply "ANY"
if all connections should be accepted.  You will also need to ensure that
any packet filters in effect on the local machine permit traffic to and
from that port, and that any other kernel-level policy mechanisms (e.g.,
selinux) will permit traffic on that port.  For selinux, the following command
will need to be executed as the superuser, which declares the chosen port to
be a milter application port:

	semanage port -a -t milter_port_t -p tcp <port>

Consult your documentation for other packet filter and policy systems.


CONFIGURING OPENDKIM
====================

NOTE: If you will only be verfiying incoming mail and will not be signing,
start at step (5).

(1) Choose a selector name.  A selector is simply a symbolic name given to
    a key you will be using to sign your mail.  You are free to choose any
    name you wish.  One current convention if you are using multiple
    mailservers is to use the hostname (hostname only, not the fully-qualified
    domain name) of the host that will be providing the service.  Another 
    convention is to use the current month and year.

(2) Run the script "opendkim-genkey -s SELECTOR". The opendkim-genkey man
    page has full details of options.  This will generate a private key
    in PEM format and output a TXT record containing the matching public
    key appropriate for insertion into your DNS zone file.  Insert it in
    your zone file, increment the serial number, and reload your DNS system
    so the data is published.

(3) Publish your public DNS key.
    The public key DNS record should appear as a TXT resource record at:

        SELECTOR._domainkey.DOMAIN

    SELECTOR is the name you chose in step (1) above and will use with the
    Selector config option or the result of the KeyTable lookup.

    DOMAIN is the domain name specified in the "-d" command line, the Domain 
    option in the configuration file, or the domain that would be returned
    from a KeyTable lookup.
  
    For a translation of the parameters and value pairs, see RFC6376.  Using 
    "t=y" is indicates you are in "test mode", advising verifiers that they
    should not take any real action based on success or failure of the use
    of this key after verifing a message.  Remove the "t=y" once you have
    tested the DKIM signing of your messages to your satisfaction.

    You might want to set a short TTL on this record during testing so
    changes are propagated to other nameservers more quickly.

    Reload your nameserver so that the record gets published. If you are
    running BIND 9 the command is "rndc reload"; for other nameservers, 
    consult your vendor documentation.

    Check that the DNS server is returning your published DNS record.  Be
    aware that synchronization to secondary DNS servers and negative caching
    of DNS resolver servers may effect the results observed.  For reliable
    results, query your DNS server directly:

        dig -t txt SELECTOR._domainkey.DOMAIN NAMESERVER
 
    NAMESERVER is a nameserver for your domain.  You can get a list of these
    using:

        dig -t ns DOMAIN

    If the key can be retreived correctly then opendkim-testkey can be used to
    verify that the key matches the private key.

        opendkim-testkey -d DOMAIN -s SELECTOR -k rsa.private

    BIND servers have a 256 byte limit on serving TXT records, so a 1024 bit 
    RSA key is recommended if using BIND as your primary DNS server. See 
    section on LARGE KEYS.

(4) Store the private key in a safe place.  We generally use a path like
    /var/db/dkim/SELECTOR.key.pem (where "SELECTOR" is the name you chose).
    The /var/db/dkim directory and the associated .pem file should be owned by
    the user that will be executing the filter (preferably not the
    superuser) and be mode 0700 and 0600 respectively.

(5) Take a look at the opendkim.conf.simple as an example configuration file 
    for your domain.  If you wish to sign mail that comes from sources other 
    than the localhost address (127.0.0.1), include these in CIDR notation in
    the confiugration file for the InternalHosts configuration option.

(6) Start opendkim.  You will need at least the "-p" option.  The current
    recommended set of command line options is:

	-l -p SOCKETSPEC -d DOMAIN -k KEYPATH -s SELECTOR

    ...where SOCKETSPEC is the socket you told your MTA to use,
    DOMAIN is the domain or set of domains for which you want to sign
    mail, KEYPATH is the path to the private key file you generated, and
    SELECTOR is the selector name you picked.  You can tack "-f" on there
    if you want it to run in the foreground instead of in the background
    as a daemon.

    Instead of command line options, you can also use a configuration file.
    In that case, you only need to tell the filter where the configuration
    file is by specifying a command line option of:

	-x CONFPATH

    ...where CONFPATH is the path to the configuration file you wish to
    use.  One or more configuration example files are provided.

(7) Configure your MTA:

    For Sendmail:

    (a) Choose a socket at which the MTA and the filter will rendezvous
        (see the documentation in libmilter for details)

    (b) Add a line like this example to your sendmail.mc using your desired
        socket specification:
	    INPUT_MAIL_FILTER(`opendkim', `S=inet:8891@localhost')

    (c) Rebuild your sendmail.cf in the usual way

    For Postfix:

    (a) Choose a socket at which the MTA and the filter will rendezvous.
        Be careful with UNIX domain sockets as on some distributions and setups
	the smtpd process is running in a chroot environment.  A UNIX socket 
	will need to be visible to the chrooted smtpd process.

    (b) Add the following lines like this example to your postfix main.cf using
        your desired socket specification:

	    smtpd_milters = inet:localhost:8891
	    non_smtpd_milters = inet:localhost:8891
	
    (c) If you have a content filter in master.cf that feeds it back into a
        different smtpd process, you should alter the second smtpd process in
	master.cf to contain '-o receive_override_options=no_milters' to
	prevent messages being signed or verified twice.  For tips on avoiding
	DKIM signature breakage, see:
	http://www.postfix.org/MILTER_README.html#workarounds

(8) Restart/reload your MTA.

    For Sendmail:
	kill -1 `head -1 /var/run/sendmail.pid`

    For Postfix:
	postfix reload

	...or the following if master.cf was changed:

	/etc/init.d/postfix restart


COMPLEX SIGNING CONFIGURATIONS
==============================

The KeyTable and SigningTable are used to define signing instructions to the
filter where use of Domain, Selector and KeyFile together are insufficient.

First, select the type of database you will use for each.  They need not
be the same.  The "DATA SETS" portion of the opendkim(8) man page describes
the possibilities and how they are formatted.  Then, construct those databases.

Let's suppose you want to sign for two domains, example.com and example.net.
Within example.com, you want to sign for user "president" differently than
everyone else.  Let's say further that you want to use a flat text file.

You've generated private key files for each of these and stored them
in the directory /usr/local/etc/dkim/keys as files "president", "excom" and
"exnet", with the obvious intents.  You want to use selectors "foo", "bar"
and "baz" for those, respectively.  The signing domains match the senders
(i.e. the signatures for example.com's stuff will be held by example.com,
and example.net likewise).

First, write the KeyTable.  This is a list of the keys you intend to use,
and you just assign arbitrary names to them.  So as a flat file, the KeyTable
for the above might look like this:

	preskey	example.com:foo:/usr/local/etc/dkim/keys/president
	comkey	example.com:bar:/usr/local/etc/dkim/keys/excom
	netkey	example.net:baz:/usr/local/etc/dkim/keys/exnet

Per the documentation, multi-field data sets that are made of flat files have
the fields separated by colons, but the key and value(s) are separated by
whitespace.

So now we've named each key file, and specified with which selector and domain
each will be used, and then given each of those groupings a name.  This
is your KeyTable.  Let's say you put it in /usr/local/etc/dkim/keytable.

Next, write the SigningTable.  This maps senders (by default, taken from the
From: header field of a message passing through the filter) to which keys
will be used to sign their mail.  Wildcards are allowed.  So to do what was
described above, we write it as follows:

	president@example.com	preskey
	*@example.com		comkey
	*@example.net		netkey

Since we want to use wildcards, we can't actually use a regular flat file.
Wildcards require a regular expression file, or "refile".  The above is
valid format for one of those.  Let's say you put this in
/usr/local/etc/dkim/signingtable.

Finally, tell the filter that it should use these files by adding this to
your configuration file:

	KeyTable	/usr/local/etc/dkim/keytable
	SigningTable	refile:/usr/local/etc/dkim/signingtable

You could put "file:" in front of the filename for the KeyTable just to be
precise, but "file:" is assumed if the value starts with a "/".


TESTING
=======

To test, send a piece of e-mail through the MTA doing signing for your
domain to one or more of these auto-responders:

	sa-test@sendmail.net
        check-auth@verifier.port25.com
	autorespond+dkim@dk.elandsys.com
	dktest@exhalus.net
	dkim-test@altn.com
	dktest@blackops.org

It should be returned to you shortly showing your message in the body of a
new message, including all of the header changes that were made in transit.

An alternative is to send a message to an email service provider such
as Gmail or Yahoo! that is known to verify DKIM signatures, and view the
full text of the message you receive there.

The message you generated should appear (either at the ESP to which you
sent or in the auto-reply you receive) with a DKIM-Signature: header field
added containing the signature data from your opendkim filter, and an 
Authentication-Results: header field that the receiving machine added after
verifying the signature.  The value of this header field should indicate a 
"pass".  If it doesn't, something in between has altered your message in a way
that invalidated the signature.  Perhaps you have other filters running that
appended to or otherwise modified a header or the message body.

The reply from the autoresponder address, or an e-mail sent back to you from
your ESP account, will also itself be signed, and in the header of the reply
you should see its signature and another Authentication-Results: header
field added by your filter, which should also read "pass".


LARGE KEYS
==========

If you wish to use a large key in DNS, there are some limitations of which
you should be aware.  A TXT record in the DNS consists of a series of
strings each of which don't exceed 255 bytes.  This is a result of the
fact that each string is preceded by a length byte (which, of course,
can't exceed 255).  Furthermore, some DNS implementations don't allow
packets larger than 512 bytes.  Some RSA keys will exceed the 255 byte
limit once encoded with base64, so some special formatting must be
used to make such a record fit.  Failing to do so can cause an incomplete
record to be published or, worse, the nameserver to refuse to serve the
record or even the entire zone.

In the case of the BIND nameserver, there are two syntax rules one can use
to make a large record fit within these boundaries:

1) TXT substrings

	Instead of a record like:

	recname	IN	TXT	"foobarbazblivitalphabravocharliedelta...zulu"

	...one can also do:

	recname	IN	TXT	"foobar" "baz" "blivit" "alpha" ... "zulu"

	(The "..." is mean to indicate continuation and is not a literal set of
	three "." characters.)

	You simply have to break up the large record into smaller strings such
	that no string exceeds 255 bytes.  DKIM implementations will
	reassemble TXT records broken down this way into the full original
	single string before processing them.

2) Line continuations

	It can be difficult for some to edit very long lines of text.
	It's therefore desirable to have a mechanism to break very long
	TXT records down so that they fit nicely within an editor window.
	In BIND, this is done by enclosing the wrapped lines within
	parentheses.  Continuing with the example above, this:

	recname	IN	TXT	"foobar" "baz" "blivit" "alpha" ... "zulu"

	...can also be expressed as:

	recname	IN	TXT	( "foobar" "baz" "blivit" "alpha"
				  "bravo" "charlie" "delta" "echo"
				  ...
				  "yankee" "zulu" )

So using these two techniques, a very large public key could be encoded
in a DNS zone file as follows:

recname	IN	TXT	( "v=DKIM1; g=*; k=rsa; "
 			  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Z4F"
 			  "JEMHjJDuBmt25zvYFVejlARZGt1L8f0s1+rLxIPYkfCogQi+Y8"
 			  "oLEg9vvEKnLx9aogZzuNt6j4Sty3LgXxaIwHnMqk0LldbA/mh3"
 			  "wLZb16Wc6btXHON0o3uDipxqGK2iRLTvcgAnNDegseOS+i0aJE"
 			  "nNSl663ywRBp/QKezhUC7cnbqR/H8dz8pEOjeawNN3nexdHGsk"
 			  "+RaafYvCFvU+70CQORcsk+mxb74SwGT2CGHWxVywQA9yrV+sYk"
 			  "JpxaufZLo6xp0Z7RZmbf1eGlCAdhkEy+KYQpQkw2Cdl7iKIK4+"
 			  "17gr+XZOrfFLJ5IwpVK/a19m3BLxADf0Kh3oZwIDAQAB" )


DEBUG FEATURES
==============

(a) The Canonicalized Content

Two configuration file entries are provided to enable detailed debugging
and forensics when tracking down failed verifications.  These are
"KeepTemporaryFiles" and "TemporaryDirectory".

When a message is to be signed or verified, its headers and body are passed
through a selected canonicalization algorithm.  The output of this algorithm
is stored in a pair of temporary files if "KeepTemporaryFiles" is enabled.
If a verifier and a signer both produce these files, they can be compared
after a failed verification to determine how the signed data and verified
data differ.

"TemporaryDirectory" simply selects the directory to be used to create
these files, if other than the default (/tmp) is desired.

Recent extensions to DKIM formalized a mechanism by which a signer can ask
a verifier to send back copies of these files when verification fails.
Specifically, if the failing signature contains an "r=y" tag, then the
signer is requesting such feedback.  If the verifier is willing to provide it,
it makes a second DNS query to find the report destination and packages up
the canonicalized forms into a message, which it sends to the named address.

The second query goes to the domain named in the signature's "d=" tag, with
"_report._domainkey." prepended to it.  For example, a failed signature from
"example.com" that also had an "r=y" tag would result in a query for a TXT
record at "_report._domainkey.example.com".  If the reply contains an
"ra=" tag, it identifies the requested destination of the report within that
same domain (e.g., "ra=foobar" means "send reports to foobar@example.com").
(See RFC 6651 for details.)

The signer, on receiving this, can unpack the two files and compare them to
the two files it generated for the same message.  This comparison usually
reveals the change made in transit that invalidated the signature.

(b) Diagnostics

There are two other features available that are less involved, but also less
likely to yield a result.  Signers can configure their systems using the
"Diagnostics" setting which, if enabled, produces signatures that contain
the original header set that was signed.  These will be stored in a "z="
tag in the signature.  A verifier can extract this information from the
signature and, on failed signatures, compare it to the header set that was
received and look for discrepancies.  If a verifier sets the
"DiagnosticDirectory" to name a directory, a file will be generated there
whenever a signature bearing a "z=" tag fails to verify, and present the
two header sets for visual comparison.  Additional detail is provided if
the package is compiled with libtre support, as it can identify reordered
header fields that differ slightly, and present them alongside each other.


REUSING DOMAINKEYS RECORDS
==========================

Published DomainKeys key records are almost totally compatible with DKIM.
The one area in which they differ is the use of the granularity ("g=") tag.

Under DomainKeys, the default granularity is the empty string.  DomainKeys
interprets this as "match anyone".  However, DKIM interprets this as
"match none".  Thus, a key record containing "g=" means one thing to
DomainKeys but the opposite to DKIM, making it incompatible.

Under DKIM, the default granularity is "*", which means "match anyone".

Since both systems have defaults that mean "match anyone", a DomainKeys
key record which doesn't have a "g=" string in it at all will work as
expected with DKIM.  Thus, if you want to use one key record with both
systems, remove the "g=" portion of the record and reload your nameserver.


UPGRADING FROM 1.x to 2.x
=========================

Configuration files from v1.x.x installations of OpenDKIM are compatible
with v2.x.x except for the "KeyList" feature, which has been dropped in
favour of a more flexible configuration.

The "KeyList" in older versions was a file of this form:

	pattern:domain:selector:keypath

...where:

	"pattern" was a pattern to be matched against the From: header field
	of a message being considered for signing, with the asterisk ("*")
	character being used as the conventional wildcard

	"domain" was the domain to be used when generating the signature

	"selector" was the selector to be used when generating the signature

	"keypath" was the path to the private key to be used to generate
	the signature

In the newer configuration files, this function has been split into two
different files.  The first, known as the "KeyTable", maps names of keys
to domains, selectors and keys, and the second, known as the "SigningTable",
maps sender patterns to entries in the first.

So if for example an older installation had the following KeyList:

	*@example.com:example.com:foo:/var/db/dkim/foo.private
	*@example.net:example.net:bar:/var/db/dkim/bar.private

This would now be split into the two new data sets.  In recent versions of
OpenDKIM, a data set can be in any of a number of actual forms (SQL databases,
flat files, LDAP directories, regular expression files, etc.) so these will
be expressed in terms of their data set keys (the item looked up) and values
(the item or items returned).  So in the new setup, a KeyTable matching
the above setup will contain:

	KEYS		VALUES
	----		------
	K1		example.com
			foo
			/var/db/dkim/foo.private

	K2		example.net
			bar
			/var/db/dkim/bar.private

This defines the keys that will be used to sign.  Then it is necessary to
tell the filter which addresses get signed by which keys, so the SigningTable
will contain:

	KEYS		VALUES
	----		------
	example.com	K1
	example.net	K2

Consult SigningTable in opendkim.conf(5) man page for a description of the
expected keys and values in the SigningTable data set.


DATA SETS
=========

Several OpenDKIM configuration values describe lists or sets of data, sometimes
with associated values.  OpenDKIM refers to these as "data sets".  They
can be stored in a variety of ways such as comma-separated lists, flat files,
files involving pattern matching (called "refiles"), Oracle/Sleepycat Berkeley
databases (hash or btree), SQL databases, Lua scripts or LDAP directories.

For some data sets, merely a test for membership in the set is done.  Such
entries only have "keys" (items in the set), with no associated "values".

`file' and `refile' data sets are nearly identical.  Each is a flat file
containing two columns separated by whitespace.  The left column is the key
and the right the value (as described above).  If more than one value appears
in the right column, then those values should be separated by colons, e.g.:

	key	value1:value2:value3

For example, the difference is that the key column of a `file' SigningTable
is matched exactly in the order described in opendkim.conf(5), while the key
column of a `refile' SigningTable is a glob-style expression using where "*"
is treated as zero or more characters.  The `refile' matching syntax is
the same as that of an OpenDKIM 1.x KeyList.

The `csl' data set works exactly he same way as a `file' data set, but instead
of specifying a file to read, it specifies a list of data separated by commas,
e.g.:

	csl:key1 value1,key2 value2

The `bdb' data set simply reads those key/value pairs from a Berkeley-style DB
file (also known as a Sleepycat DB) rather than a flat file or list.

For LDAP data sets, the search filter and the attributes to be returned
should be provided in the LDAP URI.  Any instances of `$d' in the LDAP
filter will be replaced with the domain or email address being queried for
(with the search terms as described above).

For instance, a simple SigningTable data set that searches for the "mail"
attribute and returns a "keyName" attribute might be:

	ldap://localhost/dc=example,dc=com?keyName?sub?(mail=$d)

The general format for using an LDAP directory in a data set is:

	ldap://host[:port]/basedn[?attrs[?scope[?filter[?exts]]]]

The scheme ldaps:// may be used to connect via SSL, and ldapi:// may be
used to connect via UNIX socket.

LDAP credentials are stored in the opendkim.conf file.

For ODBX (SQL database) data sets, a similar mechanism is used, but the URI
must also be tagged with the prefix "dsn:".  For the example above,
returning columns in a PostgreSQL database instead of LDAP attributes, you
might use:

	dsn:pgsql://username:password@localhost/dkim/table=dkimkeys?keycol=keyName?datacol=mail

The general format for using a database in a data set is:

	dsn:<backend>://[user[:pwd]@][port+]host/dbase[/key=val[?...]]

The special DSN keys "table", "keycol" and "datacol" are required to name the
database from which to select records, the column in which keys will be found,
and the column(s) from which their corresponding values should be extracted.

A Lua script can be specified as below:

        lua:/usr/local/keyretrive.lua

The Lua script will receive a global variable called "query" which is a string
containing the query to be performed.  The returned value is the return value 
of the script.


MANUALLY CREATING DKIM KEYS
===========================

To manually generate a public and private key:

(1 Run this command:

   openssl genrsa -out rsa.private 1024

   This generates a private key and writes it to the file "rsa.private".
   The generated key is in PEM format and is a 1024-bit key, the
   minimum required by the DKIM specification.

(2) Run this command:

    openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM

    This reads the private key generated in the previous step and
    extracts from it the matching public key.  This is written to the
    file "rsa.public".

(3) Add a TXT DNS record containing the base64 encoding of your public
    key, which is everything between the BEGIN and END lines in the
    rsa.public file generated above, with spaces and newlines removed.
    It should be in this form:

    "v=DKIM1; t=y; p=MFwwDQYJ...AwEAAQ=="

    ...using, of course, your own public key with is the base64 data 
    from the generated rsa.public file.

    For a translation of the parameter and value pairs shown here, see
    the DKIM specification (RFC6376) Section 3.6.  The specification
    is available in a file in the source code package called
    "rfc6376.txt".  Basically this key record just announces an RSA
    public key and also declares that your site is using this key in
    test mode so nobody should take any real action based on success
    or failure of the use of this key to verify a message.

    NOTE:  Key sizes above 2700 bits are unlikely to fit into a 512 byte
    DNS record.


MAILING LISTS
=============

Most common mailing list packages (e.g., Mailman) modify messages in different
ways; for instance, they add footers to the body, change Reply-to header
fields to send replies to the list by default, modify the Subject field to add
the mailing list name, and so on.  All these modifications are likely to
break existing DKIM signatures.

If you run a mailing list package, you might want to arrange that all
messages generated by the list are signed by DKIM.  If you have already
configured OpenDKIM in your MTA, follow these steps to also sign
mailing list messages:

  (1) If you are running the list software on a different domain/subdomain
      (e.g., main domain example.org, mailing lists on lists.example.org),
      you need to generate a key for that domain as well and add it to
      the KeyTable (see opendkim.conf(5)).

  (2) In the SigningTable (see opendkim.conf(5)), you need to add a key
      corresponding to the mailing list bounce address (e.g.,
      mylist-bounces@lists.example.org) and NOT the list post address;
      alternatively, you can simply sign the whole domain, adding a key
      without the local part (e.g., lists.example.org).

  (3) In opendkim.conf(5), configure the option SenderHeaders to
      "Sender,From".  Usually, OpenDKIM tries to match the contents
      of only the From field in the SigningTable.  With this configuration,
      it will use the Sender field instead, if it exists.  In messages
      generated by most mailing list software, the Sender field contains
      the mailing list bounce address, which is the one that needs to be
      checked in the SigningTable to sign the e-mail; instead, the From
      address is just the user of the mailing list that wrote the e-mail,
      which might be writing from a domain not found in the member list.
      If your mailing list software does not add the Sender field (and
      cannot be configured to), you can try using the List-Post field
      instead, though it is less preferable as it is less likely to
      show up in MUAs.

With the above configuration, all bounces generated by the mailing list
software should be signed by OpenDKIM.  If a user in your domain sends an
e-mail to a mailing list in your domain too, the e-mail will contain two DKIM
signatures; the first added when the mail was first submitted by the user,
and the second added to the message generated by the mailing list software.
In this case, the first signature will likely be invalid, but this is
perfectly normal; compliant receivers are required to treat an invalid
signature as a non-existing signature, thus the message will appear as
simply signed once to them.

Notice that mailing list software could also be configured to strip the first
signature on incoming mail, but this behaviour is considered a bad practice
as the signature is totally harmless even if broken, and it can instead be
useful for forensic analysis, just like a Received field.


SUPPORT
=======

There are two public mailing lists available for news and questions about
OpenDKIM.  To keep up to date on the latest developments, please
subscribe to one or both of the following:

	opendkim-announce@lists.opendkim.org (release announcements)
	opendkim-users@lists.opendkim.org (general discussion)

Send an email to opendkim-announce-request@lists.opendkim.org with subscribe
as the subject to receive release announcements.  You can find more
information about our mailing lists at http://www.opendkim.org.

To report bugs and feature requests, you can access the SourceForge "tracker"
facilities at http://sourceforge.net/projects/opendkim.
