diff --git a/ansible/roles/real-postfix/files/aliases b/ansible/roles/real-postfix/files/aliases new file mode 100644 index 00000000..c7ea1edb --- /dev/null +++ b/ansible/roles/real-postfix/files/aliases @@ -0,0 +1,108 @@ +# +# Aliases in this file will NOT be expanded in the header from +# Mail, but WILL be visible over networks or from /bin/mail. +# +# >>>>>>>>>> The program "newaliases" must be run after +# >> NOTE >> this file is updated for any changes to +# >>>>>>>>>> show through to sendmail. +# + +# Basic system aliases -- these MUST be present. +mailer-daemon: postmaster +postmaster: root + +# General redirections for pseudo accounts. +bin: root +daemon: root +adm: root +lp: root +sync: root +shutdown: root +halt: root +mail: root +news: root +uucp: root +operator: root +games: root +gopher: root +ftp: root +nobody: root +radiusd: root +nut: root +dbus: root +vcsa: root +canna: root +wnn: root +rpm: root +nscd: root +pcap: root +apache: root +webalizer: root +dovecot: root +fax: root +quagga: root +radvd: root +pvm: root +amandabackup: root +privoxy: root +ident: root +named: root +xfs: root +gdm: root +mailnull: root +postgres: root +sshd: root +smmsp: root +postfix: root +netdump: root +ldap: root +squid: root +ntp: root +mysql: root +desktop: root +rpcuser: root +rpc: root +nfsnobody: root + +ingres: root +system: root +toor: root +manager: root +dumper: root +abuse: root + +newsadm: news +newsadmin: news +usenet: news +ftpadm: ftp +ftpadmin: ftp +ftp-adm: ftp +ftp-admin: ftp +www: webmaster +webmaster: root +noc: root +security: root +hostmaster: root + + +# trap decode to catch security attacks +decode: root + +# Person who should get root's mail +# root: (moved to /etc/scripts/root-procmailrc so this mail gets spam filtered) + +scripts: root +signup: root +afsagent: root +logview: root +scripts-build: root + +# People who are abusing or otherwise causing problems with the mail system +# MOVED TO LDAP +# cat <&2; + exit 1 +} + +clean_locker() { + echo "${1%%@scripts.mit.edu}" +} + +canonicalize_address() { + if [[ "$1" == *@* ]]; then + echo "$1" + else + echo "$1@scripts.mit.edu" + fi +} + +list_fullname() { + echo "Top twenty sender fullnames by number of queued messages:" + mailq | grep -v '^ *(\|^-' | awk 'BEGIN { RS = "" } { print $1 }' | tr -d '*!' | xargs postcat -q -e | sed -n 's/sender_fullname: //p' | sort | uniq -c | sort -n | tail -n 20 +} + +list_from() { + echo "Top twenty sending addresses by number of queued messages:" + mailq | grep -v '^ *(\|^-' | awk 'BEGIN { RS = "" } { print $7 }' | sort | uniq -c | sort -n | tail -n 20 +} + +list_to() { + echo "Top twenty recipients by number of queued messages:" + mailq | grep -v '^ *(\|^-' | awk 'BEGIN { RS = "" } { print $8 }' | sort | uniq -c | sort -n | tail -n 20 +} + +show_rand() { + if [[ $# -eq 0 ]]; then + files=$(printf '%s\n' /var/spool/postfix/deferred/?/* | shuf -n 3) + elif [[ $# -eq 2 ]]; then + match=$2 + case "$1" in + from) dir=7;; + to) dir=8;; + *) usage;; + esac + msgids=$(mailq | grep -v '^ *(\|^-' | awk "BEGIN { RS = \"\" } (\$$dir ~ /$match/) { print \$1 }" | shuf -n 3) + files=$(for msgid in $msgids; do echo /var/spool/postfix/deferred/${msgid:0:1}/$msgid; done) + else + usage + fi + for file in $files; do + echo ">>>> $file"; + postcat "$file" + echo; + done +} + +tmpl_email() { + sender=${SSH_GSSAPI_NAME%%/*} + if [[ $# -eq 0 ]]; then + echo "Please specify a locker to generate template for." >&2 + exit 1 + fi + for locker in "$@"; do + locker=$(clean_locker "$locker") + echo "fs la /mit/$locker/" + fs la "/mit/$locker" + echo + cat <<-EOF +The scripts.mit.edu servers currently have a large number of email messages destined for the *$locker* account that are not being handled by your account and are being queued. Sufficiently large numbers of queued messages can cause stability issues for the servers, so we would like you to ensure that your account can handle all messages it receives by two weeks from now. + +You will be able to process the incoming messages if you sign up for the mail scripts service (http://scripts.mit.edu/mail/). You're welcome to simply forward all incoming mail to another address (the default is to forward it to the mit.edu address of the user who signs up); otherwise, you can configure mail scripts to process the incoming messages in some suitable fashion. + +Frequently, large numbers of queued messages are a sign that some wiki, blog, forum, or other site has been spammed. If this is the case, you should apply some appropriate spam-blocking mechanism. + +If you have any questions, feel free to contact us. + +Thanks, +scripts.mit.edu team +scripts@mit.edu --- semi-private +scripts-root@mit.edu --- service maintainers only +EOF + echo;echo + done +} + +purge_fullname() { + if [[ $# -eq 0 ]]; then + echo "Please specify a fullname to purge emails from" >&2 + exit 1 + fi + for locker in "$@"; do + mailq | grep -v '^ *(\|^-' | awk 'BEGIN { RS = "" } { print $1 }' | tr -d '*!' | xargs postcat -q -e | awk "BEGIN { RS = \"*** ENVELOPE RECORDS \" } /\\nsender_fullname: $locker\\n/ { sub(\".*/\", \"\", \$1); print \$1 }" | postsuper -d - + echo + done +} + +purge_from() { + if [[ $# -eq 0 ]]; then + echo "Please specify an address to purge emails from" >&2 + exit 1 + fi + for address in "$@"; do + address=$(canonicalize_address "$address") + echo "$address..." + mailq | grep -v '^ *(\|^-' | awk "BEGIN { RS = \"\" } (\$7 == \"$address\") { print \$1 }" | tr -d '*!' | postsuper -d - + echo + done +} + +purge_from_re() { + if [[ $# -eq 0 ]]; then + echo "Please specify a regex to purge emails from" >&2 + exit 1 + fi + for re in "$@"; do + echo "$re" + mailq | tail -n +2 | grep -v '^ *(' | awk "BEGIN { RS = \"\" } (\$7 ~ \"$re\") { print \$1 }" | tr -d '*!' | postsuper -d - + echo + done +} + +purge_to() { + if [[ $# -eq 0 ]]; then + echo "Please specify a locker to purge emails to" >&2 + exit 1 + fi + for locker in "$@"; do + address=$(canonicalize_address "$locker") + echo "$address..." + mailq | grep -v '^ *(\|^-' | awk "BEGIN { RS = \"\" } (\$8 == \"$address\" && \$9 == \"\") { print \$1 }" | tr -d '*!' | postsuper -d - + echo + done +} + +purge_to_re() { + if [[ $# -eq 0 ]]; then + echo "Please specify a regex to purge emails to" >&2 + exit 1 + fi + for re in "$@"; do + echo "$re" + mailq | tail -n +2 | grep -v '^ *(' | awk "BEGIN { RS = \"\" } (\$8 ~ \"$re\" && \$9 == \"\") { print \$1 }" | tr -d '*!' | postsuper -d - + echo + done +} + +op=${1:-} + +# We want to go ahead and show the usage message if there are no args, so +# don't let the shift fail and end the script because of "set -e" +shift || : + +case "$op" in + list-from) list_from;; + list-fullname) list_fullname;; + list-to) list_to;; + show-rand) show_rand "$@";; + email) tmpl_email "$@";; + purge-fullname) purge_fullname "$@";; + purge-from) purge_from "$@";; + purge-from-re) purge_from_re "$@";; + purge-to) purge_to "$@";; + purge-to-re) purge_to_re "$@";; + *) + usage + ;; +esac + +# vim: set sts=4 sw=4 et: diff --git a/ansible/roles/real-postfix/handlers/main.yml b/ansible/roles/real-postfix/handlers/main.yml new file mode 100644 index 00000000..459c98a1 --- /dev/null +++ b/ansible/roles/real-postfix/handlers/main.yml @@ -0,0 +1,8 @@ +- name: reload postfix + service: name=postfix state=reloaded +- name: newaliases + command: newaliases +- name: restart spamassassin + service: name=spamassassin state=restarted +- name: restart spamass-milter + service: name=spamass-milter state=restarted diff --git a/ansible/roles/real-postfix/tasks/main.yml b/ansible/roles/real-postfix/tasks/main.yml new file mode 100644 index 00000000..777345d8 --- /dev/null +++ b/ansible/roles/real-postfix/tasks/main.yml @@ -0,0 +1,89 @@ +--- +- name: Install postfix + dnf: + name: + - postfix + - postfix-ldap + - postfix-pcre + - procmail + - spamass-milter-postfix + - postfix-perl-scripts + state: present + notify: reload postfix +- name: Create /etc/systemd/system/postfix.service.d/ + file: + path: /etc/systemd/system/postfix.service.d/ + state: directory +- name: Delay postfix startup until network is online + copy: + dest: /etc/systemd/system/postfix.service.d/scripts-postfix-network-online.conf + content: | + [Unit] + Wants=network-online.target + After=network-online.target +- name: Install postfix configuration files + copy: + src: postfix/ + dest: /etc/postfix/ + notify: reload postfix +- name: Install postfix LDAP configuration + template: + src: "postfix/{{ item }}.j2" + dest: /etc/postfix/{{ item }} + loop: + - authorized-submit-users-ldap.cf + - mailbox-command-maps-ldap.cf + - virtual-alias-domains-ldap.cf + - virtual-alias-maps-ldap-reserved.cf + - virtual-alias-maps-ldap.cf + - virtual-alias-maps-relay-ldap.cf + - virtual-alias-maps-relay-user-ldap.cf + - virtual-alias-maps-relay-user-suffix-ldap.cf + notify: reload postfix +- name: Install main.cf + template: + src: main.cf.j2 + dest: /etc/postfix/main.cf + notify: reload postfix +- name: Ensure /etc/scripts exists + file: + path: /etc/scripts/ + state: directory +- name: Install root's procmailrc + template: + src: root-procmailrc.j2 + dest: /etc/scripts/root-procmailrc +- name: Install prune-mailq + copy: + dest: /etc/scripts/prune-mailq + src: prune-mailq + mode: preserve +- name: Install aliases + copy: + dest: /etc/aliases + src: aliases + notify: newaliases +- name: Configure spamass-milter + ini_file: + path: /etc/sysconfig/spamass-milter + section: null + option: EXTRA_FLAGS + value: '"-m -r -1 -- --socket=/run/spamd.socket"' + no_extra_spaces: yes + notify: restart spamass-milter +- name: Configure spamassassin + ini_file: + path: /etc/sysconfig/spamassassin + section: null + option: SPAMDOPTIONS + value: '"--username=sa-milt --groupname=sa-milt --nouser-config --socketpath=/run/spamd.socket --socketowner=sa-milt --socketgroup=sa-milt --socketmode=0600"' + no_extra_spaces: yes + notify: restart spamassassin +- name: Enable postfix + service: + name: "{{ item }}" + enabled: yes + state: started + loop: + - spamass-milter + - postfix diff --git a/ansible/roles/real-postfix/templates/main.cf.j2 b/ansible/roles/real-postfix/templates/main.cf.j2 new file mode 100644 index 00000000..b2fa74a9 --- /dev/null +++ b/ansible/roles/real-postfix/templates/main.cf.j2 @@ -0,0 +1,56 @@ +#biff = no + +# appending .domain is the MUA's job. +append_dot_mydomain = no + +# Uncomment the next line to generate "delayed mail" warnings +#delay_warning_time = 4h + +alias_maps = hash:/etc/aliases +alias_database = hash:/etc/aliases +myorigin = scripts.mit.edu +mydestination = scripts.mit.edu, scripts, $myhostname, scripts-test.mit.edu, scripts-test, localhost +relayhost = +mynetworks_style = host +mailbox_command_maps = + texthash:/etc/postfix/mailbox_command_maps, + ldap:/etc/postfix/mailbox-command-maps-ldap.cf +mailbox_size_limit = 0 +message_size_limit = 41943040 +recipient_delimiter = + +inet_interfaces = $myhostname{% for vip in vips %}{% if vip.mail | default(False) and (vip.codename|default(ansible_lsb.codename) == ansible_lsb.codename) %}, {{ vip.host }}{% endif %}{% endfor %} + +readme_directory = /usr/share/doc/postfix/README_FILES +sample_directory = /usr/share/doc/postfix/samples +sendmail_path = /usr/sbin/sendmail +html_directory = no +setgid_group = postdrop +command_directory = /usr/sbin +manpage_directory = /usr/share/man +daemon_directory = /usr/libexec/postfix +newaliases_path = /usr/bin/newaliases +mailq_path = /usr/bin/mailq +queue_directory = /var/spool/postfix +mail_owner = postfix +# virtual-alias-maps-ldap-reserved.cf redirects {abuse,webmaster,etc.}@ -> root +# For domains on this pool, virtual-alias-maps-ldap.cf does foo@bar.com -> user+foo@localhost +# For domains on another pool, virtual-alias-maps-relay-ldap.cf does foo@bar.com -> foo!bar.com@[18.4.86.22] +# For users on another pool, pass-scripts.mit.edu matches *@scripts.mit.edu and virtual-alias-maps-relay-user-ldap.cf does foo@scripts.mit.edu -> foo!scripts.mit.edu@[18.4.86.22] +# pass-scripts.mit.edu-suffix and virtual-alias-maps-relay-user-suffix-ldap.cf do the same for foo+bar@scripts.mit.edu +# Native routing does foo@scripts.mit.edu -> foo +# smtp_generic_map rewrites foo!scripts.mit.edu@[18.4.86.22] -> foo@scripts.mit.edu after the transport is selected +# To override, put something in /etc/postfix/force_pool +virtual_alias_domains = {% for vip in vips | rejectattr('type', 'defined') %}{% if (vip.codename|default(ansible_lsb.codename) == ansible_lsb.codename) %}!{{ vip.host }}, !{{ vip.host | replace('.mit.edu', '') }}, {% endif %}{% endfor %}!$myhostname, !localhost, pcre:/etc/postfix/force_pool, ldap:/etc/postfix/virtual-alias-domains-ldap.cf +virtual_alias_maps = ldap:/etc/postfix/virtual-alias-maps-ldap-reserved.cf, ldap:/etc/postfix/virtual-alias-maps-ldap.cf, ldap:/etc/postfix/virtual-alias-maps-relay-ldap.cf, pipemap:{pcre:/etc/postfix/pass-scripts.mit.edu,ldap:/etc/postfix/virtual-alias-maps-relay-user-ldap.cf}, pipemap:{pcre:/etc/postfix/pass-scripts.mit.edu-suffix,ldap:/etc/postfix/virtual-alias-maps-relay-user-suffix-ldap.cf} +smtp_generic_maps = pcre:/etc/postfix/generic-strip-pool +data_directory = /var/lib/postfix +authorized_flush_users = fail +authorized_mailq_users = /etc/postfix/mailq_users +authorized_submit_users = !ldap:/etc/postfix/authorized-submit-users-ldap.cf, static:all +non_smtpd_milters = unix:/run/spamass-milter/postfix/sock +# "all" is the default, but if we do not specify it, Fedora's packaging +# will add the wrong value here. +inet_protocols = all +# note: as of 21 Oct 2015, our IPv6 addresses do not have rDNS and are rejected by Gmail +smtp_address_preference = ipv4 +smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination diff --git a/ansible/roles/real-postfix/templates/postfix/authorized-submit-users-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/authorized-submit-users-ldap.cf.j2 new file mode 100644 index 00000000..18a12ab2 --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/authorized-submit-users-ldap.cf.j2 @@ -0,0 +1,7 @@ +# N.B. If this /does/ match, the user is /blocked/. +server_host = {{ ldap_server }} +search_base = ou=People,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=posixAccount)(uid=%s)(scriptsBlockMailSubmit=TRUE)) +result_attribute = uid +bind = no +version = 3 diff --git a/ansible/roles/real-postfix/templates/postfix/mailbox-command-maps-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/mailbox-command-maps-ldap.cf.j2 new file mode 100644 index 00000000..a83ad2dc --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/mailbox-command-maps-ldap.cf.j2 @@ -0,0 +1,6 @@ +server_host = {{ ldap_server }} +search_base = ou=People,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=posixAccount)(uid=%s)) +result_attribute = scriptsMailboxCommand +bind = no +version = 3 diff --git a/ansible/roles/real-postfix/templates/postfix/virtual-alias-domains-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/virtual-alias-domains-ldap.cf.j2 new file mode 100644 index 00000000..507ee7dd --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/virtual-alias-domains-ldap.cf.j2 @@ -0,0 +1,21 @@ +# Find any vhost with a name or alias matching the domain of the e-mail +# address. We're queried with only the domain portion to determine whether +# we accept mail at all for a given domain. If we have no matching vhost +# and return no records, Postfix will reject mail with a "Relay access denied" +# error, unless the domain is configured in $mydestination. We don't match +# the scripts.mit.edu vhost here because otherwise it'll be treated as a +# virtual alias domain and once we resolve an address to a scripts account, +# we'll end up resolving the locker@scripts.mit.edu address to go to the +# owners of the scripts.mit.edu vhost. The value we return (if we return +# anything at all) is supposedly arbitrary. We choose to return the same +# value we were queried with (the domain whose mail we host). Protocol +# version 3 is necessary to use ldapi. + +server_host = {{ ldap_server }} +search_base = ou=VirtualHosts,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=scriptsVhost)(|(scriptsVhostName=%s)(scriptsVhostAlias=%s))(!(scriptsVhostName=scripts.mit.edu))(|{% for ip in ansible_all_ipv4_addresses %}(scriptsVhostPoolIPv4={{ip}}){% endfor %})) +result_attribute = scriptsVhostName +result_format = %S +bind = no +version = 3 + diff --git a/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-ldap-reserved.cf.j2 b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-ldap-reserved.cf.j2 new file mode 100644 index 00000000..995d018e --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-ldap-reserved.cf.j2 @@ -0,0 +1,7 @@ +server_host = {{ ldap_server }} +search_base = ou=VirtualHosts,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=scriptsVhost)(|(scriptsVhostName=%d)(scriptsVhostAlias=%d))(!(scriptsVhostName=scripts.mit.edu))(scriptsReservedMail=%u)) +result_attribute = scriptsVhostName +result_format = %U +bind = no +version = 3 diff --git a/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-ldap.cf.j2 new file mode 100644 index 00000000..c333df92 --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-ldap.cf.j2 @@ -0,0 +1,22 @@ +# Find any vhost with a name or alias matching the domain of the +# e-mail address. We're queried with an entire e-mail address, but +# are only interested in checking whether the domain portion +# corresponds to a vhost; we'll simply deliver any mail for the vhost +# to its owner, appending the original lefthand side of the address as +# an extension. %d extracts only the domain. We don't match the +# scripts.mit.edu vhost here because we don't want to first resolve an +# arbitrary address to a scripts account, and then end up sending +# their mail to the owners of the scripts.mit.edu vhost. The uid +# attribute, generated by the CoS template +# cn=vhostOwnerCoS,ou=VirtualHosts,dc=scripts,dc=mit,dc=edu, is the +# name of the locker that owns the vhost. Protocol version 3 is +# necessary to use ldapi. + +server_host = {{ ldap_server }} +search_base = ou=VirtualHosts,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=scriptsVhost)(|(scriptsVhostName=%d)(scriptsVhostAlias=%d))(!(scriptsVhostName=scripts.mit.edu))(|{% for ip in ansible_all_ipv4_addresses %}(scriptsVhostPoolIPv4={{ip}}){% endfor %})) +result_attribute = uid +result_format = %s+%U@localhost +bind = no +version = 3 + diff --git a/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-ldap.cf.j2 new file mode 100644 index 00000000..408f72db --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-ldap.cf.j2 @@ -0,0 +1,11 @@ +# Check if the vhost is served from another pool; if so, we relay to +# that pool's IP. This is also used as a relay_domains map to tell +# Postfix it's a relay domain. + +server_host = {{ ldap_server }} +search_base = ou=VirtualHosts,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=scriptsVhost)(|(scriptsVhostName=%d)(scriptsVhostAlias=%d))(!(scriptsVhostName=scripts.mit.edu))(!(|{% for ip in ansible_all_ipv4_addresses %}(scriptsVhostPoolIPv4={{ip}}){% endfor %}))) +result_attribute = scriptsVhostPoolIPv4 +result_format = %U!%D@[%s] +bind = no +version = 3 diff --git a/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-user-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-user-ldap.cf.j2 new file mode 100644 index 00000000..2c7c90b4 --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-user-ldap.cf.j2 @@ -0,0 +1,10 @@ +# Check if the user's vhost is served from another pool; if so, we +# relay to that pool's IP. + +server_host = {{ ldap_server }} +search_base = ou=VirtualHosts,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=scriptsVhost)(scriptsVhostName=%u.scripts.mit.edu)(!(|{% for ip in ansible_all_ipv4_addresses %}(scriptsVhostPoolIPv4={{ip}}){% endfor %}))) +result_attribute = scriptsVhostPoolIPv4 +result_format = %U!scripts.mit.edu@[%s] +bind = no +version = 3 diff --git a/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-user-suffix-ldap.cf.j2 b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-user-suffix-ldap.cf.j2 new file mode 100644 index 00000000..eb9df9c5 --- /dev/null +++ b/ansible/roles/real-postfix/templates/postfix/virtual-alias-maps-relay-user-suffix-ldap.cf.j2 @@ -0,0 +1,11 @@ +# Check if the user's vhost is served from another pool; if so, we +# relay to that pool's IP. This lookup handles user@+suffix as the +# input (transformed by pass-scripts.mit.edu-suffix). + +server_host = {{ ldap_server }} +search_base = ou=VirtualHosts,dc=scripts,dc=mit,dc=edu +query_filter = (&(objectClass=scriptsVhost)(scriptsVhostName=%u.scripts.mit.edu)(!(|{% for ip in ansible_all_ipv4_addresses %}(scriptsVhostPoolIPv4={{ip}}){% endfor %}))) +result_attribute = scriptsVhostPoolIPv4 +result_format = %U%D!scripts.mit.edu@[%s] +bind = no +version = 3 \ No newline at end of file diff --git a/ansible/roles/real-postfix/templates/root-procmailrc.j2 b/ansible/roles/real-postfix/templates/root-procmailrc.j2 new file mode 100644 index 00000000..a3b222be --- /dev/null +++ b/ansible/roles/real-postfix/templates/root-procmailrc.j2 @@ -0,0 +1,4 @@ +:0 +! {% for maintainer in maintainers|rejectattr('root_mail', 'none') -%} +{{ maintainer.root_mail|default(maintainer.username + '@mit.edu') }}{{ '' if loop.last else ', ' }} +{%- endfor %} \ No newline at end of file