mostly t11 fixes
[distro-setup] / mail-setup
index 5166faa3a44e1a9009368ee48d9a989cfe3272d2..b3cb092539fc6b6ccd079c0c102ce74980a77d0f 100755 (executable)
@@ -3,17 +3,26 @@
 # Copyright (C) 2019 Ian Kelling
 # SPDX-License-Identifier: AGPL-3.0-or-later
 
+# todo: max line length macro changed in t11. look into it
+# todo: check that all macros we use are still valid in t11
+
+# 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.
+#eg: on eggs, on may 1st, ps grep for exim, 2 daemons running. 1 leftover from a month ago
+#Debian-+  1954     1  0 36231 11560   4 Apr02 ?        00:40:25 /usr/sbin/exim4 -bd -q30m
+#Debian-+ 23058  1954  0 36821 10564   0 20:38 ?        00:00:00 /usr/sbin/exim4 -bd -q30m
 
 #  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
 # todo: run mailping test after running, or otherwise
 # clear out terminal alert
 
-# todo: on bk, dont send email if mailvpn is not up
-
-# todo: mailtest-check should check on bk too
-
 # todo: disable postgrey
 
 # todo: in testforward-check, we should also look
@@ -287,7 +292,8 @@ i() { # install file
   local base="${dest##*/}"
   local dir="${dest%/*}"
   if [[ $dir != "$base" ]]; then
-    mkdir -p ${dest%/*}
+    # dest has a directory component
+    mkdir -p "$dir"
   fi
   ir=false # i result
   tmpdir=$(mktemp -d)
@@ -343,7 +349,6 @@ stopifactive() {
 
 mxhost=mx.iankelling.org
 mxport=587
-forward=$u@$mxhost
 
 # old setup. left as comment for example
 # mxhost=mail.messagingengine.com
@@ -386,6 +391,7 @@ EOF
 fi
 
 # light version of exim does not have sasl auth support.
+# 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
@@ -456,7 +462,7 @@ m usermod -a -G Debian-exim clamav
 
 i /etc/systemd/system/clamav-daemon.service.d/fix.conf <<EOF
 [Service]
-ExecStartPre=-/bin/mkdir /var/run/clamav
+ExecStartPre=-/bin/mkdir -p /var/run/clamav
 ExecStartPre=/bin/chown clamav /var/run/clamav
 EOF
 
@@ -464,8 +470,48 @@ EOF
 
 # old.
 #vpnser=mailvpn.service
-# todo: this hangs if it cant resolv the endpoint. we
-# want it to just retry in the background.
+# note: this hangs if it cant resolv the endpoint. we
+# want it to just retry in the background. i just use a static ip instead.
+#
+# Note: at least on t10, on reboot, the service fails to come up according to systemd, but
+# in reality it is up and working, then it tries to restart infinitely, and fails
+# because it detects that the interface exists.
+#
+# failing output:
+#
+# Aug 02 21:59:27 sy wg-quick[2092]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
+# Aug 02 21:59:27 sy wg-quick[2248]: [#] iptables-restore -n
+# Aug 02 21:59:27 sy wg-quick[2249]: Another app is currently holding the xtables lock. Perhaps you want to use the -w option?
+# Aug 02 21:59:27 sy wg-quick[2259]: [#] iptables-restore -n
+# Aug 02 21:59:27 sy wg-quick[2260]: Another app is currently holding the xtables lock. Perhaps you want to use the -w option?
+# Aug 02 21:59:27 sy systemd[1]: wg-quick@wgmail.service: Main process exited, code=exited, status=4/NOPERMISSION
+
+
+# successful output.
+# Aug 03 14:12:47 sy wg-quick[711336]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
+# Aug 03 14:12:47 sy wg-quick[711384]: [#] iptables-restore -n
+# Aug 03 14:12:47 sy wg-quick[711336]: [#] ping -w10 -c1 10.8.0.1 ||:
+# Aug 03 14:12:47 sy wg-quick[711389]: PING 10.8.0.1 (10.8.0.1) 56(84) bytes of data.
+# Aug 03 14:12:47 sy wg-quick[711389]: 64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=73.0 ms
+# Aug 03 14:12:47 sy wg-quick[711389]: --- 10.8.0.1 ping statistics ---
+# Aug 03 14:12:47 sy wg-quick[711389]: 1 packets transmitted, 1 received, 0% packet loss, time 0ms
+# Aug 03 14:12:47 sy wg-quick[711389]: rtt min/avg/max/mdev = 72.993/72.993/72.993/0.000 ms
+# Aug 03 14:12:47 sy systemd[1]: Finished WireGuard via wg-quick(8) for wgmail.
+# Aug 02 21:59:27 sy systemd[1]: wg-quick@wgmail.service: Failed with result 'exit-code'.
+# Aug 02 21:59:27 sy systemd[1]: Failed to start WireGuard via wg-quick(8) for wgmail.
+# Aug 02 21:59:47 sy systemd[1]: wg-quick@wgmail.service: Scheduled restart job, restart counter is at 1.
+# Aug 02 21:59:47 sy systemd[1]: Stopped WireGuard via wg-quick(8) for wgmail.
+# Aug 02 21:59:47 sy systemd[1]: Starting WireGuard via wg-quick(8) for wgmail...
+# Aug 02 21:59:47 sy wg-quick[3424]: wg-quick: `wgmail' already exists
+# Aug 02 21:59:47 sy systemd[1]: wg-quick@wgmail.service: Main process exited, code=exited, status=1/FAILURE
+# Aug 02 21:59:47 sy systemd[1]: wg-quick@wgmail.service: Failed with result 'exit-code'.
+# Aug 02 21:59:47 sy systemd[1]: Failed to start WireGuard via wg-quick(8) for wgmail.
+
+
+# According to iptables -S and iptables -t nat -S,
+# there are no modifications to iptables rules on a succsfull run,
+# and
+
 vpnser=wg-quick@wgmail.service
 
 case $HOSTNAME in
@@ -717,8 +763,12 @@ EOF
     for unit in ${nn_progs[@]}; do
       i /etc/systemd/system/$unit.service.d/nn.conf <<EOF
 [Unit]
-# commented for old openvpn
-Requires=$vpnser
+
+# Wants appears better than requires because with requires,
+# if the vpnser fails to start, this service won't get run at
+# all, even if the vpnser starts on an automatic restart.
+
+Wants=$vpnser
 After=network.target mailnn.service $vpnser
 JoinsNamespaceOf=mailnn.service
 BindsTo=mailnn.service
@@ -746,6 +796,19 @@ EOF
     ;;
 esac
 
+# * wghole (another mail vpn)
+
+if $bhost_t; then
+  i /etc/systemd/system/wg-quick@wghole.service.d/override.conf <<'EOF'
+[Unit]
+StartLimitIntervalSec=0
+
+[Service]
+Restart=on-failure
+RestartSec=20
+EOF
+fi
+
 # * spamassassin config
 i /etc/sysctl.d/80-iank-mail.conf <<'EOF'
 # see exim spec
@@ -950,6 +1013,14 @@ 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
@@ -987,10 +1058,8 @@ awk 'BEGIN { FS = ":" } ; $6 ~ /^\/home/ && $7 !~ /\/nologin$/ { print $1 }' /et
   esac
 done
 
-if ! grep -q "^ncsoft:" /etc/aliases; then
-  echo "ncsoft: graceq2323@gmail.com" |m tee -a /etc/aliases
-fi
 
+. /a/bin/bash_unpublished/priv-mail-setup
 
 
 m gpasswd -a iank adm #needed for reading logs
@@ -1059,11 +1128,11 @@ 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,
@@ -1074,6 +1143,13 @@ 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
 
@@ -1088,6 +1164,22 @@ cat >/etc/exim4/conf.d/main/000_local2 <<EOF
 MAIN_TRUSTED_GROUPS = $u
 EOF
 
+cd /etc/exim4
+{
+  for f in *-private.pem; do
+    echo ${f%-private.pem}
+  done
+} | i /etc/exim4/conf.d/my-dkim-domains
+
+cat >/etc/exim4/conf.d/transport/11_iank <<'EOF'
+# This unsets the default macro defined in on t11 in
+# /etc/exim4/conf.d/transport/10_exim4-config_transport-macros
+# It seems like a very odd choice that this has become
+# the default in t11. Normal smarthost clients use username/password
+# auth. Oh well.
+REMOTE_SMTP_SMARTHOST_TLS_VERIFY_HOSTS ==
+EOF
+
 cat >/etc/exim4/conf.d/main/000_local <<'EOF'
 MAIN_TLS_ENABLE = true
 
@@ -1122,13 +1214,17 @@ smtp_accept_queue_per_connection = 500
 DKIM_CANON = relaxed
 DKIM_SELECTOR = li
 
-# from comments in
+# From comments in
 # https://debian-administration.org/article/718/DKIM-signing_outgoing_mail_with_exim4
 # and its best for this to align https://tools.ietf.org/html/rfc7489#page-8
 # 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:}}
+# In t11, we cant do the above anymore because this is tainted data used in a file lookup.
+# /usr/share/doc/exim4/NEWS.Debian.gz suggests to use lookups to untaint data.
+DKIM_DOMAIN = ${lookup {${domain:$rh_from:}}lsearch,ret=key{/etc/exim4/conf.d/my-dkim-domains}}
+
 # 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}}
@@ -1142,17 +1238,17 @@ DKIM_SIGN_HEADERS = mime-version:in-reply-to:references:from:date:subject:to
 
 domainlist local_hostnames = ! je.b8.nz : ! bk.b8.nz : *.b8.nz : b8.nz
 
-hostlist iank_trusted = <; \\
+hostlist iank_trusted = <; \
 # veth0
-10.173.8.1 ; \\
+10.173.8.1 ; \
 # li li_ip6
-72.14.176.105 ; 2600:3c00::f03c:91ff:fe6d:baf8 ; \\
+72.14.176.105 ; 2600:3c00::f03c:91ff:fe6d:baf8 ; \
 # li_vpn_net li_vpn_net_ip6s
-10.8.0.0/24; 2600:3c00:e000:280::/64 ; 2600:3c00:e002:3800::/56 ;  \\
+10.8.0.0/24; 2600:3c00:e000:280::/64 ; 2600:3c00:e002:3800::/56 ;  \
 # bk bk_ip6
-85.119.83.50 ; 2001:ba8:1f1:f0c9::2 ; \\
+85.119.83.50 ; 2001:ba8:1f1:f0c9::2 ; \
 # je je_ipv6
-85.119.82.128 ; 2001:ba8:1f1:f09d::2 ; \\
+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
 
@@ -1168,6 +1264,17 @@ delay_warning_condition = ${if or {\
   } {no}{yes}}
 
 
+# enable 587 in addition to the default 25, so that
+# i can send mail where port 25 is firewalled by isp
+daemon_smtp_ports = 25 : 587
+# default of 25, can get stuck when catching up on mail
+smtp_accept_max = 400
+smtp_accept_reserve = 100
+smtp_reserve_hosts = +iank_trusted
+
+# Rules that make receiving more liberal should be on backup hosts
+# so that we dont reject mail accepted by MAIL_HOST
+LOCAL_DENY_EXCEPTIONS_LOCAL_ACL_FILE = /etc/exim4/conf.d/local_deny_exceptions_acl
 EOF
 
 rm -fv /etc/exim4/rcpt_local_acl # old path
@@ -1188,6 +1295,7 @@ accept
 EOF
 
 rm -fv /etc/exim4/data_local_acl # old path
+
 i /etc/exim4/conf.d/data_local_acl <<'EOF'
 # Except for the "condition =", this was
 # a comment in the check_data acl. The comment about this not
@@ -1206,6 +1314,8 @@ warn
 
 warn
   !hosts = +iank_trusted
+  # Smarthosts connect with residential ips and thus get flagged as spam if we do a spam check.
+  !authenticated = plain_server:login_server
   condition = ${if < {$message_size}{5000K}}
   spam = Debian-exim:true
   add_header = X-Spam_score_int: $spam_score_int
@@ -1214,11 +1324,6 @@ warn
   add_header = X-Spam_report: $spam_report
   add_header = X-Spam_action: $spam_action
 
-warn
-  condition = ${if def:malware_name}
-  remove_header = Subject:
-  add_header = Subject: [Clamav warning: $malware_name] $h_subject
-  log_message = heuristic malware warning: $malware_name
 
 #accept
 #  spf = pass:fail:softfail:none:neutral:permerror:temperror
@@ -1423,13 +1528,10 @@ if mailhost; then
 
   i /etc/systemd/system/radicale.service.d/override.conf <<EOF
 [Unit]
-# this unit is configured to start and stop whenever
-# $vpnser does
 
 After=network.target network-online.target mailnn.service $vpnser
-BindsTo=$vpnser
 
-Wants=network-online.target
+Wants=$vpnser
 JoinsNamespaceOf=mailnn.service
 StartLimitIntervalSec=0
 
@@ -1480,7 +1582,7 @@ EOF
   # disable power management feature, set to 240 min sync interval,
   # so it shouldn't be bad.
 
-  # davdroid from f-druid.
+  # davx^5 from f-droid
   # login with url and user name
   # url https://cal.iankelling.org/ian
   # username ian
@@ -1587,7 +1689,7 @@ EOF
       cat <<'EOF'
 # https://ssl-config.mozilla.org
 ssl = required
-# this is the same as the certbot list, in my cert cronjob, I check if that has changed upstream.
+# this is the same as the certbot list, i check changes in /a/bin/ds/filesystem/usr/local/bin/check-lets-encrypt-ssl-settings
 ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
 ssl_protocols = TLSv1.2
 ssl_prefer_server_ciphers = no
@@ -1598,7 +1700,7 @@ protocol lmtp {
   mail_plugins = $mail_plugins sieve
 }
 EOF
-      if dpkg --compare-versions $(dpkg-query -f='${Version}\n' --show dovecot-core) ge 1:2.3; then
+      if dpkg --compare-versions "$(dpkg-query -f='${Version}\n' --show dovecot-core)" ge 1:2.3; then
         cat <<EOF
 ssl_dh = </etc/dovecot/dhparam
 EOF
@@ -1767,6 +1869,7 @@ EOF
     i /etc/dovecot/dovecot-sql.conf.ext <<'EOF'
 # from mailinabox
 driver = sqlite
+# for je and bk, populated the testignore users for the relevant domains
 connect = /m/rc/users.sqlite
 default_pass_scheme = SHA512-CRYPT
 password_query = SELECT email as user, password FROM users WHERE email='%u';
@@ -1788,6 +1891,8 @@ extra,
 privileges TEXT NOT NULL DEFAULT '');
 EOF
     fi
+    # users.sqlite is saved into /p/c/machine_specific, so update it there!.
+    #
     # example of adding a user:
     # hash: doveadm pw -s SHA512-CRYPT -p passhere
     # sqlite3 /m/rc/users.sqlite <<'EOF'
@@ -2244,8 +2349,10 @@ fwrite(STDOUT, "<?php\n\\\$CONFIG = ");
 var_export(\$CONFIG);
 fwrite(STDOUT, ";\n");
 EOF
-    m php tmp.php >config.php
-    m rm -f tmp.php
+    e running php tmp.php
+    php tmp.php >config.php
+    # leave in place for debugging
+    #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
@@ -2279,22 +2386,39 @@ EOF
     systemctl enable --now $ncbase.timer
     i /usr/local/bin/ncup <<'EOFOUTER'
 #!/bin/bash
-if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi
-shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4
-set -eE -o pipefail
-trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" exit status: $?, PIPESTATUS: ${PIPESTATUS[*]}" >&2' ERR
 
-ncbase=$1
-if ! php /var/www/$ncbase/updater/updater.phar -n; then
+source /usr/local/lib/err
+
+m() { printf "%s\n" "$*";  "$@"; }
+err-cleanup() {
   echo failed nextcloud update for $ncbase >&2
-  /sbin/exim -t <<EOF
+  # -odf or else systemd will kill the background delivery process
+  # and the message will sit in the queue until the next queue run.
+  exim -odf -t <<EOF
 To: alerts@iankelling.org
-From: root@$(hostname -f)
+From: www-data@$(hostname -f)
 Subject: failed nextcloud update for $ncbase
 
 For logs, run: jr -u $ncbase
 EOF
+}
+
+if [[ $(id -u -n) != www-data ]]; then
+  echo error: running as wrong user: $(id -u -n), expected www-data
+  exit 1
+fi
+
+if [[ ! $1 ]]; then
+  echo error: expected an arg, nextcloud relative base dir
+  exit 1
 fi
+
+ncbase=$1
+cd /var/www/$ncbase
+m php /var/www/$ncbase/updater/updater.phar -n
+# just being overly cautious
+sleep 3
+m php occ -n upgrade
 EOFOUTER
     chmod +x /usr/local/bin/ncup
 
@@ -2383,11 +2507,20 @@ QUEUERUNNER='combined'
 QUEUEINTERVAL='30m'
 COMMONOPTIONS='-C /etc/exim4/my.conf'
 UPEX4OPTS='-o /etc/exim4/my.conf'
-#E4BCD_PANICLOG_NOISE='exim user lost privilege for using -C option'
+# i use epanic-clean for alerting if there are bad paniclog entries
+E4BCD_WATCH_PANICLOG='no'
 EOF
+    # make exim be a nonroot setuid program.
     chown Debian-exim:Debian-exim /usr/sbin/exim4
     # needs guid set in order to become Debian-exim
     chmod g+s,u+s /usr/sbin/exim4
+    # need this to avoid error on service reload:
+    # 2022-08-07 18:44:34.005 [892491] pid 892491: SIGHUP received: re-exec daemon
+    # 2022-08-07 18:44:34.036 [892491] cwd=/var/spool/exim4 5 args: /usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf
+    # 2022-08-07 18:44:34.043 [892491] socket bind() to port 25 for address (any IPv6) failed: Permission denied: waiting 30s before trying again (9 more tries)
+    # note: the daemon gives up and dies after retrying those 9 times.
+    # I came upon this by guessing and trial and error.
+    setcap CAP_NET_BIND_SERVICE+ei /usr/sbin/exim4
     i /etc/exim4/trusted_configs <<'EOF'
 /etc/exim4/my.conf
 EOF
@@ -2427,7 +2560,10 @@ ProtectHome=yes
 # this whole setting doesnt work. tried it with a newer systemd 250 though
 # an nspawn, and it worked there.
 InaccessiblePaths=${dirs[@]}
-NoNewPrivileges=yes
+# this gives us the permission denied error:
+# socket bind() to port 25 for address (any IPv6) failed: Permission denied
+# but we also have to set the file capabilities to avoid the error.
+#NoNewPrivileges=yes
 ProtectSystem=yes
 
 # when we get newer systemd
@@ -2472,26 +2608,31 @@ CHECK_RCPT_SPF = true
 CHECK_RCPT_REVERSE_DNS = true
 CHECK_MAIL_HELO_ISSUED = true
 
-# enable 587 in addition to the default 25, so that
-# i can send mail where port 25 is firewalled by isp
-daemon_smtp_ports = 25 : 587
-# default of 25, can get stuck when catching up on mail
-smtp_accept_max = 400
-smtp_accept_reserve = 100
-smtp_reserve_hosts = +iank_trusted
 
-# options exim has to avoid having to alter the default config files
-CHECK_RCPT_LOCAL_ACL_FILE = /etc/exim4/conf.d/rcpt_local_acl
 CHECK_DATA_LOCAL_ACL_FILE = /etc/exim4/conf.d/data_local_acl
-LOCAL_DENY_EXCEPTIONS_LOCAL_ACL_FILE = /etc/exim4/conf.d/local_deny_exceptions_acl
+CHECK_RCPT_LOCAL_ACL_FILE = /etc/exim4/conf.d/rcpt_local_acl
+
 # testing dmarc
 #dmarc_tld_file = /etc/public_suffix_list.dat
+
 EOF
     ;;&
 
   # ** $MAIL_HOST|bk)
   $MAIL_HOST|bk)
 
+
+    # no clamav on je, it has 1.5g memory and clamav uses most of it
+    i /etc/exim4/conf.d/clamav_data_acl <<'EOF'
+warn
+!hosts = +iank_trusted
+!authenticated = plain_server:login_server
+condition = ${if def:malware_name}
+remove_header = Subject:
+add_header = Subject: [Clamav warning: $malware_name] $h_subject
+log_message = heuristic malware warning: $malware_name
+EOF
+
     cat >>/etc/exim4/conf.d/main/000_local <<EOF
 # je.b8.nz will run out of memory with freshclam
 av_scanner = clamd:/var/run/clamav/clamd.ctl
@@ -2668,11 +2809,28 @@ deny
 EOF
     echo|i /etc/exim4/conf.d/router/880_universal_forward
 
+
+    cat >>/etc/exim4/conf.d/main/000_local <<EOF
+MAILDIR_HOME_MAILDIR_LOCATION = /m/md/Sent
+EOF
+
     # for iank@fsf.org, i have mail.fsf.org forward it to fsf@iankelling.org.
     # and also have mail.iankelling.org whitelisted as a relay domain.
     # I could avoid that if I changed this to submit to 587 with a
     # password like a standard mua.
     i /etc/exim4/conf.d/router/188_exim4-config_smarthost <<'EOF'
+# ian: save a copy of sent mail. i thought of other ways to
+# do this, for example, to only save sent mail that is not sent
+# from my mail client which saves a copy by default, but in the
+# end, it seems simplest to turn that off. We want to save
+# external mail sent by smarthosts.
+sentarchive:
+  driver = redirect
+  domains = ! +local_domains
+  condition = ${if !bool{${lookup{$local_part@$domain}lsearch{/etc/exim4/ignore-sent}{true}}}}
+  data    = vojdedIdNejyebni@b8.nz
+  unseen
+
 # ian: copied from /etc/exim4/conf.d/router/200_exim4-config_primary, and added senders = and
 # replaced DCsmarthost with hostname
 fsfsmarthost:
@@ -2696,7 +2854,6 @@ posteosmarthost:
   host_find_failed = ignore
   same_domain_copy_routing = yes
   no_more
-
 EOF
 
     # Greping /etc/exim4, unqualified mails this would end up as
@@ -2820,6 +2977,7 @@ EOF
     echo|i /etc/exim4/conf.d/rcpt_local_acl
     echo|i /etc/exim4/conf.d/router/890_backup_copy
     echo|i /etc/exim4/conf.d/main/000_local-nn
+    echo|i /etc/exim4/conf.d/clamav_data_acl
 
 
     if $bhost_t; then
@@ -2854,7 +3012,7 @@ EOF
     echo | /a/exe/cedit nn /etc/hosts || [[ $? == 1 ]]
     echo | /a/exe/cedit mail /etc/dnsmasq-servers.conf || [[ $? == 1 ]]
 
-
+    # note: condition duplicated at else
     if $bhost_t; then
       install -d /bu
       install -d -g Debian-exim -o Debian-exim -m 771 /bu/md
@@ -2902,7 +3060,7 @@ Restart=always
 RestartSec=20
 EOF
 
-    else
+    else  # if $bhost_t; then
       cat >>/etc/exim4/update-exim4.conf.conf <<EOF
 # Note: If theres like a temporary problem where mail gets sent to
 # one of these hosts, if exim isnt listening, it will be a temporary error
@@ -2934,7 +3092,34 @@ esac
 case $HOSTNAME in
   $MAIL_HOST|bk)
     # config for the non-nn exim
-    m rsync -ra --delete /etc/exim4/ /etc/myexim4
+    m rsync -ra --delete --delete-excluded --exclude=/conf.d/main/000_local-nn /etc/exim4/ /etc/myexim4
+    cat >>/etc/myexim4/conf.d/main/000_local <<'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'
@@ -2981,6 +3166,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
@@ -3027,6 +3215,12 @@ if $reload; then
   m systemctl daemon-reload
 fi
 
+# checking bhost_t is redundant, but could help us catch errors.
+if $bhost_t || [[ -e /etc/wireguard/wghole.conf ]]; then
+  # todo: in mail-setup, we have a static list of backup hosts, not *y
+  m systemctl --now enable wg-quick@wghole
+fi
+
 sysd-prom-fail-install epanicclean
 m systemctl --now enable epanicclean
 
@@ -3085,12 +3279,19 @@ case $HOSTNAME in
   $MAIL_HOST)
     # < 2.1 (eg: in t9), uses a different data format which required manual
     # migration. dont start if we are running an old version.
-    if dpkg --compare-versions $(dpkg -s radicale | awk '$1 == "Version:" { print $2 }') ge 2.1; then
+    if dpkg --compare-versions "$(dpkg -s radicale | awk '$1 == "Version:" { print $2 }')" ge 2.1; then
       m systemctl --now enable radicale
     fi
     ;;&
 esac
 
+# for debugging dns issues
+case $HOSTNAME in
+  je|bk)
+    systemctl enable --now logrotate-fast.timer
+    ;;
+esac
+
 # last use of $reload happens in previous block
 rm -f /var/local/mail-setup-reload
 
@@ -3131,39 +3332,75 @@ case $HOSTNAME in
     # note: cronjob "ian" also does some important monitoring
     # todo: this will sometimes cause an alert because mailtest-check will run
     # before we have setup network namespace and spamassassin
-    cat >/etc/cron.d/mailtest <<EOF
+    /etc/cron.d/mailtest <<EOF
 SHELL=/bin/bash
 PATH=/usr/bin:/bin:/usr/local/bin
 MAILTO=daylert@iankelling.org
 */5  * * * *   $u send-test-forward |& log-once send-test-forward
 */10 * * * *   root chmod -R g+rw /m/md/bounces |& log-once -1 bounces-chmod
-*/5  * * * *   root timeout 290 mailtest-check slow |& log-once -4 mailtest-check
 # if a bounce happened yesterday, dont let it slip through the cracks
 8   1 * * *   root export MAILTO=alerts@iankelling.org; [[ -s /var/log/exim4/mainlog.1 ]] && awk '\$5 == "**"' /var/log/exim4/mainlog.1
 EOF
+
+
     m sudo rsync -ahhi --chown=root:root --chmod=0755 \
       /b/ds/mailtest-check /b/ds/check-remote-mailqs /usr/local/bin/
+    i /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 <<EOF
-0   13 * * *  root echo "1pm alert. You are not in the matrix."
+# 10 am friday
+0   10 * * 5  root echo "weekly alert. You are not in the matrix."
 2   * * * *   root check-remote-mailqs |& log-once check-remote-mailqs
 EOF
     ;;&
   bk)
-    test_froms=(testignore@expertpathologyreview.com testignore@amnimal.ninja)
-    test_to="testignore@iankelling.org, testignore@zroe.org, testignore@je.b8.nz"
+    test_froms=(testignore@amnimal.ninja testignore@expertpathologyreview.com)
+    test_tos=(testignore@iankelling.org testignore@je.b8.nz)
+    # We dont need to send from different addresses to the same
+    # address. this breaks down our nice elegant logic of building up
+    # froms and tos , so I just handle expertpath in a special case
+    # below and set the to: to be testignore@zroe.org.  If we did sent
+    # that way, it would also mess up our mailtest-check logic that
+    # finds which messages to check.
+    # for example: from testignore@amnimal.ninja to: testignore@iankelling.org testignore@zroe.org
+    # that would become 2 messages and we'd only check 1.
     ;;&
   je)
     test_froms=(testignore@je.b8.nz)
