X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=mail-setup;h=e12149f114bc06b0b79bc980359cb6dd743f8b8d;hb=56c55d8e02cdd6ec67d2fe53cc03785d8876442e;hp=f8cb3ee83e05dec206fb5d9c7fe0103e0b1b1392;hpb=7d9ec600a5ed9f88b85e02a27ee017b85721a6ac;p=distro-setup diff --git a/mail-setup b/mail-setup index f8cb3ee..e12149f 100755 --- a/mail-setup +++ b/mail-setup @@ -3,21 +3,21 @@ # Copyright (C) 2019 Ian Kelling # SPDX-License-Identifier: AGPL-3.0-or-later -# todo: sandbox / harden exim: -# 1. stop it from running as root. how? -# https://www.exim.org/exim-html-current/doc/html/spec_html/ch-security_considerations.html -# * avoid using .forward files, remove that router -# * set deliver_drop_privilege -# * set user to run as Debian-exim in systemd -# * set port to something like 2500, and forward 25 to 2500 with iptables. same for 587. -# https://superuser.com/questions/710253/allow-non-root-process-to-bind-to-port-80-and-443/1334552#1334552 -# * consider whether other routers like postmaster need modification / removal. -# 2. restrict its filesystem access from within systemd + +# todo: setup an alert for bouncing test emails. + +# todo: bounces to my fsf mail can come from fsf@iankelling.org, +# think about making bounces go from the original address. + +# todo: add a prometheus alert for dovecot. + +# todo: handle errors like this: +# Mar 02 12:44:26 kw systemd[1]: exim4.service: Found left-over process 68210 (exim4) in control group while starting unit. Ignoring. +# Mar 02 12:44:26 kw systemd[1]: This usually indicates unclean termination of a previous run, or service implementation deficiencies. # todo: harden dovecot. need to do some research. one way is for it to only listen on a wireguard vpn interface, so only clients that are on the vpn can access it. # todo: consider hardening cups listening on 0.0.0.0 # todo: stop/disable local apache, and rpc.mountd, and kdeconnect when not in use. -# todo: check that spamd and unbound only listen locally. # todo: hosts should only allow external mail that is authed and # destined for backup route. it is a minor issue since traffic is @@ -26,13 +26,9 @@ # todo: emailing info@amnimal.ninja produces a bounce, user doesn't exist # instead of a simple rejection like it should. -# todo: auto restart of je on checkrestart - # todo: run mailping test after running, or otherwise # clear out terminal alert -# todo: reinstall bk with bigger filesystem - # todo: on bk, dont send email if mailvpn is not up # todo: mailtest-check should check on bk too @@ -149,7 +145,7 @@ fi [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@" - +# note, this is hardcoded in /etc/exim4/conf.d/main/000_local u=$(id -nu 1000) @@ -178,9 +174,10 @@ fi # background: dovecot does not yet have ocsp stapling support # reference: https://community.letsencrypt.org/t/simple-guide-using-lets-encrypt-ssl-certs-with-dovecot/2921 # -# for phone, k9mail, same thing but username alerts, pass in ivy-pass. +# for phone, k9mail, fdroid, same thing but username alerts, pass in ivy-pass. # also, bk.b8.nz for secondary alerts, username is iank. same alerts pass. -# fetching mail settings: folder poll frequency 10 minutes +# fetching mail settings: folder poll frequency 10 minutes. +# account settings, fetching mail, push folders: All. Then disable the persistent notification. ####### @@ -292,7 +289,10 @@ fi i() { # install file local tmp tmpdir dest="$1" local base="${dest##*/}" - mkdir -p ${dest%/*} + local dir="${dest%/*}" + if [[ $dir != "$base" ]]; then + mkdir -p ${dest%/*} + fi ir=false # i result tmpdir=$(mktemp -d) cat >$tmpdir/"$base" @@ -369,7 +369,7 @@ fi bhost_t=false case $HOSTNAME in $MAIL_HOST) : ;; - kd|frodo|x2|x3|kw|sy) + kd|frodo|x2|x3|kw|sy|bo) bhost_t=true ;; esac @@ -378,7 +378,7 @@ esac # * Install universal packages -# installs epanicclean +# installs epanicclean iptables-exim ip6tables-exim /a/bin/ds/install-my-scripts if [[ $(debian-codename-compat) == bionic ]]; then @@ -390,11 +390,17 @@ EOF fi # light version of exim does not have sasl auth support. -pi-nostart exim4 exim4-daemon-heavy spamassassin openvpn unbound clamav-daemon wireguard +# note: for bitfolk hosts, unbound has important config with conflink. +pi-nostart exim4 exim4-daemon-heavy spamassassin unbound clamav-daemon wireguard # note: pyzor debian readme says you need to run some initialization command # but its outdated. -pi spf-tools-perl p0f postgrey pyzor razor jq moreutils certbot +pi spf-tools-perl p0f postgrey pyzor razor jq moreutils certbot fail2ban +case $HOSTNAME in + je) : ;; + # not included due to using wireguard: openvpn + *) pi wget git unzip iptables ;; +esac # bad packages that sometimes get automatically installed pu openresolv resolvconf @@ -418,21 +424,6 @@ fi # our nostart pi fails to avoid enabling -# * user forward file -case $HOSTNAME in - $MAIL_HOST) - # afaik, these will get ignored on MAIL_HOST because they are routing to my own - # machine, but rm them is safer - rm -fv $uhome/.forward /root/.forward - ;; - *) - # this can\'t be a symlink and has permission restrictions - # it might work in /etc/aliases, but this seems more proper. - e setting $uhome/.forward to $forward - install -m 644 {-o,-g}$u <(e $forward) $uhome/.forward - ;; -esac - # * Mail clean cronjob i /etc/systemd/system/mailclean.timer <<'EOF' @@ -498,18 +489,27 @@ case $HOSTNAME in ;; esac -i /etc/systemd/system/wg-quick@wgmail.service.d/override.conf <&2 - exit 1 - fi - ;; -esac + +# With openvpn, I didn't get around to persisting the openvpn +# cert/configs into /p/c/machine_specific/bk, so I had this case to +# manually get the cert. However, we aren't using openvpn anymore, so it +# is commented out. +# +# case $HOSTNAME in +# bk) +# if [[ ! -e /etc/openvpn/client/mail.conf ]]; then +# echo "$0: error: first, on a system with /p/c/filesystem, run mail-setup, or the vpn-mk-client-cert line above this err" 2>&2 +# exit 1 +# fi +# ;; +# esac m rsync -aiSAX --chown=root:root --chmod=g-s /a/bin/ds/mail-cert-cron /usr/local/bin @@ -838,12 +849,139 @@ if $bhost_t && [[ ! -e /etc/exim4/certs/$wghost/privkey.pem ]]; then --deploy-hook /a/bin/ds/le-exim-deploy -d $wghost fi +# * fail2ban + +# todo: test that these configs actually work, eg run +# s iptables-exim -S +# and see someone is banned. + +sed 's/^ *before *= *iptables-common.conf/before = iptables-common-exim.conf/' \ + /etc/fail2ban/action.d/iptables-multiport.conf| i /etc/fail2ban/action.d/iptables-exim.conf +i /etc/fail2ban/action.d/iptables-common-exim.conf <<'EOF' +# iank: same as iptables-common, except iptables is iptables-exim, ip6tables is ip6tables-exim + +# Fail2Ban configuration file +# +# Author: Daniel Black +# +# This is a included configuration file and includes the definitions for the iptables +# used in all iptables based actions by default. +# +# The user can override the defaults in iptables-common.local +# +# Modified: Alexander Koeppe , Serg G. Brester +# made config file IPv6 capable (see new section Init?family=inet6) + +[INCLUDES] + +after = iptables-blocktype.local + iptables-common.local +# iptables-blocktype.local is obsolete + +[Definition] + +# Option: actionflush +# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action) +# Values: CMD +# +actionflush = -F f2b- + + +[Init] + +# Option: chain +# Notes specifies the iptables chain to which the Fail2Ban rules should be +# added +# Values: STRING Default: INPUT +chain = INPUT + +# Default name of the chain +# +name = default + +# Option: port +# Notes.: specifies port to monitor +# Values: [ NUM | STRING ] Default: +# +port = ssh + +# Option: protocol +# Notes.: internally used by config reader for interpolations. +# Values: [ tcp | udp | icmp | all ] Default: tcp +# +protocol = tcp + +# Option: blocktype +# Note: This is what the action does with rules. This can be any jump target +# as per the iptables man page (section 8). Common values are DROP +# REJECT, REJECT --reject-with icmp-port-unreachable +# Values: STRING +blocktype = REJECT --reject-with icmp-port-unreachable + +# Option: returntype +# Note: This is the default rule on "actionstart". This should be RETURN +# in all (blocking) actions, except REJECT in allowing actions. +# Values: STRING +returntype = RETURN + +# Option: lockingopt +# Notes.: Option was introduced to iptables to prevent multiple instances from +# running concurrently and causing irratic behavior. -w was introduced +# in iptables 1.4.20, so might be absent on older systems +# See https://github.com/fail2ban/fail2ban/issues/1122 +# Values: STRING +lockingopt = -w + +# Option: iptables +# Notes.: Actual command to be executed, including common to all calls options +# Values: STRING +iptables = /usr/local/bin/iptables-exim + + +[Init?family=inet6] + +# Option: blocktype (ipv6) +# Note: This is what the action does with rules. This can be any jump target +# as per the iptables man page (section 8). Common values are DROP +# REJECT, REJECT --reject-with icmp6-port-unreachable +# Values: STRING +blocktype = REJECT --reject-with icmp6-port-unreachable + +# Option: iptables (ipv6) +# Notes.: Actual command to be executed, including common to all calls options +# Values: STRING +iptables = /usr/local/bin/ip6tables-exim +EOF + +i /etc/fail2ban/jail.d/exim.local <<'EOF' +[exim] +enabled = true +port = 25,587 +filter = exim +banaction = iptables-exim + +# 209.51.188.13 = mail.fsf.org +# 2001:470:142::13 = mail.fsf.org +# 209.51.188.92 = eggs.gnu.org +# 2001:470:142:3::10 = eggs.gnu.org +# 72.14.176.105 2600:3c00:e000:280::2 = mail.iankelling.org +# 10.173.8.1 = non-nn net +ignoreip = 209.51.188.13 2001:470:142::13 209.51.188.92 2001:470:142:3::10 72.14.176.105 2600:3c00:e000:280::2 10.173.8.1 +EOF +if $ir; then + m systemctl restart fail2ban +fi + # * common exim4 config +## old, not using forward files anymore +rm -fv $uhome/.forward /root/.forward + + # Make all system users be aliases. preventative -# measure for things like cron mail for user without alias -awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ { print $1 }' /etc/passwd| while read -r user; do +# prevents things like cron mail for user without alias +awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ || $7 ~ /\/nologin$/ { print $1 }' /etc/passwd| while read -r user; do if [[ ! $user ]]; then continue fi @@ -852,6 +990,20 @@ awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ { print $1 }' /etc/passwd| while read fi done + +awk 'BEGIN { FS = ":" } ; $6 ~ /^\/home/ && $7 !~ /\/nologin$/ { print $1 }' /etc/passwd| while read -r user; do + case $HOSTNAME in + $MAIL_HOST) + sed -i "/^user:/d" /etc/aliases + ;; + *) + if ! grep -q "^$user:" /etc/aliases; then + echo "$user: root" |m tee -a /etc/aliases + fi + ;; + esac +done + if ! grep -q "^ncsoft:" /etc/aliases; then echo "ncsoft: graceq2323@gmail.com" |m tee -a /etc/aliases fi @@ -888,16 +1040,24 @@ if (( ${#files[@]} )); then ${files[@]} /etc/exim4 fi -# by default, only 10 days of logs are kept. increase that. -m sed -ri 's/^(\s*rotate\s).*/\11000/' /etc/logrotate.d/exim4-base - +# By default, only 10 days of logs are kept. increase that. +# And dont compress, I look back at logs too often and +# dont need the annoyance of decompressing them all the time. +m sed -ri '/^\s*compress\s*$/d;s/^(\s*rotate\s).*/\11000/' /etc/logrotate.d/exim4-base +files=(/var/log/exim4/*.gz) +if (( ${#files[@]} )); then + gunzip ${files[@]} +fi -## https://blog.dhampir.no/content/make-exim4-on-debian-respect-forward-and-etcaliases-when-using-a-smarthost -# i only need .forwards, so just doing that one. -cd /etc/exim4/conf.d/router -b=userforward_higher_priority -# replace the router name so it is unique -sed -r s/^\\S+:/$b:/ 600_exim4-config_userforward >175_$b +## disabled. not using .forward files, but this is still interesting +## for reference. +# ## https://blog.dhampir.no/content/make-exim4-on-debian-respect-forward-and-etcaliases-when-using-a-smarthost +# # i only need .forwards, so just doing that one. +# cd /etc/exim4/conf.d/router +# b=userforward_higher_priority +# # replace the router name so it is unique +# sed -r s/^\\S+:/$b:/ 600_exim4-config_userforward >175_$b +rm -fv /etc/exim4/conf.d/router/175_userforward_higher_priority # todo, consider 'separate' in etc/exim4.conf, could it help on busy systems? @@ -916,17 +1076,43 @@ rm -fv /etc/exim4/conf.d/retry/37_retry cat >/etc/exim4/conf.d/retry/17_retry <<'EOF' # Retry fast for my own domains -iankelling.org * F,1d,10m;F,14d,1h -amnimal.ninja * F,1d,10m;F,14d,1h -expertpathologyreview.com * F,1d,10m;F,14d,1h -je.b8.nz * F,1d,10m;F,14d,1h -zroe.org * F,1d,10m;F,14d,1h +iankelling.org * F,1d,4m;F,14d,1h +amnimal.ninja * F,1d,4m;F,14d,1h +expertpathologyreview.com * F,1d,4m;F,14d,1h +je.b8.nz * F,1d,4m;F,14d,1h +zroe.org * F,1d,4m;F,14d,1h eximbackup.b8.nz * F,1d,4m;F,14d,1h + +# The spec says the target domain will be used for temporary host errors, +# but i've found that isn't correct, the hostname is required +# at least sometimes. +nn.b8.nz * F,1d,4m;F,14d,1h +defaultnn.b8.nz * F,1d,4m;F,14d,1h +mx.iankelling.org * F,1d,4m;F,14d,1h +bk.b8.nz * F,1d,4m;F,14d,1h +eggs.gnu.org * F,1d,4m;F,14d,1h +fencepost.gnu.org * F,1d,4m;F,14d,1h + +# afaik our retry doesnt need this, but just using everything +mx.amnimal.ninja * F,1d,4m;F,14d,1h +mx.expertpathologyreview.com * F,1d,4m;F,14d,1h + + +mail.fsf.org * F,1d,15m;F,14d,1h EOF rm -vf /etc/exim4/conf.d/main/000_localmacros # old filename -cat >/etc/exim4/conf.d/main/000_local </etc/exim4/conf.d/main/000_local2 </etc/exim4/conf.d/main/000_local <<'EOF' MAIN_TLS_ENABLE = true # require tls connections for all smarthosts @@ -948,11 +1134,6 @@ MAIN_LOG_SELECTOR = +all # Based on spec, seems like a good idea to be nice. smtp_return_error_details = true -# normally empty, I set this so I can set the envelope address -# when doing mail redelivery to invoke filters. Also allows -# me exiqgrep and stuff. -MAIN_TRUSTED_GROUPS = $u - # default is 10. when exim has been down for a bit, fsf mailserver # will do a big send in one connection, then exim decides to put # the messages in the queue instead of delivering them, to avoid @@ -971,10 +1152,10 @@ DKIM_SELECTOR = li # There could be some circumstance when the # from: isnt our domain, but the envelope sender is # and so still want to sign, but I cant think of any case. -DKIM_DOMAIN = \${lc:\${domain:\$rh_from:}} +DKIM_DOMAIN = ${lc:${domain:$rh_from:}} # The file is based on the outgoing domain-name in the from-header. # sign if key exists -DKIM_PRIVATE_KEY = \${if exists{/etc/exim4/\${dkim_domain}-private.pem} {/etc/exim4/\${dkim_domain}-private.pem}} +DKIM_PRIVATE_KEY = ${if exists{/etc/exim4/${dkim_domain}-private.pem} {/etc/exim4/${dkim_domain}-private.pem}} # most of the ones that gmail seems to use. # Exim has horrible default of signing unincluded @@ -998,6 +1179,19 @@ hostlist iank_trusted = <; \\ 85.119.82.128 ; 2001:ba8:1f1:f09d::2 ; \\ # fsf_mit_net fsf_mit_net_ip6 fsf_net fsf_net_ip6 fsf_office_net 18.4.89.0/24 ; 2603:3005:71a:2e00::/64 ; 209.51.188.0/24 ; 2001:470:142::/48 ; 74.94.156.208/28 + + +# this is the default delay_warning_condition, plus matching on local_domains. +# If I have some problem with my local system that causes delayed delivery, +# I dont want to send warnings out to non-local domains. +delay_warning_condition = ${if or {\ + { !eq{$h_list-id:$h_list-post:$h_list-subscribe:}{} }\ + { match{$h_precedence:}{(?i)bulk|list|junk} }\ + { match{$h_auto-submitted:}{(?i)auto-generated|auto-replied} }\ + { match_domain{$domain}{+local_domains} }\ + } {no}{yes}} + + EOF rm -fv /etc/exim4/rcpt_local_acl # old path @@ -1057,14 +1251,14 @@ warn EOF -i /etc/exim4/conf.d/router/900_exim4-config_local_user <config.php - m rm tmp.php + m rm -f tmp.php m sudo -u www-data php $ncdir/occ maintenance:update:htaccess list=$(sudo -u www-data php $ncdir/occ --output=json_pretty app:list) # user_external not compaible with nc 23 @@ -2121,11 +2320,12 @@ For logs, run: jr -u $ncbase EOF fi EOFOUTER + chmod +x /usr/local/bin/ncup mkdir -p /var/www/cron-errors chown www-data.www-data /var/www/cron-errors i /etc/cron.d/$ncbase <>/etc/exim4/conf.d/main/000_local <>/etc/exim4/update-exim4.conf.conf < /etc/mailname - /a/exe/cedit nn /etc/hosts <<'EOF' || [[ $? == 1 ]] 10.173.8.2 nn.b8.nz EOF @@ -2553,7 +2822,7 @@ StartLimitIntervalSec=0 [Service] Restart=always # time to sleep before restarting a service -RestartSec=1 +RestartSec=20 EOF i /etc/default/exim4in <<'EOF' @@ -2563,9 +2832,10 @@ COMMONOPTIONS='-oP /run/exim4/eximin.pid' UPEX4OPTS='-d /etc/myexim4' EOF + echo bk.b8.nz > /etc/mailname cat >>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/update-exim4.conf.conf <>/etc/myexim4/conf.d/main/000_local-nn <<'EOF' +# this makes it easier to see which exim is doing what +log_file_path = /var/log/exim4/my%s +EOF + + cat >/etc/logrotate.d/myexim <<'EOF' +/var/log/exim4/mymain /var/log/exim4/myreject { + daily + missingok + rotate 1000 + delaycompress + notifempty + nocreate +} +/var/log/exim4/mypanic { + size 10M + missingok + rotate 10 + compress + delaycompress + notifempty + nocreate +} +EOF + + # If we ever wanted to have a separate spool, + # we could do it like this. + # cat >>/etc/exim4/conf.d/main/000_local-nn <<'EOF' + # spool_directory = /var/spool/myexim4 + # EOF cat >>/etc/myexim4/update-exim4.conf.conf <<'EOF' dc_eximconfig_configtype='smarthost' dc_smarthost='nn.b8.nz' @@ -2730,6 +3047,9 @@ if [[ -e /nocow ]]; then # without local-fs on exim, we get these kind of errors in paniclog on shutdown: # Failed to create spool file /var/spool/exim4//input//1jCLxz-0008V4-V9-D: Permission denied After=local-fs.target + +[Service] +ExecStartPre=/usr/local/bin/exim-nn-iptables EOF if ! mountpoint -q $sdir; then stopifactive exim4 exim4in @@ -2764,8 +3084,8 @@ elif [[ $uid != 608 ]]; then m usermod -u 608 Debian-exim m groupmod -g 608 Debian-exim m usermod -g 608 Debian-exim - m find / /nocow -path ./var/tmp -prune -o -xdev -uid $uid -execdir chown -h 608 {} + - m find / /nocow -path ./var/tmp -prune -o -xdev -gid $gid -execdir chgrp -h 608 {} + + m find / /nocow -xdev -path ./var/tmp -prune -o -uid $uid -execdir chown -h 608 {} + + m find / /nocow -xdev -path ./var/tmp -prune -o -gid $gid -execdir chgrp -h 608 {} + fi # * start / stop services @@ -2776,7 +3096,8 @@ if $reload; then m systemctl daemon-reload fi -m systemctl --now enable epanicclean.timer +sysd-prom-fail-install epanicclean +m systemctl --now enable epanicclean case $HOSTNAME in je) @@ -2791,11 +3112,6 @@ m /a/bin/ds/mail-cert-cron -1 sre mailcert.timer case $HOSTNAME in - bk) - # todo, this should be done in distro-begin - soff systemd-resolved - ln -sf 127.0.0.1-resolv/stub-resolv.conf /etc/resolv.conf - ;;& $MAIL_HOST|bk) m systemctl --now enable mailnn mailnnroute ;;& @@ -2817,7 +3133,7 @@ case $HOSTNAME in fi if ! systemctl is-active clamav-daemon >/dev/null; then m systemctl --now enable clamav-daemon - out=$(rsync -aiSAX --chown=root:root --chmod=g-s /a/bin/ds/filesystem/etc/systemd/system/epanicclean.{timer,service} /etc/systemd/system) + out=$(rsync -aiSAX --chown=root:root --chmod=g-s /a/bin/ds/filesystem/etc/systemd/system/epanicclean.service /etc/systemd/system) if [[ $out ]]; then reload=true fi @@ -2849,7 +3165,10 @@ rm -f /var/local/mail-setup-reload case $HOSTNAME in - $MAIL_HOST|bk|je) : ;; + $MAIL_HOST|bk|je|li) + # on li, these are never started, except $vpnser + : + ;; *) soff radicale mailclean.timer dovecot spamassassin $vpnser mailnn clamav-daemon ;; @@ -2860,7 +3179,7 @@ sre exim4 case $HOSTNAME in $MAIL_HOST) m systemctl --now enable mailbindwatchdog - ;; + ;; *) soff mailbindwatchdog ;; @@ -2884,50 +3203,97 @@ case $HOSTNAME in cat >/etc/cron.d/mailtest </etc/systemd/system/mailtest-check.service <<'EOF' +[Unit] +Description=mailtest-check +After=local-fs.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +ExecStart=/usr/local/bin/mailtest-check slow +Restart=always +RestartSec=60 + +[Install] +WantedBy=graphical.target +EOF + sysd-prom-fail-install mailtest-check + sre mailtest-check ;;& $MAIL_HOST) test_froms=(ian@iankelling.org z@zroe.org iank@gnu.org) - test_to="testignore@expertpathologyreview.com, testignore@je.b8.nz, testignore@amnimal.ninja, jtuttle@gnu.org" + test_tos=(testignore@expertpathologyreview.com testignore@je.b8.nz testignore@amnimal.ninja jtuttle@gnu.org) cat >>/etc/cron.d/mailtest <> /etc/exim4/ignore-sent + done + cat >/usr/local/bin/send-test-forward <<'EOF' #!/bin/bash +# we remove from the queue older than 4.3 minutes since we send every 5 minutes. olds=( -$(/sbin/exiqgrep -o 260 -i -r '^(testignore@(iankelling\.org|zroe\.org|expertpathologyreview\.com|amnimal\.ninja|je\.b8\.nz)|jtuttle@gnu\.org)$') +$(/usr/sbin/exiqgrep -o 260 -i -r '^(testignore@(iankelling\.org|zroe\.org|expertpathologyreview\.com|amnimal\.ninja|je\.b8\.nz)|jtuttle@gnu\.org)$') ) if (( ${#olds[@]} )); then - /sbin/exim -Mrm "${olds[@]}" >/dev/null + /usr/sbin/exim -Mrm "${olds[@]}" >/dev/null fi EOF for test_from in ${test_froms[@]}; do + + test_to=${test_tos[0]} + for t in ${test_tos[@]:1}; do + test_to+=", $t" + done + case $test_from in + testignore@expertpathologyreview.com) + test_to=testignore@zroe.org + ;; + esac + cat >>/usr/local/bin/send-test-forward <