
jam at jamux
Mar 2, 2007, 7:16 AM
Post #1 of 3
(1918 views)
Permalink
|
|
postfix-policyd-spf-perl white listing howto?
|
|
How does one apply different restrictions to messages that get a SPF pass from postfix-policyd-spf-perl than are applied to messages that do not? Here is an approach used in one form or another since late 2003. The present version is adapted from postfix-policyd-spf-perl-2.002 and is in use with Postfix-2.3.7-4 on Debian. It follows the suggestions given by Wietse Venema in the Postfix RESTRICTION_CLASS_README document under "Postfix Per-Client/User/etc. Access Control". This adds "/SPF pass or not" before Wietse's "/etc." above thus providing "Postfix White Listing SPF pass messages Per-Client/User/etc." I would welcome comments and suggestions for improvement. The present implementation is composed of three parts: 1. A modified postfix-policyd-spf-perl here called postfix-policyd-jam-spf-perl. 2. An additional 'helper' policy daemon called postfix-policyd-passed-spf-perl. 3. The Postfix restriction classes and restrictions needed to glue the above together. The postfix-policyd-jam-spf-perl differs from postfix-policyd-spf-perl by returning restriction classes as actions for SPF pass where postfix-policyd-spf-perl would return a PREPEND action or no action. The restriction class returned distinguish between HELO PASS and MFROM PASS and whether PREPEND is needed. A diff is shown in the first boxquote below. The additional helper policy daemon produces PREPEND actions corresponding to those not produced by postfix-policyd-jam-spf-perl. The helper daemon consists mainly of print statements that produce the Received-SPF text from the given policy attributes and fixed text selected by the particular restriction class produced by postfix-policyd-jam-spf-perl. The resulting text is the same as would have been produced by postfix-policyd-spf-perl except that the matching mechanism is omitted. (The matching mechanism does however appear in the logs produced by postfix-policyd-jam-spf-perl.) The script consists mainly of what remains after replacing much of the code in postfix-policyd-spf-perl with two print statements. A copy of the entire postfix-policyd-passed-spf-perl is shown in the second boxquote below. The USAGE immediately below shows how to glue the above together. An understanding or the Postfix RESTRICTION_CLASS_README may be helpful. USAGE (Postfix-2.3.7-4 on Debian) ===== /etc/postfix/master.cf: policy-spf unix - n n - - spawn user=nobody argv=/usr/bin/perl /usr/lib/postfix/postfix-policyd-jam-spf-perl policy-passed-spf unix - n n - - spawn user=nobody argv=/usr/bin/perl /usr/lib/postfix/postfix-policyd-passed-spf-perl /etc/postfix/main.cf: smtpd_policy_service_timeout = 3600s smtpd_restriction_classes = mfrom_passed_spf mfrom_passed_spf_continue helo_passed_spf helo_passed_spf_continue normal smtpd_recipient_restrictions = reject_unauth_destination check_policy_service unix:private/policy-spf normal # from check_policy_service unix:private/policy-spf mfrom_passed_spf = check_policy_service unix:private/policy-passed-spf mfrom_passed_spf_continue mfrom_passed_spf_continue = # Restrictions placed here will be applied only after mfrom pass normal # from check_policy_service unix:private/policy-spf helo_passed_spf = check_policy_service unix:private/policy-passed-spf helo_passed_spf_continue helo_passed_spf_continue = # Restrictions placed here will be applied only after helo pass normal normal = # Restrictions placed here may be applied after all of the above permit ===== ,----[ diff -c postfix-policyd-spf-perl postfix-policyd-jam-spf-perl ] *** postfix-policyd-spf-perl Tue Feb 20 05:52:33 2007 --- postfix-policyd-jam-spf-perl Thu Mar 1 01:07:48 2007 *************** *** 1,6 **** #!/usr/bin/perl # http://www.openspf.org/Software # version 2.002 # --- 1,6 ---- #!/usr/bin/perl # http://www.openspf.org/Software # version 2.002 # *************** *** 22,27 **** --- 22,30 ---- # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + # Modified by John A. Martin to enable postfix spf-pass access restrictions + # requires postfix-policyd-passed-spf-perl 27 Feb 2007 + use version; our $VERSION = qv('2.002'); use strict; *************** *** 233,240 **** return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } elsif ($attr->{sender} eq '') { ! return "PREPEND $helo_spf_header" ! unless $cache->{added_spf_header}++; } # ------------------------------------------------------------------------- --- 236,248 ---- return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } elsif ($attr->{sender} eq '') { ! if ($helo_result->is_code('pass')) { ! return "helo_passed_spf" unless $cache->{added_spf_header}++; ! return "helo_passed_spf_continue"; ! } else { ! return "PREPEND $helo_spf_header" ! unless $cache->{added_spf_header}++; ! } } # ------------------------------------------------------------------------- *************** *** 291,296 **** --- 299,308 ---- elsif ($mfrom_result->is_code('temperror')) { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; } + elsif ($mfrom_result->is_code('pass')) { + return "mfrom_passed_spf" unless $cache->{added_spf_header}++; + return "mfrom_passed_spf_continue"; + } else { return "PREPEND $mfrom_spf_header" unless $cache->{added_spf_header}++; `---- ,----[ cat postfix-policyd-passed-spf-perl ] #!/usr/bin/perl # postfix-policyd-passed-spf-perl # http://www.openspf.org/Software # version 2.002 # # (C) 2007 Scott Kitterman <scott [at] kitterman> # (C) 2007 Julian Mehnle <julian [at] mehnle> # (C) 2003-2004 Meng Weng Wong <mengwong [at] pobox> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Modified by John A. Martin to assist postfix-policyd-jam-spf-perl # to enable postfix spf-pass access restrictions 27 Feb 2007 use version; our $VERSION = qv('2.002'); use strict; use IO::Handle; use Sys::Syslog qw(:DEFAULT setlogsock); use NetAddr::IP; use Sys::Hostname; # ---------------------------------------------------------- # configuration # ---------------------------------------------------------- # Adding more handlers is easy: my @HANDLERS = ( { name => 'exempt_localhost', code => \&exempt_localhost }, { name => 'spf_mfrom_passed', code => \&spf_mfrom_passed }, { name => 'spf_helo_passed', code => \&spf_helo_passed } ); my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; # NOTE: myhostname wants to agree with postfix's notion of myhostname my $myhostname = (gethostbyname(Sys::Hostname::hostname))[0]; # # Syslogging options for verbose mode and for fatal errors. # NOTE: comment out the $syslog_socktype line if syslogging does not # work on your system. # my $syslog_socktype = 'unix'; # inet, unix, stream, console my $syslog_facility = 'mail'; my $syslog_options = 'pid'; my $syslog_ident = 'postfix/policy-passed-spf'; use constant localhost_addresses => map( NetAddr::IP->new($_), qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) ); # Does Postfix ever say "client_address=::ffff:<ipv4-address>"? # ---------------------------------------------------------- # initialization # ---------------------------------------------------------- # # Log an error and abort. # sub fatal_exit { syslog(err => "fatal_exit: @_"); syslog(warning => "fatal_exit: @_"); syslog(info => "fatal_exit: @_"); die("fatal: @_"); } # # Unbuffer standard output. # STDOUT->autoflush(1); # # This process runs as a daemon, so it can't log to a terminal. Use # syslog so that people can actually see our messages. # setlogsock($syslog_socktype); openlog($syslog_ident, $syslog_options, $syslog_facility); # ---------------------------------------------------------- # main # ---------------------------------------------------------- # # Receive a bunch of attributes, evaluate the policy, send the result. # my %attr; while (<STDIN>) { chomp; if (/=/) { my ($key, $value) =split (/=/, $_, 2); $attr{$key} = $value; next; } elsif (length) { syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); next; } if ($VERBOSE) { for (sort keys %attr) { syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); } } my $action = $DEFAULT_RESPONSE; foreach my $handler (@HANDLERS) { my $handler_name = $handler->{name}; my $handler_code = $handler->{code}; my $response = $handler_code->(attr => \%attr); if ($VERBOSE) { syslog(debug => "handler %s: %s", $handler_name, $response); } # Pick whatever response is not 'DUNNO' if ($response and $response !~ /^DUNNO/i) { syslog(info => "handler %s: is decisive.", $handler_name); $action = $response; last; } } syslog(info => "%s: Policy action=%s", $attr{queue_id}, $action); STDOUT->print("action=$action\n\n"); %attr = (); } # ---------------------------------------------------------- # handler: localhost exemption # ---------------------------------------------------------- sub exempt_localhost { my %options = @_; my $attr = $options{attr}; if ($attr->{client_address} != '') { my $client_address = NetAddr::IP->new($attr->{client_address}); return 'PREPEND X-Comment SPF not applicable to localhost connection, skipped check' if grep($_->contains($client_address), localhost_addresses); }; return 'DUNNO'; } # ---------------------------------------------------------- # handler: spf mfrom passed # ---------------------------------------------------------- sub spf_mfrom_passed { my %options = @_; my $attr = $options{attr}; if ($attr->{sender} eq '') { return 'DUNNO'; } my ($senderlocal, $senderdom) = split /@/, $attr->{sender}; if ($VERBOSE) { syslog( info => "%s: SPF MFROM Passed Envelope-from: %s, IP Address: %s, Recipient: %s", $attr->{queue_id}, $attr->{sender}, $attr->{client_address}, $attr->{recipient} ); } my $mfrom_spf_header = sprintf( "Received-SPF: pass (%s: %s is authorized to use '%s' in 'mfrom' identity) receiver=%s; identity=mfrom; envelope-from=\"%s\"; helo=%s; client-ip=%s", $senderdom, $attr->{client_address}, $attr->{sender}, $myhostname, $attr->{sender}, $attr->{helo_name}, $attr->{client_address} ); return "PREPEND $mfrom_spf_header"; } # ---------------------------------------------------------- # handler: spf helo passed # ---------------------------------------------------------- sub spf_helo_passed { my %options = @_; my $attr = $options{attr}; if ($attr->{sender} ne '') { return 'DUNNO'; } if ($VERBOSE) { syslog( info => "%s: SPF HELO Passed: %s, IP Address: %s, Recipient: %s", $attr->{queue_id}, $attr->{helo_name}, $attr->{client_address}, $attr->{recipient} ); } my $helo_spf_header = sprintf( "Received-SPF: pass (%s: %s is authorized to use '%s' in 'helo' identity) receiver=%s; identity=helo; helo=%s; client-ip=%s", $attr->{helo_name}, $attr->{client_address}, $attr->{helo_name}, $myhostname, $attr->{helo_name}, $attr->{client_address} ); return "PREPEND $helo_spf_header"; } `---- Enjoy. jam ------- To unsubscribe, change your address, or temporarily deactivate your subscription, please go to http://v2.listbox.com/member/?list_id=1007
|