(Generated by  groff(1))

S-dkim-sign manual

This plain  groff(1) HTML output has only been fixed slightly — i am sorry for false list indentions etc.!

S-dkim-sign [v0.6.0, 2024-04-14] — postfix-only RFC 6376/[8301]/8463 D(omain)K(eys) I(dentified) M(ail) sign-only milter


s-dkim-sign [options]
[options] −−test-mode [options]

s-dkim-sign-key-create [ed25519|rsa[:bits]] path-prefix



A postfix(1)-only RFC 6376/[8301]/8463 D(omain)K(eys) I(dentified) M(ail) sign-only milter. DKIM permits a person, role, or organization to claim responsibility for a message by associating a domain name (RFC 1034; rather: RFC 5321) with the message (RFC 5322) through a cryptographically verifiable signature stored in a ‘DKIM-Signature’ mail header. The signature can be validated by fetching the appropriate public key(s) from the domain’s DNS (RFC 8499) record. DKIM defines two preparation algorithms, simple and relaxed: S-dkim-sign only uses the latter.

The program is intended to be started via the postfix(1) spawn(8) daemon, which takes care of the correct user and group credentials, timeouts and stale server garbage collection etc.; efficient operation requires postfix versions which support the milter operation ‘SMFIC_QUIT_NC’ (not yet as of version 3.9), elder versions will start a new program instance for every SMTP or local connection.

The general operation of milters is that they see all messages that pass the mail server, without knowing whether a message is “incoming” or “outgoing”. This milter will sign all messages from ‘localhost’ or configured −−client s with all registered −−key s; with −−milter-macro ‘sign’ only server connections announcing a match are considered. It will perform configured −−remove s for messages not from ‘localhost’ or configured −−client s; again, with −−milter-macro ‘verify’ only dedicated connections are considered. Decisions are only narrowed down: once ‘sign’ or ‘verify’ paths are taken only ‘pass’ remains as an option.

Fine-grained control of signing activity is available via −−sign relations that link specific −−key s to mail addresses, plain domain names, or domain name wildcards, as are extracted from (the first address of) ‘From’ headers of messages seen by the milter. Receivers will search for the public key DNS record of either the domain defined by the matching −−sign relation, the specified −−domain-name , or otherwise that of the extracted address.

In the following example all messages from ‘localhost’ via all deliveries (‘milter_macro_daemon_name’ global) of the user ‘joe’ at domain ‘his.domain’, including all subdomains, for example ‘at.home.his.domain’, are signed with the key ‘edape’, all messages from ‘my.domain’ including all subdomains with the keys ‘edape’ and ‘rsape’, however, if messages come from the domain ‘bla.my.domain’ only the key ‘edape’ will be used. Other messages will not be signed.

#@ /etc/postfix/main.cf:
milter_default_action = accept
non_smtpd_milters = unix:private/dkim-sign
smtpd_milters = unix:private/dkim-sign
milter_macro_daemon_name = egressplus

#@ /etc/postfix/master.cf:
smtp inet n - n - - smtpd
-o smtpd_tls_security_level=may
-o milter_macro_daemon_name=ingress
localhost:smtp inet n - n - - smtpd
-o syslog_name=localsmtp
#localhost:421 inet n - n - - smtpd
# -o syslog_name=lhlist
# -o smtpd_milters=unix:private/dkim-sign-list
dkim-sign unix - n n - - spawn
argv=/usr/libexec/s-dkim-sign -R /etc/dkim-sign.rc
#dkim-sign-list unix - n n - - spawn
# user=ANON-USR
# argv=/usr/libexec/s-dkim-sign -R /etc/dkim-sign.rc --header-seal=+

#@ /etc/s-dkim-sign.rc
key rsa-sha256, rsape, /etc/dkim-pri-rsa.pem
key ed25519-sha256, edape, /etc/dkim-pri-ed25519.pem

milter-macro sign, {daemon_name}, egressplus
milter-macro verify, {daemon_name}, ingress
#client localhost

