diff --git a/roles/docker-mailu/Spam_Issues.md b/roles/docker-mailu/Spam_Issues.md index d4b8ff0c..f5d14166 100644 --- a/roles/docker-mailu/Spam_Issues.md +++ b/roles/docker-mailu/Spam_Issues.md @@ -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 🚫 diff --git a/roles/docker-mailu/tasks/TODO.md b/roles/docker-mailu/tasks/TODO.md new file mode 100644 index 00000000..92d9c454 --- /dev/null +++ b/roles/docker-mailu/tasks/TODO.md @@ -0,0 +1,2 @@ +# Todos +- Check if DKIM generation works on new setups \ No newline at end of file diff --git a/roles/docker-mailu/tasks/generate-and-read-dkim.yml b/roles/docker-mailu/tasks/generate-and-read-dkim.yml new file mode 100644 index 00000000..f1a8c7a6 --- /dev/null +++ b/roles/docker-mailu/tasks/generate-and-read-dkim.yml @@ -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('') }} \ No newline at end of file diff --git a/roles/docker-mailu/tasks/main.yml b/roles/docker-mailu/tasks/main.yml index d9c8ebb7..41ea93dd 100644 --- a/roles/docker-mailu/tasks/main.yml +++ b/roles/docker-mailu/tasks/main.yml @@ -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 \ No newline at end of file diff --git a/roles/docker-mailu/tasks/set-mailu-dns-records.yml b/roles/docker-mailu/tasks/set-mailu-dns-records.yml new file mode 100644 index 00000000..b44e07ce --- /dev/null +++ b/roles/docker-mailu/tasks/set-mailu-dns-records.yml @@ -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 \ No newline at end of file diff --git a/roles/docker-mailu/vars/mailu-dns.yml b/roles/docker-mailu/vars/mailu-dns.yml new file mode 100644 index 00000000..faadec6b --- /dev/null +++ b/roles/docker-mailu/vars/mailu-dns.yml @@ -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 \ No newline at end of file diff --git a/roles/letsencrypt/tasks/main.yml b/roles/letsencrypt/tasks/main.yml index 131dff61..b8db39d8 100644 --- a/roles/letsencrypt/tasks/main.yml +++ b/roles/letsencrypt/tasks/main.yml @@ -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 diff --git a/roles/letsencrypt/tasks/set-caa-records.yml b/roles/letsencrypt/tasks/set-caa-records.yml new file mode 100644 index 00000000..0c0271af --- /dev/null +++ b/roles/letsencrypt/tasks/set-caa-records.yml @@ -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 }}" \ No newline at end of file diff --git a/tasks/constructor.yml b/tasks/constructor.yml index 9262bda0..2ece37bf 100644 --- a/tasks/constructor.yml +++ b/tasks/constructor.yml @@ -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: [] diff --git a/templates/vars/applications.yml.j2 b/templates/vars/applications.yml.j2 index 3bb0f7ce..8586ecb2 100644 --- a/templates/vars/applications.yml.j2 +++ b/templates/vars/applications.yml.j2 @@ -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,