Access Control Lists
Access Control Lists (ACL) allows Exim to check incoming messages, most of them apply to SMTP input but to can check non-SMTP messages as well. ACLs in the default configuration forbid relaying and impose some standard checks on messages from other hosts.
ACLs have there own section in the configuration file, each ACL starts with its name, terminated by a colon
ACL configuration | begin acl ## beginning of the ACL section small_acl: ## ACL definition called "small_acl" accept hosts = one.datadisk.co.uk |
You can have as many ACLs as you wish, and the order in which they appear does not matter. The two most common uses for ACLs are
I will now go through the default configuration that is supplied when you first configure Exim.
Default ACL Configuration |
|
Default Configuration | acl_check_rcpt: accept hosts = : deny message = Restricted characters in address domains = +local_domains local_parts = ^[.] : ^.*[@%!/|\#&?] deny message = Restricted characters in address domains = !+local_domains local_parts = ^[./|] : ^.*[@%!\'`#&?] : ^.*/\\.\\./ accept local_parts = postmaster domains = +local_domains require verify = sender accept hosts = +relay_from_hosts control = submission accept authenticated = * control = submission require message = relay not permitted domains = +local_domains : +relay_to_domains require verify = recipient accept |
Now to explain what everything means rule by rule
Breakdown of default configuration |
|
acl_check_rcpt: | ACL Name The start of the ACL and names it |
accept hosts = : |
Messages from local Processes accepts unconditionally all recipients in messages that are submitted by SMTP from local processes using the standard input and output (that is not using TCP/IP) Basically Exim matches incoming messages that did not arrive over a TCP/IP connection. |
deny message = Restricted characters in address domains = +local_domains local_parts = ^[.] : ^.*[@%!/|\#&?] |
Guarding against incoming rogue local parts If both conditions are true (domains and local_parts) access is denied and the value of message is sent. domains means that the domain in the recipients address must match one domain in the list (notice the + this means a list) local_parts has two expressions, which match any address that start with a dot or contain any special characters. The expressions are used to filter out spam email before we do anything else. |
deny message = Restricted characters in address domains = !+local_domains local_parts = ^[./|] : ^.*[@%!\'`#&?] : ^.*/\\.\\./ |
Guarding against outgoing rogue local parts This is the same as above but less restrictive |
accept local_parts = postmaster domains = +local_domains |
Mail for postmaster This statement accepts any messages if the local part is postmaster and the domain is listed in the local_domains list, this makes sure that all mail to postmaster is accepted. |
require verify = sender |
Verifying the senders address This statement requires that the senders address to be verified before any subsequent ACL statement can be used. Verification consists of trying to route the address, to see if the message could be delivered to it. |
accept hosts = +relay_from_hosts control = submission |
Allow relaying from identified hosts hosts states that the messages must come from a specific host, in the case from the relay_from_hosts list The control statement (control modifier) means that the message is to be processed as a submission from an MUA. submission mode is used when receiving messages from a MUA client, is applies extra checks and transformations that are not used for messages that arrive from other MTA's |
accept authenticated = * control = submission |
Allow relaying from authenticated hosts This statement accepts the address only if the client host has authenticated itself, again assuming submission mode when it does so. By default Exim does not define any authenticators so this would fail normally |
require message = relay not permitted domains = +local_domains : +relay_to_domains |
Reject unrecognized domains This statement checks that the domain of the incoming recipient address is either in the list of local domains, or in the list of domains for which incoming relaying is permitted. Any other domains is rejected as an invalid relay attempt. |
require verify = recipient |
Verify the recipient address This statement requires the recipient address to be verified, in the same way that the sender address was verified earlier |
accept | Accept a valid address If the messages passed all the checks before then accept it |
You have to tell Exim when to use a ACL by specifying it in the configuration file, you can specify different ACLs for different SMTP commands
Calling a ACL | # ACL external file # ACL defined in Exim's configuration file |
There are many ACL options
acl_not_smtp | ACL for non-SMTP messages |
acl_not_smtp_mime | ACL for each non-SMTP MME part |
acl_not_smtp_start | ACL for the start of a non-SMTP message |
acl_smtp_auth | ACL for AUTH commands, the code block will run but no data has been sent yet, thus you can reject the message before getting the data saving resources getting the data. |
acl_smtp_connect | ACL for the start of a SMTP connection, runs at the start of a SMTP connection |
acl_smtp_data | ACL for the end of DATA commands |
acl_smtp_etrn | ACL for ETRN commands |
acl_smtp_expn | ACL for EXPN commands |
acl_smtp_helo | ACL for HLO or EHLO commands |
acl_smtp_mail | ACL for MAIL commands |
acl_smtp_mailauth | ACL for AUTH parameters of MAIL commands |
acl_smtp_mime | ACL for each STP MIME part , is run before acl_smtp_data |
acl_smtp_predata | ACL for the start of DATA commands, the code block will run but no data has been sent yet, thus you can reject the message before getting the data saving resources getting the data. |
acl_smtp_quit | ACL for QUIT commands |
acl_smtp_rcpt | ACL for RCPT commands |
acl_smtp_starttls | ACL for STARTTLS commands |
acl_smtp_vrfy | ACL for VRFY commands |
The result of an ACL is either accept or deny, or if a test cannot complete defer. These results cause 2xx, 5xx, and 4xx return codes to be used in the SMTP dialogue.
When an ACL is run for an incoming SMTP command, the contents of the command are placed in $smtp_command. The setting of other variables depends on the type of SMTP command, here are a list of the most commonly used ones
$smtp_command | contains the contents of the SMTP command |
$smtp_command_argument | contains the argument that follows the SMTP command |
$sender_host_address | contains information about the senders host |
$sender_address | contains information about message's sender |
$domain | contains information about domain part of the address |
$local_part | contains information about local part of the address |
$authenicated_sender | contains the value obtained from the AUTH parameter of the MAIL command |
$message_size | set to the value of the SIZE parameter on the MAIL command |
$rcpt_count | increases by one everytime a RCPT command is received before the ACL is run |
$recipients_count | increases by one each time a RCPT command is accepted, so while an ACL is being processed, contains the number previously accepted recipients |
An individual ACL consists of a number of statements, each statements starts with a verb followed by a number of conditions and other modifiers, these are processed in order, if all the conditions are meet then the action of the verb is taken.
ACL statements are processed in order, depending on the verb some conditions may not be tested if other conditions have already failed, for instance the require verb will not test any of the other conditions below one that has failed, where deny will test all conditions regardless if any have failed.
statement checks | require domains = +local_domains verify = recipient message = unrecognized address ## will never be reached if rejection occurs before |
The verbs are listed as follows
accept | if all conditions are true the ACL returns accept, if any of the conditions are false what happens depends on whether endpass appears among the conditions. If the failing condition precedes endpass, control is passed to the next ACL statement, if it follows endpass the ACL returns deny. |
defer | if all the conditions are true the ACL returns deny, if any are false then it is passed to the next ACL |
deny | if all the conditions are true the ACL returns deny, if any are false then it is passed to the next ACL |
discard | It acts like accept, except that it causes recipients to be discarded |
drop | if all the conditions are true the action taken is the same as deny, except that after a permanent errors the SMTP connection is dropped |
require | if all the conditions are true it is passed to the next ACL, if any are false then the ACL returns deny. |
warn | Always pass to the next ACL, it is used for its side effects, if all conditions are true and a log_message modifier is present its data is written to Exim's main log. You can use the logwrite modifier as well |
You can negate conditions
negate a condition | deny Note: causes the ACL to return deny if the recipient domains ends in datadisk.co.uk and the recipient address cannot be verified. |
There are a number of ACL modifiers
add_header | adds a header to an incoming message |
control | has a number of different arguments that vary the way in which an incoming message is processed. It normally is used with the warn verb so that other ACLs can make the decision to accept or deny the message |
delay | will wait until the interval time supplied has passed before processing |
endpass | is only used in accept or discard statements, it is used between statements to supply a if like statement. |
log_message | Always writes to Exim's main log file and adds the text "Warning:" to the start of the message |
logwrite | writes a message to Exim's main log file, you can specify a different log file to write to. |
message | sets a up message that is used if the current statement causes the ACL to terminate with an accept or deny or defer status. |
set | is used to put values into ACL variables |
Arguments for the Control Modifier
There are a number of arguments for the control modifier, some of which are used in special circumstances only.
caseful_local_part caselower_local_part |
using caseful_local_part any uppercase letters in the original local part are restored for the rest of the ACL or until caselower_local_part is encountered |
queue_only freeze |
when using queue_only the message is queued and not delivered immediately, you can freeze a message by using the freeze option. This is only on a per message bases and not apply to the next message useless it uses this ACL. |
submission | If a message is received by the MTA via a MUA (client), the message is modified to look like a normal message for example if the Date: header line is missing it is added. |
There are many ACL conditions, some have been used already here is a list of the rest
acl | Allows one ACL to call another |
authenicated | Tests for SMTP authentication |
condition | A general customizable condition, this should result in yes or no |
decode | decode a MIME part |
dnslist | Checks DNS black lists |
domains | Tests the domain of the recipient |
encrypted | Tests for encrypted SMTP session |
hosts | Tests for specific sending hosts |
local_parts | Tests for local part of a recipient address |
malware | Call external virus scanner |
mime_regex | Scan MIME part with regex |
ratelimit | Check rate of message submission |
recipients | Tests a recipient address |
regex | Scan the message with a regex |
sender_domains | Tests the domain of a sender address |
senders | Tests the sender address |
spam | Call spamAssassin |
verify | Verifies sender and recipient addresses and a number of other message characteristics |
I am not going into too much details but i will supply a few examples
Checking the identify of the client host | accept hosts = +relay_from_hosts |
Using DNS black lists | deny Note: the message is only logged if the condition fails |
Checking senders address | deny Note: blocking specific addresses and domains |
Checking recipients address | deny Note: check the recipients address |
Check message header lines | require Note: reject messages with an incorrect header, ideal to capture @ or <> header lines |
Custom ACL check | accept |
Ratelimit | defer you can also use $sender_rate, $sender_rate_limit and $sender_rate_period in log_message |
Calling another ACL | acl_check_rcpt: accept domains = +local_domains endpass acl = acl_local acl_local: accept local_parts = postmaster domains = +relay_to_domains |
logging to a different file | warn Note: the :reject: means that this will be logged to the reject log instead of mainlog, the rule here basically states that i should only receive emails to the local domain from my mailhost only. see message filtering for more information on logfiles |