Skip to content

sys4/dkimpy-role

Repository files navigation

dkimpy-role: ansible role for dkimpy-milter

This ansible role installs and configures dkimpy-milter. It provides means to create and use rsa and ed25519 type DKIM keys.

Installation

You can either simply download this project as an archive and place it in your projects roles/ subdirectory or you add it as git submodule to your project – in case your project is under git control. The latter has the benefit that you can simply use git to update changes from this project.

To add this ansible role as independent submodule to your "superproject" go to your roles subdirectory and issue the following command:

$ git submodule add https://github.com/sys4/dkimpy-role

After that you should be able to use the role in your project by adding it to your playbook like this:

- hosts: mail
  gather_facts: true
  roles:
      - { role: dkimpy-role, tags: ['dkim'] }

Assuming your playbook would be mail.yml you can use the dkimpy-role using the ansible-playbook command like this:

$ ansible-playbook -t dkim mail.yml

Executed without any configuration that specifies rsa or ed25519 type DKIM keys the role will install the dkimpy-milter via pip, create a user and group dkimpy-milter, register and enable the service and create the necessary directories to hold dkimpy-milter tables, key files and files containing the values that need to be added to DNS ressource records.

# tree -ugp /usr/local/etc/
/usr/local/etc/
├── [drwxr-x--- dkimpy-milter dkimpy-milter]  dkim                 (1)
│   ├── [drwxr-x--- dkimpy-milter dkimpy-milter]  keys             (2)
│   ├── [-rw-r----- dkimpy-milter dkimpy-milter]  KeyTable
│   ├── [-rw-r----- dkimpy-milter dkimpy-milter]  KeyTableEd25519
│   ├── [-rw-r----- dkimpy-milter dkimpy-milter]  SigningTable
└── [-rw-r----- dkimpy-milter dkimpy-milter]  dkimpy-milter.conf   (3)
  1. All tables, keys and dns-files are located below /usr/local/etc/dkim. located in /usr/local/etc/.

  2. Once you’ve configured the dkimpy-role to create keys, they will be stored in this directory.

  3. The dkimpy-milter systemd unit file expects dkimpy-milter.conf to be located in /usr/local/etc/.

Key management

dkimpy-role implements a simple key management process. It allows to specify an arbitrary number of rsa and/or ed25519 keys. Once specified dkimpy-role will create the *.key- and corresponding *.dns-files in /usr/local/etc/dkim/keys next time you play the role.

In order to specify keys dkimpy-role should create for you add the following dictionaries to your hosts vars file. Following the previous example for a playbook mail.yml a corresponding ansible/host_vars/mail.yml would specify DKIM keys to create and enable like this:

rsa_keys:                               (1)
  preskey:
    sender: president@example.com
    enabled: yes
    selector: 201912
  comkey:
    sender: example.com
    enabled: yes
    selector: 201912
  netkey:
    sender: example.net
    enabled: yes
    selector: 201912
  netkey2:                              (2)
    sender: example.net
    enabled: no                         (3)
    selector: 202001
ed25519_keys:                           (4)
  preskey:
    sender: president@example.com
    enabled: yes
    selector: 201912
  comkey:
    sender: example.com
    enabled: yes
    selector: 201912
  netkey:
    sender: example.net
    enabled: yes
    selector: 201912
  netkey2:
    sender: example.net
    enabled: no
    selector: 202001
  1. The dictionary rsa_keys controls rsa type DKIM keys

  2. Use a unique dictionary key (here: netkey2 as a successor of netkey) for your DKIM keys. They must be unique or only the last one will be used.

  3. A DKIM key set to enabled: no will be created, but not added to the Signing-Table or KeyTable-Tables (see also: [_dkim_rollout_process]).

  4. The dictionary rsa_keys controls rsa type DKIM keys.

Here’s what the options of a DKIM key control:

sender

The sender option assigns the key to a specific sender or senderdomain.

enabled

The enabled option controls if a key will be used to sign a message that matches the sender.

selector

The selector option controls the selector that will be added to the DKIM header, which will be used to identify the corresponding DKIM pubkey in the senderdomains DNS.

Once you’ve created your DKIM key dictionary/dictionaries you need to play the role to have dkimpy-role create the specified keys. After that you will find them, along with their corresponding *.dns-files in /usr/local/etc/dkim/keys:

