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 |