• If you are still using CentOS 7.9, it's time to convert to Alma 8 with the free centos2alma tool by Plesk or Plesk Migrator. Please let us know your experiences or concerns in this thread:
    CentOS2Alma discussion

Question How to reject incomming SPAM mail instead of Delete, Move or just Mark

S. Schröder

New Pleskian
Is it possible in a Plesk/spamassassin/postfix environment to reject mails that exceed a certain spam value instead of moving, deleting or just marking them?

The normal way as in an environment without Plesk using
"/etc/postfix/header_checks"
and
/^X-Spam-Level: \*{10,}.*/ REJECT
does not work because the postfix header_checks are executed before spamassassin.
"X-Spam-Level:" does not exist at this time.

Regards,
Stephan
 
Last edited:
Hi Stephan,

I had the same question for a long time and I did not find a easy solution which is easy like the one you suggested and works with Plesk. Maybe we should start a feature request for Plesk.
My Solution is that on my system is running the Spamassassin for my customers and I also inject headers and body checks to directly reject emails which are annoying me and my customers very frequently. Therefore I have a DB and php script which is producing the header and boy rules and also creates Spamassassin rules, for the day when another solution is coming and for rules below the $SPAM_VALUE.

With the value of $SPAM_VALUE = 4 you can set which rules from your DB will result in direct reject trough the body/header rules.

Here is my PHP script an small example of my DB, which of course could be improved but it is just my own solution. Maybe it helps you.

PHP:
<?php

//Database
$USER = "spam_database";
$PASS = "PASSWORD";
$HOST = "localhost";
$DBNM = "spamdb";

$SPA_FILE = "/var/lib/spamassassin/3.004001/your_folder/60_prefix_spamrules.cf"; # Path to SPAMASSISSIN Rule File
$SPA_PREFIX = "YOURPREFIX";
$SPA_DESC_PREFIX = "MY SPAM Protection";
$SPA_CHECK_CMD = "spamassassin --lint";
$SPA_RELOAD_CMD = "/etc/init.d/spamassassin reload";

$PF_DESC_PREFIX = "YOURPREFIX_SPAM";
$PF_HEADER_FILE = "/etc/postfix/header_checks";
$PF_BODY_FILE = "/etc/postfix/body_checks";
$PF_ACTION = "REJECT";
$PF_RELOAD_CMD = "postfix reload";

$SPAM_VALUE = 4;

$PF_PRE_RULE_SET = "/^X-Spam-Flag: .YES/ REJECT SPAM" . "\n";
$PF_PRE_RULE_SET .= "/^\s*(Received: from)[^\\n]*(.*)/ REPLACE $1 [127.0.0.1] (localhost [127.0.0.1])$2" . "\n";
$PF_PRE_RULE_SET .= "/^\s*X-Originating-IP:/ IGNORE" . "\n";
$PF_PRE_RULE_SET .= "/^\s*X-Mailer: .*/ IGNORE" . "\n";
$PF_PRE_RULE_SET .= "/^\s*User-Agent/ IGNORE" . "\n";
$PF_PRE_RULE_SET .= "/^\s*X-Enigmail/ IGNORE" . "\n";
$PF_PRE_RULE_SET .= "/^\s*X-KMail .*/ IGNORE" . "\n";

$pdo = new PDO("mysql:host=localhost;dbname=$DBNM", $USER, $PASS);

$spa_rules = "";
$postfix_header_rules = "# ANONYMIZE HEADERS\n" . "# #####################################################################" . "\n" . $PF_PRE_RULE_SET . "\n\n" . "# SPAM RULES" . "\n" . "# #####################################################################" . "\n";
$postfix_body_rules = "# SPAM RULES" . "\n" . "# #####################################################################" . "\n";
$output = "";
$spa_count = 0;
$pf_header_count = 0;
$pf_body_count = 0;

