Can I submit a patch to the code?

Asked by Scott Savarese

I have written a patch to the script centered around excluding domains from SPF validation. I have found some domains have incorrect SPF records and trying to get them fixed is not possible. As a result, I want to exclude them. Unfortunately this may not be able to be fixed in the "smtpd_recipient_restrictions" portion of the postfix config so I added a mechanism in the spf filter to do this. Please, feel free to add this patch to the script.

diff -u postfix-policyd-spf-perl.old postfix-policyd-spf-perl
--- postfix-policyd-spf-perl.old 2017-10-21 09:19:26.289881631 -0400
+++ postfix-policyd-spf-perl 2017-10-21 09:20:58.951968643 -0400
@@ -64,6 +64,10 @@
         code => \&exempt_relay
     },
     {
+ name => 'exempt_domains',
+ code => \&exempt_domains
+ },
+ {
         name => 'sender_policy_framework',
         code => \&sender_policy_framework
     }
@@ -73,6 +77,9 @@

 my $DEFAULT_RESPONSE = 'DUNNO';

+# Read in exempt domains list
+my $exempt_domains = get_exempt_domains( "/etc/postfix/exempt_spf_domains" );
+
 #
 # Syslogging options for verbose mode and for fatal errors.
 # NOTE: comment out the $syslog_socktype line if syslogging does not
@@ -185,6 +192,50 @@
 }

 # ----------------------------------------------------------
+# handler: domain exemption
+# ----------------------------------------------------------
+sub get_exempt_domains {
+ my ( $file ) = @_;
+
+ my $list = {};
+
+ # Return nothing if file not found
+ if ( ! -r $file ) {
+ return $list;
+ }
+
+ # Read the file into one variable, split on space or comma (or all)
+ open ( FILE, $file ) or die "Can't open $file: $!\n";
+ my $text = "";
+ while ( my $tmp = <FILE> ) {
+ $text .= $tmp;
+ }
+ close( FILE );
+
+ foreach my $domain ( split( /[\s,]+/, $text ) ) {
+ $list->{$domain} = 1;
+ }
+
+ return $list;
+}
+
+sub exempt_domains {
+ my %options = @_;
+ my $attr = $options{attr};
+
+ my $domain = ( split( /\@/, $attr->{sender} ) )[1];
+ return 'DUNNO' if ( ( ! defined( $domain ) ) or ( $domain eq '' ) );
+
+ # Check the domain against our list of ignored domains
+ if ( defined( $exempt_domains->{$domain} ) ) {
+ return "PREPEND Authentication-Results: $host; none " .
+ "(SPF exempted by policy)";
+ }
+
+ return 'DUNNO';
+}
+
+# ----------------------------------------------------------
 # handler: localhost exemption
 # ----------------------------------------------------------

Question information

Language:
English Edit question
Status:
Solved
For:
postfix-policyd-spf-perl Edit question
Assignee:
No assignee Edit question
Solved by:
Scott Kitterman
Solved:
Last query:
Last reply:
Revision history for this message
Scott Kitterman (kitterman) said :
#1

That's fine. Thanks. Please update the README to describe the new option:

https://bazaar.launchpad.net/~kitterman/postfix-policyd-spf-perl/trunk/view/head:/README

Revision history for this message
Scott Savarese (scottsavarese) said :
#2

Here is an additional patch you can add with README changes. I didn't update copyright information, but feel free to put my name in if the patch is worthy enough.

Thanks,
Scott

[savarese@mail ~]$ diff -u README*
--- README 2018-07-19 17:35:54.258511843 -0400
+++ README.new 2018-07-19 17:43:03.280587531 -0400
@@ -36,6 +36,12 @@
 list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is prepended and logged. IPv6 localhost is also skipped.

+A configuration file, /etc/postfix/exempt_spf_domains, can be used to
+ignore domains that have broken SPF configurations that would normally
+fail. For those domains, add the domain to the file (one per line), and
+restart postfix so that the policy server can reload its configuration.
+The policy server will ignore the domain going forward.
+
 Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO.

Revision history for this message
Scott Kitterman (kitterman) said :
#3

Thanks. I've pushed your changes into git for the next release:

https://code.launchpad.net/~kitterman/postfix-policyd-spf-perl/+git/postfix-policyd-spf-perl/+ref/master

I also attempted (unsuccessfully to adapt your solution to the existing relay address handler (historically one just had to edit the code, but that's not a very satisfactory solution). Do you think you might be able to take a look at it and see if you can figure out what's missing?

Revision history for this message
Scott Savarese (scottsavarese) said :
#4

I don't see an issue just by looking at it, but I'll try running. What issues are you seeing? The idea is that you want to whitelist mail servers on a per IP basis, right?

Revision history for this message
Scott Savarese (scottsavarese) said :
#5

OK... Here's what I see...

In the grep line where you check if the client address is in relay_addresses, I get an error that relay_addresses doesn't have a $ in front of it to identify it as a variable. That definitely will cause a failure.

It looks like there is also a problem adding addresses to your list. NetAddr::IP doesn't store a list of IPs. It stores just one. So, the map line was wrong...

When reading in the list of relays, you store them as a hash. The idea of using the hash is to make checking if the thing exists quickly. Instead of using a for loop or grep to loop through all values, in one quick line you can check if an element exists. Which is why all the values are "1"; we just don't care about the values, just if a key exists. But since you are using grep to loop through the list of relays anyway, you don't need the hash. By using contains in your lookup, you are not just thinking about one IP... You're thinking about whitelisting entire netblocks. Not a bad idea, but it negates the purpose of the hash... So turn the relay list into an array. I think the grep will work right there.

So, I made the following changes... Let me know if they work for you...

In exempt_relay, the grep line becomes this:
           if grep($_->contains($client_address), @$relay_addresses);
The get_exempt_address function looks like this:

sub get_exempt_address {
   my ( $file ) = @_;

    my $list = [];

    # Return nothing if file not found
    if ( ! -r $file ) {
        return $list;
    }

    # Read the file into one variable, split on space or comma (or all)
    open ( FILE, $file ) or die "Can't open $file: $!\n";
    my $text = "";
    while ( my $tmp = <FILE> ) {
        $text .= $tmp;
    }
    close( FILE );

    foreach my $addr ( split( /[\s,]+/, $text ) ) {
        push( @$list, NetAddr::IP->new($addr) );
    }
    return $list;
}

I hope that helps,
Scott

Revision history for this message
Best Scott Kitterman (kitterman) said :
#6

Perfect. Thanks.

I ended up being the "maintainer" of this project by default as I was the only postfix user active in the SPF project, but (as you can probably tell), I don't do Perl. If you are interested in getting more involved in maintaining and improving this package, I would definitely welcome it. If so, I would encourage you to clone the git branch and git familiar with submitting changes that way.

Since not all operating systems install postfix in the same location, I'll probably move the configuration files to their own directory and make it less hard coded (depending on how much time I have). I'll do a new release with your changes soon.

Thanks for your contributions.

Revision history for this message
Scott Kitterman (kitterman) said :
#7

I've done the release now.

Revision history for this message
Scott Savarese (scottsavarese) said :
#8

Thanks Scott Kitterman, that solved my question.