====== Exim4 - Access Control ======
Exim allows you to apply access control lists at various points of the SMTP transaction by specifying an ACL to use and defining its conditions in exim.conf.
===== HELO checking =====
You could start with the HELO string.
# Specify the ACL to use after HELO
acl_smtp_helo = check_helo
# Conditions for the check_helo ACL:
check_helo:
deny message = Gave HELO/EHLO as "friend"
log_message = HELO/EHLO friend
condition = ${if eq {$sender_helo_name}{friend} {yes}{no}}
deny message = Gave HELO/EHLO as our IP address
log_message = HELO/EHLO our IP address
condition = ${if eq {$sender_helo_name}{$interface_address} {yes}{no}}
accept
**WARNING**: Pursue **HELO** checking at your own peril. The HELO is fairly unimportant in the grand scheme of SMTP these days, so don't put too much faith in whatever it contains. Some spam might seem to use a telltale HELO string, but you might be surprised at how many legitimate messages start off with a questionable HELO as well. Anyway, it's just as easy for a spammer to send a proper HELO than it is to send HELO im.a.spammer, so consider yourself lucky if you're able to stop much spam this way.
===== HELO checking =====
Often spammers send for the HELO argument the name or the IP of your host. Here my own domain is "sharewiz.net" and my own IP is 5.42.134.35.
acl_check_helo:
accept
hosts = +own_hosts
# If the HELO pretend to be this host
deny condition = ${if or { \
{eq {${lc:$sender_helo_name}}{sharewiz.net}} \
{eq {${lc:$sender_helo_name}}{5.42.134.35}} \
} {true}{false} }
# by default we accept
accept
===== Remote host IP checking =====
Allow connections from our own hosts and a white-list (Some hosts from big internet providers) with no more check. We refuse connections with some hosts.
acl_check_host:
accept
hosts = +own_hosts : /etc/exim4/filters/host_white.list
deny
log_message = match host_reject.list
hosts = /etc/exim4/filters/host_reject.list
accept
===== Remote host IP checking by DNS black-list =====
Hosts listed by the dns list **sbl-xbl.spamhaus.org** are spammers or relays for spams. Often if you refuse the connection for one of these hosts then a new try is done by another relay some seconds later. A better solution is to do the rejection when the RCPT is received. Then the spammer does not try again.
acl_check_rcpt:
. . .
drop
log_message = match sbl-xbl.spamhaus.org
dnslists = sbl-xbl.spamhaus.org
===== Sender checking =====
To refuse some senders.
acl_check_sender:
deny senders = /etc/exim4/filters/sender_reject.list
accept
===== Sender Address or Remote Host ACL =====
You can perform a check on the sender address or remote host. This shows how to do that after the **RCPT TO** command; if you reject here, as opposed to rejecting after the **MAIL FROM**, you'll have better data to log, such as who the message was intended for.
# Specify the ACL to use after RCPT TO
acl_smtp_rcpt = check_recipient
# Conditions for the check_recipient ACL
check_recipient:
# [...]
drop hosts = /etc/exim_reject_hosts
drop senders = /etc/exim_reject_senders
# [ Probably a whole lot more... ]
This example uses two plain text files as blacklists. Add appropriate entries to these files - hostnames/IP addresses to /etc/exim_reject_hosts, addresses to /etc/exim_reject_senders, one entry per line.
===== Recipient: no hack =====
(From /usr/share/doc/exim4-doc-html/html/C043.txt.gz):
Deny if the local part contains @ or % or / or | or !. These are rarely found in genuine local parts, but are often tried by people looking to circumvent relaying restrictions.
Also deny if the local part starts with a dot. Empty components aren't strictly legal in RFC 2822, but Exim allows them because this is common. However, actually starting with a dot may cause trouble if the local part is used as a file name (e.g. for a mailing list).
acl_check_rcpt:
. . .
# refuse if the recipient string is a hack,
# see exim file example C043.txt.gz
deny
local_parts = ^.*[@%!/|] : ^\\.
===== Recipient: no relay =====
I refuse to relay spams:
acl_check_rcpt:
. . .
# For the rest, the domain of the recipient address
# must be my public domain. (no relay)
require
log_message = no relay.
domains = +public_domains
===== Recipient: manual redirect by the sender =====
The idea is to send an automatic reply, using "mail" command in a filter, to inform that an email is blocked and that the user must use an other email address. This can be used to change a user email which receive to much spam or to protect a public email address.
In a filter:
### reply for someone@msharewiz.net
if $original_local_part is "someone" then
seen mail from drop@sharewiz.net subject "Re: $h_subject" file .someone_reponse.txt
finish
endif
===== Recipient: emails addresses to catch spams =====
You can publish a sacrified email address in a web page to trap spammers (some spammers crawl other web pages to get emails). When this email address matches then an error is returned and all the message reception is dropped. There are changes that the spammer software will not retry with this recipient removed.
When you write to a suspicious company wich could send you spam or when you write in a newsgroup, you can use a special email, with date (like echant-td-n040531@sharewiz.net) or with an included identifier (like echant-tr-lemonde@sharewiz.net). Then if you receive spam for this email you can put it in the drop list (in this example: /etc/exim4/filters/recipients_drop.list).
acl_check_rcpt:
. . .
drop
log_message = match recipients_drop.list.
recipients = /etc/exim4/filters/recipients_drop.list
I use this script in cron.daily/ to update my emails with a date incorporated. The letter before the date is used to trace the origin (web, news, email).
#!/bin/bash
#
# Update my email to include todays date.
set -e
T=$(tempfile)
D=$(date '+%y%m%d')
function mod_file {
EMAIL="$1"
LETTRE="$2"
CONF="$3"
if [ -f "$CONF" -a -r "$CONF" ]; then
lockfile-create "$CONF"
sed "s/${EMAIL}-td-${LETTRE}[0-9]\{6\}@sharewiz.net/${EMAIL}-td-${LETTRE}${D}@sharewiz.net/g" <"$CONF" >"$T"
cp "$T" "$CONF"
lockfile-remove "$CONF"
fi
}
# The first line will replace echant-td-n040625@sharewiz.net
# with echant-td-n040626@sharewiz.net
mod_file echant n /home/john/.kde/share/config/knoderc
mod_file echant e /home/john/.sylpheed/accountrc
mod_file echant e /home/john/.initvar
# For apache we should reload but it is done by
# logrotate from time to time.
mod_file echant w /etc/apache-extern/httpd.conf
rm $T
===== Content ACL =====
It is also possible to perform [[http://www.exim.org/exim-html-4.50/doc/html/spec_40.html|content scanning]] using a regex against the body of a message, though obviously this can cause Exim to use more CPU than it otherwise would need to, especially on large messages.
# Specify the ACL to use after DATA
acl_smtp_data = check_message
# Conditions for the check_messages ACL
check_message:
deny message = "Sorry, Charlie: $regex_match_string"
regex = ^Subject:: .*Lower your self-esteem by becoming a sysadmin
accept
===== Greylist =====
Greylisting use the fact that most of the time spammers softwares do not take account tempory errors to retry later. It's very effective. You can use a daemon like [[http://packages.debian.org/unstable/mail/greylistd|greylistd]].
When exim send a "temporary error":
* Not if the host is in a white list of "good" hosts.
* Not if there is no sender: it is a bounce message.
* Not if the HELO argument is well configured and the host name seams owned by the sender.
* if the greylist daemon want to.
The two lines with **set acl_m9** are used to send the request to the daemon and get the result. The **"/24"** is here because some big MTA can be spread over multiple hosts.
######################################################################
# MAIN CONFIGURATION SETTINGS #
######################################################################
# Mandatory to use "verify = helo"
helo_try_verify_hosts = !+own_hosts
. . .
######################################################################
# ACL CONFIGURATION #
######################################################################
# ACL "subroutine" used by acl_check_rcpt below. Used to detect
# hosts which have not their own registered domain-name (probably spammer).
# Return ok if the HELO argument correspond to the connected HOST and
# if the argument does not contain an IP in decimal or hexa.
# I have created this ACL subroutine because we can't do a list of "or"
# in ACL (it's a list of "and"), so I use a negation of "and":
# no (no A and no B) = A or B.
acl_clean_helo:
accept
verify = helo
condition = ${if match{$sender_helo_name}{\N(\d{1,3}[.-]\d{1,3}[.-]\d{1,3}[.-]\d{1,3})|([0-9a-f]{8})|([0-9A-F]{8})\N}{false}{true}}
acl_check_rcpt:
. . .
# Greylisting, if the HELO argument seems bad or
# a dialin name (with IP included in the name). Some hosts from big
# providers are in a white list to avoid testing. When there is no
# sender then it is a bounce message, so no greylist.
defer
message = Please try later.
!hosts = /etc/exim4/filters/host_white.list
!senders = :
!acl = acl_clean_helo
log_message = greylisted.
set acl_m9 = ${mask:$sender_host_address/24} $sender_address $local_part@$domain
set acl_m9 = ${readsocket{/var/run/greylistd/socket}{$acl_m9}{5s}{}{}}
condition = ${if eq {$acl_m9}{grey}{true}{false}}
===== Anti-virus: Windows executable in attachment =====
It's a very basic anti-virus: every emails with a windows executable as attachment is rejected.
acl_check_data:
. . .
deny message = This message contains an attachment of a type which we do not accept (.$found_extension)
demime = bat:btm:cmd:com:cpl:dll:exe:lnk:msi:pif:prf:reg:scr:vbs:url
===== anti-virus: clamav =====
######################################################################
# MAIN CONFIGURATION SETTINGS #
######################################################################
av_scanner = clamd:/var/run/clamd.ctl
. . .
######################################################################
# ACL CONFIGURATION #
######################################################################
acl_check_data:
. . .
deny message = This message contains a virus or other harmful content ($malware_name)
demime = *
malware = *
anti-spam external detector: spamassassin
We add a "X-SA-Score:" in the header of all emails, a "X-SA-Report:" for all email with spam score >0, we consider it a spam if score >5 (adding "X-SA-Status: Yes" and we don't accept the email if score >7.
Because of the "accept" we must put this acl block at the end of the acl_check_data.
######################################################################
# MAIN CONFIGURATION SETTINGS #
######################################################################
spamd_address = 127.0.0.1 783
. . .
######################################################################
# ACL CONFIGURATION #
######################################################################
acl_check_data:
. . .
## spamassassin, spams are never big and spamassassin can die on big emails, so we
## limit its use under 500k.
accept condition = ${if >={$message_size}{500k}{yes}{no}}
warn message = X-SA-Score: $spam_score
spam = nobody:true
warn message = X-SA-Report: $spam_report
spam = nobody:true
condition = ${if >{$spam_score_int}{0}{true}{false}}
warn message = X-SA-Status: Yes
spam = nobody:true
condition = ${if >{$spam_score_int}{50}{true}{false}}
deny message = This message scored $spam_score spam points.
spam = nobody:true
condition = ${if >{$spam_score_int}{70}{true}{false}}
In your "~/.forward" you can redirect spams (5< score ≤7) in a special inbox:
# Exim filter <<== do not edit or remove this line!
if $h_X-SA-Status: matches "^Yes" then
save $home/.Mailboxes/incoming/spam
finish
endif
===== Checking source of email associated with your domain in whois =====
If you have an email published in a whois database (spammers scan these databases) but want emails just from your registrar, you can add this in your "~/.forward" filter:
# Exim filter <<== do not edit or remove this line!
if $original_local_part is "sharewiz-tr-myregistrar"
then
if $sender_address_domain is "myregistrar.net" then
deliver john
else
save $home/.Mailboxes/incoming/spam
finish
endif
endif