DEBOPS.OPENDKIM(5)

NAME
debops.opendkim - Manage OpenDKIM service using Ansible

SYNOPSIS

debops service/opendkim [--limit group,host,...] [--diff] [--check] [--tags tag1,tag2,...] [--skip-tags tag1,tag2,...] [<ansible-playbook options>] ...

DESCRIPTION

DomainKeys Identified Mail <https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail> (DKIM) standard can be used by organizations to automatically sign and verify e-mail messages sent by their SMTP server(s). Other organizations can verify signed messages using public keys retrieved from the DNS database; the signature validity can then be used to classify e-mail messages as wanted or not.

The OpenDKIM <http://opendkim.org/> library implements DKIM in the form of a Sendmail milter service, which can be used by SMTP servers. You can use the debops.opendkim Ansible role to configure the OpenDKIM service on one or multiple hosts. If debops.postfix role is used together with this one, Postfix can be automatically configured to sign and verify e-mail messages.

GETTING STARTED

  Default DKIM configuration

The role by default will create a DomainKey for the host's DNS domain, which will use a mail DKIM selector. The private key can be found in the secret/opendkim/domainkeys/ directory on the Ansible Controller (as well as on the remote hosts). Using the installed script in the secret/opendkim/lib/ directory you can extract the public key in the form of a DNS TXT record and place it in your DNS zone.

OpenDKIM will be configured to sign mail messages from localhost and the host's FQDN. Messages sent from the host's own DNS domain, as well as any messages from the subdomains will be signed by default.

  Support for Unbound DNS resolver

The debops.opendkim role checks if the Unbound service has been installed on a given host, by checking for the Ansible local facts defined by the debops.unbound role. If Unbound is present, OpenDKIM will automatically use it to resolve DNS queries and check DNSSEC validity.

  Postfix integration

If the debops.opendkim role detects an installed Postfix instance by checking the Ansible local facts created by the debops.postfix Ansible role, Postfix support will be enabled automatically.

OpenDKIM will be reconfigured to create its listening socket in /var/spool/postfix/opendkim/ directory. This directory is created with SGID bit set, and its group is set to the Postfix main group, so that the socket created by OpenDKIM will be automatically accessible by Postfix.

The debops.opendkim role will add Postfix main.cf configuration options using the debops.postfix dependent role variables. OpenDKIM filter will be added to the smtpd_milters as well as non_smtpd_milters configuration options.

  Example inventory

The install and configure OpenDKIM on a host, it needs to be present in the [debops_service_opendkim] Ansible inventory group:

[debops_service_opendkim]
hostname

  Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.opendkim role:

---
- name: Manage OpenDKIM service
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_service_opendkim' ]
  become: True
  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'
  pre_tasks:
    - name: Prepare opendkim environment
      import_role:
        name: 'opendkim'
        tasks_from: 'main_env'
      tags: [ 'role::opendkim', 'role::secret' ]
    - name: Prepare postfix environment
      import_role:
        name: 'postfix'
        tasks_from: 'main_env'
      vars:
        postfix__dependent_maincf:
          - role: 'opendkim'
            config: '{{ opendkim__postfix__dependent_maincf }}'
      when: opendkim__postfix_integration|bool
      tags: [ 'role::postfix', 'role::secret', 'role::ferm' ]
  roles:
    - role: secret
      tags: [ 'role::secret', 'role::opendkim', 'role::postfix' ]
      secret__directories:
        - '{{ postfix__secret__directories  | d([]) }}'
        - '{{ opendkim__secret__directories | d([]) }}'
    - role: postfix
      tags: [ 'role::postfix', 'skip::postfix' ]
      postfix__dependent_maincf:
        - role: 'opendkim'
          config: '{{ opendkim__postfix__dependent_maincf }}'
      when: opendkim__postfix_integration|bool
    - role: opendkim
      tags: [ 'role::opendkim', 'skip::opendkim' ]

  Ansible tags

You can use Ansible --tags or --skip-tags parameters to limit what tasks are performed during Ansible run. This can be used after a host was first configured to speed up playbook execution, when you are sure that most of the configuration is already in the desired state.

Available role tags:

role::opendkim
    Main role tag, should be used in the playbook to execute all of the role tasks as well as role dependencies.

DEFAULT VARIABLE DETAILS

some of debops.opendkim default variables have more extensive configuration than simple strings or lists, here you can find documentation and examples for them.

  opendkim__config

Configuration of the opendkim__*_config variables is described in a separate document, Default variable details: opendkim__config.

  opendkim__keys

The opendkim__*_keys variables define what DomainKeys are created and used by OpenDKIM. The private keys are generated on the Ansible Controller, stored in the secret/opendkim/domainkeys/ directory (see debops.secret role for details) and copied to the remote hosts. The role can install the same private key on multiple hosts, which can be useful in environments with multiple SMTP servers handling the same domains.

