This ansible role installs and configures
dkimpy-milter. It provides means to create
and use rsa and ed25519 type DKIM keys.
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-roleAfter 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.ymlExecuted 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)-
All tables, keys and dns-files are located below
/usr/local/etc/dkim. located in/usr/local/etc/. -
Once you’ve configured the
dkimpy-roleto create keys, they will be stored in this directory. -
The dkimpy-milter systemd unit file expects
dkimpy-milter.confto be located in/usr/local/etc/.
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-
The dictionary
rsa_keyscontrolsrsatype DKIM keys -
Use a unique dictionary key (here:
netkey2as a successor ofnetkey) for your DKIM keys. They must be unique or only the last one will be used. -
A DKIM key set to
enabled: nowill be created, but not added to theSigning-Table orKeyTable-Tables (see also: [_dkim_rollout_process]). -
The dictionary
rsa_keyscontrolsrsatype DKIM keys.
Here’s what the options of a DKIM key control:
- sender
-
The
senderoption assigns the key to a specific sender or senderdomain. - enabled
-
The
enabledoption controls if a key will be used to sign a message that matches thesender. - selector
-
The
selectoroption 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.confThe SigningTable and the KeyTable will map the sender or senderdomains
accordingly. The SigningTable maps senders or senderdomains to specific
identifiers:
# 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:
# 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. |
-
Specify the key(s) in your hosts var file but don’t enable them.
-
Let
dkimpy-rolecreate the keys. -
Add the values from the files ending on
dnsto the domains DNS. -
Verify the DKIM pubkeys exist in the domains DNS.
-
Set the key to
enabled: yesonce you want to use them.
|
❗
|
Make sure only one key for a sender or senderdomain is enabled at a time. |
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: postfixYou 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.
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: inetwill configuredkimpy-milterto create a TCP socket that listens on port 8892 on the IPv4 localhost interface. - local (default)
-
Setting
dkimpy_socket: localwill configuredkimpy-milterto create a local UNIX socket located at/run/dkimpy-milter/dkimpy-milter.sock. - local_postfix
-
Setting
dkimpy_socket: local_postfixwill configuredkimpy-milterto create a local UNIX socket located at/var/spool/postfix/dkimpy-milter/dkimpy-milter.sock.
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.
Set dkimpy_socket: inet in the hosts vars file to let dkimpy-milter listen
on a TCP socket for communication with Postfix:
dkimpy_socket: inetSetting 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-milterto listen on this port. - dkimpy_socket_tcp_type (default: ipv4)
-
By default
dkimpy-milterexpects an IPv4 address. To let it listen on an IPv6 address, specifyipv6asdkimpy_socket_tcp_type. - dkimpy_socket_tcp_address (default: 127.0.0.1)
-
Setting this option will cause
dkimpy-milterto 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:8892After a systemctl reload postfix integration has been completed and
dkimpy-milter will begin to sign messages.
Set dkimpy_socket: local_postfix in the hosts vars file to prepare
dkimpy-milter for integration with the Postfix MTA.
dkimpy_socket: local_postfixSetting this option causes this ansible role to change how dkimpy-milter
integrates as follows:
-
The role will create a subdirectory
dkimpy-milterin Postfix' main instances spool directory/var/spool/postfix. -
The directory will be accessible only to the user
dkimpy-milterruns as and the group Postfix runs as (see:postfix_groupin the defaults). -
dkimpy-milterwill establish a socket nameddkimpy-milter.sockin the newly created subdirectory. -
The socket will have the permissions
0777to 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.sockNow 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.sockAfter 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. |