$sql = "SELECT rule_id, type, header, value, score FROM rules";
foreach ($pdo->query($sql) as $row) {
    $name = $SPA_PREFIX . "_" . $row['type'] . "_" . $row['rule_id'];
    $name = strtoupper($name);
    if ( !empty($row['header']) ) {
        $delimiter = $row['header'] . ':=~';
    } else{
        $delimiter = '';
    }
    $spa_rules .= $row['type'] . " " . $name . "\t\t" . $delimiter . "/" . utf8_encode( spa_replace_special_chars($row['value']) ) . "/" . "\n";
    $spa_rules .= "describe " . $name . "\t\t" . strtoupper($SPA_DESC_PREFIX . " " . $row['type'] . " " . $row['rule_id']) . "\n";
    $spa_rules .= "score " . $name . "\t\t" . $row['score'] . "\n" . "\n";
    $spa_count++;
    // Postfix Header Rules
    if (!empty($row['header']) && $row['score'] >= $SPAM_VALUE) {
        $pf_value = utf8_encode( $row['value'] );
        $postfix_header_rules .= "/^" . $row['header'] . ":.*" . postfix_replace_special_chars($pf_value) . "/" . "\t\t" . $PF_ACTION . " " . $PF_DESC_PREFIX . "_" . strtoupper($row['type']) . "_" . $row['rule_id'] . "\n";
        $pf_header_count++;
    }
    // Postfix Body Rules
    if ($row['type'] === 'body' && $row['score'] >= $SPAM_VALUE) {
        $pf_value = utf8_encode( $row['value'] );
        $postfix_body_rules .= "/" . postfix_replace_special_chars($pf_value) . "/" . "\t\t" . $PF_ACTION . " " . $PF_DESC_PREFIX . "_" . strtoupper($row['type']) . "_" . $row['rule_id'] . "\n";
        $pf_body_count++;
    }
}


$output .= "Start writing to Files" . "\n";
$spa_bytes = file_put_contents($SPA_FILE, $spa_rules);
if ($spa_bytes != false && $spa_bytes > 0){
$output .= $spa_count . " " . "SpamAssassin Rules created" . " with " . $spa_bytes . " Bytes" . "\n";
} else {
$output .= "Error writing SpamAssassin Rules". "\n";
}

$pf_header_bytes = file_put_contents($PF_HEADER_FILE, $postfix_header_rules);
if ($pf_header_bytes != false && $pf_header_bytes > 0){
$output .= $pf_header_count . " " . "Postfix HEADER Rules created" . " with " . $pf_header_bytes . " Bytes" . "\n";
} else {
$output .= "Error writing Postfix HEADER Rules". "\n";
}

$pf_body_bytes = file_put_contents($PF_BODY_FILE, $postfix_body_rules);
if ($pf_body_bytes != false && $pf_body_bytes > 0){
$output .= $pf_body_count . " " . "Postfix BODY Rules created" . " with " . $pf_body_bytes . " Bytes" . "\n";
} else {
$output .= "Error writing Postfix BODY Rules". "\n";
}

$output .= shell_exec($SPA_CHECK_CMD); // Restart Spamassassin
$output .= "Restart SpamAssassin:". "\n";
$output .= shell_exec($SPA_RELOAD_CMD); // Restart Spamassassin
$output .= "\n";
$output .= "Restart Postfix:". "\n";
$output .= shell_exec($PF_RELOAD_CMD); // Restart Postfix
$output .= "\n";
echo "$output";


function postfix_replace_special_chars($string){
    $search = array("Ä", "Ö", "Ü", "ä", "å", "ö", "ü", "ß", "è", "é", "€", "@", "&", ">", "<", "°", "#", ":", " ", "-", "«", "»", "%","?", "/", "*");
    $replace = array(".*", ".*", ".*", ".*", ".*", ".*", ".*", ".*", ".*", ".*", ".*", "\@", ".*", ".*", ".*", ".*", "\#", "\:", ".", ".", "\«", "\»", "\%", "\?", "\/", ".*");
    return str_replace($search, $replace, $string);
}

function spa_replace_special_chars($string){
    $search = array("€", "@", "&", ">", "<", "°", "#", ":", "%");
    $replace = array(".*", "\@", ".*", ".*", ".*", ".*", "\#", "\:", "\%");
    return str_replace($search, $replace, $string);
}