-    test_to="testignore@iankelling.org, testignore@zroe.org, testignore@expertpathologyreview.com, testignore@amnimal.ninja"
+    test_tos=(testignore@iankelling.org testignore@zroe.org testignore@expertpathologyreview.com testignore@amnimal.ninja)
     ;;&
   $MAIL_HOST|bk|je)
+
+    # Dont put these test messages into the sent folder or else it will
+    # overwhelm it, plus i dont want to save a copy at all.
+    rm -f /etc/exim4/ignore-sent
+    for t in ${test_tos[@]}; do
+      echo $t >> /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=(
 $(/usr/sbin/exiqgrep -o 260 -i -r '^(testignore@(iankelling\.org|zroe\.org|expertpathologyreview\.com|amnimal\.ninja|je\.b8\.nz)|jtuttle@gnu\.org)$')
 )
@@ -3172,11 +3409,22 @@ if (( ${#olds[@]} )); then
 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 <<EOFOUTER
-/usr/sbin/exim -f $test_from -t <<EOF
+/usr/sbin/exim -odf -f $test_from -t <<EOF
 From: $test_from
 To: $test_to
-Subject: test \$(date +%Y-%m-%dT%H:%M:%S%z) \$(date +%s)
+Subject: test \$(date +%Y-%m-%dT%H:%M:%S%z) \$EPOCHSECONDS
 
 /usr/local/bin/send-test-forward
 EOF
@@ -3185,7 +3433,10 @@ EOFOUTER
     m chmod +x /usr/local/bin/send-test-forward
     ;;
   *)
-    rm -fv /etc/cron.d/mailtest
+    soff mailtest-check.service
+    rm -fv /etc/cron.d/mailtest \
+       /var/lib/prometheus/node-exporter/mailtest-check.prom* \
+       /var/local/cron-errors/check-remote-mailqs*
     ;;
 esac