mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
When running a diff for dkimpy's output, we expect that diff to exit with non-zero code. Unfortunately, the way we set that expectation (by prefixing the diff invocation with `!` is incorrect. Running `! diff ...` will not cause the hook to fail if diff exits with 0, instead `!` will cause the exit code to be ignored. This patch fixes the problem by running `diff ... && exit 1` instead. This was caught by shellcheck, https://www.shellcheck.net/wiki/SC2251.
124 lines
3.7 KiB
Bash
Executable File
124 lines
3.7 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# This file is an example post-data hook that will run standard filtering
|
|
# utilities if they are available.
|
|
#
|
|
# - greylist (from greylistd) to do greylisting.
|
|
# - spamc (from Spamassassin) to filter spam.
|
|
# - rspamc (from rspamd) or chasquid-rspamd to filter spam.
|
|
# - clamdscan (from ClamAV) to filter virus.
|
|
# - dkimsign (from driusan/dkim or dkimpy) to do DKIM signing.
|
|
#
|
|
# If it exits with code 20, it will be considered a permanent error.
|
|
# Otherwise, temporary.
|
|
|
|
set -e
|
|
|
|
|
|
# Note greylistd needs you to add the user to the "greylist" group:
|
|
# usermod -a -G greylist mail
|
|
if [ "$AUTH_AS" == "" ] && [ "$SPF_PASS" == "0" ] && \
|
|
command -v greylist >/dev/null && \
|
|
groups | grep -q greylist;
|
|
then
|
|
REMOTE_IP=$(echo "$REMOTE_ADDR" | rev | cut -d : -f 2- | rev)
|
|
if ! greylist update "$REMOTE_IP" "$MAIL_FROM" 1>&2; then
|
|
echo "greylisted, please try again"
|
|
exit 75 # temporary error
|
|
fi
|
|
echo "X-Greylist: pass"
|
|
fi
|
|
|
|
|
|
TF="$(mktemp --tmpdir post-data-XXXXXXXXXX)"
|
|
trap 'rm "$TF"' EXIT
|
|
|
|
# Save the message to the temporary file, so we can pass it on to the various
|
|
# filters.
|
|
cat > "$TF"
|
|
|
|
|
|
if command -v spamc >/dev/null; then
|
|
if ! SL=$(spamc -c - < "$TF") ; then
|
|
echo "spam detected"
|
|
exit 20 # permanent
|
|
fi
|
|
echo "X-Spam-Score: $SL"
|
|
fi
|
|
|
|
|
|
# Spam filter through rspamd.
|
|
#
|
|
# Use chasquid-rspamd (from https://github.com/Thor77/chasquid-rspamd) if
|
|
# available, otherwise fall back to rspamc.
|
|
if command -v chasquid-rspamd >/dev/null; then
|
|
chasquid-rspamd < "$TF" 2>/dev/null
|
|
elif command -v rspamc >/dev/null; then
|
|
# Note the actions emitted by rspamc come from the thresholds
|
|
# configured in /etc/rspamd/actions.conf.
|
|
# The ones handled here are common defaults, but they might require
|
|
# adjusting to match your rspamd configuration.
|
|
# Note that greylisting is disabled in rspamc by design, so the
|
|
# "greylist" action is ignored here to prevent false rejections.
|
|
ACTION=$( rspamc < "$TF" 2>/dev/null | grep Action: | cut -d " " -f 2- )
|
|
case "$ACTION" in
|
|
reject)
|
|
echo "spam detected"
|
|
exit 20 # permanent error
|
|
;;
|
|
esac
|
|
echo "X-Spam-Action:" "$ACTION"
|
|
fi
|
|
|
|
|
|
if command -v clamdscan >/dev/null; then
|
|
if ! clamdscan --no-summary --infected - < "$TF" 1>&2 ; then
|
|
echo "virus detected"
|
|
exit 20 # permanent
|
|
fi
|
|
echo "X-Virus-Scanned: pass"
|
|
fi
|
|
|
|
# DKIM sign with either driusan/dkim or dkimpy.
|
|
#
|
|
# Do it only if all the following are true:
|
|
# - User has authenticated.
|
|
# - dkimsign binary exists.
|
|
# - domains/$DOMAIN/dkim_selector file exists.
|
|
# - certs/$DOMAIN/dkim_privkey.pem file exists.
|
|
#
|
|
# Note this has not been thoroughly tested, so might need further adjustments.
|
|
if [ "$AUTH_AS" != "" ] && command -v dkimsign >/dev/null; then
|
|
DOMAIN=$( echo "$MAIL_FROM" | cut -d '@' -f 2 )
|
|
|
|
if [ -f "domains/$DOMAIN/dkim_selector" ] \
|
|
&& [ -f "certs/$DOMAIN/dkim_privkey.pem" ];
|
|
then
|
|
# driusan/dkim and dkimpy both provide the same binary (dkimsign) but
|
|
# take different arguments, so we need to tell them apart.
|
|
# This is awful but it should work reasonably well.
|
|
if dkimsign --help 2>&1 | grep -q -- --identity; then
|
|
# dkimpy
|
|
dkimsign \
|
|
"$(cat "domains/$DOMAIN/dkim_selector")" \
|
|
"$DOMAIN" \
|
|
"certs/$DOMAIN/dkim_privkey.pem" \
|
|
< "$TF" > "$TF.dkimout"
|
|
# dkimpy doesn't provide a way to just show the new
|
|
# headers, so we have to compute the difference.
|
|
# ALSOCHANGE(test/t-19-dkimpy/config/hooks/post-data)
|
|
diff --changed-group-format='%>' \
|
|
--unchanged-group-format='' \
|
|
"$TF" "$TF.dkimout" && exit 1
|
|
rm "$TF.dkimout"
|
|
else
|
|
# driusan/dkim
|
|
dkimsign -n -hd \
|
|
-key "certs/$DOMAIN/dkim_privkey.pem" \
|
|
-s "$(cat "domains/$DOMAIN/dkim_selector")" \
|
|
-d "$DOMAIN" \
|
|
< "$TF"
|
|
fi
|
|
fi
|
|
fi
|