mail fixes, additional testing
authorIan Kelling <ian@iankelling.org>
Tue, 10 Nov 2020 19:52:58 +0000 (14:52 -0500)
committerIan Kelling <ian@iankelling.org>
Tue, 10 Nov 2020 19:52:58 +0000 (14:52 -0500)
conflink
mail-setup
mailtest-check
system-status

index 450edf7cdb51a5b8639332bfe90fcae9fa945b7c..b70e26f17689f10245ea4e27234a29c1c00657d0 100755 (executable)
--- a/conflink
+++ b/conflink
@@ -128,6 +128,7 @@ common-file-setup() {
 
 }
 
+install-my-scripts
 user=$(id -un)
 all_dirs=({/a/bin/ds,/p/c}{,/machine_specific/$HOSTNAME})
 # note, we assume a group of hosts does not have the
index d07660086acc87653ec12618ab89e9cf53197522..c446bbb5a3ff7b495daed12eac9abdbca729f857 100755 (executable)
@@ -3,14 +3,22 @@
 # Copyright (C) 2019 Ian Kelling
 # SPDX-License-Identifier: AGPL-3.0-or-later
 
+# todo: disable greylisting
+
+# todo: enable plus addressing and sort out mail filtering.
+# consider maildrop, procmail, etc.
+
+# todo: test that bounces dont help create valid mailtest-check
+
 # todo: move mail stuff in distro-end into this file
 
+# todo: consider rotating dkim & publishing key so every past email I sent
+# isnt necessarily signed
+
 # todo: consider how to get clamav out of Debian-exim group
 # so it cant read/write the whole mail spool, for better
 # security.
 
-# todo: fix ipv6 addr for li
-
 # todo: create a cronjob to update or warn on expiring dnssec keys
 
 # todo: we should test failed mail daily or so
 
 # todo: mailtest-check failure on remote hosts is not going to alert me.
 # sort that out.
-# todo: test that mail is dkim signing in our mail test.
-# todo: send sending from all domains
 # todo: test mail failure as well as success.
-# todo: we should monitor spamhaus etc to make sure we dont get blacklisted.
+#
+# todo: validate that mailtest-check is doing dnsbl checks.
 
 # background: I want to run exim in a network namespace so it can send
 # and receive through a vpn. This is needed so it can do ipv6, because
@@ -191,7 +198,7 @@ fi
 # echo "bind txt record: remember to truncate $domain so its relative to the bind zone"
 # cat <<EOF
 # li._domainkey.$domain        TXT     (
-# v=DKIM1\059 k=rsa\059 p="$(openssl rsa -in $domain-private.pem -pubout |&sed -rn '${x;s/\n//g;s/^(.*)(.{240}$)/\1"\n"\2/p};3,$H')" )
+# "v=DKIM1\059 k=rsa\059 p=$(openssl rsa -in $domain-private.pem -pubout |&sed -rn '${x;s/\n//g;s/^(.*)(.{240}$)/\1"\n"\2/p};3,$H')" )
 # EOF
 # # sed explanation: skip the first few lines, then put them into the hold space, then
 # # on the last line, back to the patern space, remove the newlines, then add a newline
@@ -467,7 +474,7 @@ PrivateNetwork=true
 # dns from leaking in my network namespaced vpn.
 # I also like the idea of patching systemd-resolved so it
 # will listen on other interfaces, but its not worth my time.
-BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind
+BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind /etc/nn-resolv:/etc/nsswitch:norbind
 Restart=always
 # time to sleep before restarting a service
 RestartSec=1
@@ -490,6 +497,7 @@ nameserver 127.0.0.1
 options edns0
 EOF
 
+
 # this is just a bug fix for trisquel.
 f=/etc/apparmor.d/usr.sbin.unbound
 line="/usr/sbin/unbound flags=(attach_disconnected) {"
@@ -505,6 +513,7 @@ fi
 nn_progs=(exim4 unbound)
 if mailhost; then
   # Note dovecots lmtp doesnt need to be in the same nn to accept delivery.
