Table of Contents
Recipient Validation / Recipient Access
Recipient Validation is absolutely necessary in mail gateways. Some reasons are :
- Mail servers shall not accept messages sent to invalid recipients, to avoid backscattering.
- You may want to protect some valid email addresses from outside of your domain. E.g. you can define a valid address - everybody@example.com - and allow it receive only messages coming from inside your organisation.
- Checking the validity of a recipient allow the filter to detect outside people guessing valid email addresses in your domain.
Recipient Validation is the set of data and procedures needed to identify the class of the recipient being handled (valid or protected recipient, spamtrap, …) and the action to be taken, eventually different from normal transparent actions (ACCEPT, REJECT, DISCARD, …), which may depend on data other than simply the recipient email address (e.g., SMTP client IP address, …).
Why check recipient access inside the filter ? sendmail access database isn't enough ? Yes and no !
- In sendmail versions older than 8.14.0 the milter recipient callback is called before sendmail does its own recipient handling. This results in many unwanted situations. E.g., when doing greylisting, even when the recipient doesn't exist locally an entry is added to the pending database.
- one interesting feature of ze-filter is the ability to accept messages to some recipients only if it comes from local networks (“intranet” protected recipients).
- ze-filter can manage a list of SMTP clients doing too many errors, dynamically adding and removing IP addresses. It's much easier to do this in the filter than in the MTA.
- it's easier to manage all this if all recipient data and actions are concentrated in a single place.
To check recipient access with ze-filter, enable it at ze-filter.cf file and define all entries at ze-rcpt database.
CHECK_RCPT_ACCESS YES DB_RCPT ze-rcpt.db
Recipient Database - ze-rcpt
This database is used to :
- define for which target domains recipient validation will be done
- define default access for target domains
- define access for each recipient inside target domains.
There are two kind of records :
Domain records - These records allow you to configure for which domains you want to check recipient access. Syntax for these entries are as follows :
CheckRcptDomain:KEY VALUE
KEY may be something like domain.com or *.domain.com. The first entry matches only domain.com and the second one matches all subdomains of domain.com.
VALUE may be one of :
- NO - No check for recipients in this domain - transparent handling.
- YES - Access check will be done for all recipients in this domain, using rcpt full address.
- LOCAL - Access check will be done for this domain, using both full address and local part. This option is useful only if your mailserver serves many domains, the same recipient exists in many domains and you are unable to enumerate recipients in each domain. If possible, you should avoid this as there may be some unwanted behaviour.
- REJECT - All messages for this domain will be rejected.
- TEMPFAIL - All messages for this domain will be rejected with a tempfail error.
- SPAMTRAP - The entire domain is a honeypot.
Recipient records - These records allow you to configure access for each recipient.
RcptAccess:RCPT ACTION
RCPT can be a domain name (to define the default action for this domain), a full address or the user part of an address.
ACTION can be one of :
- OK - This is a normal recipient
- REJECT - Reject all messages for this recipient
- TEMPFAIL -
- SPAMTRAP - This is a fake recipient
- IGNORE - This is a normal recipient, but don't apply any other filtering check at this SMTP command step : filtering will be reconducted for this recipient, for DATA content.
- LOCAL-NET - Accept messages for this recipient only if the connection comes from the LOCAL network.
- DOMAIN-NET - Accept messages for this recipient only if the connection comes from a LOCAL or DOMAIN network..
- FRIEND-NET - Accept messages for this recipient only if the connection comes from a LOCAL or DOMAIN or FRIEND network.
- KNOWN-NET - Accept messages for this recipient only if the connection comes from any defined and known network.
- USER-UNKNOWN - This is an unknown or inexistent recipient.
A typical ze-rcpt database looks something like :
# Enable checking recipients for this domain CheckRcptDomain:example.com YES # Define a default value RcptAccess:example.com USER-UNKNOWN # Entries needed by RFCs (don't remove...) RcptAccess:abuse@example.com OK RcptAccess:postmaster@example.com OK # Users in the domain RcptAccess:alice@example.com OK RcptAccess:bob@example.com OK # all@example.com can't receive messages from the "world" RcptAccess:all@example.com KNOWN-NET
Some Examples
- Checking access for local users. This kind of configuration is useful when it's possible to enumerate all valid users.
# Check recipient access for domain.com ... CheckRcptDomain:domain.com LOCAL # # ... and for all sub domains of this domain CheckRcptDomain:.domain.com LOCAL # # default is "Unknown user" if not defined RcptAccess:domain.com USER-UNKNOWN # # toto exists in domain.com and all sub domains RcptAccess:toto@ OK # # titi exists only in domain.com RcptAccess:titi@domain.com OK # # this is a spamtrap RcptAccess:spam@domain.com SPAMTRAP # # accept messages for everybody only if the connection # comes from a known network RcptAccess:everybody@domain.com KNOWN-NET
- Checking recipient access for some users of some directions. This configuration is useful when it isn't possible to list all valid users.
# CheckRcptDomain:domain.com YES # # default access is "OK" RcptAccess:domain.com OK # # this is a spamtrap RcptAccess:spam@domain.com SPAMTRAP # # accept messages for everybody only if the connection # comes from a known network RcptAccess:everybody@domain.com KNOWN-NET
- I want to get login@*.univ.fr working (common login, several MXs)
CheckRcptDomain:univmed.fr LOCAL CheckRcptDomain:*.univmed.fr LOCAL RcptAccess:univmed.fr USER-UNKNOWN RcptAccess:3pe@aaa.univ.fr OK RcptAccess:7emepcrdt@bbb.univ.fr OK RcptAccess:aad@ OK RcptAccess:aae@aaa.univ.fr OK ...
aad is ok for *.univ.fr, but aae only for aaa.univ.fr
- I want to get another domain fully qualified (separate name space login)
CheckRcptDomain:univ.fr YES CheckRcptDomain:*.univ.fr REJECT RcptAccess:univ.fr USER-UNKNOWN CheckRcptDomain:aaa.univ.fr YES RcptAccess:aaa.univ-mrs.fr USER-UNKNOWN RcptAccess:abac@aaa.univ.fr OK
Here RcptAccess is allways fully qualified
Recipient Database Management Recipes
Here are some ideas, which can be combined, to put all you need in place to manage the database of recipients.
Create the ze-rcpt.txt database (text file version) from a list of valid recipients
If you have a list of valid recipients, say users.txt
, one recipient per line, you can use the mk_rcpt.pl script to build the textual version of ze-rcpt database.
mk_rcpt.pl -d example.com -a USER-UNKNOWN users.txt > ze-rcpt.txt
Use -d option to define your domain name and -a option to define default action for undefined users.
Getting the list of valid recipients
If your mailserver is runs sendmail
, it's enough to dump the list of all valid users. It shall be something like this (but not necessarily) :
- to dump the list of valid recipients
#! /bin/sh # begin with an empty file cp /dev/null users.txt # add real users getent passwd | awk -F: '{print $1}' >> users.txt # dump all your aliases for db in /etc/mail/aliases*.db do makemap -u hash $db | awk '{print $1}' >> users.txt done # dump the table of virtual users db=/etc/mail/virtusertable.db makemap -u hash $db | awk '{print $1}' | \ awk -F@ '/@example.com/ {print $1}' >> users.txt
postfix
.
Assigning attributes to some recipients
If you want to assign particular attributes to some recipients - other than simply saying that the recipient is a valid one, the simpler way is to use an additional file (named, e.g., users.plus). Put one recipient per line, with its attribute separated by an space :
all intranet john known accounting local ceo domain john.smith spamtrap root reject grey tempfail
ze-rcpt database will be created with :
mk_rcpt.pl -d example.com -a USER-UNKNOWN users.txt users.plus > ze-rcpt.txt
users.txt
and users.plus
files, the one with an attribute will be selected.
Recipient Validation on a SMTP gateway
As said before, if you run ze-filter on an inbound SMTP gateway, you must do recipient validation, for all your internal mail servers (domains and sub domains), mainly to avoid generating backscatter. One way (but not the only one) to create the recipient database is :
- Retrieve the list of valid users for all internal mail servers
- Create one access list text file (ze-rcpt format) for each internal mail server
- Concatenate all access list files in a single one.
- You may use cron to periodically update your recipient database.
Using a Makefile to automate database management
It's possible to use Makefile rules to automate the generation of ze-rcpt database for a bunch of domains, if the names of input files follow the template “domain.action”, where :
- domain is the domain part of the address.
- action is the default action for unlisted addresses for this domain :
- relay or unknown - default result for unlisted recipients is
“User Unknown”
- local - unlisted recipients may be valid but can receive messages only from LOCAL NetClass of addresses.
- domain - unlisted recipients may be valid but can receive messages only from LOCAL or DOMAIN NetClass of addresses.
- friend or known - unlisted recipients may be valid but can receive messages only from any KNOWN NetClass of addresses.
- If
mk_rcpt.pl
finds a file with file extensionplus
and the samedomain
part of the input file being handled, it appends its contents to the end of current input file. This is useful, e.g., if you have a fixed number of recipients with special attributes. - You'll find these scripts inside
contrib/rcpt-database
directory of ze-filter distribution.
# You need GNU make to process this Makefile .SUFFIXES : .rcpt .unknown .relay .local .domain .friend .known .txt .db RSRC = $(wildcard *.relay) ROBJ := $(RSRC:%.relay=%.rcpt) USRC = $(wildcard *.unknown) UOBJ := $(USRC:%.unknown=%.rcpt) LSRC = $(wildcard *.local) LOBJ := $(LSRC:%.local=%.rcpt) DSRC = $(wildcard *.domain) DOBJ := $(DSRC:%.domain=%.rcpt) KSRC = $(wildcard *.known) KOBJ := $(KSRC:%.known=%.rcpt) OBJ = $(ROBJ) $(LOBJ) $(DOBJ) $(KOBJ) $(UOBJ) .unknown.rcpt : mk_rcpt.pl -a USER-UNKNOWN $< > $@ .relay.rcpt : mk_rcpt.pl -a USER-UNKNOWN $< > $@ .local.rcpt : mk_rcpt.pl -a LOCAL-NET $< > $@ .domain.rcpt : mk_rcpt.pl -a DOMAIN-NET $< > $@ .known.rcpt : mk_rcpt.pl -a KNOWN-NET $< > $@ .txt.db : ze-makemap -m e -m u -b $@ < $< all : ze-rcpt.db ze-rcpt.txt : $(OBJ) @ cat $(OBJ) > ze-rcpt.txt clean : rm -f $(OBJ) ze-rcpt.txt ze-rcpt.db *~
Getting valid recipient lists from internal mail servers
If you're running the filter in an inbound SMTP gateway and you don't have direct access to the whole list of valid users of your domain, it's surely possible to make each internal server dumps its own list of valid addresses, and to aggregate them using the ideas explained above (Makefile rules). You can use wget
or rsync
to collect each list of valid addresses, this is how I do. You can do something like this :
- to retrieve distant list of valid users
# Using wget to retrieve the list of remote users HOST="math.univ.fr" echo $HOST | awk '{ printf " . %-32s", $0 }' /usr/sbin/ping $HOST 1 > /dev/null if [ "$?" = "0" ] ; then echo " 8-)" wget -q -t 2 --connect-timeout=5 --read-timeout=10 \ -N http://$HOST/mail/$HOST.relay else echo " ;-(" fi # Using rsync to retrieve the list of remote users HOST="phys.univ.fr" echo $HOST | awk '{ printf " . %-32s", $0 }' /usr/sbin/ping $HOST 1 > /dev/null if [ "$?" = "0" ] ; then echo " 8-)" rsync -aq rsync://$HOST/mail/$HOST.relay . else echo " ;-(" fi