sign .my.domain
sign bla.my.domain , my.domain , ::edape : :
sign joe@.his.domain,my.domain,edape

header-sign *
header-seal *

remove a-r

s-dkim-sign-key-create is a simple shell script which uses IEEE Std 1003.2 (“POSIX.2”) standard tools and openssl(1) to create the private/public key tuple to anticipate in DKIM, as well as readily formatting the according DNS record. In interactive usage it will print a result abstract.


Options may be given in short or long form, −−resource-file s only support the long form, and only a (logical) option subset. −−test-mode performs a dry-run configuration syntax test, and outputs a normalized resource file.

−−client [
]spec, −C..

Define rules for connecting clients as announced by the milter macro ‘_’ (postfix(1) passes only “validated client name and address”). The optional action is either ‘s[ign]’ (default), ‘v[erify]’ or ‘p[ass]’ (no action), spec is either a domain name or an IPv4 or IPv6 internet address, optionally in RFC 1519 CIDR notation with network mask. Domain names (see −−domain-name ) are matched exactly unless starting with a period ‘.’ to enforce wildcard matches for it and its subdomains; only period is a “super-wildcard” matching all domains. Dictionaries are used except for CIDR ranges, which are matched last and in the given order. Defining any rule replaces the built-in ‘sign,localhost’ / ‘verify,.’ pair, and establishes a default action ‘pass,.’.

# Match d.a.s but also a.b.c.d.a.s
-C .d.a.s

−−client-file [
]path, −c..

Load a file of entries in the syntax described for the spec argument of −−client ; Lines are read as via −−resource-file . An optional action applies to all entries.


Dry-run sandbox: no real action is performed, but only logged. All messages remain umodified.

−−domain-name domain, −d..

Define the ‘d=’ domain name to be used in signatures unless overwritten by a −−sign relation. If unused, and without relation, the domain name of (the first address of) ‘From’ will be announced in signatures. domain can start with RFC 20 ASCII letters and digits, followed by also ‘.’ period and ‘-’ hyphen-minus; lengths are not tested. The DKIM standard requires a real domain name, literal( addresse)s are not allowed. Receivers will try to locate the public key(s) identified by the chosen −−key s selectors as DNS TXT records in the form ‘selector._domainkey.domain’.

−−header-sign list, −˜..

Define (comma-separated) list of case-insensitive header fields (names) to sign. Alternatively built-in defaults may be used and modified by starting list with a commercial at ‘@’, or an extended variant with asterisk ‘*’; further entries may then be prefixed with exclamation mark ‘!’ to denote desired list exclusion. Attempts to exclude ‘From’ is an error, no other rules apply (untested: name validity, duplicates). An effectively empty list is not used.

--header-sign ’* !message-id , !to ,, !cc , ,’


Show the built-in −−header-sign lists, then exit.

−−header-seal list, !..

Like −−header-sign , but “oversign”, meaning DKIM signatures include an (additional) empty instance of given headers, as such preventing attempts to inject headers not covered by, and therefore not verified through the signature (without causing errors). Default lists are built-in, but sealing needs to be activated explicitly; “signing non-present fields” is described in RFC 6376 from 2011, however. Sealing headers not included in −−header-sign (can) result(s) in broken signatures: −−test-mode will catch this.

Remarks: In order not to break mailing-list posts (handled by software which does not recognize message signatures) the built-in defaults exclude ‘Reply-To’ and all the mailing-list related fields of RFC 2369. In order to ease DKIM signing for mailing-lists as such sealing provides another built-in default, addressable via plus sign ‘+’.


Show the built-in −−header-seal lists, then exit.

−−key algo-digest,selector,path, −k..