+  # Its in the nn so remote clients can connect to it.
   nn_progs+=(spamassassin dovecot)
 fi
 
@@ -523,7 +532,7 @@ StartLimitIntervalSec=0
 
 [Service]
 PrivateNetwork=true
-BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind
+BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind /etc/nn-resolv:/etc/nsswitch:norbind
 
 Restart=always
 # time to sleep before restarting a service
@@ -572,16 +581,14 @@ EOF
 # 2020-10-19 remove old file. remove this when all hosts updated
 rm -fv /etc/systemd/system/spamddnsfix.{timer,service}
 
-# per readme.debian, allow nightly cronjob to run
-m sed -i '/^\s*CRON\s*=/d' /etc/default/spamassassin
-e CRON=1 /etc/default/spamassassin
-
-l='NICE="--nicelevel 15"'
-if grep -qFx "$l" /etc/default/spamassassin; then
-  # Just noticed this in the config file, seems like a good idea.
-  m sed -i '/^\s*NICE\s*=/d' /etc/default/spamassassin
-  e "$l" | tee -a /etc/default/spamassassin
-fi
+i /etc/default/spamassassin <<'EOF'
+# defaults
+OPTIONS="--create-prefs --max-children 5 --helper-home-dir"
+PIDFILE="/var/run/spamd.pid"
+# my additions
+NICE="--nicelevel 15"
+CRON=1
+EOF
 #####   end spamassassin config
 
 
@@ -774,9 +781,9 @@ hostlist iank_trusted = <; \\
 # veth0
 10.173.8.1 ; \\
 # li li_ip6
-72.14.176.105 ; 2600:3c00::f03c:91ff:fe6d:baf8; \\
-# li_vpn_net li_vpn_net_ip6
-10.8.0.0/24; 2600:3c00:e000:280::/64 ; \\
+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 ;  \\
 # bk bk_ip6
 85.119.83.50 ; 2001:ba8:1f1:f0c9::2 ; \\
 # je je_ipv6
@@ -929,7 +936,7 @@ JoinsNamespaceOf=mailnn.service
 
 [Service]
 PrivateNetwork=true
-BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind
+BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind /etc/nn-resolv:/etc/nsswitch:norbind
 
 [Install]
 RequiredBy=mailvpn.service
@@ -1536,21 +1543,30 @@ EOF
       echo $v >$verf
     fi
 
+    # So, strangely, this worked in initial testing, but then
+    # on first run it wouldn't show the existing contacts until
+    # I went into the carddav settings and did "force immediate sync",
+    # which seemed to fix things. Note, some of these settings
+    # get initalized per/addressbook in the db, then need changing
+    # there or through the settings menu.
+
+    # About categories, see https://www.davx5.com/tested-with/nextcloud
+    # https://github.com/blind-coder/rcmcarddav/blob/master/doc/GROUPS.md
     i $rcdir/plugins/carddav/config.inc.php <<EOF;
 <?php
-\$prefs['_GLOBAL']['hide_preferences'] = true;
+\$prefs['_GLOBAL']['hide_preferences'] = false;
 \$prefs['davserver'] = array(
 # name in the UI is kind of dumb. This is just something short that seems to fit ok.
   'name'         =>  'Main',
   'username'     =>  '%u', // login username
   'password'     =>  '%p', // login password
-  'url'          =>  'https://$domain/nextcloud/remote.php/carddav/addressbooks/%u/contacts',
+  'url'          =>  'https://$domain/nextcloud/remote.php/dav/addressbooks/users/%u/contacts',
   'active'       =>  true,
   'readonly'     =>  false,
   'refresh_time' => '00:10:00',
   'fixed'        =>  array('username','password'),
+  'use_categories' => false,
   'hide'        =>  false,
-  'use_categories' => true, // https://www.davx5.com/tested-with/nextcloud
 );
 ?>
 EOF
@@ -1710,6 +1726,30 @@ fi
 
 
 # * exim host conditional config
