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 :

  1. define for which target domains recipient validation will be done
  2. define default access for target domains
  3. 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
You can write a similar script to get the list of valid recipients, if you're using 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
If an user is found twice or both in 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 :

  1. Retrieve the list of valid users for all internal mail servers
  2. Create one access list text file (ze-rcpt format) for each internal mail server
  3. Concatenate all access list files in a single one.
  4. 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.
  1. If mk_rcpt.pl finds a file with file extension plus and the same domain 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.
  2. 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
doc/spam/recipient_access.txt · Last modified: 2018/02/09 16:59 (external edit)
CC Attribution-Noncommercial-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0