Code:
-- phpMyAdmin SQL Dump
-- version 4.8.3
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Erstellungszeit: 10. Okt 2018 um 11:32
-- Server-Version: 5.7.23-0ubuntu0.16.04.1
-- PHP-Version: 7.1.14

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Datenbank: `spamdb`
--

-- --------------------------------------------------------

--
-- Tabellenstruktur für Tabelle `rules`
--

CREATE TABLE `rules` (
  `rule_id` int(11) NOT NULL,
  `type` varchar(30) NOT NULL,
  `header` varchar(50) NOT NULL,
  `value` varchar(500) NOT NULL,
  `score` float NOT NULL,
  `note` varchar(500) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Daten für Tabelle `rules`
--

INSERT INTO `rules` (`rule_id`, `type`, `header`, `value`, `score`, `note`) VALUES
(1, 'body', '', 'p.pimpor.store', 3, ''),
(3, 'body', '', 'Notice #: 345384', 2, ''),
(4, 'header', 'Subject', 'Natural penis enlargement', 3, ''),
(5, 'header', 'Subject', 'Mercane 2017 New E-scooter with FOB Price', 4, ''),
(6, 'header', 'Subject', '*Expiration SEO', 5.5, ''),
(7, 'uri', '', 'trudoce.gdn', 5, ''),
(8, 'uri', '', 'm.bereza.store', 5, ''),
(9, 'header', 'Subject', 'Generika Potenzmittel', 5, ''),
(10, 'body', '', 'Schnarcht nachts deine Liebste? Dieses Gegenmittel wirkt sofort', 5, ''),
(11, 'body', '', 'ALLES DICHT LED Reflektor', 5, ''),
(12,'header','List-Unsubscribe','http://*/ru/unsubscribe/do?hash=*',5,'')

--
-- Indizes der exportierten Tabellen
--

--
-- Indizes für die Tabelle `rules`
--
ALTER TABLE `rules`
  ADD PRIMARY KEY (`rule_id`);

--
-- AUTO_INCREMENT für exportierte Tabellen
--

--
-- AUTO_INCREMENT für Tabelle `rules`
--
ALTER TABLE `rules`
  MODIFY `rule_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=14;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
 
Last edited:
You can integrate SpamAssassin in Postfix directly using either libmilter interface (e.g. via spamass-milter) or Postfix after-queue content filter, say with Amavis. The only drawback in this case is that, for the mailboxes where the user has enabled spam filtering in Plesk, the email will be scanned by SA twice if it doesn't get rejected first time.
 
You can integrate SpamAssassin in Postfix directly using either libmilter interface (e.g. via spamass-milter) or Postfix after-queue content filter, say with Amavis. The only drawback in this case is that, for the mailboxes where the user has enabled spam filtering in Plesk, the email will be scanned by SA twice if it doesn't get rejected first time.

Do you have a how to tutorial or steps to reproduce to implement your idea?
 
Because Spamassassin is already on the server, this solution probably help:

1. Editing Postfix

We need to configure Postfix so that it can pipe mails through SpamAssassin. So run the command below to edit Postfix main configurations file:

Code:
$ sudo nano  /etc/postfix/master.cf

Look for the line:

Code:
smtp      inet  n       -       y       -       -       smtpd

And change to:

Code:
smtp      inet  n       -       y       -       -       smtpd  -o content_filter=spamassassin

Also, you need to add the line below to setup after queue content filter:

Code:
 spamassassin unix - n n - - pipe user=spamd argv=/usr/bin/spamc -f -e  /usr/sbin/sendmail -oi -f ${sender} ${recipient}

I think should be before this line:

Code:
plesk_virtual unix - n n - - pipe flags=DORhu user=popuser:popuser argv=/usr/lib/plesk-9.0/postfix-local -f ${sender} -d ${recipient} -p /var/qmail/mailnames


2. Restart Postfix and SpamAssassin

For the changes to take effect, you need to restart Postfix and SpamAssassin using the commands below:

Code:
$ sudo service postfix restart
$ sudo service spamassassin restart
 
Last edited:
Back
Top