I’ve used this in the past to limit the amount of SPAM by only allowing relay back to my exchange server of email addresses that are valid.
in /etc/postfix/main.cf you will need to modify “smtpd_recipient_rectrictions” adding the following. I called my file user-list.
- “check_recipient_access hash:/etc/postfix/user-list.db”
Adding this will accept mail for email addresses that are listed in the file.
On the Windows Exchange server, for this I’ll assume we are using OWA and we can drop the list of addresses within the inetpub directory, allowing us to pick it up using wget.
On the exchange server I use the following script:
'================================================================================================== ' ' VBScript Source File ' ' NAME: LISTPROXYADDRESSES.VBS ' VERSION: 0.9 ' AUTHOR: Bharat Suneja , Bharat Suneja ' CREATE DATE : 5/06/2004 ' LAST MODIFIED : 9/23/2005 '================================================================================================== ' COMMENT: ' '================================================================================================== 'Set up constant for deleting values from multivalued attribute memberOf Const ADS_PROPERTY_NOT_FOUND = &h8000500D Const ADS_UF_ACCOUNTDISABLE = 2 'For UserAccountControl Const strX400Search = "X400" '______________________________________________________ 'Set RootDSE Set objRootDSE = GetObject("LDAP://rootDSE") strDomain = objRootDSE.Get("defaultNamingContext") strADPath = "LDAP://" & strDomain 'wscript.Echo strADPath Set objDomain = GetObject(strADPath) 'wscript.echo "objDomain: " & objDomain.distinguishedName 'Setup ADODB connection Set objConnection = CreateObject("ADODB.Connection") objConnection.Open "Provider=ADsDSOObject;" Set objCommand = CreateObject("ADODB.Command") objCommand.ActiveConnection = objConnection 'Execute search command to look for Contacts & Groups objCommand.CommandText = _ "<" & strADPath & ">" & ";(&(|(objectClass=contact)(objectClass=group))(mail=*))" & ";distinguishedName,displayName,mail,proxyAddresses;subtree" 'Execute search to get Recordset Set objRecordSet = objCommand.Execute 'Start procedure strResult = strResult & VbCrLf & "Domain: " & strDomain strResult = strResult & VbCrlf & "#Total Records Found (other accounts): " & objRecordSet.RecordCount & VbCrlf AddressCount = 0 While Not objRecordSet.EOF 'Iterate through the search results strUserDN = objRecordSet.Fields("distinguishedName") 'Get User's distinguished name from Recordset into a string set objUser= GetObject("LDAP://"& strUserDN & "") 'Use string to bind to user object strResult = strResult & VbCrlf & "cn: " & objUser.cn strResult = strResult & VbCrlf & "mail: " & objUser.mail arrProxyAddresses = objRecordSet.Fields("proxyAddresses") If IsArray(objRecordSet.Fields("proxyAddresses")) Then strResult = strResult & VbCrLf & "Proxy Addresses" For Each ProxyAddress in arrProxyAddresses 'Sub: Check X400 If InStr(ProxyAddress, strX400Search) <> 0 Then 'Wscript.Echo "#This was an x400" Else strResult = strResult & VbCrlf & proxyAddress End If 'Ends loop for X400 address Next Else strResult = strResult & VbCrlf & "#Object does not have proxy addresses" End If strResult = strResult & VbCrLf objRecordSet.MoveNext Wend '************************************* 'Begin second query for users varDisabledCounter = 0 'Execute search command to look for user objCommand.CommandText = _ "<" & strADPath & ">" & ";(&(objectClass=user)(mail=*))" & ";distinguishedName,displayName,mail,proxyAddresses;subtree" 'Execute search to get Recordset Set objRecordSet = objCommand.Execute strResult = strResult & vbCrlf & "#Users" strResult = strResult & VbCrlf & "#Total Records Found (users): " & objRecordSet.RecordCount & VbCrlf While Not objRecordSet.EOF 'Iterate through the search results strUserDN = objRecordSet.Fields("distinguishedName") 'Get User's distinguished name from Recordset into a string set objUser= GetObject("LDAP://"& strUserDN & "") 'Use string to bind to user object If objUser.AccountDisabled = TRUE Then 'If User account disabled, then skip proxy address enum varDisabledCounter = varDisabledCounter + 1 strResult2 = strResult2 & VbCrLf & varDisabledCounter & " " & objUser.displayName & VbCrLf strResult2 = strResult2 & "cn: " & objUser.cn strResult2 = strResult2 & VbCrlf & "mail: " & objUser.mail arrProxyAddresses = objRecordSet.Fields("proxyAddresses") If IsArray(objRecordSet.Fields("proxyAddresses")) Then strResult2 = strResult2 & VbCrLf & "Proxy Addresses" For Each ProxyAddress in arrProxyAddresses 'Sub: Check X400 If InStr(ProxyAddress, strX400Search) <> 0 Then 'Wscript.Echo "#This was an x400" Else strResult2 = strResult2 & VbCrlf & proxyAddress AddressCount = AddressCount + 1 End If 'Ends loop for X400 address Next Else strResult2 = strResult2 & VbCrLf & "#Object does not have proxy addresses" End If strResult2 = strResult2 & VbCrLf Else strResult = strResult & VbCrlf & "cn: " & objUser.cn strResult = strResult & VbCrlf & "mail: " & objUser.mail arrProxyAddresses = objRecordSet.Fields("proxyAddresses") If IsArray(objRecordSet.Fields("proxyAddresses")) Then strResult = strResult & VbCrLf & "Proxy Addresses" For Each ProxyAddress in arrProxyAddresses 'Sub: Check X400 If InStr(ProxyAddress, strX400Search) <> 0 Then 'Wscript.Echo "#This was an x400" Else strResult = strResult & VbCrlf & proxyAddress AddressCount = AddressCount + 1 End If 'Ends loop for X400 address Next Else strResult = strResult & VbCrLf & "#Object does not have proxy addresses" End If strResult = strResult & VbCrLf End If 'End check for disabled user objRecordSet.MoveNext Wend 'End second query for users strResult = "Users, Groups & Contacts" & VbCrLf & "-------------------------" & VbCrLf & strResult strResult = strResult & VbCrLf & "Disabled Users" & VbCrLf & "-------------------------" & VbCrLf & strResult2 'WScript.Echo strResult 'Output to a text file Set objFileSystem = CreateObject("Scripting.FileSystemObject") Set objOutputFile = objFileSystem.CreateTextFile("C:\inetpub\wwwroot\postfix\proxyaddresses.txt") objOutputFile.Write strResult
You will want to modify the location that it places the file to the correct location for you “Set objOutputFile = objFileSystem.CreateTextFile(“C:\inetpub\wwwroot\postfix\proxyaddresses.txt”)”
After testing this I also created a scheduled task on the exchange server to run this every 15 min. This way when users are added or new email addresses are created they will automatically be placed in a location where they can be picked up by our postfix server.
On the postfix server, I created the following script to pull the file from the exchange server and format it correctly for postfix:
#!/bin/bash #pull exhange user email #written by David Hedges mkdir /tmp/exch-users cd /tmp/exch-users wget http://<YOUR SERVER IP or FQDN>/postfix/proxyaddresses.txt #you may need to add sed statements for other domain extensions if you use one that is not listed grep -i smtp proxyaddresses.txt |grep -v ADdomain.local |sed 's/smtp://g' |sed 's/SMTP://g'|sed 's/\.com/\.com\tOK/g' |sed 's/\.COM/\.COM\tOK/g' |sed 's/\.net/\.net\tOK/g' |sed 's/\.NET/\.NET\tOK/g' |sed 's/\.tv/\.tv\tOK/g' |sed 's/\.TV/\.TV\tOK/g' |sed 's/\.org/\.org\tOK/g' |sed 's/\.ORG/\.ORG\tOK/g' |sed 's/\.info/\.info\tOK/g' |sed 's/\.INFO/\.INFO\tOK/g' | sed 's/\.us/\.us\tOK/g' | sed 's/\.US/\.US\tOK/g' >/tmp/exch-users/user-list /usr/sbin/postmap user-list mv ./user-list.db /etc/postfix/ mv ./user-list /etc/postfix cd /tmp rm -R /tmp/exch-users
This will place both a plain text and a hash file in your /etc/postfix directory.
For this I also created a cron job to run it every 5 min. That way it will pick up the changes as the exchange host publishes them.
Finally, you will need to restart your postfix instance for the change to read the user-list.db file to begin.
Leave a Reply