ACL HELO Trick
This section contains tricks to identify spam based on the HELO message.
The examples below use "drop"; you may prefer to use "deny", in which case you might also consider adding something like
deny
condition = ${if eq{$sender_helo_name}{}}
message = HELO required before MAILto your acl_smtp_mail.
Invalid HELO
What is valid?
Valid HELOs are according to RFC2821 4.1.1.1:
- some.domain.name
[<ipv4-address>]
[<ipv4-address>] optional arbitrary information, maybe with a finishing dot
[IPv6:<ipv6-address>]
[IPv6:<ipv6-address>] optional arbitrary information
RFC2821 is ambiguous about the "optional arbitrary information" after address literals; it says
In situations in which the SMTP client system does not have a meaningful domain name ... the client SHOULD send an address literal ... optionally followed by information that will help to identify the client system.
However it later goes on to specify that the syntax is (in part):
ehlo = "EHLO" SP Domain CRLF
helo = "HELO" SP Domain CRLF
Domain = (sub-domain 1*("." sub-domain)) / address-literal
address-literal = "[" IPv4-address-literal /
IPv6-address-literal /
General-address-literal "]"
; See section 4.1.3which leaves no scope for "optional arbitrary information" tagged onto the end of the HELO.
Invalid HELOs
If you want to strictly apply the RFCs then all other HELOs are invalid. Here follow some recipes for detecting and blocking some of the more common types of invalid HELO.
HELO is an IP address
Type: syntax error
drop
condition = ${if isip{$sender_helo_name}}
message = Access denied - Invalid HELO name (See RFC2821 4.1.3)A plain IP address is not allowed. It needs to be an address literal, i.e. an IP address enclosed in [ ].
HELO is neither FQDN nor address literal
Type: syntax error
drop
# Required because "[IPv6:<address>]" will have no .s
condition = ${if match{$sender_helo_name}{\N^\[\N}{no}{yes}}
condition = ${if match{$sender_helo_name}{\N\.\N}{no}{yes}}
message = Access denied - Invalid HELO name (See RFC2821 4.1.1.1)Neither an address literal nor something containing dots.
drop
condition = ${if match{$sender_helo_name}{\N\.$\N}}
message = Access denied - Invalid HELO name (See RFC2821 4.1.1.1)Something ending with a dot.
DISCUSSION: If I understand rfc2821 4.1.3 correctly, helo might end with a dot (see examples above).
drop
condition = ${if match{$sender_helo_name}{\N\.\.\N}}
message = Access denied - Invalid HELO name (See RFC2821 4.1.1.1)Something containing two subsequent dots. Wouldn't be good for FQDN nor IP address.
HELO is my hostname
Type: forgery
Sometimes spammers impersonate the hostname of the MX they are delivering their junk to. This ACL drops those connections. Since only spam tools seem to use such an HELO, this ACL is pretty safe.
drop message = "REJECTED - Bad HELO - Host impersonating [$sender_helo_name]"
condition = ${if match{$sender_helo_name}{$primary_hostname}}
HELO is one of my Domains
Type: forgery
Sometimes spammers will try to send spam by impersonating one of our domains in the HELO. This ACL assumes that you have a domainlist called all_mail_handled_locally.
Be sure to use this AFTER your authenticated SMTP and other bless email that you will forward for, since some mail clients use the domain part of the senders address as the HELO string.
drop message = "REJECTED - Bad HELO - Host impersonating [$sender_helo_name]"
condition = ${if match_domain{$sender_helo_name}{+all_mail_handled_locally}{true}{false}}(There is a small chance that mail which uses this kind of HELO is actually valid; e.g. company example.com might have a web server whose canonical hostname is example.com. If you handle mail for that domain, then the web server could legitimately connect to your mail server and say "HELO example.com". However that is pretty theoretical; I've never seen that actually happen in practice.)
HELO is faked interface address
Type: forgery
Some spammers put the server's interface address they connect to in their HELO, maybe asuming it is whitelisted or something.
drop condition = ${if eq{[$interface_address]}{$sender_helo_name}}
message = $interface_address is _my_ addressNote: If you are running your mail server behind NAT, you should replace $interface_address with your external IP address.
Note: If you have more than one ip-addresses on your's interface use @[] instead of $interface_address, full acl will be:
drop message = Bad helo name
condition = ${if \
and{ \
{isip {$sender_helo_name}} \
{match_ip{$sender_helo_name}{@[]}} \
}{yes}{no} \
}