3 # Usage: mail-test-check [slow] [anything]
5 # slow: do slow checks, like spamassassin
7 # anything: consider non-interactive, dont print unless something went
11 source /b
/errhandle
/err
13 [[ $EUID == 0 ]] ||
exec sudo
-E "${BASH_SOURCE[0]}" "$@"
17 e
() { $int ||
return 0; printf "mailtest-check: %s\n" "$*"; }
20 ## Minutes before we give error.
21 # We run this cronjob along with sending the test email every 5 minutes,
22 # so give it 1 minute to arrive, then if the latest email is older than
23 # 7 minutes, the last 2 haven't arrived in a reasonable amount of time.
24 # However, when machines reboot things can get delayed, so add 10 mins,
25 # not sure if that is a good number or not.
29 # spamassassin checking takes about 8 seconds. only do that every
32 if [[ $1 == slow
]]; then
38 if [[ $SUDO_USER ||
$SSH_CONNECTION ]]; then
42 if [[ $1 == int
]]; then
46 if [[ $1 == nonint
]]; then
55 # avoid errors like this:
56 # Nov 8 08:16:05.439 [6080] warn: plugin: failed to parse plugin (from @INC): Can't locate Mail/SpamAssassin/Plugin/WLBLEval.pm: lib/Mail/SpamAssassin/Plugin/WLBLEval.pm: Permission denied at (eval 59) line 1.
57 #Nov 8 08:16:05.439 [6080] warn: plugin: failed to parse plugin (from @INC): Can't locate Mail/SpamAssassin/Plugin/VBounce.pm: lib/Mail/SpamAssassin/Plugin/VBounce.pm: Permission denied at (eval 60) line 1.
58 # i dont know why, i just found the solution online
60 # TODO, get je to deliver the local mailbox: /m/md/INBOX
61 # dovecot appears to setup, i can t be sure.
65 folders
=(/m
/md
/{expertpathologyreview.com
,amnimal.ninja
}/testignore
)
66 froms
=(ian@iankelling.org z@zroe.org testignore@je.b8.nz iank@gnu.org
)
69 froms
=(ian@iankelling.org z@zroe.org testignore@expertpathologyreview.com testignore@amnimal.ninja
)
70 folders
=(/m
/md
/je.b8.nz
/testignore
)
73 folders
=(/m
/md
/l
/testignore
)
74 froms
=(testignore@je.b8.nz testignore@expertpathologyreview.com testignore@amnimal.ninja ian@iankelling.org z@zroe.org iank@gnu.org
)
76 timeout
120 rsync
--chown iank
:iank
-e "ssh -oIdentitiesOnly=yes -F /dev/null -i /root/.ssh/jtuttle" -t --inplace -r 'jtuttle@fencepost.gnu.org:/home/j/jtuttle/Maildir/new/' /m
/md
/l
/testignore
/new
82 if [[ ! $spamdpid ||
! -d /proc
/$spamdpid ]]; then
83 # try twice in case we are restarting, it happens.
85 spamdpid
=$
(systemctl show
--property MainPID
--value spamassassin |
sed 's/^[10]$//' ||
:)
86 if [[ $spamdpid ]]; then
95 if [[ -e /var
/lib
/prometheus
/node-exporter
]]; then
96 cat >>/var
/lib
/prometheus
/node-exporter
/mailtest-check.prom.$$
99 # first time we write, overwrite anything existing
100 if [[ -e /var
/lib
/prometheus
/node-exporter
]]; then
101 cat >/var
/lib
/prometheus
/node-exporter
/mailtest-check.prom.$$
<<EOF
102 mailtest_check_found_spamd_pid_bool $(( ${spamdpid:-0} > 0 ))
105 e spamdpid
: $spamdpid
106 if [[ ! $spamdpid ]]; then
107 echo $HOSTNAME mailtest spamd pid not found. systemctl status spamassassin
:
108 systemctl status spamassassin
111 declare -i unexpected
=0
112 for folder
in ${folders[@]}; do
113 for from
in ${froms[@]}; do
117 if ! grep -rlFx "From: $from" $folder/{new
,cur
} >$tmpfile; then
118 e
"no message found from: $from"
121 # webmail sends them to cur it seems
122 while read -r file; do
123 if [[ $file -nt $latest ]]; then
128 if [[ ! $latest ]]; then
129 # 10 is an arbitrary bad value
132 to
=$
(awk '/^Envelope-to: / {print $2}' $latest)
133 last_sec
=$
(awk '/^Subject: / {print $4}' $latest)
137 find $folder/new
$folder/cur
-type f
-mmin +1080 -delete
140 if [[ $spamdpid ]]; then
141 if [[ $
(readlink
/proc
/$$
/ns
/net
) != "$(readlink /proc/$spamdpid/ns/net)" ]]; then
142 spamcpre
="nsenter -t $spamdpid -n -m"
146 # pyzor fails for our test message, so dont put useless load on their
148 # example line that sed is parsing:
149 # (-0.1 / 5.0 requ) DKIM_SIGNED=0.1,DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,SPF_HELO_PASS=-0.001,SPF_PASS=-0.001,TVD_SPACE_RATIO=0.001 autolearn=_AUTOLEARN
150 raw_results
="$($spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$latest" | tail -n2 | head -n1 | sed -r 's/^\([^)]*\) *//;s/=[^, ]*([, ]|$)/ /g')"
151 for r
in $raw_results; do
153 # got this in an update 2022-01. dun care
154 T_SCC_BODY_TEXT_LINE|SCC_BODY_SINGLE_WORD
) : ;;
155 # we have a new domain, ignore this.
156 # it seems like some versions of spamassassin do BODY_SINGLE_WORD, others dont, we dun care.
157 # bayes_00 is a new one indicating ham, we dont care if its missing.
158 BAYES_00|BODY_SINGLE_WORD|FROM_FMBLA_NEWDOM
*|autolearn
) : ;;
160 # some of my domains use neutral spf, treat them the same.
161 results
[SPF_HELO_PASS
]=t
169 # e results = ${!results[@]}
172 keys
=(DKIM_SIGNED DKIM_VALID
{,_AU
,_EF
} SPF_HELO_PASS SPF_PASS TVD_SPACE_RATIO
)
173 if [[ $to == *@gnu.org
&& $from == *@gnu.org
]]; then
174 keys
=(ALL_TRUSTED TVD_SPACE_RATIO
)
175 elif [[ $to == *@gnu.org
]]; then
176 # eggs has RCVD_IN_DNSWL_MED
177 keys
+=(RCVD_IN_DNSWL_MED
)
178 elif [[ $from == *@gnu.org
]]; then
180 keys
+=(RCVD_IN_DNSWL_MED DKIMWL_WL_HIGH
)
183 for t
in ${keys[@]}; do
184 if [[ ${results[$t]} ]]; then
186 elif [[ $t == DKIM_VALID_EF
&& $from == *@
[^.
]*.
[^.
]*.
[^.
]* ]]; then
188 # third level domains dont hit this. its because
189 # /usr/share/perl5/Mail/SpamAssassin/Plugin/DKIM.pm checks
190 # if its signed with the registryboundaries domain. afaik:
191 # we need the actual domain to sign it, this would result in
192 # a second signature. I only use second level domains for
193 # testing atm, fsf doesnt use them for anything but the
194 # forum and I dont expect that to have any deliverability
195 # problems. So, not bothering atm.
200 if (( ${#results[@]} ||
${#missing[@]} )); then
201 printf "$HOSTNAME spamtest %s/%s\n" "$latest"
202 if (( ${#results[@]} )); then
203 printf "unexpected %s" "${!results[*]} "
205 if (( ${#missing[@]} )); then
206 printf "missing %s" "${missing[*]}"
209 echo mailtest-check
: cat $latest:
211 echo mailtest-check
: end of
cat
212 printf "$(tput setaf 5 2>/dev/null ||:)█$(tput sgr0 2>/dev/null||:)%.0s" $
(eval echo "{1..${COLUMNS:-60}}")
216 fi # if [[ $latest ]]
219 limit
=$
(( now
- 60 * min_limit
))
220 age_sec
=$
(( now
- last_sec
))
221 e $
((age_sec
/ 60)):$
(( age_sec
% 60 )) ago. to
:$to from
:$from $latest
223 if (( last_sec
<= limit
)); then
224 echo $HOSTNAME mailtest
$folder $from $
(date -d @
$last_sec +'%a %m-%d %H:%M')
226 # usec = unix seconds
228 mailtest_check_last_usec{folder="$folder",from="$from"} $last_sec
234 mailtest_check_unexpected_spamd_results $unexpected
238 dir
=/var
/lib
/prometheus
/node-exporter
239 if [[ -e $dir ]]; then
240 mv $dir/mailtest-check.prom.$$
$dir/mailtest-check.prom