You can use the secret/opendkim/lib/extract-domainkey-zone Bash script to get the DomainKey public keys which then need to be configured in your DNS zone.

Each list element is either a string that represents the DomainKey selector for the current host domain, or a YAML dictionary with specific parameters:

name or selector
    Required. The DomainKey selector used for this DomainKey.

domain
    Optional. The DNS domain which will use this DomainKey. If not specified, opendkim__domain will be used by default.

regenerate
    Optional, supported since Ansible 2.10. Value passed to regenerate parameter of openssl_privatekey module. It should be set to never or fail when keys are managed externally. If not specified it is set to full_idempotence.

size
    Optional. The size of the autogenerated RSA private key. If not specified, opendkim__default_key_size will be used.

type
    Optional. The private key style (rsa or dsa) which should be generated by the openssl command. Currently only rsa makes sense.

state
    Optional. If not defined or present, the key will be generated on the Ansible Controller and copied to the remote hosts. If absent, key still will be generated, but it will be not copied, and existing private key will be removed from the remote hosts.

  Examples

Create two DomainKeys for the current domain:

opendkim__keys:
  - 'selector1'
  - 'selector2'

They will be placed in the DNS database as:

selector1._domainkey.example.com
selector2._domainkey.example.com

Create a DomainKey for a different domain:

opendkim__keys:
  - name: 'mail'
    domain: 'example.org'

  opendkim__signing_table

The opendkim__*_signing_table variables define a mapping between the contents of the From: header field in a mail message and the DomainKey used to sign the message. The format of the From: header interpreted by OpenDKIM depends on the type of the table used (see opendkim.conf(5) <https://manpages.debian.org/opendkim.conf(5)>). The role by default maps the entire domain, without specifying any users.

Each list entry is a YAML dictionary with specific parameters:

name or selector
    Required. Specify the DomainKey selector to use for a given signing table entry.

from
    Required. Specify the contents of the From: header used to lookup the DomainKey. By default you should use only domain names here, otherwise you need to reconfigure the SigningTable configuration option. See opendkim.conf(5) <https://manpages.debian.org/opendkim.conf(5)> for details.

domain
    Optional. The DNS domain used to lookup the DomainKey for a given signing table entry. If not specified, the opendkim__domain value is used by default.

subdomains
    Optional, boolean. If True, the from value will be added again with a leading dot (.), which signifies that subdomains of a given domain should also be signed. By default subdomains are not signed.

state
    Optional. if not specified or present, a given entry will be included in the signing table. If absent, a given entry will not be included in the configuration.

  Examples

Sign mails from a given domain and its subdomains with the default DomainKey:

opendkim__signing_table:
  - name: 'mail'
    from: 'example.org'
    domain: '{{ ansible_domain }}'
    subdomains: True

  opendkim__trusted_hosts

The opendkim__*_trusted_hosts variables are YAML lists which contain IP addresses, CIDR subnets and hostnames of "trusted hosts". These hosts will be stored in the /etc/opendkim/dkimkeys/TrustedHosts file which is by default used in the OpenDKIM configuration by the InternalHosts and ExternalIgnoreList options. Mail messages from these hosts will be automatically signed rather than verified by OpenDKIM.

  Examples

Trust localhost and a given subnet:

opendkim__trusted_hosts:
  - 'localhost'
  - '127.0.0.1'
  - '::1'
  - '192.0.2.0/24'
  - '2001:db8::/32'

DEFAULT VARIABLE DETAILS: OPENDKIM__CONFIG

The opendkim__*_config variables are used to define the contents of the /etc/opendkim.conf configuration file. The variables are YAML lists, concatenated together into opendkim__combined_config variable, which is passed to the configuration template.

Each list entry is a YAML dictionary, which can be written in a simple or complex form. Entries that control OpenDKIM parameters of the same name will be combined together in order of appearance.

  Simple form of the configuration parameters

Simple form of the OpenDKIM configuration uses the dictionary key as a option name, and its value as that option's parameters:

opendkim__config:
  # Option with boolean value
  - Syslog: True
  # Option with integer value
  - AutoRestartCount: 0
  # Option with string value
  - Domain: 'example.com'
  # Option with multiple values in a list
  - OversignHeaders: [ 'Header1', 'Header2' ]

The result of the above configuration in /etc/opendkim.conf:

Syslog              yes
AutoRestartCount    0
Domain              example.com
OversignHeaders     Header1,Header2

The parameters in the configuration file will be present in the order they were first defined in the variables.

  Complex form of the configuration parameters

Complex form of the OpenDKIM configuration is detected when a dictionary key contains a name parameter. In that case, the role will interpret the entry using specific parameters:

name
    The name of the configuration option to manage. This parameter is used as an identifier during the variable parsing.

value
    Required. A value which should be set for a given option. Values can be YAML strings, integers, booleans and lists (not dictionaries). Lists can contain simple strings, numbers, or YAML dictionaries that describe each value in greater detail. See Configuration values and their interactions for more details.

option
    Optional. If specified, the option will use this string as the "name" instead of the name value. This is useful to create examples in the configuration file that have the same name as existing configuration options.

comment
    Optional. String or a YAML dictionary with additional comments for a given configuration option.

separator
    Optional, boolean. if True, an empty line will be added above a given option, useful for readability.

state
    Optional. If not specified or present, the option will be present in the finished configuration file.

    If absent, the option will not be included in the configuration file.

    If ignore, the given entry will not be evaluated by the role, and no changes will be done to the preceding parameters with the same name. This can be used to conditionally activate entries with different configuration.

    If hidden, the option will not be displayed in the configuration file, but any comments will be present. This can be used to add free-form comments in the Postfix configuration file.

    If comment, the option will be present, but it will be commented out. This can be used to add examples in the configuration file.

weight
    Optional. A positive or negative number which affects the position of a given option in the configuration file. The higher the number, the more a given option "weighs" and the lower it will be placed in the finished configuration file. Negative numbers make the option "lighter" and it will be placed higher.

copy_id_from
    Optional. This is an internal role parameter which can be used to change the relative position of a given option in the configuration file. If you specify a name of an option, it's internal "id" number (used for sorting) will be copied to the current option. This can be used to move options around to different configuration file sections.

  Examples

Define the previous example using complex form:

opendkim__config:
  - name: 'Syslog'
    comment: 'Log to syslog'
    value: True
  - name: 'AutoRestartCount'
    value: 0
  - name: 'Domain'
    value: 'example.com'
  - name: 'OversignHeaders'
    value: [ 'Header1', 'Header2' ]

The result of the above configuration in /etc/postfix/main.cf:

# Log to syslog
Syslog              yes
AutoRestartCount    0
Domain              example.com
OversignHeaders     Header1,Header2

The parameters in the configuration file will be present in the order they were first defined in the variables, unless the weight parameter is added, which will change the order.

  Configuration values and their interactions

The OpenDKIM configuration file <http://opendkim.org/opendkim.conf.5.html> uses key-value format, with values being either strings, numbers, booleans or lists. The first three types are handled by the debops.opendkim role as normal.

List values are by default concatenated to allow easy extension of existing values. The values in a list are either YAML strings, numbers, or can be defined as YAML dictionaries with specific parameters:

name or param
    Required. The value itself, usually a string.

state
    Optional. If not defined or present, the value will be included in the list.

    If absent, the value will be removed from the list.

    If ignore, the given entry will not be evaluated by the role, and will not change the state of the value. This can be used to enable or disable values conditionally.

weight
    Optional. A positive or negative number which affects the position of a given value in the list. The higher the number, the more a given value "weighs" and the lower it will be placed in the finished list. Negative numbers make the value "lighter" and it will be placed higher.

  Example list

Define a list with conditional values:

opendkim__config:
  - name: 'OversignHeaders'
    value:
      - 'From'
      - name: 'To'
        state: '{{ "present"
                   if (ansible_domain.split(".")|count > 1)
                   else "ignore" }}'
      - name: 'Subject'
        weight: 100

  Base value replacement