# tree -ugp /usr/local/etc/
/usr/local/etc/
├── [drwxr-x--- dkimpy-milter dkimpy-milter]  dkim
│   ├── [drwxr-x--- dkimpy-milter dkimpy-milter]  keys
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.ed25519.example.com.dns
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.ed25519.example.com.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.ed25519.example.net.dns
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.ed25519.example.net.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.ed25519.president@example.com.dns
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.ed25519.president@example.com.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.rsa.example.com.dns
│   │   ├── [-rw------- dkimpy-milter dkimpy-milter]  201912.rsa.example.com.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.rsa.example.net.dns
│   │   ├── [-rw------- dkimpy-milter dkimpy-milter]  201912.rsa.example.net.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  201912.rsa.president@example.com.dns
│   │   ├── [-rw------- dkimpy-milter dkimpy-milter]  201912.rsa.president@example.com.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  202001.ed25519.example.net.dns
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  202001.ed25519.example.net.key
│   │   ├── [-rw-r--r-- dkimpy-milter dkimpy-milter]  202001.rsa.example.net.dns
│   │   ├── [-rw------- dkimpy-milter dkimpy-milter]  202001.rsa.example.net.key
│   ├── [-rw-r----- dkimpy-milter dkimpy-milter]  KeyTable
│   ├── [-rw-r----- dkimpy-milter dkimpy-milter]  KeyTableEd25519
│   ├── [-rw-r----- dkimpy-milter dkimpy-milter]  SigningTable
└── [-rw-r----- dkimpy-milter dkimpy-milter]  dkimpy-milter.conf

The SigningTable and the KeyTable will map the sender or senderdomains accordingly. The SigningTable maps senders or senderdomains to specific identifiers:

SigningTable
# Ansible managed
# senderdomain    identifier

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

# vim: set ft=jinja:

The identifier in the KeyTable specifies the senderdomain, the selector and the path to the key that should be used:

Keytable
# Ansible managed
# identifier senderdomain:selector:/path/to/signing.key

preskey    example.com:201912:/usr/local/etc/dkim/keys/201912.rsa.president@example.com.key
comkey    example.com:201912:/usr/local/etc/dkim/keys/201912.rsa.example.com.key
netkey    example.net:201912:/usr/local/etc/dkim/keys/201912.rsa.example.net.key

# vim: set ft=jinja:

Also in /usr/local/etc/dkim/keys you will find files that end with a dns suffix. These contain the values you will need to add to the senderdomains DNS within the domains subdomain _domainkey:

v=DKIM1; k=ed25519; p=TDvnokQfN5DYwMKRJgZS25rS4zoXkx7qnlUK26bFgi4=
ℹ️

Maintaining the DNS entries is out of scope of this role. Consult your DNS hosters manual or use an additional ansible role to add the entries to the domains DNS.

DKIM rollout process

  1. Specify the key(s) in your hosts var file but don’t enable them.

  2. Let dkimpy-role create the keys.

  3. Add the values from the files ending on dns to the domains DNS.

  4. Verify the DKIM pubkeys exist in the domains DNS.

  5. Set the key to enabled: yes once you want to use them.

Make sure only one key for a sender or senderdomain is enabled at a time.

Customization

This ansible dkimpy-milter role comes with defaults. You can find them in dkimpy-role/defaults/main.yml:

dkimpy_user: dkimpy-milter
dkimpy_group: dkimpy-milter
dkimpy_conf_dir: /usr/local/etc/dkim
dkimpy_key_store: "{{ dkimpy_conf_dir }}/keys"
rsa_key_file_name: "{{ item.value.selector }}.rsa.{{ item.value.sender }}.key"
ed25519_key_file_name: "{{ item.value.selector }}.ed25519.{{ item.value.sender }}.key"
dkimpy_mode: sv
dkimpy_canonicalization: relaxed/simple
dkimpy_key_table: "{{ dkimpy_conf_dir }}/KeyTable"
dkimpy_key_ed25519_table: "{{ dkimpy_conf_dir }}/KeyTableEd25519"
dkimpy_signing_table: "{{ dkimpy_conf_dir }}/SigningTable"
dkimpy_socket: local
dkimpy_postfix_socket_directory: /var/spool/postfix/dkimpy-milter
postfix_group: postfix

