Backscatter? Well, this happens when there is an spam filter in front of an MTA, but the filter don’t know which mailboxes do really exist in the MTA and which ones not.

The procedure explained here is about how to tell a Postfix filter which are the recipients of an Exchange MTA and thus avoid becoming a backscatter

What is Backscatting?

When a server receives an email for an address that does not exist, it should reject it during the SMTP conversation, when the origin server is still connected. Thus the origin can inform the real sender about the problem. Otherwise, if the server accepts an email which is unable to deliver, it must generate a bounce that will be sent to the mail “From” header. Spammers manipulate the sender header of messages, and a misconfigured server ends up sending spam bounces. This type of spam is called Backscatter.

Postfix in front of an Exchange

In our scenario we have a Postfix MTA that receives mail, checks it with for spam, virus and other policies, and finally sends it to a Microsoft Exchange. Since this Postfix MTA does not have mailboxes, we were accepting all mail addressed to our domains. After a while our IP was added to a blacklist for Backscatting.

To fix this we need to know which mailboxes exist in Exchange, in order to reject emails to recipients that does not exist.

The easy part is the Postfix setup, at main.cf we add relay_recipient_maps, to check recipients.

relay_domains = example.com
relay_recipient_maps = hash:/etc/postfix/relay_recipients

we create /etc/postfix/relay_recipients with mailbox list, like this:

foo@example.com     OK
bar@example.com     OK

we need to do postmap and reload postfix

postmap /etc/postfix/relay_recipients
/etc/init.d/postfix reload

Keep the list updated

We are not generating Backscatting any more, but must keep the mailbox list up to date. That list is stored in an ActiveDirectory, to get it we used this VBS script, with some changes:

' 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://DC=example,DC=com")
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 and Groups
'Added groups after Милен Панков mailed me about it :)
Case "user", "group"
  'Select all proxyAddresses
 Dim Alias
  If IsEmpty(User.proxyAddresses) Then
    ' do nothing.
 ElseIf (TypeName(User.proxyAddresses) = "String") Then
      OutPutFile.WriteLine "alias: " & User.proxyAddresses
  Else
    For Each Alias in User.proxyAddresses
    OutPutFile.WriteLine "alias: " & Alias
    Next
  End If
 
Case "organizationalunit", "container"
  EnumerateUsers User

End Select
Next
End Sub

the script output looks like this:

alias: smtp:foo@example.com
alias: smtp:bar@example.com

the script is called daily from a scheduled task and writes the mailbox list to a file named virtual.txt
To access this file from Postfix server we decided to mount its folder with cifs+autofs. We installed and configured autofs:

# /etc/auto.master
/auto /etc/auto.cifs

# /etc/auto.cifs
activedirectory -fstype=cifs,rw,username=Administrator,domain=EXAMPLE,password=secret   ://192.168.1.17/spamassassin

so when accessing to /auto/activedirectory it will mount //192.168.1.17/spamassassin through samba, that’s where we left the VBS script and the virtual.txt file

Finally another script, called from cron, updates the relay_recipients file and restarts postfix if anything changed

#!/bin/bash

USERSFILE=/auto/activedirectory/virtual.txt
TMPFILE=/root/exchangesync/users_exchange.txt
TMPFILE2=/root/exchangesync/users_exchange.parsed

echo "--- Start: `date` ----------"

# Try to access file 10 times so autofs
# have time to mount folder through samba
for i in `seq 1 10`;
do
  if [ -f $USERSFILE ]; then
    echo "found $USERSFILE"
    break
  else
    if [ $i -eq 10 ]; then
      echo "can't access $USERSFILE after 10 retries"
      echo "------ Fi: `date` ----------"
      exit 1
    fi
    echo "can't access $USERSFILE, retrying..."
    sleep 2
  fi
done

cat $USERSFILE > $TMPFILE
dos2unix $TMPFILE
awk -F: '/alias: (SMTP|smtp):/ {printf("%s\tOK\n",$3)}' $TMPFILE | sort > $TMPFILE2
oldmd5=`cat /etc/postfix/relay_recipients.md5`
newmd5=`md5sum $TMPFILE2 | cut -d" " -f1`
if [ "$oldmd5" != "$newmd5" ]; then
  echo "md5 differ, reloading postfix"
  diff -b /etc/postfix/relay_recipients $TMPFILE2
  cp /etc/postfix/relay_recipients /etc/postfix/relay_recipients.old
  cp $TMPFILE2 /etc/postfix/relay_recipients
  echo -n $newmd5 > /etc/postfix/relay_recipients.md5
  /usr/sbin/postmap /etc/postfix/relay_recipients && /etc/init.d/postfix reload
else
  echo "md5 match, not reloading postfix"
fi

echo "------ End: `date` ----------"
echo