Copyright © 2003 Patrick Ben Koetter, Ralf Hildebrandt
| Revision History | ||
|---|---|---|
| Revision 0.30 | 10 May 2003 | PK |
| Added example and notes for the transport map and translated german notes in the Makefile to english. | ||
| Revision 0.20 | 07 May 2003 | RH |
| Added config for main.cf | ||
| Revision 0.10 | 16 Jan 2003 | PK |
| Initial Release | ||
Table of Contents
Junk mail is war. RFCs do not apply.
--Wietse VenemaAbstract
The Microsoft Exchange server is without any doubt a very powerful groupware server. It's security record and it's stability, when it comes to attacks, are not that famous. That's why many postmasters want to combine the groupware functionality with the security and stability of UNIX smtp gateways.
This is a little sketch to give you an idea how this can be done, using Postfix - an alternative to the widely-used Sendmail program.
First and foremost the idea is to keep the Exchange Server from connecting directly to other servers and vice versa have other clients that come from the Internet connect directly to the Exchange server. This can easily be done by following man transport (5). You need to configure Postfix to accept mails and transport them to the final destination - your Exchange server.
In our scenario the Exchange server is the heart of our communication infrastructure and we want to keep it beating constantly without any disturbance. Part of running a service smoothly is making sure that anything that could disturb it is blocked as soon as possible.
If you run Postfix as a simple mailrelay it will have no notion of the users the Exchange Server holds. This means that by default Postfix will accept messages for its destination and transport those to the Exchange server. The Exchange server will accept the message, and notice later that the recipient doesn't even exist. In that case the mail will be bounced.
If it wasn't for SPAM this would be an OK approach. But SPAM is all over the place and some spammers try to get their message delivered using dictionary attacks. A dictionary attack equals a brute force attack. To withstand brute force you must be able to decide what's right and what's wrong very fast and you need lots of performance, stability (and a few nasty tricks).
![]() | Note |
|---|---|
In a dictionary attack the attacker tries any username/alias it can think of in combination with the domainname the SMTP server is registered for, in order to get messages delivered to any user known by the mailserver. Thousands of messages are sent to the final destination every minute, challenging the mailserver to examine every message and make a decision on the recipients validity.
| |
The right thing™ to do, is to build maps that were built from files who were built from LDAP queries run against the Exchanges' ADS. Huh?
![]() | Warning |
|---|---|
Probably the first impulse will be to have Postfix do LDAP lookups on demand by connecting to the Exchange Server through LDAP and query for valid recipients. This is not recommended, because in critical situations e.g. during a dictionary attack there will be thousands of queries a minute, distracting the Exchange server from its primary job being a groupware server. There will be so many queries that the Exchange server will slow down so badly that the dictionary attack will result in a DoS to your groupware server. | |
The Exchange server can be queried for valid recipients using LDAP. However in a big Exchange Server installation there is a default limit that might permit a query from getting all valid recipients; a regular query is allowed to return at most 1.000 results. After that the server will not answer and further requests by the source that seeks recipients.
The solution is to use Microsoft's own scripting language and write a Visual Basic script that will tell the LDAP server not to give all results at once, but to hand them out in pages. The following steps sketch a way how to query an Exchange Server for valid recipients, copy them to the Postfix mailrelay and have Postfix use them to reject unknown recipients before they reach the Exchange Server.
Put the Exchange server into your LAN and keep it hidden from the internet by a firewall. All you need to allow is:
Allow incoming connections on Port 25 from your mailrelay to the exchange server
The idea is to have a script query an Exchange server for proxyAddresses which is the correct attribute when you are looking for valid recipients.
Edit the script and change Set Container=GetObject("LDAP://CN=Users,DC=office,DC=example,DC=com") to fit your LDAP structure.
Example 1. export_recipients.vbs
Download export_recipients.vbs.
' Export all valid recipients (= proxyAddresses) into a
' file virtual.txt
'
' Ferdinand Hoffmann & Patrick Koetter
' 20021100901
' Shamelessly stolen from
' http://www.microsoft.com/windows2000/techinfo/ \
' planning/activedirectory/bulksteps.asp
'Global variables
Dim Container
Dim OutPutFile
Dim FileSystem
'Initialize global variables
Set FileSystem = WScript.CreateObject("Scripting.FileSystemObject")
Set OutPutFile = FileSystem.CreateTextFile("virtual.txt", True)
Set Container=GetObject("LDAP://CN=Users,DC=office,DC=example,DC=com")
'Enumerate Container
EnumerateUsers Container
'Clean up
OutPutFile.Close
Set FileSystem = Nothing
Set Container = Nothing
'Say Finished when your done
WScript.Echo "Finished"
WScript.Quit(0)
'List all Users
Sub EnumerateUsers(Cont)
Dim User
'Go through all Users and select them
For Each User In Cont
Select Case LCase(User.Class)
'If you find Users
Case "user"
'Select all proxyAddresses
Dim Alias
If Not IsEmpty(User.proxyAddresses) Then
For Each Alias in User.proxyAddresses
OutPutFile.WriteLine "alias: " & Alias
'WScript.Echo Alias
Next
End If
Case "organizationalunit" , "container"
EnumerateUsers User
End Select
Next
End SubRun the script and check the output. It should give you something like this:
alias: SMTP:newuser3@office.example.comalias: SMTP:newuser@office.example.com alias: SMTP:Administrator@office.example.com alias: X400:c=us;a= ;p=Example Organiza;o=Exchange;s=Administrator; alias: smtp:postmaster@office.example.com
alias: X400:c=us;a= ;p=Example Organiza;o=Exchange;s=Doe;g=Jon; alias: SMTP:testuser@example.com alias: SMTP:postfix@office.example.com alias: X400:c=us;a= ;p=Example Organiza;o=Exchange;s=postfix;
Install and configure any secure copy mechanism, e.g. PuTTY to scp virtual.txt to the Postfix machine using key files for unattended copying. You can also use rsync over ssh from the cygwin package.
Use the Windows Scheduler to run export_recipients.vbs and scp as often as needed.
Parse the virtual.txt file for smtp and SMTP entries and write them into a Postfix map format to relay_recipients.proto. Then run postmap /path/to/relay_recipients.proto to make it a Postfix readable DB. We use this Makefile which can be invoked from cron using: cd /etc/postfix && make
Example 2. Makefile
Download Makefile
DB=db
all: relay_recipients.$(DB)
# "all" means to build virtual.db
relay_recipients.proto: virtual.txt
awk -F: '/alias: (SMTP|smtp):/ {printf("%s\tOK\n",$$3)}' virtual.txt > relay_recipients.proto
# We need virtual.txt to build relay_recipients.proto
# awk will use ":" as field separator and for each line
# that contains "alias: (SMTP|smtp):" it will do:
# print the third row, insert a TAB, insert "OK" and add a newline
# into relay_recipients.proto
%.$(DB): %.proto
/usr/sbin/postmap $*.proto && mv $*.proto.$(DB) $*.$(DB)
# Building a *.db requires a *.proto file. If that exists,
# postmap is called to build the map from *.proto. If postmap is successful
# the *.proto map will be renamed to *.dbOnce the makefile has built the recipient map we need to configure Postfix to make use of the map. We must also tell Postfix where to transport messages to as soon as they have passed the recipient test. In /etc/postfix/main.cf we add these parameters:
Example 3. /etc/postfix/main.cf
relay_domains = office.example.com, example.com relay_recipient_maps = hash:/etc/postfix/relay_recipients transport_maps = hash:/etc/postfix/transport
The parameter transport_maps in main.cf points to the location of the transport map. The map contains entries that tell Postfix how and where to transport messages for a given recipient domain to.
Example 4. /etc/postfix/transport
domain smtp:[ho.st.na.me]domain smtp:[ip.ad.dr.es]