Optimized DKIM and DNS for mailu

This commit is contained in:
Kevin Veen-Birkenbach 2025-04-29 15:49:06 +02:00
parent 0f12ffd513
commit f4db4ca6ea
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
10 changed files with 196 additions and 3 deletions

View File

@ -6,7 +6,7 @@ Use the following tools to monitor your domain and email deliverability:
- [Google Postmaster](https://postmaster.google.com/) - Analyzes deliverability and spam issues for Gmail.
- [Yahoo Postmaster](https://postmaster.yahooinc.com) - Provides insights and delivery reports for Yahoo.
- [mxtoolbox.com](https://mxtoolbox.com)
- [MXToolbox](https://mxtoolbox.com)
## Blacklist Check 🚫

View File

@ -0,0 +1,2 @@
# Todos
- Check if DKIM generation works on new setups

View File

@ -0,0 +1,13 @@
- name: Generate DKIM key
command: >
docker compose exec -T antispam
rspamadm dkim_keygen -s dkim -d {{ applications[application_id].domain }} -k {{ applications[application_id].domain }}.dkim.key
register: dkim_keygen_output
chdir: "{{ docker_compose.directories.instance }}"
- name: Extract DKIM record from parentheses
set_fact:
mailu_dkim_public_key: >-
{{ dkim_keygen_output.stdout
| regex_search('(?s)\((.*?)\)', '\\1')
| default('') }}

View File

@ -42,7 +42,26 @@
loop_var: item
when: run_once_docker_mailu is not defined
- name: Run the docker_mailu tasks once
- name: "Load Mailu DNS variables"
include_vars: vars/mailu-dns.yml
when: dns_provider == 'cloudflare'
- name: Generate DKIM public key
include_tasks: generate-and-read-dkim.yml
when:
- not applications[application_id].credentials.dkim_public_key is defined
- name: Set DKIM public key
set_fact:
mailu_dkim_public_key: >-
{{ applications[application_id].credentials.dkim_public_key }}
when: applications[application_id].credentials.dkim_public_key is defined
- name: Set Mailu DNS records
include_tasks: set-mailu-dns-records.yml
when: dns_provider == 'cloudflare'
- name: Run the docker_mailu roles once
set_fact:
run_once_docker_mailu: true
when: run_once_docker_mailu is not defined

View File

@ -0,0 +1,80 @@
- name: "Set A record for mail server"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: A
name: "{{ domain }}"
content: "{{ mailu_dns_ip }}"
proxied: false
ttl: 3600
state: present
- name: "Set CNAME record for autoconfig"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: CNAME
name: "autoconfig.{{ mailu_dns_zone }}"
value: "{{ domain }}"
proxied: false
ttl: 3600
state: present
- name: "Set MX record"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: MX
name: "{{ mailu_dns_zone }}"
value: "{{ domain }}"
priority: 10
ttl: 3600
state: present
- name: "Set SRV records"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: SRV
name: "_{{ item.key }}._tcp"
data:
service: "_{{ item.key }}"
proto: "_tcp"
name: "{{ mailu_dns_zone }}"
priority: "{{ item.value.priority }}"
weight: "{{ item.value.weight }}"
port: "{{ item.value.port }}"
target: "{{ domain }}"
ttl: 3600
state: present
loop: "{{ mailu_dns_srv_records | dict2items }}"
- name: "Set SPF TXT record"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: TXT
name: "{{ mailu_dns_zone }}"
value: "v=spf1 mx a:{{ domain }} ~all"
ttl: 3600
state: present
- name: "Set DMARC TXT record"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: TXT
name: "_dmarc.{{ mailu_dns_zone }}"
value: "v=DMARC1; p=reject; ruf=mailto:{{ mailu_dmarc_ruf }}; adkim=s; aspf=s"
ttl: 3600
state: present
- name: "Set DKIM TXT record"
community.general.cloudflare_dns:
api_token: "{{ cloudflare_record_api_token }}"
zone: "{{ mailu_dns_zone }}"
type: TXT
name: "dkim._domainkey.{{ mailu_dns_zone }}"
value: "{{ mailu_dkim_public_key }}"
ttl: 3600
state: present

View File

@ -0,0 +1,38 @@
# vars/mailu-dns.yml
mailu_dns_zone: "{{ applications[application_id].domain }}"
mailu_dns_ip: "{{ networks.internet.ip4 }}"
cloudflare_record_api_token: "{{ certbot_dns_api_token }}"
mailu_dmarc_ruf: "{{ applications[application_id].users.administrator.email }}"
mailu_dns_srv_records:
submission:
port: 587
priority: 20
weight: 1
submissions:
port: 465
priority: 20
weight: 1
imaps:
port: 993
priority: 20
weight: 1
imap:
port: 143
priority: 20
weight: 1
pop3s:
port: 995
priority: 20
weight: 1
pop3:
port: 110
priority: 20
weight: 1
autodiscover:
port: 443
priority: 20
weight: 1

View File

@ -5,6 +5,12 @@
notify: restart nginx
when: run_once_letsencrypt is not defined
- name: "Set CAA records for all base domains"
include_tasks: set-caa-records.yml
when:
- dns_provider == 'cloudflare'
- run_once_letsencrypt is not defined
- name: flush nginx service
meta: flush_handlers
when: run_once_letsencrypt is not defined

View File

@ -0,0 +1,28 @@
---
# tasks/main.yml
# Creates and sets CAA records (issue, issuewild, iodef) for all base domains
- name: "Define CAA entries"
set_fact:
caa_entries:
- tag: issue
value: "letsencrypt.org"
- tag: issuewild
value: "letsencrypt.org"
- tag: iodef
value: "mailto:{{ users.administrator.email }}"
- name: "Ensure all CAA records are present"
community.general.cloudflare_dns:
api_token: "{{ certbot_dns_api_token }}"
zone: "{{ item.0 }}"
record: "@"
type: CAA
flag: 0
tag: "{{ item.1.tag }}"
value: "{{ item.1.value }}"
ttl: 1
state: present
loop: "{{ base_sld_domains | product(caa_entries) | list }}"
loop_control:
label: "{{ item.0 }} → {{ item.1.tag }}"

View File

@ -71,6 +71,10 @@
+ (redirect_domain_mappings | map(attribute='source') | list)
}}
- name: Extract sld.tld from base_domains
set_fact:
base_sld_domains: "{{ base_domains | map('regex_replace', '^(?:.*\\.)?([^.]+\\.[^.]+)$', '\\1') | list | unique | sort }}"
- name: Initialise all_domains as empty list
set_fact:
all_domains: []

View File

@ -319,7 +319,9 @@ defaults_applications:
mailu:
version: "2024.06" # Docker Image Version
setup: false # Set true in inventory file to execute the setup and initializing procedures
users:
adminsitrator:
email: "{{users.administrator.email}}" # Administrator Email for DNS Records
oidc:
email_by_username: true # If true, then the mail is set by the username. If wrong then the OIDC user email is used
enable_user_creation: true # Users will be created if not existing
@ -329,6 +331,7 @@ defaults_applications:
# database_password: # Needs to be set in inventory file
# api_token: # Configures the authentication token. The minimum length is 3 characters. This is a mandatory setting for using the RESTful API.
# initial_administrator_password: # Initial administrator password for setup
# dkim_public_key: # Must be set in inventory file
{% endraw %}{{ features.render_features({
'matomo': true,
'css': true,