You can override these defaults by adding the option you want to change to your hosts vars file along with a value that suits your needs.

Integration

By default dkimpy-milter establishes a local UNIX socket located at /run/dkimpy-milter/dkimpy-milter.sock. To override this default set dkimpy_socket in the hosts vars file.

dkimpy_socket: ...

Available options for dkimpy_socket are:

inet

Setting dkimpy_socket: inet will configure dkimpy-milter to create a TCP socket that listens on port 8892 on the IPv4 localhost interface.

local (default)

Setting dkimpy_socket: local will configure dkimpy-milter to create a local UNIX socket located at /run/dkimpy-milter/dkimpy-milter.sock.

local_postfix

Setting dkimpy_socket: local_postfix will configure dkimpy-milter to create a local UNIX socket located at /var/spool/postfix/dkimpy-milter/dkimpy-milter.sock.

Postfix MTA

Configuring dkimpy-milter to work with Postfix requires to modify dkimpy-milter and Postfix configuration. The following sections explain how to either use a TCP socket or use a local UNIX socket to connect dkimpy-milter and Postfix with each other.

Integrate using a TCP socket

Set dkimpy_socket: inet in the hosts vars file to let dkimpy-milter listen on a TCP socket for communication with Postfix:

dkimpy_socket: inet

Setting this option causes this ansible role to change how dkimpy-milter integrates as follows:

  • It listens on TCP address 127.0.0.1.

  • It listens on port 8892.

If you want to adapt these setting to suit your needs override the following options in your hosts vars file:

dkimpy_socket_tcp_port (default: 8892)

Setting this option will cause dkimpy-milter to listen on this port.

dkimpy_socket_tcp_type (default: ipv4)

By default dkimpy-milter expects an IPv4 address. To let it listen on an IPv6 address, specify ipv6 as dkimpy_socket_tcp_type.

dkimpy_socket_tcp_address (default: 127.0.0.1)

Setting this option will cause dkimpy-milter to listen on this address.

🔥

Do not expose the socket to a public network unless you have other means in place that will restrict access.

Once you’ve rolled out the changes you can start configuring Postfix. Assuming all messages entering the Postfix mail system via the Postfix smtpd SMTP server and you haven’t modified the port or address dkimpy-milter listens on add the following option to the smtpd_milters parameter in Postfix main.cf configuration file:

smtpd_milters =
    inet:127.0.0.1:8892

After a systemctl reload postfix integration has been completed and dkimpy-milter will begin to sign messages.

Integrate using a local UNIX socket

Set dkimpy_socket: local_postfix in the hosts vars file to prepare dkimpy-milter for integration with the Postfix MTA.

dkimpy_socket: local_postfix

Setting this option causes this ansible role to change how dkimpy-milter integrates as follows:

  • The role will create a subdirectory dkimpy-milter in Postfix' main instances spool directory /var/spool/postfix.

  • The directory will be accessible only to the user dkimpy-milter runs as and the group Postfix runs as (see: postfix_group in the defaults).

  • dkimpy-milter will establish a socket named dkimpy-milter.sock in the newly created subdirectory.

  • The socket will have the permissions 0777 to allow the Postfix user to read/write to it.

After these changes the role will restart dkimpy-milter and the socket will be established in the new directory:

# ls -la /var/spool/postfix/dkimpy-milter/
total 8
drwx--x---  2 dkimpy-milter postfix       4096 Jan  2 20:46 .
drwxr-xr-x 17 root          root          4096 Jan  2 20:19 ..
srwxrwxrwx  1 dkimpy-milter dkimpy-milter    0 Jan  2 20:46 dkimpy-milter.sock

Now configure Postfix to use dkimpy-milter as a Milter service. Assuming all messages entering the Postfix mail system via the Postfix smtpd SMTP server add the following option to the smtpd_milters parameter in Postfix main.cf configuration file:

smtpd_milters =
    unix:dkimpy-milter/dkimpy-milter.sock

After a systemctl reload postfix integration has been completed and dkimpy-milter will begin to sign messages.

ℹ️

Specifying a relative path as shown above will work in a non-chroot and in a chrooted Postfix environment.

About

An ansible role for dkimpy-milter

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published