Add a key to be used. Unless −−sign relations have been established messages will be signed with all keys. In a comma-separated list of three fields, the first defines algorithm and digest, separated by ‘-’ hyphen-minus, for example rsa-sha1. Dependent on the used crypto library ed25519-sha256, rsa-sha256, and rsa-sha1 (obsoleted by RFC 8301, causes a warning) can be supported; the output of −−long-help shows what is built-in. The second field specifies the (DNS) selector of the public key (please see −−sign ); it must obey −−domain-name syntax. The third field is the path to the private key file in PEM format; file accessibility is not tested, but recommendable is tight-most (readable by only root and the user identity running S-dkim-sign). (In 2024, with multiple and mixed RSA/Ed25519 keys, placing the RSA one(s) first may improve interoperability.)

−−long-help , −H

A long help listing that also shows available signature algorithms.

−−milter-macro action,name[
], −M..

Only apply action (‘sign’ or ‘verify’) if servers announce macro (with any of the given value(s)), or let the connection pass. With postfix(1) one should postconf(5) ‘milter_macro_daemon_name=VAL1’, seen by the milter as the ‘{daemon_name}’ macro with ‘VAL1’, therefore −−milter-macro=sign,{daemon_name},VAL1,VAL2 would be needed. The macro is expected to be received when clients connect.

−−remove type[
], −r..

Remove headers of type (‘a-r’ meaning RFC 8601 ‘Authentication- Results’). Without specs headers matching the milter macro ‘j’ (postfix(1)-expanded to ‘$myhostname’) are removed, otherwise the given strings in order; Note: they are matched as domain names are for −−client , syntax validity is however not verified due to their RFC 8601 definition; As a special case a sole exclamation mark ‘!’ matches invalid header instances.

−−resource-file path, −R..

A configuration file with long options (without ‘−−’ double hyphen-minus). Each line forms an entry, leading and trailing whitespace is removed. If the first non-whitespace character is the ‘#’ number-sign the line is a comment and discarded. Empty lines are ignored, other lines can be folded over multiple input lines with a reverse-solidus ‘\’ before the newline: all leading whitespace of the next line is ignored.

# Comment \
macro \
sign , {daemon_name}

−−sign spec[
,selector..]], −S..

Establish a relation in between spec and none to maximally five selector(s), which (in the end) have to relate to −−key s; fields form a comma-separated, multiple selectors instead a ‘:’ colon-separated list; all keys are used if there are none. If any sign relation is established only those messages which match a relation will be signed. selector order does not matter. A given domain overrides −−domain-name .

spec is matched against the (first address of the) ‘From’ header of processed messages; It can be a mail address like ‘local-part@dom.ain’, or only a domain without ‘local-part@’; domains with a leading ‘.’ period, as in ‘.dom.ain’, match all subdomains, for example ‘a.b.c.dom.ain’, or ‘joe@c.dom.ain’. Only a period is a “super-wildcard” that matches all domains, for example ‘jack@.’ will match all jack’s.

Remarks: Some ‘local-part’s require quoting, for example ‘t"i(%;)@"@gh.t’. Since normalization is applied during actual processing, ‘"ti(%;)@"@gh.t’ will have to be written in order for this to match. (−−test-mode will catch this.) Remarks: commas cannot be used even in a quoted ‘local-part’.

−−sign-file path, −s..

Load a file of −−sign relations; Lines are read as via −−resource-file .

−−test-mode , −#

Enable test mode: all options are evaluated, thereafter the final settings are shown in resource file format. The exit status indicates error. It is highly recommended to use this for configuration checks.

−−ttl seconds, −t..

Impose a time-to-live expiration upon generated DKIM signatures after which newly receiving parties shall see them as invalidated. seconds must not be smaller than 30 and greater than 86400000 (1000 days).


Increase log verbosity (two levels).


postfix(1), postconf(5), spawn(8),


Steffen Nurpmeso <steffen@sdaoden.eu>.

Copyright (c) 1997 - 2024, Steffen Nurpmeso <steffen@sdaoden.eu>
@(#)site/code-dkim-sign.html-w42 1.3 2024-04-06T21:52:28+0000