I had problems with outgoing spam emails from compromised email accounts sending spam mails from my server and landing on blacklists. So I followed the guidelines for setting up postfix with amavis:

Everything seems to work fine, however I'd like to block outgoing spam/virus emails and rather notify the sender, that his e-mail has been rejected (via MAILER-DAEMON for example). Is there any possibility to do this? I only was able to figure out how to:

  • Reject the mail completely and not notify the sender (which isn't really good, is it?):
    Aug 25 12:05:35 ns207813 amavis[24728]: (24728-01) Blocked SPAM {NoBounceOpenRelay,Quarantined}, <root@mail.org> -> <john@example.com>, quarantine: J/spam-Jfuzg0ScCmKf.gz, Message-ID: <GTUBE1.1010101@example.net>, mail_id: Jfuzg0ScCmKf, Hits: 1004.054, size: 935, 2013 ms
  • Send the mail anyways (but marked as "SPAM"):
    Aug 25 12:19:10 ns207813 amavis[25182]: (25182-01) Passed SPAM {RelayedTaggedInbound,Quarantined}, []:65071 [] <removed@mail.me> -> <removed@mail.com>, quarantine: i/spam-iy3rVCiRk8k2.gz, Queue-ID: 5B9D722AAA, Message-ID: <74576B87-1986-4179-A262-B96640387C9E@mail.me>, mail_id: iy3rVCiRk8k2, Hits: 999.001, size: 2663, queued_as: DD67222ABE, 1379 ms

My current amavis settings are:

$sa_spam_subject_tag = '[SPAM] ';
$sa_tag_level_deflt  = undef;  # add spam info headers if at, or above that level
$sa_tag2_level_deflt = 5; # add 'spam detected' headers at that level
$sa_kill_level_deflt = 20; # triggers spam evasive actions
$sa_dsn_cutoff_level = 10;   # spam level beyond which a DSN is not sent

$final_virus_destiny      = D_DISCARD;  # (data not lost, see virus quarantine)
$final_banned_destiny     = D_BOUNCE;   # D_REJECT when front-end MTA
$final_spam_destiny       = D_PASS;
$final_bad_header_destiny = D_PASS;     # False-positive prone (for spam)

I'd also like to mark incoming viruses as a virus instead of rejecting the mail completely, if possible (so no mail gets lost).

Thank you for your help!

  • Why don't you just take illegitimate users off your network rather than allowing them to remain and trying to fight their spam in real time? – Peter Aug 25 '14 at 11:50
  • Because it takes much time and effort and constant surveilance – machete Aug 25 '14 at 12:13

What you want to do requires to treat mails from users using your server as their MSA (i.e. their outgoing relay) with a different policy than those received from 3rd parties (i.e. when your mailserver is acting in it's MX role). Fortunately, amavis has just the right tool for you: Policy Banks.

Let's see how you could define a policy for your users:

$policy_bank{'PREQ-SUB'} = {
        originating => 1, # indicates client is ours, allows signing
        final_spam_destiny => D_DISCARD, # discard spam
        final_virus_destiny => D_DISCARD, # discard spam
        warnspamsender => 1, # send a warning 
        forward_method => 'smtp:', # you probably need to adjust this
        smtpd_discard_ehlo_keywords => ['8BITMIME'], # force mail conversion to Q/P
        smtpd_greeting_banner => '${helo-name} ${protocol} ${product} SUBMISSION service ready',
        spam_admin_maps  => ["postmaster\@example.net"],  # warn of spam from us
        virus_admin_maps => ["postmaster\@example.net"],  # warn of viruses from us

From the naming of this policy bank, you can already guess that I'm running this as a pre-queue filter that get's triggered if mail is delivered via the submission TCP port 587. To make this configuration work, I told my Postfix MTA to deliver mails that the submission service received to localhost on port 10028 (whereas, when acting as public MX, the server forwards mail to port 10024). To activate two ports in amavis and bind the PREQ-SUB policy to port 10028, I'm using these settings:

# policy bank definition
$inet_socket_port = [10024, 10028];  # listen on listed inet tcp ports
$interface_policy{'10028'} = 'PREQ-SUB'; # mail submitted using TLS on submission/smtps port

The corresponding master.cf entry for Postfix is:

submission inet n - - - - smtpd -o smtpd_tls_security_level=encrypt 
  -o tls_preempt_cipherlist=$submission_tls_preempt_cipherlist 
  -o smtpd_tls_protocols=$submission_smtpd_tls_protocols 
  -o smtpd_tls_ciphers=$submission_smtpd_tls_ciphers 
  -o smtpd_tls_exclude_ciphers=$submission_smtpd_tls_exclude_ciphers 
  -o smtpd_sasl_auth_enable=yes 
  -o smtpd_recipient_restrictions=$submission_smtpd_recipient_restrictions 
  -o milter_macro_daemon_name=ORIGINATING 
  -o smtpd_proxy_filter= 
  -o syslog_name=postfix-submission/smtpd
  -o receive_override_options=no_header_body_checks

Note that this does actually do a bit more than simply send mails to amavis, e.g. set cipher lists and so on (you'll notice the main.cf variable references).

So, what can you do if your users do NOT submit their mail on port 587, or not all of them do? Well, you'll have to leave the land of 100% certainty then. amavis can analyze the contents of a mail and act on the presence of headers. One such headers could be the authenticated user's name which Postfix adds if you set smtpd_sasl_authenticated_header = yes. You could then tell amavis to act on this header:

package Amavis::Custom;
use strict;
        import Amavis::Conf qw(:platform :confvars c cr ca $myhostname);
        import Amavis::Util qw(do_log untaint safe_encode safe_decode);
        import Amavis::rfc2821_2822_Tools;
        import Amavis::Notify qw(build_mime_entity);
sub new {
        my($class,$conn,$msginfo) = @_;
        my($self) = bless {}, $class;
        my $auth_sender = 0;
        foreach my $line (@{$msginfo->{'orig_header'}}) {
                $line =~ s/\n    / /g;
                # WARNING: you need to improve this to AT LEAST also match
                # for your OWN mail servers name!
                $auth_sender = 1 if $line =~ m/^Authenticated sender/i;
        if ($auth_sender) {
                do_log(2, sprintf("Load pre-queue submission policy bank"));
        return $self;
1;  # insure a defined return

Please don't ignore the warning within this code: Headers are easily fakeable, and other mail servers could insert an "Authenticated sender" header, too, so better match for something like "your-mailserver.example.net.*Authenticated sender".

On a final note, regarding one of your comments: Running a mail server DOES take a lot of time, and requires you to constantly monitor it for abuse. There is not "get out of jail free" card when it comes to taking part in the global email system!

| improve this answer | |
  • Thank you very much for your extensive answer. I'll try to have a look into the details tonight or tomorrow. Unfortunately I do not have enough reputation here to upvote your comment, but rest assured that your answer is much appreciated! – machete Aug 26 '14 at 10:25
  • You don't need reputation to accept the answer ;) – Pharaoh Sep 11 '14 at 12:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.