postfix: LDAP Abfragen gegen das Windows 2003 Active Directory

0

Linux eMail Systeme werden häufig vor Exchange Server geschaltet um unter anderem ungültige Adressen zu blocken. Diest ist alles gut und schön, doch aber einem bestimmten Umfang an eMail Adressen macht es keinen Sinn mehr diese separat auf dem Linux Rechner zu pflegen. Hier bietet sich dann eine ldap basierte Abfrage an.

Doch der Vorteil dass die eMail Adresse nicht mehr extra gepflegt werden müssen hat auch einen großen Nachteil. Sollte die ldap Abfrage aus irgend einem Grund nicht mehr funktionieren werden auch keine eMails mehr angenommen.

Dieses Problem lässt sich aber umgehen, indem man die ldap Abfrage in eine Datei schreibt. Somit exisitiert beim Ausfall des LDAPs immerhin noch eine relativ "aktuelle" Empfängerliste. Die Aktualisierung der Liste kann per Crontab zeitgesteuert gestartet werden.

Installation:

Um das ganz nun zu implementieren wird als erstes das entsprechende perl ldap Paket sowie das postfix ldap Paket benötigt:

apt-get install libnet-ldap-perl postfix-ldap

Nach der Installation der Pakete muss nun das Abfrage script erstellt werden. Das script habe ich auf dieser Seite gefunden. Den korrekten Link finden Sie hier. Verwenden Sie bitte den Link um das entsprechende script abzuspeichern. Nachfolgende ist zwar auch das script abgebildet, aber aufgrund der Zeichenübersetzung im CMS kommt es bei copy & paste zu Fehlern.

#!/usr/bin/perl -T -w
# Version 1.02
# 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.
# Be sure to double-check the path to perl above.
# This requires Net::LDAP to be installed. To install Net::LDAP, at a shell
# type "perl -MCPAN -e shell" and then "install Net::LDAP"
use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" );
# Enter the path/file for the output
$VALID = "/etc/postfix/example_recipients";
# Enter the FQDN of your Active Directory domain controllers below
$dc1="domaincontroller1.example.com";
$dc2="domaincontroller2.example.com";
# Enter the LDAP container for your userbase.
# The syntax is CN=Users,dc=example,dc=com
# This can be found by installing the Windows 2000 Support Tools
# then running ADSI Edit.
# In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" &
# you will see, for example, DC=example,DC=com (this is your base).
# The Users Container will be specified in the right pane as
# CN=Users depending on your schema (this is your container).
# You can double-check this by clicking "Properties" of your user
# folder in ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com
# which would be $hqbase="cn=Users,dc=example,dc=com"
# Note: You can also use just $hqbase="dc=example,dc=com"
$hqbase="cn=Users,dc=example,dc=com";
# Enter the username & password for a valid user in your Active Directory
# with username in the form cn=username,cn=Users,dc=example,dc=com
# Make sure the user's password does not expire. Note that this user
# does not require any special privileges.
# You can double-check this by clicking "Properties" of your user in
# ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com
# which would be $user="cn=user,cn=Users,dc=example,dc=com"
# Note: You can also use the UPN login: "user@example.com"
$user="cn=user,cn=Users,dc=example,dc=com";
$passwd="password";
# Connecting to Active Directory domain controllers
$noldapserver=0;
$ldap = Net::LDAP->new($dc1) or
$noldapserver=1;
if ($noldapserver == 1) {
$ldap = Net::LDAP->new($dc2) or
die "Error connecting to specified domain controllers $@ n";
}
$mesg = $ldap->bind ( dn => $user,
password =>$passwd);
if ( $mesg->code()) {
die ("error:", $mesg->code(),"n","error name: ",$mesg->error_name(),
"n", "error text: ",$mesg->error_text(),"n");
}
# How many LDAP query results to grab for each paged round
# Set to under 1000 for Active Directory
$page = Net::LDAP::Control::Paged->new( size => 990 );
@args = ( base => $hqbase,
# Play around with this to grab objects such as Contacts, Public Folders, etc.
# A minimal filter for just users with email would be:
# filter => "(&(sAMAccountName=*)(mail=*))"
filter => "(& (mailnickname=*) (| (&(objectCategory=person)
(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))
(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)
(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))
(objectCategory=group)(objectCategory=publicFolder)(objectClass=msExchDynamicDistributionList) ))",
control => [ $page ],
attrs => "proxyAddresses",
);
my $cookie;
while(1) {
# Perform search
my $mesg = $ldap->search( @args );
# Filtering results for proxyAddresses attributes
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 ) {
push(@valid, $mail." OKn");
}
}
}
# 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);
}
if ($cookie) {
# We had an abnormal exit, so let the server know we do not want any more
$page->cookie($cookie);
$page->size(0);
$ldap->search( @args );
# Also would be a good idea to die unhappily and inform OP at this point
die("LDAP query unsuccessful");
}
# Only write the file once the query is successful
open VALID, ">$VALID" or die "CANNOT OPEN $VALID $!";
print VALID @valid;
# Add additional restrictions, users, etc. to the output file below.
#print VALID "user@example.com OKn";
#print VALID "user1@example.com 550 User unknown.n";
#print VALID "bad.example.com 550 User does not exist.n";
close VALID;

Um Fehler beim anpassen des scriptes zu vermeiden können Sie sich die entsprechenden Pfade im ldap über einen Windows 2003 Server mittels

dsquery user -name Mustermann*

ausgeben lassen.

Die aus diesem script resultierende Datei können Sie in postfix über die relay_recipient_maps angeben. Denken Sie daran, dass die Datei noch mittels postmap verarbeitet werden muss:

./getadsmtp.pl && postmap plusone_recipients

Bemerkung:

Wenn Sie als Suchpfad eine OU angeben die weiter OUs enthält werden auch die untergeordneten verarbeitet.

Dann gibt es noch ein Problem mit script. Denn wenn die LDAP Abfrage fehl schlägt wird die Adress-Date trotzdem überschrieben und ist unter Umständen leer. Das script versuche ich aber in einer ruhigen Minuten noch dahingehend anzupassen (bin leider kein perl Spezialist)

Direkte LDAP Abfrage:

Für eine direkte LDAP Abfrage empfehle ich folgende Links:

http://www.howtoforge.com/mandriva_postfix_antispam_antivirus_exchange_p4
http://openwiki.ru/wiki/Postfix

oder folgendes in der main.cf ergänzen 😉

relay_recipient_maps = ldap:/etc/postfix/ldap_relay_recipient_maps.cf

Anschließend die ldap_relay_recipient_maps.cf erstellen:

server_host = xx.xx.xx.xx:389
search_base = OU=Benutzer,DC=domain,DC=tlc
version = 3
bind_dn = user@domain.tld
bind_pw = password
query_filter = (proxyAddresses=smtp:%s)
result_attribute = mail

testen können Sie die LDAP abfrage mit:

/usr/sbin/postmap -q "user@domain.tld" ldap:/etc/postfix/postfix/ldap_relay_recipient_maps.cf
Teilen.

Über den Autor

Seit der Ausbildung zum Fachinformatiker Systemintegration (2002-2005) bei der DaimlerChrysler AG, beruflich im Bereich der E-Mail Kommunikation (Exchange, Linux) sowie des ActiveDirectory, mit entsprechenden Zertifizierungen (MCSE 2003, MCITP Ent.-Admin 2008, MCSE 2012, LPIC 1-3) tätig. Abgeschlossenes Studium zum Master of Science der IT-Management an der FOM sowie zertifizierter Datenschutzbeauftragter. Aktuell im Projektmanagement tätig.

Antworten