Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Exchange to Postfix Firewall Configuration

by madbombX (Hermit)
on Jun 29, 2006 at 16:00 UTC ( [id://558351]=sourcecode: print w/replies, xml ) Need Help??
Category: Utility Scripts
Author/Contact Info Eric Lubow <eric@lubow.org>
http://eric.lubow.org/
Description: This script will pull all users SMTP addresses from your Active Directory (including primary and secondary email addresses) and list them in the format "user@example.com OK" which Postfix uses with relay_recipient_maps. It will also automatically create a mynetworks file, transport file, and relay_domains file to ensure all information is properly included. Additionally, if you have amavisd installed, you can specify whitelist and blacklist information for the information retrieved. You can also include senders and recipients not on either list and assign them a score. Don't forget to restart postfix after running this script.

Project Page: http://eric.lubow.org/projects/getcrr.php

There are links to the latest version of the file and the latest version of the config file located on the project page.

#!/usr/bin/perl

=begin comment info
 +--------------------------------------------------------------------
+------+
 | spam-learn
 | $Id: getcrr.pl,v 1.8 2006-11-26 17:00:55 eric Exp $
 | Description: Downloads email addresses and creates postfix recipien
+t lists
 +--------------------------------------------------------------------
+------+
=end comment info
=cut

use strict;
use warnings;
use Benchmark;
use Config::ApacheFormat;
use Fcntl qw(:flock);
use Getopt::Std qw(getopts);
use Log::Log4perl qw(get_logger :levels);
use Mail::Sender;
use Net::hostent;
use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" );
use Parallel::ForkManager;
use POSIX qw(strftime);
use Socket;
use Sys::Hostname;

use vars qw
(
  %STATS %opt
  @Server @addresses @ldap_args @mynetworks @_t
  $LOG $_appender $_layout $_pm
  $DATE $EMAIL $HEAD $HOST
  $config $domains $ldap $relay_domains
);

######################################################################
+##########
# DO NOT EDIT BELOW HERE!!!
#   Unless you are hacking up the script :)
######################################################################
+##########

 # Just in case we get through the whole script
 Bench_Begin();

 # Get the options from the command line
 getopts('c:dhl:ns:V',\%opt);

 # Parse the options and do Usage/Version when necessary
 Usage() if $opt{'h'};
 Version() if $opt{'V'};

 $config = Config::ApacheFormat->new(
             expand_vars    =>    1,
            hash_directives =>    [qw( PostfixBase )],
            valid_blocks    =>    [qw( Server )]
                     );

 # Use the new config file if its specified
 if ($opt{'c'}) { $config->read($opt{'c'}); }
 else { $config->read("/etc/getcrr.conf")
   or die "Config File: $!";
 }

 Setup_Log();
 Create_PID();
 Setup_ForkManager($config->get("MaxProc"));
 Setup_Defaults();

 # Log completed initialization
 @Server = $config->get("Server");
 $LOG->info("Loaded ". ($#Server + 1) ." Total Servers");
 $EMAIL .= "Total Servers:             ". ($#Server + 1). "\n";

# Iterate over our Server hash
foreach my $sid (@Server) {
  my $host = $config->block($sid);

  # Skip it if the record's ACTIVE boolean is < 1
  if ($host->get("Active") < 0) {
    $LOG->info("Skipping Record: ". join(" ",$host->get("Company")) ."
+ - ". $host->get("Host"));
    $EMAIL .= join(" ",$host->get("Company")) ." - ". $host->get("Host
+") ." Skipped\n";
    next;
  } elsif ($host->get("Active") == 1) {
    push @mynetworks,
      "/^". inet_ntoa(inet_aton($host->get("Host"))) ."\\/32\$/       
+ OK\n";
  }
 
  # Fork off the children and get going on the queries
  my $pid = $_pm->start($host) and next;
 
  if ($host->get("Type") =~ /Exchange/) { GetExchange($host); }
  elsif ($host->get("Type") =~ /Nitix/) { GetNitix($host); }
  else {
    $LOG->error(join(" ",$host->get("Company")). ": '". $host->get("Ty
+pe") ."' is unknown server type.");
    $EMAIL .= join(" ",$host->get("Company")). ": '". $host->get("Type
+") ."' is unknown server type.\n";
    $_pm->finish;
  }

  # Closing the forked process
  $_pm->finish;
}

# Ensure all the child processes finish
$_pm->wait_all_children;

 # Generate all the Postfix Files
 Write_Networks($config->get("MyNetworks"));
 Write_Relay_Domains($config->get("RelayDomains"));
 Write_Transport($config->get("TransportMaps"));
 Write_Relay_Recipients($config->get("RelayRecipients"));
 Write_SenderScores($config->get("SenderScoresFile"))
   if ($config->get("SenderScoresFile"));

 # We're done, so remove the PID file.
 unlink $config->get("PIDFile");
 $LOG->debug("Removed PID file:". $config->get("PIDFile"));

 my $runtime = Bench_End();

 do {
   Send_Email($EMAIL, $runtime)
     if defined($config->get("AdminEmail"));
 } unless ($opt{'n'});

######################################################################
+##########
# DESCRIPTION:    Does the Benchmark open and logs it to the $LOG
# PARAMETERS:    None
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Bench_Begin
{
  $_t[1] = new Benchmark;
}

######################################################################
+##########
# DESCRIPTION:    Does the Benchmark diff and writes it to $LOG
# PARAMETERS:    None
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Bench_End
{
  $_t[2] = new Benchmark;
  $_t[0] = timediff($_t[2], $_t[1]);
  $LOG->info("$0 took: ". timestr($_t[0]));
  return timestr($_t[0]);
}

######################################################################
+##########
# DESCRIPTION:    Creates the PID file if it doesn't alreay exist
# PARAMETERS:    None
# RETURN:    Nothing
# NOTES:    Dies if PIDFile exists
######################################################################
+##########
sub Create_PID
{ 

  Die_Clean("$0 is already running or ended improperly",
            "$0 is already running or ended improperly.\n".
               "Either delete ". $config->get("PIDFile") ."or wait for
+ the process to end.\n")
    if (-e $config->get("PIDFile"));

  open (PID, ">".$config->get("PIDFile"));
  print PID $$;
  close (PID);
  $LOG->debug("Created PID file: ". $config->get("PIDFile"));
}

######################################################################
+##########
# DESCRIPTION:    Removes the PID file and dies with @param text
# PARAMETERS:    Text of $LOG->fatal(), Text of die()
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Die_Clean
{
  my ($fatal, $die) = @_;

  $LOG->fatal($fatal);

  unlink $config->get("PIDFile");
  $LOG->debug("Removed PID file: ". $config->get("PIDFile"));

  Bench_End();
  die($die);
}

######################################################################
+##########
# DESCRIPTION:    Queries Exchange Server for email addresses
# PARAMETERS:    $host hash
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub GetExchange
{
  my $host = shift;

  # Connecting to Active Directory domain controllers
  $LOG->debug("Beginning ". join(" ",$host->get("Company")));
  unless ($ldap = Net::LDAP->new($host->get("IP"))) {
    $LOG->warn($host->get("Host"). ": Error connecting to specified do
+main controllers $@ \n");
    $_pm->finish;
    next;
  }

  my $mesg = $ldap->bind ( dn         => $host->get("User"),
                              password     => $host->get("Pass"));
  if ( $mesg->code()) {
      $LOG->warn ($host->get("Host"). ":\n". "error:", $mesg->code(),"
+\n","error name: ",$mesg->error_name(),
          "\n", "error text: ",$mesg->error_text(),"\n");
      $_pm->finish;
      next;
  }

  # How many LDAP query results to grab for each paged round
  # Set to under 1000 for Active Directory
  my $page = Net::LDAP::Control::Paged->new( size => 990 );

  @ldap_args = ( 
      base     => $host->get("Base"),
    # Change to grab various objects (Contacts, Public Folders, etc.)
    # A minimal filter for just users with email would be:
    # filter => "(&(sAMAccountName=*)(mail=*))"
        filter => "(& (mailnickname=*) (| (&(objectCategory=person)
                      (objectClass=user)(!(homeMDB=*))(!(msExchHomeSer
+verName=*)))
                      (&(objectCategory=person)(objectClass=user)(|(ho
+meMDB=*)
                      (msExchHomeServerName=*)))(&(objectCategory=pers
+on)(objectClass=contact))
                      (objectCategory=group)(objectCategory=publicFold
+er) ))",
        control  => [ $page ],
        attrs  => "proxyAddresses",
  );

  my $cookie;
  while(1) {
    # Perform search
    $LOG->debug(join(" ",$host->get("Company")). ": Performming Search
+");
    my $mesg = $ldap->search( @ldap_args );
    $LOG->debug(join(" ",$host->get("Company")). ": Search Completed")
+;

    # Setup filenames and open the files
    my $DC_DOMAIN = $config->get("Libdir"). "/". $host->get("Host") ."
+_domain";
    my $DC_RECIPIENT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_recipients";
    my $DC_TRANSPORT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_transport";
    open DC_DOMAIN, ">$DC_DOMAIN";
    open DC_RECIPIENT, ">$DC_RECIPIENT";
    open DC_TRANSPORT, ">$DC_TRANSPORT";
    $LOG->debug(join(" ",$host->get("Company")). ": Opened Lib Files")
+;

    # Print the headers for each file
    print DC_DOMAIN $HEAD; print DC_RECIPIENT $HEAD; print DC_TRANSPOR
+T $HEAD;

    # Filtering results for proxyAddresses attributes  
    $LOG->debug(join(" ",$host->get("Company")). ": Filtering Results"
+);
    foreach my $entry ( $mesg->entries ) {
      my $name = $entry->get_value( "cn" );
      # LDAP Attributes are multi-valued, so we have to print each one
+.
      foreach my $mail ( $entry->get_value( "proxyAddresses" ) ) {
        # Test if the Line starts with one of the following lines:
        # proxyAddresses: [smtp|SMTP]:
        # and also discard this starting string, so that $mail is only
+ the
        # address without any other characters...
        if ( $mail =~ s/^(smtp|SMTP)://gs ) {
          # Escape '/' in addresses
          $mail =~ s/\//\\\//g;

      # Split the address into domains to build the relay_domains file
      my ($u, $domain) = split ('@', $mail);
      $relay_domains->{$domain} = $host->get("Host");

      # Address the address to the array
          print DC_RECIPIENT "/^". $mail ."\$/    OK\n"; 
        }
      }
    }
    $LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_RECI
+PIENT");
    
    # Create the $DC_DOMAIN file
    foreach my $dom ( keys %{$relay_domains} ) {
      print DC_DOMAIN 
      "/$dom\$/    OK\n".
    "/\\.?$dom\$/    OK\n"; 
      print DC_TRANSPORT
      "/$dom\$/    smtp:[". $host->get("IP") ."]\n".
        "/\\.?$dom\$/    smtp:[". $host->get("IP") ."]\n";
    }

    undef $relay_domains;
    $LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_DOMA
+IN");
    $LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_TRAN
+SPORT");

    close DC_DOMAIN; close DC_RECIPIENT; close DC_TRANSPORT;
    $LOG->debug(join(" ",$host->get("Company")). ": Closed Lib Files")
+;

    # Only continue on LDAP_SUCCESS
    $mesg->code and last;

    # Get cookie from paged control
    my($resp)  = $mesg->control( LDAP_CONTROL_PAGED ) or last;
    $cookie    = $resp->cookie or last;

    # Set cookie in paged control
    $page->cookie($cookie);
    $LOG->debug(join(" ",$host->get("Company")). ": Completed");
  }

  if ($cookie) {
    # We had an abnormal exit, so let the server know we do not want a
+ny more
    $page->cookie($cookie);
    $page->size(0);
    $ldap->search( @ldap_args );
    $LOG->warn(join(" ",$host->get("Company")). ": LDAP query unsucces
+sful");
    $_pm->finish;
  }

  # Completed a server
  $LOG->debug(join(" ",$host->get("Company")). ": Completed");

}

######################################################################
+##########
# DESCRIPTION:    Queries Nitix Server for email addresses
# PARAMETERS:    $host hash
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub GetNitix
{
  my $host = shift;

  # Connecting to Active Directory domain controllers
  $LOG->debug("Beginning ". join(" ",$host->get("Company")));
  unless ($ldap = Net::LDAP->new($host->get("IP"))) {
    $LOG->warn($host->get("Host"). ": Error connecting to specified do
+main controllers $@ \n");
    $_pm->finish;
    next;
  }

  my $mesg = $ldap->bind ( dn         => $host->get("Base"));

  if ( $mesg->code()) {
      $LOG->warn ($host->get("Host"). ":\n". "error:", $mesg->code(),"
+\n","error name: ",$mesg->error_name(),
          "\n", "error text: ",$mesg->error_text(),"\n");
      $_pm->finish;
      next;
  }

  # How many LDAP query results to grab for each paged round
  my $page = Net::LDAP::Control::Paged->new( size => 990 );

  @ldap_args = ( 
      base     => $host->get("Base"),
    filter => "(&(mail=*))",
        control  => [ $page ],
        attrs  => "mail",
  );

  my $cookie;
  while(1) {
    # Perform search
    $LOG->debug(join(" ",$host->get("Company")). ": Performming Search
+");
    my $mesg = $ldap->search( @ldap_args );
    $LOG->debug(join(" ",$host->get("Company")). ": Search Completed")
+;

    # Setup filenames and open the files
    my $DC_DOMAIN = $config->get("Libdir"). "/". $host->get("Host") ."
+_domain";
    my $DC_RECIPIENT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_recipients";
    my $DC_TRANSPORT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_transport";
    open DC_DOMAIN, ">$DC_DOMAIN";
    open DC_RECIPIENT, ">$DC_RECIPIENT";
    open DC_TRANSPORT, ">$DC_TRANSPORT";
    $LOG->debug(join(" ",$host->get("Company")). ": Opened Lib Files")
+;

    # Print the headers for each file
    print DC_DOMAIN $HEAD; print DC_RECIPIENT $HEAD; print DC_TRANSPOR
+T $HEAD;

    # Filtering results for proxyAddresses attributes  
    $LOG->debug(join(" ",$host->get("Company")). ": Filtering Results"
+);
    foreach my $entry ( $mesg->entries ) {
      my $mail = $entry->get_value( "mail" );
      # Split the address into domains to build the relay_domains file
      my ($u, $domain) = split ('@', $mail);
      $relay_domains->{$domain} = $host->get("Host");

      # Address the address to the array
      print DC_RECIPIENT "/^". $mail ."\$/    OK\n"; 
    }

    $LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_RECI
+PIENT");
    
    # Create the $DC_DOMAIN file
    foreach my $dom ( keys %{$relay_domains} ) {
      print DC_DOMAIN 
      "/$dom\$/    OK\n".
    "/\\.?$dom\$/    OK\n"; 
      print DC_TRANSPORT
      "/$dom\$/    smtp:[". $host->get("IP") ."]\n".
        "/\\.?$dom\$/    smtp:[". $host->get("IP") ."]\n";
    }

    undef $relay_domains;
    $LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_DOMA
+IN");
    $LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_TRAN
+SPORT");

    close DC_DOMAIN; close DC_RECIPIENT; close DC_TRANSPORT;
    $LOG->debug(join(" ",$host->get("Company")). ": Closed Lib Files")
+;

    # Only continue on LDAP_SUCCESS
    $mesg->code and last;

    # Get cookie from paged control
    my($resp)  = $mesg->control( LDAP_CONTROL_PAGED ) or last;
    $cookie    = $resp->cookie or last;

    # Set cookie in paged control
    $page->cookie($cookie);
    $LOG->debug(join(" ",$host->get("Company")). ": Completed");
  }

  if ($cookie) {
    # We had an abnormal exit, so let the server know we do not want a
+ny more
    $page->cookie($cookie);
    $page->size(0);
    $ldap->search( @ldap_args );
    $LOG->warn(join(" ",$host->get("Company")). ": LDAP query unsucces
+sful");
    $_pm->finish;
  }

  # Completed a server
  $LOG->debug(join(" ",$host->get("Company")). ": Completed");

  return;
}

######################################################################
+##########
# DESCRIPTION:    Sends the stats email
# PARAMETERS:    $EMAIL
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Send_Email
{
  my ($body,$runtime) = @_;
  $body .= "Runtime:                $runtime\n";
  # Create the host portions of the message
  foreach my $host (sort keys %STATS) {
    my $length = length $host;
    $body  .= "\n\n+-". "-" x $length ."-+\n".
              "| $host |\n".
          "+-". "-" x $length ."-+\n\n".
              "Total Domains:            ". $STATS{$host}->{"RelayDoma
+ins"}. "\n".
              "Total Recipients:            ". $STATS{$host}->{"RelayR
+ecipients"}. "\n".
          "Total Runtime:            ". $STATS{$host}->{"Diff"} ."\n";
  }

  # Fix this ugly module addition
  $Mail::Sender::NO_X_MAILER = 1;

  eval {
    (new Mail::Sender)->MailMsg(
    {
      smtp => "hood.tforge.com",
      from => "\"Postmaster\" <postmaster\@tforge.com>",
      to   => scalar join(" ", $config->get("AdminEmail")),
      cc   => scalar join(" ", $config->get("AdminEmailCC")),
      subject => "CRR Statistics ($HOST on $DATE)",
      msg  => "$body"
    })
      or die "Error Sending Mail: $Mail::Sender::Error";
  };

  $LOG->info("Sent email to ". scalar join(" ", $config->get("AdminEma
+il")));
  $LOG->info("Sent email to ". scalar join(" ", $config->get("AdminEma
+ilCC")))
    if (defined($config->get("AdminEmailCC")));

  return;
}

######################################################################
+##########
# DESCRIPTION:    Sets up global program defaults
# PARAMETERS:    None
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Setup_Defaults
{

  $DATE = strftime("%Y-%b-%d", localtime(time));
  $HOST = Sys::Hostname::hostname();

  $EMAIL = <<EMAIL;
 +---------------------------------+
 | Get Concurrent Relay Recipients |
 |      $DATE on $HOST        |
 +---------------------------------+

+--------+
| Totals |
+--------+

EMAIL

  # Prepare and write the file header
  my ($now) = strftime("%Y-%b-%d %H:%M:%S ", localtime(time));

$HEAD =  <<HEAD;
# File automatically generated
# DO NOT EDIT BY HAND.  YOUR CHANGES WILL BE LOST.
# Last created on: $now

HEAD

  # Add the localhost class to @mynetworks
  push @mynetworks,
    "/^127.0.0.0\\/8\$/        OK\n";

  return;
}

######################################################################
+##########
# DESCRIPTION:    Sets up everything for $LOG
# PARAMETERS:    None
# RETURN:    Help text to STDOUT
# NOTES:    
######################################################################
+##########
sub Setup_Log
{

 # Setup the logging functionality
 # Logging Levels: WARN,DEBUG,ERROR,INFO,FATAL
 $LOG = get_logger();
 $LOG->level($INFO);
 $LOG->level($DEBUG)
   if ($config->get("Debug") or $opt{'d'});

 $_appender = Log::Log4perl::Appender->new(
   "Log::Dispatch::File",
   filename => $config->get("LogFile"),
   mode       => "append",
 );

 # Create the log formatting
 $_layout = Log::Log4perl::Layout::PatternLayout->new(
   "%d{MMM dd HH:mm:ss} %p> %F{1}[%P]: %L: %m%n");
 $LOG->add_appender($_appender);
 $_appender->layout($_layout);

 return;
}

######################################################################
+##########
# DESCRIPTION:    Sets up everything for the Parallel Forkmanager
# PARAMETERS:    None
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Setup_ForkManager
{
 # Begin ForkManager
 my $_max_procs = shift;
 $_pm = new Parallel::ForkManager($_max_procs);
 
 # Log at process fork
 $_pm ->run_on_start(
   sub { my ($pid, $host) = @_;
     $LOG->debug("Forking process PID: $pid\n");
     $STATS{$host->get("Host")}->{"Start"} = new Benchmark;
   }
 );
 
 # Log at process copmletion
 $_pm ->run_on_finish(
   sub { my ($pid, $exit_code, $host) = @_;
     $LOG->debug("Finishing up process PID: $pid\n");
     $STATS{$host->get("Host")}->{"End"} = new Benchmark;
     $STATS{$host->get("Host")}->{"Diff"} = timestr(timediff($STATS{$h
+ost->get("Host")}->{"End"}, $STATS{$host->get("Host")}->{"Start"}));
   }
 );
 
 $_pm->run_on_wait(
   sub {
     $LOG->debug("Waiting for children to finish")
   },
   5.0
 );

 $LOG->debug("ForkManger initialized");
 return;

}
 
######################################################################
+##########
# DESCRIPTION:    Prints the Usage Menu to STDOUT
# PARAMETERS:    None
# RETURN:    Help text to STDOUT
# NOTES:    
######################################################################
+##########
sub Usage
{
  print <<USAGE;
$0 -dhnV -c <log_file>
  -c    Use this config file (default: /etc/config/getcrr.conf)
  -d    Debugging mode (via logfile)
  -h    Prints out this help menu
  -n    Do not send any email
  -V    Prints out version information
USAGE

exit(0);
}

######################################################################
+##########
# DESCRIPTION:    Prints the Version to STDOUT
# PARAMETERS:    None
# RETURN:    Version text to STDOUT
# NOTES:    
######################################################################
+##########
sub Version
{
  my $VERSION = do { my @r = (q$Revision: 1.8 $ =~ /\d+/g); sprintf "%
+d."."%03d" x $#r, @r };

  print <<VERSION;
Get Concurrent Relay Recipients v$VERSION
VERSION

exit(0);
}

######################################################################
+##########
# DESCRIPTION:    Write the NETWORKS file
# PARAMETERS:    Location of MyNetworks map
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Write_Networks
{
  my $NETWORKS = shift;
  open NETWORKS, ">$NETWORKS" or
    Die_Clean("Error Opening $NETWORKS: $!", "Error Opening $NETWORKS:
+ $!");

  # Lock the file
  flock NETWORKS, LOCK_EX;

  print NETWORKS $HEAD;
  print NETWORKS @mynetworks;
  flock NETWORKS, LOCK_UN;
  close NETWORKS;

  $EMAIL .= "Total Networks:            ". ($#mynetworks + 1) ."\n";

  $LOG->info("Wrote $NETWORKS");
  return;
}

######################################################################
+##########
# DESCRIPTION:    Write the Relay_Domains file
# PARAMETERS:    Location of the RelayDomains map
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Write_Relay_Domains
{
  my $DOMAINS = shift;
  my (@exclude_domains, $total_domains);

  # Begin iterating over the domain files in the directory
  $LOG->debug("Creating $DOMAINS file");
  
  # Only write the file once all the queries are successful
  open DOMAINS, ">$DOMAINS" or
    Die_Clean("Cannot Open $DOMAINS: $!", "Cannot Open $DOMAINS: $!");
  
  # Lock the file
  flock DOMAINS, LOCK_EX;
  
  print DOMAINS $HEAD;
  
  foreach my $sid (@Server) {
    my $host = $config->block($sid);
    @exclude_domains = $host->get("ExcludeDomain");
    next if $host->get("Active") == -1;
    $LOG->debug("Processing ". $host->get("Host") ."_domain file");
    open DC_DOMAIN, $config->get("Libdir") ."/". $host->get("Host") ."
+_domain"
      or do {
        $LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_domain");
        next;
        };
    while (my $line = <DC_DOMAIN>) {
      next unless $line =~ /^\/.*$/;
      my $domain = $1 if ($line =~ /^\/\.?(.*)\$\/.*$/);
      do {
        $STATS{$host->get("Host")}->{"RelayDomains"}++;
        $total_domains++;
        print DOMAINS $line;
      } unless (grep { m/$domain/ } @exclude_domains );
    }
    close DC_DOMAIN;
  }
  
  flock DOMAINS,LOCK_UN;
  close DOMAINS;
  
  $LOG->info("Wrote $DOMAINS");
  $EMAIL .= "Total Relay Domains:        ". ($total_domains / 2) ."\n"
+;
  return;
}

######################################################################
+##########
# DESCRIPTION:    Write the Relay_Recipients file
# PARAMETERS:    None
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Write_Relay_Recipients
{
  my $RECIP = shift;
  my (@exclude_domains,$reject_domains,$total_recipients);
  $LOG->debug("Creating $RECIP file");
  
  # Only write the file once all the queries are successful
  open RECIP, ">$RECIP" or
    Die_Clean("Cannot Open $RECIP: $!", "Cannot Open $RECIP: $!");
  
  # Lock the file
  flock RECIP, LOCK_EX;
  
  print RECIP $HEAD;
  foreach my $sid (@Server) {
    my $host = $config->block($sid);
    @exclude_domains = $host->get("ExcludeDomain");
    next if $host->get("Active") == -1;
    $LOG->debug("Processing ". $host->get("Host") ."_recipients file")
+;
    open DC_RECIP, $config->get("Libdir") ."/". $host->get("Host") ."_
+recipients"
      or do {
        $LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_recipients");
        next;
        };
    while (my $line = <DC_RECIP>) {
      next unless $line =~ /^\/.*$/;
      my ($user,$domain) = ($1,$2) if ($line =~ /^\/\^(.*?)\@(.*?)\$\/
+.*OK$/);
      do {
    $STATS{$host->get("Host")}->{"RelayRecipients"}++;
    $total_recipients++;
        print RECIP $line;
        $domains->{$domain} = 1;
      } unless (grep { m/$domain/ } @exclude_domains );
    }
    close DC_RECIP;
  }
  
  print RECIP "\n\n".
          "# REJECT all other emails not destined for a user in our do
+mains.\n".
          "\n";
  foreach my $dom ( keys %{$domains} ) {
    $reject_domains++;
    $dom =~ s/OK/REJECT/;
    print RECIP "/^.*\@$dom\$/            REJECT\n";
  }
  flock RECIP, LOCK_UN;
  close RECIP;
  
  $LOG->info("Wrote $RECIP");
  $EMAIL .= "Total Relay Recipients:        $total_recipients\n".
          "Total Reject Domains:        $reject_domains\n";
  
  return;
}

######################################################################
+##########
# DESCRIPTION:    Prints the SPAM recipients to the SPAM hash file
# PARAMETERS:    Hash of domains to be added
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Write_SenderScores
{
  my $SCORES = shift;
  $LOG->debug("Creating $SCORES file");
  
  # Only write the file once all the queries are successful
  open SCORES, ">$SCORES" or
    Die_Clean("Cannot Open $SCORES: $!", "Cannot Open $SCORES: $!");
  
  # Lock the file
  flock SCORES, LOCK_EX;
  
  print SCORES $HEAD;
  foreach my $sid (@Server) {
    my $host = $config->block($sid);
    next if $host->get("Active") == -1;
    $LOG->debug("Processing ". $host->get("Host") ."_recipients file")
+;
    open DC_SCORES, $config->get("Libdir") ."/". $host->get("Host") ."
+_recipients"
      or do {
        $LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_recipients");
        next;
        };
    while (my $line = <DC_SCORES>) {
      next unless $line =~ /^\/.*$/;
      my ($user,$domain) = ($1,$2) if ($line =~ /^\/\^(.*?)\@(.*?)\$\/
+.*OK$/);
      # The format of the SPAM_SITES file
      # .example.com        -4.0

      print SCORES "$user\@$domain            ". $config->get("WLSende
+rScore") ."\n";
      $domains->{$domain} = 1;
    }
    close DC_SCORES;
  }

  # Add the whitelist and blacklist sender specified in the config fil
+e
  my (@wl) = $config->get("WLSenders");
  my (@bl) = $config->get("BLSenders");
  $EMAIL .= "Total Whitelist Senders:    ". ($#wl + 1) ."\n".
            "Total Blacklist Senders:    ". ($#bl + 1) ."\n";

  do {
    print SCORES "\n\n".
      "# Additional Whitelist senders\n";
    foreach my $sender (@wl) {
      print SCORES "$sender            ". $config->get("WLSenderScore"
+) ."\n";
    } 
  } if ($config->get("WLSenderScore"));

  do {
    print SCORES "\n\n".
        "# Additional Blacklist senders\n";
    foreach my $sender (@bl) {
      print SCORES "$sender            ". $config->get("BLSenderScore"
+) ."\n";
    }
  } if ($config->get("BLSenderScore"));
  
  print SCORES "\n\n".
          "# SCORES all other emails seemingly from a user in one of o
+ur domains.\n".
          "\n";
  foreach my $dom ( keys %{$domains} ) {
    print SCORES ".$dom            ". $config->get("BLLocalSenderScore
+") ."\n"
        unless ($dom =~ /\$\//);
  }
  flock SCORES, LOCK_UN;
  close SCORES;
  
  $LOG->info("Wrote $SCORES");
  
  return;
}

######################################################################
+##########
# DESCRIPTION:    Write the TRANSPORT file
# PARAMETERS:    Location of the Transport map
# RETURN:    Nothing
# NOTES:    
######################################################################
+##########
sub Write_Transport
{
  my $TRANSPORT = shift;
  my (@exclude_domains,$total_transports);
  $LOG->debug("Creating $TRANSPORT file");
  
  # Only write the file once all the queries are successful
  open TRANSPORT, ">$TRANSPORT" or
    Die_Clean("Cannot Open $TRANSPORT: $!", "Cannot Open $TRANSPORT: $
+!");
  
  # Lock the file
  flock TRANSPORT, LOCK_EX;
  
  print TRANSPORT $HEAD;
  foreach my $sid (@Server) {
    my $host = $config->block($sid);
    @exclude_domains = $host->get("ExcludeDomain");
    next if $host->get("Active") == -1;
    $total_transports++;
    $LOG->debug("Processing ". $host->get("Host") ."_transport file");
    open DC_TRANSPORT, $config->get("Libdir") ."/". $host->get("Host")
+ ."_transport"
      or do {
        $LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_transport");
        next;
        };
    while (my $line = <DC_TRANSPORT>) {
      next unless $line =~ /^\/.*$/;
      my $domain = $1 if ($line =~ /^\/\.?(.*)\$\/.*$/);
      print TRANSPORT $line
        unless (grep { m/$domain/ } @exclude_domains );
    }
    close DC_TRANSPORT;
  }


  my (@bmx) = $config->get("BackupMX");
  do {
    print TRANSPORT "\n\n".
               "# Secondary MX Records\n\n";
    foreach my $server (@bmx) {
      print TRANSPORT "/^.*\$/        relay:[$server]\n";
    } 

  $EMAIL .= "Total Backup MX Servers:    ". ($#bmx + 1) ."\n";
  } if ($config->get("BackupMX"));
  
  flock TRANSPORT, LOCK_UN;
  close TRANSPORT;
  
  $LOG->info("Wrote $TRANSPORT");
  $EMAIL .= "Total Transports:            $total_transports\n";
  
  return;
}

######################################################################
+##########

=head1 NAME

 GetCRR - Get Company Relay Recipients

=head1 SYNOPSIS

 Usage: getcrr.pl && postfix reload

=cut

=head1 VERSION

 $Revision: 1.8 $

=cut

=head1 DESCRIPTION

 This script will pull all users SMTP addresses from your Active Direc
+tory
 (including primary and secondary email addresses) and list them in th
+e
 format "user@example.com OK" which Postfix uses with relay_recipient_
+maps.
 It will also automatically create a mynetworks file, transport file, 
+and 
 relay_domains file to ensure all information is properly included.
 Additionally, if you have amavisd installed, you can specify whitelis
+t
 and blacklist information for the information retrieved.  You can als
+o
 include senders and recipients not on either list and assign them a s
+core.
 Be sure to double-check the path to perl above.
 Don't forget to restart postfix after running this script.

=cut

=head1 TODO

 Need to modify to do the following:
   + Add the Usage() output to SYNOPSIS
   + Incorporate reloading postfix and amavisd into the script
   + Add Perl formatting for the output files so everything looks pret
+ty
   + Add option in config to accept email for *@domain.tld
     - May need to modify type
   + Add to statistics
     - Probable email address count (less the stupid Exchange email ad
+dy's)

=cut

=head1 CAVEATS

 It is possible for an email account on an Exchange server to the same
+ on
 another Exchange server.  For instance, if you are querying ex01 and 
+ex02
 and tom@ex.com is an admin and has an account on both, then this scri
+pt
 will create the postfix transport map for whichever address is proces
+sed
 first.  To fix this you have to make a "force" entry in the config fi
+le.

=cut

=head1 CONFIGURATION

 The default configuration file is /etc/getcrr.conf.  All variables
 listed in the config file are explained in the config file.

=cut

######################################################################
+##########

1;
Replies are listed 'Best First'.
Example Config File
by madbombX (Hermit) on Jun 29, 2006 at 16:13 UTC
    Below is an example config file for the script. It should be located at /etc/getcrr.conf if you are using the default configuration. You can always specify another config file on the command line.
    # General Config # # The temp dir that getcrr will use when building up the final map fil +es # Note: This directory must exist, it will not be created Libdir /etc/postfix/crr # Path/file for the pid file PIDFile /var/run/getcrr.pid # The logfile location and filename LogFile /var/log/getcrr.log # Turn Debugging on in the log file Debug 0 # The maximum number of processes that should be allowed to fork off t +o # to be used by the forking manager MaxProc 5 # Email address of the administrator (Leave commented to not receive e +mail) # AdminEmail "Eric Lubow" <admin@server.com> #AdminEmailCC "Tech Reports" <tech@server.com> # Postfix Config # # Note: All postfix map files are created in pcre format. # Ensure your main.cf matches accordingly # # This is the base dir of your postfix configuration PostfixBase /etc/postfix # Path/file for the postfix relay domains file RelayDomains ${PostfixBase}/relay_domains # Path/file for the postfix mynetworks file MyNetworks ${PostfixBase}/mynetworks # Path/file for the postfix relay recipients file RelayRecipients ${PostfixBase}/relay_recipients # Path/file for the postfix transport maps file TransportMaps ${PostfixBase}/transport # List any backup mail servers here BackupMX mail2.server.com # Amavis/SpamAssassin/DSpam Config # # If you are using amavisd and SPAM Assassin to filter SPAM, then it # would be a good idea to use this option. In amavisd it is necessary # to setup the @score_sender_maps array. This is a by-recipient hash # lookup table. Add the line: # read_hash("/etc/amavis/sender_scores_sitewide"), # where /etc/amavis/sender_scores_sitewide is the location of the # external hashfile that will be generated by this script. # Comment this out for the file not to be created SenderScoresFile /etc/amavis/sender_scores_sitewide # All messsages that appear to be coming from one of our domains, but # from an invalid recipient will get this threshold added BLLocalSenderScore 1.0 # Scoring change on a message sent by a whitelisted sender listed in # the sender_scores_sitewide file WLSenderScore -5.0 # An additional list of white list senders WLSenders mom@myfamily.com \ dad@myfamily.com # An additional list of white list senders & their score BLSenders example@example.com BLSenderScore 4.5 # Server Listings # Password MyPass # Exchange Server <Server ex01> Company Exchange Examples Host ex01.server.com IP 192.168.1.10 Type Exchange Base dc=Example,dc=com User ex@example Pass ${Password} Active 1 Comment Example Exchange Server ExcludeDomain vtext.com mobile.mycingular.com </Server> # Nitix Server <Server consolco-clifton> Company Nitix Examples Host nitix.server.com IP 192.168.2.10 Type Nitix Base dc=server,dc=com User ex@dc=server,dc=com Attrib mail Active 1 Comment Nitix Example Server </Server>

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://558351]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (1)
As of 2024-04-25 12:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found