[pmg-devel] [RFC pmg-api 4/4] add support for before queue filtering

Stoiko Ivanov s.ivanov at proxmox.com
Tue Nov 12 15:16:14 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 only reply with 554 (permfail) if all recepients are blocked.
In the case that some accept the mail, we reply with 250 OK and generate bounces
for all blocking recepients by default. (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 currently dependent on 2 booleans
in the 'mail' section of 'pmg.conf':
* before_queue_filtering (whether to enable it at all defaulting to 0)
* before_queue_ndr (whether to send out the NDRs or silently ignore that some
  recepients did not get the mail (the same behaviour as with our current
  after-queue filtering)

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          | 12 ++++++++++++
 src/PMG/SMTP.pm            | 24 +++++++++++++++---------
 src/templates/master.cf.in | 14 ++++++++++++++
 3 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
index 4cbc520..ccff002 100755
--- a/src/PMG/Config.pm
+++ b/src/PMG/Config.pm
@@ -627,6 +627,16 @@ sub properties {
 	    minimum => 0,
 	    default => 1
 	},
+	before_queue_filtering => {
+	    description => "Enable before queue filtering by pmg-smtp-filter",
+	    type => 'boolean',
+	    default => 0
+	},
+	before_queue_ndr => {
+	    description => "Send out NDR when some recepients reject a mail in a multirecepient smtp-session",
+	    type => 'boolean',
+	    default => 1
+	},
     };
 }
 
@@ -661,6 +671,8 @@ sub options {
 	verifyreceivers => { optional => 1 },
 	dnsbl_sites => { optional => 1 },
 	dnsbl_threshold => { optional => 1 },
+	before_queue_filtering => { optional => 1 },
+	before_queue_ndr => { optional => 1 },
     };
 }
 
diff --git a/src/PMG/SMTP.pm b/src/PMG/SMTP.pm
index b311265..8674b45 100644
--- a/src/PMG/SMTP.pm
+++ b/src/PMG/SMTP.pm
@@ -148,16 +148,22 @@ 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}");
+			my $cfg = $data->{pmg_cfg};
+			if ($cfg->get('mail', 'before_queue_ndr')) {
+			    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