Setup dkim on postfix with amavisd-new
step by step instruction coming soon…
Setting up DKIM mail signing and verification from http://www.ijs.si/software/amavisd/
Setting up DKIM mail signing and verification
A DKIM standard (RFC 4871) states the following, which applies
to its predecessor DomainKeys (historical: RFC 4870) as well:
DomainKeys Identified Mail (DKIM) defines a mechanism by which email
messages can be cryptographically signed, permitting a signing domain
to claim responsibility for the introduction of a message into the
mail stream. Message recipients can verify the signature by querying
the signer’s domain directly to retrieve the appropriate public key,
and thereby confirm that the message was attested to by a party in
possession of the private key for the signing domain.
The DomainKeys specification was a primary source from which the
DomainKeys Identified Mail [DKIM] specification has been derived.
The purpose in submitting the RFC 4870 document is as an historical reference
for deployed implementations written prior to the DKIM specification.
The main advantage of DKIM signing to sending domains
is that it allows recipients to reliably validate mail origin for
purposes of whitelisting on spam checks and whitelisting
reception of otherwise banned mail contents. By signing outbound
mail you give your correspondents a chance to distinguish between
your genuine mail, and fraud or spam mail which may happen to carry
your domain name as a sender address. Signing outbound mail is a
kind gesture towards recipients, making it much easier for them
to treat your mail as important or desirable if they choose so.
The main advantage of DKIM signature verification to recipients
is that it allows them to reliably distinguish genuine mail originating
from a claimed sending domain from other (possibly faked) mail. It
makes signature-based whitelisting a reliable mechanism.
It also makes it possible to recognize and automatically discard
fake mail claiming to be from domains which are known to always
sign their outbound mail and to always send mail directly. Coupled
with reputation schemes (mostly manual/static at present,
or dynamic in the future) makes it possible to assign score points
(positive or negative) based on merit and past experience
with each signing domain. A valid signature also offers
non-repudiation: a domain which signed a message can not
disclaim message origin, which offers recipient a strong argument
when reporting abuse to the signing domain.
For the impatient – signing from scratch
Here is a quick Spartanic setup of DKIM signing and DKIM/DK
verification by amavisd for the impatient, without much explanation,
assuming all originating mail comes from internal networks (not
from authenticated roaming clients), only one domain needs
signing, using default signature tags, no milters are in use
and no mailing list manager needs signing. No changes in Postfix
configuration is necessary for this simple setup. For more
information and more complex setups please see sections
further on.
Generate a signing key:
$ amavisd genrsa /var/db/dkim/example-foo.key.pem
add to amavisd.conf:
$enable_dkim_verification = 1;
$enable_dkim_signing = 1;
dkim_key('example.com', 'foo', '/var/db/dkim/example-foo.key.pem');
@dkim_signature_options_bysender_maps = (
{ '.' => { ttl => 21*24*3600, c => 'relaxed/simple' } } );
@mynetworks = qw(0.0.0.0/8 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12
192.168.0.0/16); # list your internal networks
run:
$ amavisd showkeys
add the public key (as displayed) to your DNS zone, increment SOA
sequence number and reload DNS; then test signing and a published key:
$ amavisd testkeys
if all went well:
$ amavisd reload
For the impatient – replacing
signing by dkim-milter with signing by amavisd
For sites already signing their mail by dkim-milter, most work
of preparing signing keys and publishing public keys in DNS has
already been done. All it needs to be done is to declare these
signing keys in amavisd.conf and turn on $enable_dkim_signing.
To facilitate transition of DKIM signing from dkim-milter to amavisd-new,
a new command-line tool is available with amavisd-new-2.6.2 (the extra
utility code is not loaded during normal operation), taking a file name
as its argument, e.g.:
$ amavisd convert_keysfile /var/db/dkim/keysfile.txt
and writing to stdout a set of lines that may be directly included into
amavisd.conf configurations file, matching semantics of a dkim-filter
keys file. It can be useful during transition, or for those who prefer
to specify signing keys and sender-to-key mappings as a file in a syntax
compatible with options -K -k of dkim-filter, and can live with limitations
of such syntax. See dkim-filter(8) man page for details on the
syntax.
The produced output consists of signing key declarations (calls to
a procedure dkim_key), where each call normally corresponds to exactly
one DNS resource record publishing a corresponding DKIM public key.
When necessary output also produces an assignment to a list of lookup
tables @dkim_signature_options_bysender_maps, which supplies non-default
mappings of sender domains to signing keys, e.g. when third-party
signatures are desired.
Implementation and mail flow
Signing of originating mail (or mail being redistributed by our domain),
and verifying signatures of incoming mail are two tasks that can be
performed by the same program, or they can be performed by separate entities.
Traditionally with sendmail, both tasks are performed by one milter,
which may be easier to maintain, but has certain disadvantages.
Verifying signatures should be performed early, before any local mail
transformations get a chance of invalidating a signature, e.g. by performing
MIME conversions to quote-printable, by fixing syntactically invalid mail
header section, by reformatting or reordering some header fields (some MTAs
do it frivolously), by modifying/inserting/removing certain header
fields, or by a local mailing list modifying mail text, e.g. by appending
footers.
Signing outgoing mail should be performed late, after mail sanitation,
after conversion to 7-bit characters (to avoid later uncontrollable
changes by a relaying or receiving MTA), and after editing header
section by a content filter. Similar applies to local mailing lists,
which may be rewriting messages, requiring them to be re-signed by
the domain hosting a mailing list, just before being sent out.
Starting with amavisd-new version 2.6.0, DKIM signing can be
performed directly by amavisd (using a Perl module Mail::DKIM,
which is the same module as used by DKIMproxy and by SpamAssassin).
Signing directly by amavisd reduces setup complexity using a milter
or DKIMproxy, and avoids additional data transfers. Regarding mail
flow through the system there are similarities between signing in
amavisd and signing by dkim-milter, which is why the diagram below
shows both possibilities.
For verification there are three choices: either amavisd itself
can do it by calling Mail::DKIM directly, or a SpamAssassin plugin
can do it by calling the same Perl module, or a milter in
verification-only mode can be invoked by an incoming Postfix
smtpd service.
Advantage of invoking signature verification by amavisd
is that all mail is checked for signatures, regardless of
whether SpamAssassin is called or not. Typically messages beyond
a certain size are not passed to SpamAssassin, and neither are
infected message or identified bounces. Amavisd also offers loading
of policy banks based on valid DKIM/DK signatures (e.g. allowing
some domains to send-in otherwise banned files, or whitelisting on
spam), offers to add score points based on signing domain reputation,
and adds Authentication-Results header field (like a dkim-milter
does).
Invoking signature verification by SpamAssassin has an
advantage that DKIM-based or DomainKeys-based whitelisting or scoring
can be used, but has a disadvantage that possibly not all mail is
checked (e.g. large mail and infected mail may be exempt from spam
checks). Performing the same signature validation task twice (by
amavisd and by SA) may seem wasteful, but in practice it is not
too bad: thanks to DNS server caching a network lookup for a
public signing key is only done once, and as SpamAssassin does not
receive large mail for processing, its signature verification is
very quick: few milliseconds for non-signed mail, and of the order
of a tenth of a second for signed mail.
Invoking signature verification by calling a milter from
incoming smtpd service has an advantage that it has the best chance
of seeing mail in its pristine form (before canonical and virtual
mapping or masquerading by MTA, regardless of their settings).
Because it is poorly integrated with the rest of the chain (e.g. with
SpamAssassin rules and amavisd policy banks), and because it adds
one extra data transfer, it is mainly still useful as a way to
double-check the correctness of DKIM validation by having two
independent implementations in use, each inserting its independently
derived Authentication-Results header field into passed mail.
To sign as late as possible with a dkim-milter, the signing
milter can be invoked by a Postfix smtpd service which is receiving
content-checked mail from a content filter such as amavisd-new.
As this second-stage smtpd service does not reliably know how a
given message came into a mail system and whether it is supposed
to be signed or not, a clean solution is to provide two (or more)
parallel paths through MTA and through a content filter, one used
for mail that is eligible for being signed (originating mail),
the other for all the rest. This same dual path approach through
amavisd is beneficial for signing by amavisd too, for the same
reason of providing a reliable source of information on mail
origin to a signature choosing code:
+------+
|verify| (verify)
+--+---+ | (by amavisd and/or SA)
^^^ milter |
incoming: ||| +---v-------+
MX ----> 25 smtpd ---> 10024 > >---> 10025 smtpd -->
|| | |
SASL --> 25 smtpd \ | amavisd | (notifications)
submission | +-> | >--->_
mynets-> 25 smtpd ---> 10026 >ORIGINATING>---> 10027 smtpd -->
submission +-> +-------^---+ |
--> 587 smtpd / : | v milter
(convert | +------+
to 7-bit) (sign) | sign |
+------+
There are other benefits to providing two parallel paths: a content
filter may be configured to apply different rules and settings to mail
that is known to be originating from our users. Some suggestions: apply
less strict banning rules, enable spam administrator notifications for
internally originating spam and viruses, letting SpamAssassin rules be
conditionalized based on amavisd-new policy banks loaded, etc.
Configuring multiple mail paths
in Postfix
Here is one way of configuring Postfix for providing two paths
through a content filter. Locally submitted or authenticated mail
will go to a content filter to its port 10026 and will be signed on
its way out (either by amavisd or by a signing milter). All other
mail (incoming) will be diverted to port 10024 for normal content
filtering, and will not be eligible for signing.
main.cf:
# on re-queueing of a message smtpd_*_restrictions do not apply,
# so we'd better provide a safe default for a content_filter,
# even at an expense of later flipping the choice twice
# (which adds a bit to log clutter, but never mind)
#
content_filter = amavisfeed:[127.0.0.1]:10024
# each triggered FILTER deposits its argument into a
# content_filter setting, the last deposited value applies
#
smtpd_sender_restrictions =
check_sender_access regexp:/etc/postfix/tag_as_originating.re
permit_mynetworks
permit_sasl_authenticated
permit_tls_clientcerts
check_sender_access regexp:/etc/postfix/tag_as_foreign.re
# Make sure to assign FILTER tags in restrictions which
# are only invoked once per message, e.g. client or sender
# restrictions, but NOT on smtpd_recipient_restrictions,
# as a message may have multiple recipients, so multiple
# passes through FILTER tag assignments can yield a
# surprising (and incorrect) result.
/etc/postfix/tag_as_originating.re:
/^/ FILTER amavisfeed:[127.0.0.1]:10026
/etc/postfix/tag_as_foreign.re:
/^/ FILTER amavisfeed:[127.0.0.1]:10024
In master.cf set up two listening smtpd services for receiving
filtered mail from amavisd (as per README.postfix), one on tcp
port 10025 (for inbound mail) and the other on port 10027 (for
originating mail). If a signing milter is in use it will be
attached to a smtpd service on 10027 only. If no milters are
in use and signing is done by amavisd, both smtpd services can
have exactly the same settings, and in fact only one suffices,
in which case redirecting $forward_method and $notify_method to
’smtp:[127.0.0.1]:10027′ in later example can be disregarded.
Configuring multiple mail paths
in amavisd
In amavisd.conf two parallel paths need to be provided,
one receiving on port 10024 and forwarding to 10025,
the other receiving on port 10026 and forwarding to 10027.
$inet_socket_port = [10024,10026]; # listen on two ports
The 10024>10025 path will be controlled by a default policy bank,
the other (10026>10027), dedicated to mail intended to be signed,
will use a policy bank (arbitrarily) named ORIGINATING:
$forward_method = 'smtp:[127.0.0.1]:10025'; # MTA with non-signing service
$notify_method = 'smtp:[127.0.0.1]:10027'; # MTA with signing service
# switch policy bank to 'ORIGINATING' for mail received on port 10026:
$interface_policy{'10026'} = 'ORIGINATING';
$policy_bank{'ORIGINATING'} = { # mail originating from our users
originating => 1, # indicates client is ours, allows signing
#
# force MTA to convert mail to 7-bit before DKIM signing
# to avoid later conversions which could destroy signature:
smtpd_discard_ehlo_keywords => ['8BITMIME'],
#
# forward to a smtpd service providing DKIM signing service
# (if using a signing milter instead of signing by amavisd):
forward_method => 'smtp:[127.0.0.1]:10027',
#
# other special treatment of locally originating mail,
# just some suggestions here:
spam_admin_maps => ["spamalert\@$mydomain"], # warn of spam from us
virus_admin_maps => ["virusalert\@$mydomain"],
banned_filename_maps => ['ALT-RULES'], # more relaxed rules
spam_quarantine_cutoff_level_maps => undef, # quarantine all spam
spam_dsn_cutoff_level_maps => undef,
spam_dsn_cutoff_level_bysender_maps => # bounce to local senders only
[ { lc(".$mydomain") => undef, '.' => 15 } ],
};
The smtpd_discard_ehlo_keywords=>['8BITMIME'] serves
to persuade Postfix to convert mail to 7-bit quoted-printable before
submitting it to content filtering and signing. Avoiding 8-bit characters
in mail body makes signatures less susceptible to breaking by some
relaying or receiving MTA over which we have no control.
The same effect (making Postfix convert outgoing mail to 7-bits
before DKIM signing) could be achieved by a Postfix setting
smtp_discard_ehlo_keywords=8bitmime on a smtp service
feeding mail-to-be-signed to amavisd, but this would require setting
up two such services, one with the option and one without.
Note that 8-bit to 7-bit conversion may break a S/MIME or PGP signature,
so if mail signing is in use, it may not be desirable to let Postfix
do the conversion, and it may be acceptable to take a risk that a remote
MTA will clobber signatures if it decides the mail text is to be converted
to 7-bits QP. The only reliable solution in this case is to configure
MUA clients to stick to 7-bit characters/encodings before generating
S/MIME or PGP signatures.
The following text from the Postfix documentation file MILTER_README
should be disregarded — amavisd is 8-bit clean,
and we do want Postfix to convert to 7-bits on the signing path
but not on the other path:
Content
filters may break domain key etc. signatures. If you use an SMTP-based
content filter, then you should add a line to master.cf with
“-o disable_mime_output_conversion=yes”, as described in the
advanced content filter example.
While testing how the configured system plays with some mailing lists
(such as postfix-users or SpamAssassin users list), one has
to keep in mind that amavisd-new caches spam checking results of recently
seen message bodies: a mail going out to a mailing list is not yet signed
as it reaches a content filter, but the SpamAssassin verdict is remembered
at that point (claiming the message is not signed). When this message
with unchanged body comes back from a mailing list, this time signed
in the header section by our domain, the signature should prove correct,
yet the cached result from a minute ago still claims the message is not
signed. If this is of concern, one can turn off caching of spam checking
results for ham by setting: $spam_check_negative_ttl = 0;
While on the topic of providing multiple paths through amavisd,
when one has to deal with a mailing list manager (e.g. Mailman) in the
same setup, and re-signing of its fan-out mail is desired, it may be
useful to add a third path through amavisd, this one stripped down to
bare bones, providing only DKIM signing and nothing else (no virus or
spam checks, no decoding), as these checks were already done once on
mail before it reached a mailing list manager. Here is one possibility,
accepting mail on port 10028 and sending it to 10025:
$inet_socket_port = [10024,10026,10028];
$interface_policy{'10028'} = 'NOCHECKS';
$policy_bank{'NOCHECKS'} = { # no checks, just DKIM signing
originating => 1, # allows signing
forward_method => 'smtp:[127.0.0.1]:10025',
smtpd_greeting_banner =>
'${helo-name} ${protocol} ${product} NOCHECKS service ready',
mynetworks_maps => [], # avoids loading MYNETS policy unnecessarily
os_fingerprint_method => undef,
penpals_bonus_score => undef,
bounce_killer_score => 0,
bypass_decode_parts => 1,
bypass_header_checks_maps => [1],
bypass_virus_checks_maps => [1],
bypass_spam_checks_maps => [1],
bypass_banned_checks_maps => [1],
spam_lovers_maps => [1],
banned_files_lovers_maps => [1],
archive_quarantine_to_maps => [],
remove_existing_x_scanned_headers => undef,
remove_existing_spam_headers => undef,
signed_header_fields => { 'Sender' => 1 },
};