+
+# ** exim certs
+
+all_dirs=(/p/c/filesystem)
+for x in /p/c/machine_specific/*.hosts /a/bin/ds/machine_specific/*.hosts; do
+  if grep -qxF $HOSTNAME $x; then all_dirs+=( ${x%.hosts} ); fi
+done
+files=()
+for d in ${all_dirs[@]}; do
+  f=$d/etc/exim4/passwd
+  if [[ -e $f  ]]; then
+    files+=($f)
+  fi
+  tmp=($d/etc/exim4/*.pem)
+  if (( ${#tmp[@]} )); then
+    files+=(${tmp[@]})
+  fi
+done
+
+if (( ${#files[@]} )); then
+  sudo rsync -ahhi --chown=root:Debian-exim --chmod=0640 ${files[@]} /etc/exim4/
+fi
+
+
 # ** auth
 case $HOSTNAME in
   $MAIL_HOST)
@@ -1802,9 +1842,7 @@ EOF
 MAIN_HARDCODE_PRIMARY_HOSTNAME = mail.iankelling.org
 EOF
 
-    /a/exe/cedit nn.b8.nz /etc/hosts <<'EOF' || [[ $? == 1 ]]
-# note: i put this into bind for good measure
-10.173.8.2 nn.b8.nz
+    /a/exe/cedit defaultnn /etc/hosts <<'EOF' || [[ $? == 1 ]]
 # this is just here to avoid mainlog errors, however, it doesnt seem to work
 # todo: look into it more. nsswitch.conf? cached result? i dunno
 # list matching forced to fail: failed to find host name for 10.173.8.1
@@ -1875,14 +1913,14 @@ EOF
     # This name won\'t appear on From: lines of outgoing messages if rewriting is enabled.
     echo iankelling.org > /etc/mailname
 
-    sudo rsync -ahhi --chown=root:Debian-exim --chmod=0640 \
-         /p/c/filesystem/etc/exim4/passwd /p/c/filesystem/etc/exim4/*.pem /etc/exim4/
 
     # mail.iankelling.org so local imap clients can connect with tls and
     # when they happen to not be local.
-    sed -ri -f - /etc/hosts <<'EOF'
-/^127\.0\.1\.1.* mail\.iankelling\.org\b/{p;d}
-/^127\.0\.1\.1 /s/ *$/ mail.iankelling.org/
+    # todo: this should be 10.8.0.4
+
+    /a/exe/cedit nn /etc/hosts <<'EOF' || [[ $? == 1 ]]
+# note: i put nn.b8.nz into bind for good measure
+10.173.8.2 nn.b8.nz mail.iankelling.org
 EOF
 
     # note: systemd-resolved will consult /etc/hosts, dnsmasq wont. this assumes
@@ -1924,6 +1962,11 @@ EOF
   bk)
     echo amnimal.ninja > /etc/mailname
 
+    /a/exe/cedit nn /etc/hosts <<'EOF' || [[ $? == 1 ]]
+10.173.8.2 nn.b8.nz
+EOF
+
+
     i /etc/myexim4/conf.d/router/180_vpnmanual <<'EOF'
 # copied from dnslookup, altered domains, added route_list,
 # changed driver, removed ignore_target_hosts since it
@@ -1984,12 +2027,9 @@ EOF
       rm -fv $f
     done
 
-    # remove mail. uses 2 lines to properly remove whitespace
-    sed -ri -f - /etc/hosts <<'EOF'
-s#^(127\.0\.1\.1 .*) +mail\.iankelling\.org$#\1#
-s#^(127\.0\.1\.1 .*)mail\.iankelling\.org +(.*)#\1\2#
-EOF
-
+    # dont i dont care if defaultnn section gets left, it wont
+    # get used.
+    echo | /a/exe/cedit nn /etc/hosts || [[ $? == 1 ]]
     echo | /a/exe/cedit mail /etc/dnsmasq-servers.conf || [[ $? == 1 ]]
 
     cat >>/etc/exim4/update-exim4.conf.conf <<EOF
@@ -2120,6 +2160,13 @@ sre mailcert.timer
 case $HOSTNAME in
   $MAIL_HOST|bk)
     sstart mailnn
+    if ! systemctl is-active clamav-daemon >/dev/null; then
+      sstart clamav-daemon
+      # checking a log, clamav took 27 seconds to start.
+      # we get paniclog entries if its not available
+      m sleep 30
+    fi
+
     if $reload; then
       sre mailvpn unbound
     else
@@ -2130,7 +2177,7 @@ case $HOSTNAME in
     ;;&
   $MAIL_HOST|bk|je)
     # start spamassassin/dovecot before exim.
-    sre dovecot spamassassin clamav-daemon
+    sre dovecot spamassassin
     sstart mailclean.timer
     ;;&
   $MAIL_HOST)
@@ -2141,7 +2188,7 @@ esac
 case $HOSTNAME in
   $MAIL_HOST|bk|je) : ;;
   *)
-    soff radicale mailclean.timer dovecot spamassassin mailvpn mailnn
+    soff radicale mailclean.timer dovecot spamassassin mailvpn mailnn clamav-daemon
     ;;
 esac
 
@@ -2164,7 +2211,8 @@ PATH=/usr/bin:/bin:/usr/local/bin
 MAILTO=alerts@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 * * * *   $u mailtest-check |& log-once -1 mailtest-check
+5-59/5 * * * *   root mailtest-check |& log-once -1 mailtest-check
+0 * * * *   root mailtest-check slow |& log-once -1 mailtest-slow
 EOF
     m sudo rsync -ahhi --chown=root:root --chmod=0755 \
       /b/ds/mailtest-check /b/ds/check-remote-mailqs /usr/local/bin/
index 59b7dce86641cf02b55cee5ecc7fadcc54e8ac8e..9e35a0ac45ac3e8f5ebe4b76d1b84663da038dc6 100755 (executable)
@@ -17,44 +17,119 @@ e() { $int || return 0; printf "mailtest-check: %s\n" "$*"; }
 # 7 minutes, the last 2 haven't arrived in a reasonable amount of time.
 min_limit=7
 
-int=true
-if [[ ! $1 && $- != *i* ]]; then
+
+# spamassassin checking takes about 8 seconds. only do that every
+# once in a while.
+slow=false
+if [[ $1 == slow ]]; then
+  slow=true
+  shift
+fi
+
+# add any argument to say we are running interactively
+if [[ $SUDO_USER && ! $1 ]]; then
+  int=true
+else
   int=false
   sleep 60
 fi
 
 
 folders=(/m/md/l/testignore{,zroe})
+froms=(testignore@je.b8.nz testignore@expertpathologyreview.com testignore@amnimal.ninja)
 case $HOSTNAME in
   bk)
     folders=(/m/md/{expertpathologyreview.com,amnimal.ninja}/testignore)
+    froms=(ian@iankelling.org z@zroe.org testignore@je.b8.nz)
     ;;
   je)
+    froms=(ian@iankelling.org z@zroe.org testignore@expertpathologyreview.com testignore@amnimal.ninja)
     folders=(/m/md/je.b8.nz/testignore)
     ;;
 esac
 
+
 for folder in ${folders[@]}; do
+  for from in ${froms[@]}; do
+    latest=
+    cd $folder
+    last_sec=0
+    # webmail sends them to cur it seems
+    while read -r file; do
+      if [[ $file -nt $latest ]]; then
+        latest=$file
+      fi
+    done < <(grep -rlFx "From: $from" new cur)
 
-  find $folder/new $folder/cur -type f -mmin +300 -delete
-  cd $folder
-  last_sec=0
-  # webmail sends them to cur it seems
-  for file in new/* cur/*; do
-    if [[ $file -nt $latest ]]; then
-      latest=$file
-    fi
-  done
+    if [[ $latest ]]; then
+      e latest = $folder/$latest
+      last_sec=$(awk '/^Subject: / {print $4}' $latest)
+
+      if $slow; then
+        find $folder/new $folder/cur -type f -mmin +300 -delete
+        if [[ ! $spamdpid ]]; then
+          spamdpid=$(systemctl status spamassassin| sed -n '/^ *Main PID:/s/[^0-9]//gp')
+        fi
+        if [[ $spamdpid ]]; then
+          if [[ $(readlink /proc/$$/ns/net) != "$(readlink /proc/$spamdpid/ns/net)" ]]; then
+            spamcpre="nsenter -t $spamdpid -n -m"
+          fi
 
-  if [[ $latest ]]; then
-    e latest = $folder/$latest
-    last_sec=$(awk '/^Subject: / {print $4}' $latest)
-  fi
+          declare -A results
+          for r in $($spamcpre spamc -y <"$latest" |sed 's/,/ /g'); do
+            case $r in
+              # we have a new domain, ignore this.
+              FROM_FMBLA_NEWDOM*) : ;;
+              SPF_HELO_NEUTRAL)
+                # some of my domains use neutral spf, treat them the same.
+                results[SPF_HELO_PASS]=t
+                ;;
+              *)
+                results[$r]=t
+                ;;
+            esac
+          done
+          # debugging
+          # e results = ${!results[@]}
+          missing=()
+          for t in BODY_SINGLE_WORD DKIM_SIGNED DKIM_VALID{,_AU,_EF} SPF_HELO_PASS SPF_PASS TVD_SPACE_RATIO; do
+            if [[ ${results[$t]} ]]; then
+              unset "results[$t]"
+            elif [[ $t == DKIM_VALID_EF && $from == *@[^.]*.[^.]*.[^.]* ]]; then
+              :
+              # third level domains dont hit this. its because
+              # /usr/share/perl5/Mail/SpamAssassin/Plugin/DKIM.pm checks
+              # if its signed with the registryboundaries domain. afaik:
+              # we need the actual domain to sign it, this would result in
+              # a second signature. I only use second level domains for
+              # testing atm, fsf doesnt use them for anything but the
+              # forum and I dont expect that to have any deliverability
+              # problems.  So, not bothering atm.
+            else
+              missing+=($t)
+            fi
+          done
+          if (( ${#results[@]} || ${#missing[@]} )); then
+            printf "$HOSTNAME spamtest %s %s" "$folder" "$from"
+            if (( ${#results[@]} )); then
+              printf " unexpected %s" "${!results[*]}"
+            fi
+            if (( ${#missing[@]} )); then
+              printf " missing %s" "${missing[*]}"
+            fi
+            echo
+          fi
+        else
+          echo $HOSTNAME mailtest spamd pid not found
+        fi
+      fi # if $slow
+    fi # if [[ $latest ]]
 
-  now=$(date +%s)
-  limit=$(( now - 60 * min_limit ))
+    now=$(date +%s)
+    limit=$(( now - 60 * min_limit ))
 
-  if (( last_sec <= limit )); then
-    echo $HOSTNAME mailtest $folder $(date -d @$last_sec +'%a %m-%d %H:%M')
-  fi
+    if (( last_sec <= limit )); then
+      echo $HOSTNAME mailtest $folder $from $(date -d @$last_sec +'%a %m-%d %H:%M')
+    fi
+  done
 done
index b7f4433d5e8460534db3d40a5d3529e51e8f6298..93d31e4462b414166ef33f17bc5fd30378d7ef75 100644 (file)
@@ -67,10 +67,14 @@ write-status() {
   if [[ -e ${glob[0]} ]]; then
     chars+=("A")
   fi
-  tmp=(~/cron-errors/mailtest-check*)
+  tmp=(/var/local/cron-errors/mailtest-check*)
   if (( ${#tmp[@]} )); then
     chars+=("MAILPING")
   fi
+  tmp=(/var/local/cron-errors/mailtest-slow*)
+  if (( ${#tmp[@]} )); then
+    chars+=("SPAMD")
+  fi
 
   if ! qlen=$(/usr/sbin/exiqgrep -o 60 -c -b | awk '{print $1}'); then
     # early in install process, we dont have permission yet for exiqgrep