Repeating the string, number or boolean option will result in the latter entry replacing the former entry:

opendkim__config:
  # Old value
  - Domain: 'example.com'
  # New, active value
  - Domain: 'example.org'

The result of the above configuration in /etc/opendkim.conf:

Domain          example.org

  Lists are merged together

The list parameters behave differently. Specifying the same option multiple times, if the preceding option was a list, will add the specified parameters to the list:

opendkim__config:
  - InternalHosts: [ '127.0.0.1', 'localhost' ]
  - InternalHosts: [ '192.0.2.1' ]

The result of the above configuration in /etc/opendkim.conf:

InternalHosts          127.0.0.1,localhost,192.0.2.1

  How to reset a list

If the option was a list, and subsequent option specified a boolean, string or a number, the value will replace the previous one, instead of adding to a list. This can be used to reset the list instead of appending to it.

opendkim__config:
  - InternalHosts: [ '127.0.0.1', '::1' ]
  - Internalhosts: 'localhost'

The result of the above configuration in /etc/opendkim.conf:

InternalHosts          localhost

  Lists don't add duplicates

The role checks if a given list element is already present, and it won't add a duplicate value to the list:

opendkim__config:
  - Domain: [ 'example.org', 'localhost' ]
  - Domain: [ 'example.org' ]

The result of the above configuration in /etc/opendkim.conf:

Domain           example.org,localhost

AUTHOR
Maciej Delmanowski

COPYRIGHT
2014-2022, Maciej Delmanowski, Nick Janetakis, Robin Schneider and others