diff --git a/group_vars/mail b/group_vars/mail index c3f0e9f..3ac22c5 100644 --- a/group_vars/mail +++ b/group_vars/mail @@ -1,3 +1,67 @@ dovecot_users: ccchb.de: - crest: '{BLF-CRYPT}$2y$05$KcRfdL6C5DjSFZST5x5mo.JiEBz2.nGOl9UuKc39vHBdEBbm6udGu' + crest: '{BLF-CRYPT}$2y$05$Yz0c1tu.a3.SFxgCAkJb9O3aIK8vxMCTrxLzjAs8HFf2ySW4zs2J.' + deelkar: '{BLF-CRYPT}$2y$05$tzLicykk.2DfjPZkQTZAJOuUgCtsnL5f1BgkJSJ7YbSOZ5DWRPP/y' + jpa: '{BLF-CRYPT}$2y$05$/Q7zyzR3XzGajZQ5BCYObOlKPVYqonF7fjup0ZuJzSFPO3TZoPrXm' + inj4n: '{BLF-CRYPT}$2y$05$h34aPYEN5/p2AnzPtg6wweh14RdPl9ZpEuqyahkl6s3oYWVwzggvi' + +mlmmj_lists: + - name: 'vorstand' + domain: 'lists.ccchb.de' + description: CCC Bremen Vorstand Mailingliste + owner: crest@ccchb.de + addresses: + - vorstand@lists.ccchb.de + - vorstand@ccchb.de + subscription_moderated: yes + prefix: '[CCCHB Vorstand]' + + - name: ccc + domain: lists.ccchb.de + description: CCC Bremen Mailinglist + owner: crest@ccchb.de + addresses: + - ccc@lists.ccchb.de + - ccc@ccchb.de + subscription_moderated: no + prefix: '[CCCHB]' + + - name: announce + domain: lists.ccchb.de + description: CCC Bremen Announce Mailinglist + owner: crest@ccchb.de + addresses: + - announce@lists.ccchb.de + - announce@ccchb.de + subscription_moderated: no + prefix: '[CCCHB Announce]' + + - name: haecksen + domain: lists.ccchb.de + description: CCC Bremen Haecksen Mailinglist + owner: crest@ccchb.de + addresses: + - haecksen@lists.ccchb.de + - haecksen@ccchb.de + subscription_moderated: no + prefix: '[CCCHB Haecksen]' + + - name: mitglieder + domain: lists.ccchb.de + description: CCC Bremen Mitglieder Mailinglist + owner: crest@ccchb.de + addresses: + - mitglieder@lists.ccchb.de + - mitglieder@ccchb.de + subscription_moderated: yes + prefix: '[CCCHB Mitglieder]' + + - name: presse + domain: lists.ccchb.de + description: CCC Bremen Presse Mailinglist + owner: crest@ccchb.de + addresses: + - presse@lists.ccchb.de + - presse@ccchb.de + subscription_moderated: yes + prefix: '[CCCHB Presse]' diff --git a/mail.yml b/mail.yml index 752d026..6343982 100644 --- a/mail.yml +++ b/mail.yml @@ -19,3 +19,4 @@ - dovecot - rspamd - postfix + - mlmmj diff --git a/roles/mlmmj/handlers/main.yml b/roles/mlmmj/handlers/main.yml new file mode 100644 index 0000000..7a2797a --- /dev/null +++ b/roles/mlmmj/handlers/main.yml @@ -0,0 +1,20 @@ +--- +- name: Restart Postfix + command: s6-svc -wU -T 5000 -ru /run/service/postfix + +- name: Reload Postfix + command: s6-svc -h /run/service/postfix + +- name: Rebuild mlmmj virtual aliases + command: postmap hash:/usr/local/etc/postfix/virtual_mlmmj + +- name: Rebuild mlmmj transport map + command: postmap hash:/usr/local/etc/postfix/transport_mlmmj + +- name: Reload s6-rc + service: + name: s6-rc + state: reloaded + +- name: Restart mlmmj-maintd + command: s6-svc -wU -T 5000 -ru /run/service/mlmmj-maintd diff --git a/roles/mlmmj/tasks/main.yml b/roles/mlmmj/tasks/main.yml new file mode 100644 index 0000000..1718cbd --- /dev/null +++ b/roles/mlmmj/tasks/main.yml @@ -0,0 +1,280 @@ +--- +- name: Install mlmmj + package: + name: + - mlmmj + state: present + +- name: Create mlmmj group + group: + name: mlmmj + gid: 20003 + +- name: Create mlmmj user + user: + name: mlmmj + uid: 20003 + group: mlmmj + create_home: no + home: /var/vmail/mlmmj + shell: /sbin/nologin + + +- name: Create lists directory + file: + path: /var/vmail/mlmmj + state: directory + owner: mlmmj + group: mlmmj + +- name: Create archive directory + file: + path: /var/vmail/mlmmj-archive + state: directory + owner: mlmmj + group: mlmmj + +- name: Create mlmmj postfix service + lineinfile: + path: /usr/local/etc/postfix/master.cf + regexp: '^{{ transport_service.name }} +{{ transport_service.type }}' + value: '{{ transport_service.value }}' + notify: + - Restart Postfix + +- name: Configure Postfix + postconf: + name: '{{ item.name }}' + value: '{{ item.value | default(omit) }}' + state: '{{ item.state | default(omit) }}' + with_items: '{{ postfix_config }}' + notify: + - Reload Postfix + +- name: Generate mlmmj virtual aliases + template: + dest: /usr/local/etc/postfix/virtual_mlmmj + src: virtual_mlmmj.j2 + mode: 0444 + owner: root + group: wheel + notify: + - Rebuild mlmmj virtual aliases + +- name: Generate mlmmj transport map + template: + dest: /usr/local/etc/postfix/transport_mlmmj + src: transport_mlmmj.j2 + mode: 0444 + owner: root + group: wheel + notify: + - Rebuild mlmmj transport map + +- name: Create mlmmj-maintd service directories + file: + path: '/etc/s6-rc/service/{{ item }}' + state: directory + owner: root + group: wheel + mode: 0755 + with_items: '{{ mlmmj_service_dirs }}' + notify: + - Reload s6-rc + - Restart mlmmj-maintd + +- name: Generate mlmmj-maintd service scripts + template: + dest: '/etc/s6-rc/service/{{ item }}' + src: '{{ item }}.j2' + mode: 0555 + owner: root + group: wheel + with_items: '{{ mlmmj_service_scripts }}' + notify: + - Reload s6-rc + - Restart mlmmj-maintd + +- name: Generate mlmmj-maintd service configuration + copy: + dest: '/etc/s6-rc/service/{{ item.name }}' + content: '{{ item.content }}' + mode: 0444 + owner: root + group: wheel + loop_control: + label: '{{ item.name }} = {{ item.content }}' + with_items: '{{ mlmmj_service_config }}' + notify: + - Reload s6-rc + - Restart mlmmj-maintd + +- name: Flush handlers + meta: flush_handlers + +- name: Start mlmmj-maintd + command: fdmove -c 2 1 s6-rc -u -v 2 change mlmmj-maintd + register: change + changed_when: change.stdout | length > 0 + +- name: Enable mlmmj-maintd + lineinfile: + path: /etc/s6-rc/service/enabled/contents + regexp: "^mlmmj-maintd$" + line: "mlmmj-maintd" + notify: + - Reload s6-rc + +- name: Flush handlers (again) + meta: flush_handlers + +- name: Create mailing lists + command: > + mlmmj-make-ml -s /var/vmail/mlmmj -L {{ item.name }} + args: + creates: '/var/vmail/mlmmj/{{ item.name }}/control/listaddress' + stdin: | + {{ item.domain }} + {{ item.owner }} + en + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Change ownership and permissions + file: + path: '/var/vmail/mlmmj/{{ item.name }}' + recurse: yes + owner: mlmmj + group: mlmmj + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Disable subscriber listing + copy: + dest: '/var/vmail/mlmmj/{{ item.name }}/control/nolistsubsemail' + content: '' + owner: mlmmj + group: mlmmj + mode: 0444 + force: no + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Strip these headers from incoming messages + lineinfile: + path: '/var/vmail/mlmmj/{{ item.0.name }}/control/delheaders' + create: yes + owner: mlmmj + group: mlmmj + mode: 0444 + regexp: '^{{ item.1 }}:$' + line: '{{ item.1 }}:' + loop_control: + label: '{{ item.0.name }}@{{ item.0.domain }} : {{ item.1 }}' + with_nested: + - '{{ mlmmj_lists }}' + - '{{ mlmmj_delete_headers }}' + +- name: Add List-Id header + lineinfile: + path: '/var/vmail/mlmmj/{{ item.name }}/control/customheaders' + create: yes + owner: mlmmj + group: mlmmj + mode: 0444 + regexp: '^List-Id:' + line: 'List-Id: {{ item.description }} <{{ item.name }}.{{ item.domain }}>' + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Add List-Subscribe header + lineinfile: + path: '/var/vmail/mlmmj/{{ item.name }}/control/customheaders' + create: yes + owner: mlmmj + group: mlmmj + mode: 0444 + regexp: '^List-Subscribe:' + line: 'List-Subscribe: ' + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Add List-Unsubscribe header + lineinfile: + path: '/var/vmail/mlmmj/{{ item.name }}/control/customheaders' + create: yes + owner: mlmmj + group: mlmmj + mode: 0444 + regexp: '^List-Unsubscribe:' + line: 'List-Unsubscribe: ' + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Add List-Post header + lineinfile: + path: '/var/vmail/mlmmj/{{ item.name }}/control/customheaders' + create: yes + owner: mlmmj + group: mlmmj + mode: 0444 + regexp: '^List-Post:' + line: 'List-Post: ' + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Add List-Help header + lineinfile: + path: '/var/vmail/mlmmj/{{ item.name }}/control/customheaders' + create: yes + owner: mlmmj + group: mlmmj + mode: 0444 + regexp: '^List-Help:' + line: 'List-Help: ' + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Add prefix to subjects + copy: + dest: '/var/vmail/mlmmj/{{ item.name }}/control/prefix' + content: | + {{ item.prefix }} + owner: mlmmj + group: mlmmj + mode: 0444 + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Configure list addresses + template: + dest: '/var/vmail/mlmmj/{{ item.name }}/control/listaddress' + src: listaddress.j2 + owner: mlmmj + group: mlmmj + mode: 0444 + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' + +- name: Moderate subscription + copy: + dest: '/var/vmail/mlmmj/{{ item.name }}/control/submod' + content: '' + force: no + owner: mlmmj + group: mlmmj + mode: 0444 + when: item.subscription_moderated | default(false) + loop_control: + label: '{{ item.name }}@{{ item.domain }}' + with_items: '{{ mlmmj_lists }}' diff --git a/roles/mlmmj/templates/listaddress.j2 b/roles/mlmmj/templates/listaddress.j2 new file mode 100644 index 0000000..d0e4e6d --- /dev/null +++ b/roles/mlmmj/templates/listaddress.j2 @@ -0,0 +1,3 @@ +{% for address in item.addresses %} +{{ address }} +{% endfor %} diff --git a/roles/mlmmj/templates/mlmmj-maintd-log/finish.j2 b/roles/mlmmj/templates/mlmmj-maintd-log/finish.j2 new file mode 100644 index 0000000..00e3945 --- /dev/null +++ b/roles/mlmmj/templates/mlmmj-maintd-log/finish.j2 @@ -0,0 +1,13 @@ +#!/usr/local/bin/execlineb -S2 +# {{ ansible_managed }} + +s6-envdir ./env +multisubstitute { + importas -i -u NAME NAME +} + +fdmove -c 1 2 +ifelse { test "${1}" -eq 0 } { + echo "${NAME}: Stopped." +} + echo "${NAME}: Failed with exit status (${1}, ${2})." diff --git a/roles/mlmmj/templates/mlmmj-maintd-log/run.j2 b/roles/mlmmj/templates/mlmmj-maintd-log/run.j2 new file mode 100644 index 0000000..1804fdb --- /dev/null +++ b/roles/mlmmj/templates/mlmmj-maintd-log/run.j2 @@ -0,0 +1,23 @@ +#!/usr/local/bin/execlineb -P +# {{ ansible_managed }} + +s6-envdir ./env +multisubstitute { + importas -i -u NAME NAME + importas -i -u USER USER + importas -i -u GROUP GROUP + importas -i -u MODE MODE + importas -i -u DIR DIR +} + +foreground { fdmove -c 1 2 echo "${NAME} log: Starting." } + +ifelse -n { install -d -o "${USER}" -g "${GROUP}" -m "${MODE}" "$DIR" } { + foreground { fdmove -c 1 2 echo "${NAME} log: Failed to create logging directory." } + false +} + +fdmove -c 2 1 + +s6-envuidgid $USER +s6-log -d 3 T $DIR diff --git a/roles/mlmmj/templates/mlmmj-maintd/finish.j2 b/roles/mlmmj/templates/mlmmj-maintd/finish.j2 new file mode 100644 index 0000000..82c4216 --- /dev/null +++ b/roles/mlmmj/templates/mlmmj-maintd/finish.j2 @@ -0,0 +1,14 @@ +#!/usr/local/bin/execlineb -S2 +# {{ ansible_managed }} + +s6-envdir ./env +multisubstitute { + importas -i -u NAME NAME +} + +fdmove -c 1 2 +ifelse { test "${1}" -eq 0 } { + echo "${NAME}: Stopped." +} + +echo "${NAME}: Failed with exit status (${1}, ${2})." diff --git a/roles/mlmmj/templates/mlmmj-maintd/run.j2 b/roles/mlmmj/templates/mlmmj-maintd/run.j2 new file mode 100644 index 0000000..5e66b81 --- /dev/null +++ b/roles/mlmmj/templates/mlmmj-maintd/run.j2 @@ -0,0 +1,12 @@ +#!/usr/local/bin/execlineb -P +# {{ ansible_managed }} + +s6-envdir ./env +multisubstitute { + importas -i -u NAME NAME +} + +foreground { fdmove -c 1 2 echo "${NAME}: Starting." } + +fdmove -c 2 1 +snooze -v -H "*" /usr/local/bin/mlmmj-maintd -F -d /var/vmail/mlmmj diff --git a/roles/mlmmj/templates/transport_mlmmj.j2 b/roles/mlmmj/templates/transport_mlmmj.j2 new file mode 100644 index 0000000..03dc8f0 --- /dev/null +++ b/roles/mlmmj/templates/transport_mlmmj.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for list in mlmmj_lists %} +{{ list.domain }}--{{ list.name }}@localhost.mlmmj mlmmj:{{ list.name }} +{% endfor %} diff --git a/roles/mlmmj/templates/virtual_mlmmj.j2 b/roles/mlmmj/templates/virtual_mlmmj.j2 new file mode 100644 index 0000000..d0539cb --- /dev/null +++ b/roles/mlmmj/templates/virtual_mlmmj.j2 @@ -0,0 +1,7 @@ +# {{ ansible_managed }} + +{% for list in mlmmj_lists %} +{% for address in list.addresses %} +{{ address }} {{ list.domain }}--{{ list.name }}@localhost.mlmmj +{% endfor %} +{% endfor %} diff --git a/roles/mlmmj/vars/main.yml b/roles/mlmmj/vars/main.yml new file mode 100644 index 0000000..3500bfc --- /dev/null +++ b/roles/mlmmj/vars/main.yml @@ -0,0 +1,72 @@ +--- +transport_service: + name: mlmmj + type: unix + value: 'mlmmj unix - n n - - pipe flags=ORhu user=mlmmj argv=/usr/local/bin/mlmmj-receive -F -L /var/vmail/mlmmj/$nexthop' + +postfix_config: + - name: mlmmj_destination_recipient_limit + value: 1 + state: present + + - name: transport_maps + value: hash:/usr/local/etc/postfix/transport_mlmmj + state: present + +mlmmj_service_dirs: + - mlmmj-maintd + - mlmmj-maintd/env + - mlmmj-maintd/data + - mlmmj-maintd-log + - mlmmj-maintd-log/env + +mlmmj_service_scripts: + - mlmmj-maintd/run + - mlmmj-maintd/finish + - mlmmj-maintd-log/run + - mlmmj-maintd-log/finish + +mlmmj_service_config: + - name: mlmmj-maintd/type + content: longrun + - name: mlmmj-maintd/dependencies + content: postfix + - name: mlmmj-maintd/env/NAME + content: mlmmj-maintd + - name: mlmmj-maintd/env/PATH + content: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin + - name: mlmmj-maintd/producer-for + content: mlmmj-maintd-log + + - name: mlmmj-maintd-log/type + content: longrun + - name: mlmmj-maintd-log/notification-fd + content: '3' + - name: mlmmj-maintd-log/consumer-for + content: mlmmj-maintd + - name: mlmmj-maintd-log/env/NAME + content: mlmmj-maintd + - name: mlmmj-maintd-log/env/PATH + content: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin + - name: mlmmj-maintd-log/env/MODE + content: '750' + - name: mlmmj-maintd-log/env/USER + content: s6-log + - name: mlmmj-maintd-log/env/GROUP + content: s6-log + - name: mlmmj-maintd-log/env/DIR + content: /var/log/mlmmj-maintd + +mlmmj_delete_headers: + - List-Archive + - List-Help + - List-ID + - List-Owner + - List-Post + - List-Subscribe + - List-Unsubscribe + - List-Unsubscribe-Post + - Return-Receipt-To + - Disposition-Notification-To + - X-Confirm-Reading-To + - X-Pmrqc