From b56ca3899d8765c4847d2226b5a75f21a8950710 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 9 May 2023 23:01:57 +0200 Subject: [PATCH] Add `send_only_mailserver` role and deploy Send-Only-Mailserver with it Co-authored-by: yuri --- .../z9/host_vars/send-only-mailserver.yaml | 8 +++ inventories/z9/hosts.yaml | 3 + playbooks/deploy_send_only_mailserver.yaml | 6 ++ .../roles/send_only_mail_server/README.md | 35 ++++++++++ .../files/etc_rspamd_settings.conf | 7 ++ .../send_only_mail_server/handlers/main.yaml | 11 ++++ .../meta/argument_specs.yaml | 31 +++++++++ .../send_only_mail_server/meta/main.yaml | 7 ++ .../tasks/ensure_dkim_keypair.yaml | 55 ++++++++++++++++ .../send_only_mail_server/tasks/main.yaml | 65 +++++++++++++++++++ .../templates/etc_rspamd_dkim_signing.conf.j2 | 12 ++++ .../templates/etc_smtpd.conf.j2 | 15 +++++ ...nsupdate_add_dkim_public_key_txt_record.j2 | 4 ++ ...date_delete_dkim_public_key_txt_records.j2 | 4 ++ 14 files changed, 263 insertions(+) create mode 100644 inventories/z9/host_vars/send-only-mailserver.yaml create mode 100644 playbooks/deploy_send_only_mailserver.yaml create mode 100644 playbooks/roles/send_only_mail_server/README.md create mode 100644 playbooks/roles/send_only_mail_server/files/etc_rspamd_settings.conf create mode 100644 playbooks/roles/send_only_mail_server/handlers/main.yaml create mode 100644 playbooks/roles/send_only_mail_server/meta/argument_specs.yaml create mode 100644 playbooks/roles/send_only_mail_server/meta/main.yaml create mode 100644 playbooks/roles/send_only_mail_server/tasks/ensure_dkim_keypair.yaml create mode 100644 playbooks/roles/send_only_mail_server/tasks/main.yaml create mode 100644 playbooks/roles/send_only_mail_server/templates/etc_rspamd_dkim_signing.conf.j2 create mode 100644 playbooks/roles/send_only_mail_server/templates/etc_smtpd.conf.j2 create mode 100644 playbooks/roles/send_only_mail_server/templates/nsupdate_add_dkim_public_key_txt_record.j2 create mode 100644 playbooks/roles/send_only_mail_server/templates/nsupdate_delete_dkim_public_key_txt_records.j2 diff --git a/inventories/z9/host_vars/send-only-mailserver.yaml b/inventories/z9/host_vars/send-only-mailserver.yaml new file mode 100644 index 0000000..a57c477 --- /dev/null +++ b/inventories/z9/host_vars/send-only-mailserver.yaml @@ -0,0 +1,8 @@ +send_only_mail_server__mail_server_fqdn: send-only-mailserver.ccchh.net +send_only_mail_server__mail_server_fqdn_zone: ccchh.net +send_only_mail_server__mail_domains: + - name: send-only-mail.ccchh.net + zone: ccchh.net +send_only_mail_server__bind_9_host: authoritative-dns + +cert__acme_account_email: j+letsencrypt-ccchh@jsts.xyz diff --git a/inventories/z9/hosts.yaml b/inventories/z9/hosts.yaml index 659635d..1ba3d2f 100644 --- a/inventories/z9/hosts.yaml +++ b/inventories/z9/hosts.yaml @@ -27,3 +27,6 @@ all: keycloak: ansible_host: keycloak.z9.ccchh.net ansible_user: chaos + send-only-mailserver: + ansible_host: send-only-mailserver.z9.ccchh.net + ansible_user: chaos diff --git a/playbooks/deploy_send_only_mailserver.yaml b/playbooks/deploy_send_only_mailserver.yaml new file mode 100644 index 0000000..3fd9667 --- /dev/null +++ b/playbooks/deploy_send_only_mailserver.yaml @@ -0,0 +1,6 @@ +--- +- name: Deploy the Send-Only-Mailserver + hosts: send-only-mailserver + become: true + roles: + - send_only_mail_server diff --git a/playbooks/roles/send_only_mail_server/README.md b/playbooks/roles/send_only_mail_server/README.md new file mode 100644 index 0000000..f391786 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/README.md @@ -0,0 +1,35 @@ +# Role `send_only_mail_server` + +Makes sure a send-only mail server is deployed using OpenSMTPD and Rspamd for DKIM signing. + +Make sure to manually set a DMARC record and MX record for the mail domains. + +## Supported Distributions + +The following distributions are supported: + +- Debian 11 + +## Required Arguments + +For the required arguments look at the [`argument_specs.yaml`](./meta/argument_specs.yml) + +Also make sure to set the following for the `cert` role dependency: + +- `cert__acme_account_email` + +## Updates + +This role doesn't handle updates. +However it uses the system package manager for installing all the packages, so when you're making sure the system packages are up-to-date, you're handling updates for the packages installed by this role as well. + +## `hosts` + +The `hosts` for this role need to be the machines on which you want to deploy a mail server. + +## Links & Resources + +- +- +- +- diff --git a/playbooks/roles/send_only_mail_server/files/etc_rspamd_settings.conf b/playbooks/roles/send_only_mail_server/files/etc_rspamd_settings.conf new file mode 100644 index 0000000..a117504 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/files/etc_rspamd_settings.conf @@ -0,0 +1,7 @@ +dkim_signing { + id = "dkim_signing"; + apply { + symbols_enabled = ["DKIM_SIGNED"]; + flags = ["skip_process"]; # Disable expensive MIME processing + } +} diff --git a/playbooks/roles/send_only_mail_server/handlers/main.yaml b/playbooks/roles/send_only_mail_server/handlers/main.yaml new file mode 100644 index 0000000..852d66f --- /dev/null +++ b/playbooks/roles/send_only_mail_server/handlers/main.yaml @@ -0,0 +1,11 @@ +- name: Restart `opensmtpd.service` + ansible.builtin.systemd: + name: opensmtpd.service + state: restarted + become: true + +- name: Restart `rspamd.service` + ansible.builtin.systemd: + name: rspamd.service + state: restarted + become: true diff --git a/playbooks/roles/send_only_mail_server/meta/argument_specs.yaml b/playbooks/roles/send_only_mail_server/meta/argument_specs.yaml new file mode 100644 index 0000000..bcf83d9 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/meta/argument_specs.yaml @@ -0,0 +1,31 @@ +argument_specs: + main: + options: + send_only_mail_server__mail_server_fqdn: + description: The FQDN of the mail server host itself. + type: str + required: true + send_only_mail_server__mail_server_fqdn_zone: + description: > + The DNS zone on the BIND 9 server for records for the mail server host + FQDN. + type: str + required: true + send_only_mail_server__mail_domains: + description: The domains the mail server should send mails for. + type: list + elements: dict + required: true + options: + name: + description: The domain name. + type: str + required: true + zone: + description: The DNS zone on the BIND 9 server. + type: str + required: true + send_only_mail_server__bind_9_host: + description: The machine running BIND 9 to deploy DNS records via. + type: str + required: true diff --git a/playbooks/roles/send_only_mail_server/meta/main.yaml b/playbooks/roles/send_only_mail_server/meta/main.yaml new file mode 100644 index 0000000..f730c82 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/meta/main.yaml @@ -0,0 +1,7 @@ +dependencies: # noqa meta-no-info + - role: distribution_check + vars: + distribution_check__supported_distributions: + - name: Debian + versions: + - "11" diff --git a/playbooks/roles/send_only_mail_server/tasks/ensure_dkim_keypair.yaml b/playbooks/roles/send_only_mail_server/tasks/ensure_dkim_keypair.yaml new file mode 100644 index 0000000..76ad2c6 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/tasks/ensure_dkim_keypair.yaml @@ -0,0 +1,55 @@ +- name: make sure DKIM private key exists + community.crypto.openssl_privatekey: + path: "/etc/mail-dkim/{{ item.name }}.key" + size: 1024 + type: RSA + owner: "root" + group: "_rspamd" + mode: "0640" + become: true + notify: Restart `rspamd.service` + +- name: make sure DKIM public key exists + community.crypto.openssl_publickey: + path: "/etc/mail-dkim/{{ item.name }}.pub" + privatekey_path: "/etc/mail-dkim/{{ item.name }}.key" + return_content: true + become: true + notify: Restart `rspamd.service` + register: send_only_mail_server__dkim_public_key + +- name: deploy DKIM public key DNS entry # noqa: no-handler + delegate_to: "{{ send_only_mail_server__bind_9_host }}" + when: send_only_mail_server__dkim_public_key.changed + block: + - name: Add file containing nsupdate commands for removing DKIM public key TXT records + ansible.builtin.template: + src: nsupdate_delete_dkim_public_key_txt_records.j2 + dest: /root/nsupdate_delete_dkim_public_key_txt_records + owner: root + group: root + mode: "0600" + + - name: Remove DNS records from BIND 9 server via nsupdate # noqa: no-changed-when + ansible.builtin.command: /usr/bin/nsupdate -l /root/nsupdate_delete_dkim_public_key_txt_records + + - name: Add file containing nsupdate commands for adding DKIM public key TXT record + ansible.builtin.template: + src: nsupdate_add_dkim_public_key_txt_record.j2 + dest: /root/nsupdate_add_dkim_public_key_txt_record + owner: root + group: root + mode: "0600" + + - name: Add DNS record to BIND 9 server via nsupdate # noqa: no-changed-when + ansible.builtin.command: /usr/bin/nsupdate -l /root/nsupdate_add_dkim_public_key_txt_record + always: + - name: Remove file containing nsupdate commands for removing DKIM public key TXT records again + ansible.builtin.file: + path: /root/nsupdate_delete_dkim_public_key_txt_records + state: absent + + - name: Remove file containing nsupdate commands for adding DKIM public key TXT record again + ansible.builtin.file: + path: /root/nsupdate_add_dkim_public_key_txt_record + state: absent diff --git a/playbooks/roles/send_only_mail_server/tasks/main.yaml b/playbooks/roles/send_only_mail_server/tasks/main.yaml new file mode 100644 index 0000000..d88cec0 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/tasks/main.yaml @@ -0,0 +1,65 @@ +- name: make sure packages are installed + ansible.builtin.apt: + name: + - opensmtpd + - rspamd + - opensmtpd-filter-rspamd + become: true + +- name: make sure certificates exist + ansible.builtin.include_role: + name: cert + vars: + cert__domains: + - "{{ send_only_mail_server__mail_server_fqdn }}" + cert__owner: root + cert__group: opensmtpd + cert__bind_9_zone: "{{ send_only_mail_server__mail_server_fqdn_zone }}" + cert__bind_9_host: "{{ send_only_mail_server__bind_9_host }}" + cert__privkey_pem_permissions: "0640" + cert__fullchain_pem_permissions: "0640" + cert__chain_pem_permissions: "0640" + cert__cert_pem_permissions: "0640" + +- name: make sure the OpenSMTPD config is deployed + ansible.builtin.template: + src: etc_smtpd.conf.j2 + dest: /etc/smtpd.conf + owner: root + group: root + mode: "0600" + become: true + notify: Restart `opensmtpd.service` + +- name: make sure `/etc/mail-dkim` directory exists + ansible.builtin.file: + path: /etc/mail-dkim + state: directory + owner: root + group: root + mode: "755" + become: true + +- name: make sure DKIM keypairs for all domains exist + loop: "{{ send_only_mail_server__mail_domains }}" + ansible.builtin.include_tasks: ensure_dkim_keypair.yaml + +- name: make sure the Rspamd `dkim_signing.conf` is deployed + ansible.builtin.template: + src: etc_rspamd_dkim_signing.conf.j2 + dest: /etc/rspamd/local.d/dkim_signing.conf + owner: root + group: root + mode: "0600" + become: true + notify: Restart `rspamd.service` + +- name: make sure the Rspamd `settings.conf` is deployed + ansible.builtin.copy: + src: etc_rspamd_settings.conf + dest: /etc/rspamd/local.d/settings.conf + owner: root + group: root + mode: "0600" + become: true + notify: Restart `rspamd.service` diff --git a/playbooks/roles/send_only_mail_server/templates/etc_rspamd_dkim_signing.conf.j2 b/playbooks/roles/send_only_mail_server/templates/etc_rspamd_dkim_signing.conf.j2 new file mode 100644 index 0000000..8872c20 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/templates/etc_rspamd_dkim_signing.conf.j2 @@ -0,0 +1,12 @@ +allow_username_mismatch = true; + +use_esld = false; + +domain { +{% for mail_domain in send_only_mail_server__mail_domains %} + {{ mail_domain.name }} { + path = "/etc/mail-dkim/{{ mail_domain.name }}.key"; + selector = "key"; + } +{% endfor %} +} diff --git a/playbooks/roles/send_only_mail_server/templates/etc_smtpd.conf.j2 b/playbooks/roles/send_only_mail_server/templates/etc_smtpd.conf.j2 new file mode 100644 index 0000000..d148fc2 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/templates/etc_smtpd.conf.j2 @@ -0,0 +1,15 @@ +# Managed by Ansible. +# This configuration enables sending emails using this server, but to not receiving any. + +pki {{ send_only_mail_server__mail_server_fqdn }} cert "/etc/ansible_certs/certs/{{ send_only_mail_server__mail_server_fqdn }}/fullchain.pem" +pki {{ send_only_mail_server__mail_server_fqdn }} key "/etc/ansible_certs/certs/{{ send_only_mail_server__mail_server_fqdn }}/privkey.pem" + +filter "rspamd-dkim-signing" proc-exec "filter-rspamd -settings-id dkim_signing" + +listen on lo +listen on eth0 smtps pki {{ send_only_mail_server__mail_server_fqdn }} auth filter "rspamd-dkim-signing" +listen on eth0 tls-require pki {{ send_only_mail_server__mail_server_fqdn }} auth filter "rspamd-dkim-signing" + +action "outbound" relay helo {{ send_only_mail_server__mail_server_fqdn }} + +match from any auth for any action "outbound" diff --git a/playbooks/roles/send_only_mail_server/templates/nsupdate_add_dkim_public_key_txt_record.j2 b/playbooks/roles/send_only_mail_server/templates/nsupdate_add_dkim_public_key_txt_record.j2 new file mode 100644 index 0000000..37977f9 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/templates/nsupdate_add_dkim_public_key_txt_record.j2 @@ -0,0 +1,4 @@ +debug +zone {{ item.zone }} +update add key._domainkey.{{ item.name }} 60 TXT v=DKIM1;k=rsa;p={{ send_only_mail_server__dkim_public_key.publickey | replace('\n', '') | replace('-----BEGIN PUBLIC KEY-----', '') | replace('-----END PUBLIC KEY-----', '') }} +send diff --git a/playbooks/roles/send_only_mail_server/templates/nsupdate_delete_dkim_public_key_txt_records.j2 b/playbooks/roles/send_only_mail_server/templates/nsupdate_delete_dkim_public_key_txt_records.j2 new file mode 100644 index 0000000..94695a6 --- /dev/null +++ b/playbooks/roles/send_only_mail_server/templates/nsupdate_delete_dkim_public_key_txt_records.j2 @@ -0,0 +1,4 @@ +debug +zone {{ item.zone }} +update delete key._domainkey.{{ item.name }} TXT +send