[pmg-devel] [PATCH pmg-api v2 2/2] add support for before queue filtering

Stoiko Ivanov s.ivanov at proxmox.com
Thu Nov 14 17:35:07 CET 2019


Support for rejecting mails during the SMTP-dialog with 550 (permanent failure)
instead of seemingly accepting the mail (250 OK) and dropping it if it is
rejected by the rule-system is also known as 'before queue filtering' [0].

This patch adds minimal support for before queue filtering to pmg-smtp-filter.

Since pmg-smtp-filter is currently called via LMTP (and the 'scan' service in
'master.cf') we can adapt the already existing branch dealing with SMTP to
send a 550 selectively.

We can reply with 554 (permfail) if all recepients are blocked.

In the case that some accept the mail, we reply with 250 OK.
Depending on the setting of 'ndr_on_block' we generate ndrs
for all blocking recepients. (This is also the behavior that postfix
has when not enabling receiver verification and the downstream server rejects
recepients).

Configuration of before-queue filtering is done via the
'before_queue_filtering' boolean in the 'mail' section of 'pmg.conf':

the before_queue_filtering flag is used when rendering '/etc/postfix/master.cf'
to adapt the needed config-options for both, inbound and outbound, smtpd
servers. The settings were adapted from [0].

[0] http://www.postfix.org/SMTPD_PROXY_README.html

Signed-off-by: Stoiko Ivanov <s.ivanov at proxmox.com>
---
 src/PMG/Config.pm          |  6 ++++++
 src/PMG/SMTP.pm            | 23 ++++++++++++++---------
 src/templates/master.cf.in | 14 ++++++++++++++
 3 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
index e1a9cd4..ed4a665 100755
--- a/src/PMG/Config.pm
+++ b/src/PMG/Config.pm
@@ -627,6 +627,11 @@ sub properties {
 	    minimum => 0,
 	    default => 1
 	},
+	before_queue_filtering => {
+	    description => "Enable before queue filtering by pmg-smtp-filter",
+	    type => 'boolean',
+	    default => 0
+	},
 	ndr_on_block => {
 	    description => "Send out NDR when mail gets blocked",
 	    type => 'boolean',
@@ -666,6 +671,7 @@ sub options {
 	verifyreceivers => { optional => 1 },
 	dnsbl_sites => { optional => 1 },
 	dnsbl_threshold => { optional => 1 },
+	before_queue_filtering => { optional => 1 },
 	ndr_on_block => { optional => 1 },
     };
 }
diff --git a/src/PMG/SMTP.pm b/src/PMG/SMTP.pm
index 00a448e..544d0a5 100644
--- a/src/PMG/SMTP.pm
+++ b/src/PMG/SMTP.pm
@@ -154,16 +154,21 @@ sub loop {
 			}
 		    }
 		} else {
-		    my $all_done = 1;
-
-		    foreach $a (@{$self->{to}}) {
-			if (!($self->{queue}->{status}->{$a} eq 'delivered' ||
-			      $self->{queue}->{status}->{$a} eq 'blocked')) {
-			    $all_done = 0;
+		    my $queueid = $self->{queue}->{logid};
+		    my $qstat = $self->{queue}->{status};
+		    my @rec = keys %$qstat;
+		    my @success_rec = grep { $qstat->{$_} eq 'delivered' } @rec;
+		    my @reject_rec = grep { $qstat->{$_} eq 'blocked' } @rec;
+
+		    if (scalar(@reject_rec) == scalar(@rec)) {
+			$self->reply ("554 5.7.1 Rejected for policy reasons");
+		        syslog('info', "reject mail $queueid");
+		    } elsif ((scalar(@reject_rec) + scalar(@success_rec)) == scalar(@rec)) {
+			$self->reply ("250 2.5.0 OK ($queueid)");
+			if ($cfg->get('mail', 'ndr_on_block')) {
+			    my $dnsinfo = $cfg->get_host_dns_info();
+			    generate_ndr($self->{from}, [ @reject_rec ], $dnsinfo->{fqdn}, $queueid) if scalar(@reject_rec);
 			}
-		    }
-		    if ($all_done) {
-			$self->reply ("250 2.5.0 OK ($self->{queue}->{logid})");
 		    } else {
 			$self->reply ("451 4.4.0 detected undelivered mail");
 		    }
diff --git a/src/templates/master.cf.in b/src/templates/master.cf.in
index cbf4677..b7761ea 100644
--- a/src/templates/master.cf.in
+++ b/src/templates/master.cf.in
@@ -72,13 +72,21 @@
 #               (yes)   (yes)   (yes)   (never) (100)
 # ==========================================================================
 
+[% IF ! pmg.mail.before_queue_filtering -%]
 scan      unix  -       -       n       -       [% pmg.mail.max_filters %]      lmtp
   -o lmtp_send_xforward_command=yes
   -o lmtp_connection_cache_on_demand=no
   -o disable_dns_lookups=yes
+[% END -%]
 
 [% pmg.mail.int_port %]       inet  n -       -       -       [% pmg.mail.max_smtpd_out %]      smtpd
+[% IF pmg.mail.before_queue_filtering -%]
+  -o smtpd_proxy_filter=127.0.0.1:10023
+  -o smtpd_proxy_options=speed_adjust
+  -o smtpd_client_connection_count_limit=[% pmg.mail.conn_count_limit div 5 %]
+[%- ELSE -%]
   -o content_filter=scan:127.0.0.1:10023
+[%- END %]
   -o smtpd_recipient_restrictions=permit_mynetworks,reject_unauth_destination
   -o smtpd_helo_restrictions=
   -o smtpd_client_restrictions=
@@ -87,7 +95,13 @@ scan      unix  -       -       n       -       [% pmg.mail.max_filters %]
 [% pmg.mail.ext_port %]       inet  n -       -       -       1 postscreen
 
 smtpd       pass  - -       -       -       [% pmg.mail.max_smtpd_in %]      smtpd
+[% IF pmg.mail.before_queue_filtering -%]
+  -o smtpd_proxy_filter=127.0.0.1:10024
+  -o smtpd_proxy_options=speed_adjust
+  -o smtpd_client_connection_count_limit=[% pmg.mail.conn_count_limit div 5 %]
+[%- ELSE -%]
   -o content_filter=scan:127.0.0.1:10024
+[%- END %]
   -o receive_override_options=no_address_mappings
   -o smtpd_discard_ehlo_keywords=silent-discard,dsn
   -o mynetworks=127.0.0.0/8,[% postfix.int_ip %]
-- 
2.20.1




More information about the pmg-devel mailing list