a nice idea is probably to abuse fail2ban.
you need a jail maybe named "smtp-out", a filter which triggers on each sent email. And you has to build a new action which blocks the OUTGOING iptable for connections to everyone port 25.
HOWEVER, i'm using another way
/usr/local/bin/hitd-mailq-check
Bash:
#! /bin/bash
#
declare -i DEBUG
declare -i ALARM
declare -i ALARM_1
declare -i MAILS_IN_SPOOL
declare -i ALARM_1
DEBUG=0
if [ -n "$1" ] ; then
ALARM_0="$1"
fi
[ "${MAILQ}"] || MAILQ="/usr/sbin/postqueue"
[ "${MAILQOPTION}"] || MAILQOPTION="-p"
[ "${WC}" ] || WC="/usr/bin/wc"
[ "${GREP}" ] || GREP="/usr/bin/grep"
[ "${LOGGER}" ] || LOGGER="/usr/bin/logger"
[ "${LOGPRTY}" ] || LOGPRTY="user.notice"
[ "${BASH}" ] || BASH="/bin/bash"
[ "${ECHO}" ] || ECHO="echo"
[ "${ALARM_1}" ] || ALARM_1=5
[ "${DIRECTMAIL}" ] || DIRECTMAIL="/usr/local/bin/hitd-directmail-mailq-alert"
[ "${DEBUGLOG}" ] || DEBUGLOG="/var/tmp/debug-mailq-check"
[ "${VERBOSE}" ] || VERBOSE="-v"
if [ $((DEBUG)) -eq 0 ]; then
VERBOSE=""
DEBUGLOG="/dev/null"
fi
MAILS_COUNTED=`"${MAILQ}" "${MAILQOPTION}" | "${GREP}" "^[0-9a-fA-F].*" | "$WC" -l`
MAILS_IN_SPOOL=$((MAILS_COUNTED))
[ $((DEBUG)) -lt 4 ] || "${ECHO}" "mails in spool: $((MAILS_IN_SPOOL))"
if [ $((ALARM_1)) -gt $((ALARM_0)) ]; then
ALARM=$((ALARM_1))
else
ALARM=$((ALARM_0))
fi
[ $((DEBUG)) -lt 9 ] || ALARM=-1
if [ $((MAILS_IN_SPOOL)) -gt $((ALARM)) ]; then
"${LOGGER}" -p "${LOGPRTY}" -t "hitd-mailq-check" "ALARM MAILSPOOL $((MAILS_IN_SPOOL)) mails in spool"
BASH_CMD="2>${DEBUGLOG} 1>&2 "
BASH_CMD="${BASH_CMD} ${DIRECTMAIL} \"ALARM MAILSPOOL $((MAILS_IN_SPOOL)) mails in spool\""
BASH_CMD="${BASH_CMD} ; if [ $? -eq 0 ]; then \"${LOGGER}\" -p \"${LOGPRTY}\" -t \"hitd-mailq-check\" \"ALARM MAILSPOOL sent by email\"; fi"
"${BASH}" -c "${BASH_CMD}" &
fi
/usr/local/bin/hitd-directmail-mailq-alert
Perl:
#! /usr/bin/perl -wtT
use strict;
use Email::Sender::Simple qw(sendmail);
use Email::MIME;
use Email::Sender::Transport::SMTP;
use Try::Tiny;
my $SMTP_ENVELOPE_FROM_ADDRESS = '[email protected]';
my $SMTP_ENVELOPE_TO_ADDRESS = '[email protected]';
my $SMTP_HOSTNAME = 'remote.yyyyyy.de';
my $SMTP_PORT = 25;
my $to = "$SMTP_ENVELOPE_TO_ADDRESS";
my $from = "$SMTP_ENVELOPE_FROM_ADDRESS";
my $subject = 'mailq warning xxxxxx.startdedicated.de';
my $message = 'mailq läuft über !';
my $parm = $ARGV[0];
my $msg = Email::MIME->create(
header => [
To => "$to",
From => "$from",
Subject => "$subject"
],
parts => [
Email::MIME->create(
body => "$message\n$parm",
attributes => {
content_type => "text/plain",
encoding => "8bit",
charset => "UTF-8",
}
)
],
);
try {
sendmail(
$msg,
{
from => $SMTP_ENVELOPE_FROM_ADDRESS,
to => $SMTP_ENVELOPE_TO_ADDRESS,
transport => Email::Sender::Transport::SMTP->new({
host => $SMTP_HOSTNAME,
port => $SMTP_PORT,
})
}
);
} catch {
warn "sending failed: $_";
exit 1;
};
exit 0;
These code goes to /usr/local/bin
hitd-mailq-check runs by cron every 10 minutes. Because of graylisting the mailq becomes bigger and bigger.
As long you try only send a mail, cron can run this using a nonprivileged user.
If you got the mail on cellphone you can interrupt the server.
Of course, if running as root you can change to, i think.
DIRECTMAIL="/usr/sbin/systemctl stop postfix.service;/usr/local/bin/hitd-directmail-mailq-alert"
This shutdown postfix and send mail after that. note, that the perl script hitd-directmail-mailq-alert runs without postfix. Usualy you got a mailaccout from your cellphone provider. Another mailserver is mandatory, because postfix was shutdown already or if not, you have to wait until postfix had process all the spam-mails.