Spam fighting techniques using Sendmail

[Work in progress. Stay tuned for more developments]

This page is more of an aide-mémoire for me to point to resources for fighting spam with the environment I use (CentOS 4, Sendmail, SpamAssassin, MailScanner and Clamav). I do not wish to start a religious war on which MTA is best. I am sure Qmail and Postfix are great. As it stands, I grew up with Sendmail and feel good about it. I also use MailScanner, which does not mean that I consider Amavis a bad product.Therefore, your experience may be different but I guess the general concepts are identical, regardless of which software combination you use.

Sendmail

Caveat: the Sendmail configuration file, sendmail.cf, is fussy about its syntax. I urge you to edit the sendmail.mc file instead. In both files, spaces are not tabs and vice-versa. If you copy code from this page, be sure to replace spaces with tabs where necessary.

DNS black lists

Sendmail offers several features that really help fighting spam. The best known are the DNS black lists.

They are added to sendmail.mc with a line saying:

FEATURE(`enhdnsbl’,`sbl-xbl.spamhaus.org’,`”Message from “$&{client_addr}” rejected – see: http://www.spamhaus.org/SBL”

‘sbl-xbl.spamhaus.org’ being the FQDN of the DNSBL server. The message is the string returned to the sending MTA and inserted in the log file. If you use several DNSBLs, try to standardize the returned message, as it will make it easier for your log file analyser, like Logwatch, to tally the daily totals.

There are several DNSBLs to choose from. An exhaustive list can be found on Declude. I have had good results with Spamhaus, Spamcop, Sorbs, NAJBL and Ordb. Your mileage may vary, of course.

DNS white list

Why should you have a DNS white list ? Sometimes, you may need to accept e-mails from machines placed on DNS black lists. In this case, running a local white list is an answer. Beware that the following line should appear in your sendmail.mc BEFORE the lines mentioning the blacklists.

HACK(`dnswl’, `whitelist.yourserver.com’)dnl

Replace yourserver.com with your own DNS server. You should also add a file named dnswl.m4 in /usr/share/sendmail-cf/hack containing:

divert(8)
R$* $: $&{client_addr}
R::ffff:$-.$-.$-.$- $: $(host $4.$3.$2.$1._ARG_. $: NotFound $)
R$-.$-.$-.$- $: $(host $4.$3.$2.$1._ARG_. $: NotFound $)
RNotFound $: OKSOFAR
R$+ $@
divert(-1)

On the DNS side (I am assuming you are using Bind), you would create a zone file with lines such as:

$ttl 5M
whitelist.yourserver.com. IN SOA whitelist.yourserver.com. hostmaster.yourserver.com. (
2006010300
1D
2H
30D
4D )

whitelist.yourserver.com. IN NS dns.yourserver.com.
IN A 0.0.0.0
;
; test entry
2.0.0.127 IN A 127.0.0.2
IN TXT “Test Entry”
;
; remember to reverse the octets!
;
; consider exempting your own mail server
1.0.168.192 IN A 127.0.0.2 ; my mail server IP address

; These are the server we want to whitelist. Do not forget to reverse the octets !
1.0.0.10 IN A 127.0.0.2

Rather than Bind, you could also run rbldnsd, a small daemon that would make it easier to serve DNS white (or black) lists. Its main advantage is that you can add IP addresses to its zone file in native (i.e. not reversed) format, possibly saving yourself a few scripting lines if you would automate the process of adding hosts to the list.

Using connection throttling against attacks

Spammers sometimes try to send messages to every e-mail address it can think of. It would, for example, try every first name it has in its dictionary, and send hundreds of messages to non-existing recipients on your side and eat of lot of your CPU and connectivity resources. You can prevent this by setting the following line in sendmail.mc:

define(`confBAD_RCPT_THROTTLE’, `1′)dnl

Delaying the initial connection

A real MTA would wait that for your MTA to acknowledge the connection before sending data. Others, like PCs turned into SMTP proxies tend to send their data immediately. Inserting the following line in sendmail.mc will prevent these machines to spam you:

FEATURE(`greet_pause’,5000)

Local config rules

Rejecting mail form DHCP assigned addresses

Since most of the spam and viruses come through machines which have been hacked by malware to turn them into SMTP proxies, I decided some time ago to reject any message from a connection containing the strings ‘Dialup’,'IPpool’, ‘Cable’, ‘Dial,’ DHCP’, ‘ISDN’ and ‘DSL’. After all, most machines connected to the Internet through dial-up or dynamic IP are workstations and should relay their e-mail through their ISP server. The one exception are the Linux and.*bsd home users. Sorry for them, but I cannot make any exception to the above rule.

Anyway, RFC 2821 mandates that the reverse lookup on the IP address should return a legitimate FQDN that matches the name advertised in the SMTP session.

define(`_DIALUP_REJECT_’,`”550 Your mailserver does not comply to RFC 2821.”‘)dnl

LOCAL_CONFIG

# Special rewrite rule helper for dialup detection
Kundash regex -a.LOOP -d. -o -s1,2 (.+)-(.+)
F{DIALUP}/etc/mail/dialup-marks

LOCAL_RULESETS

SLocal_check_relay
R$* $: $[ $&{client_name} $]
R$*. $1
R$j $@ $j Ignore myself
R$=w $@ $1 Ignore my aliases
R$*$=M $@ $1 Ignore my masqueraders
R$*$=R $@ $1 Ignore my relay class domains
R[$&{client_addr}] $@ [$&{client_addr}] Address literals always match
R$* $: $1.LOOP
R$*.LOOP $(undash $1 $: $1 $) Dialups hide as IP-dashed string
R$* $&{client_addr} $* $#error $@ 5.7.1 $: _DIALUP_REJECT_
R$* $={DIALUP} $* $#error $@ 5.7.1 $: _DIALUP_REJECT_
R$+ $- $: $2 $>reverse $1
R$* $&{client_addr} $* $#error $@ 5.7.1 $: _DIALUP_REJECT_

We need an additional file /etc/mail/dial-up marks. Mine says:

Dialup
IPpool
Cable
Dial
DHCP
ISDN
DSN
DSL

Check for valid IP addresses

This set of rules will check if the IP address is not spoofed. Typically, it would reject any mail coming from machines advertising private IP space.

LOCAL_CONFIG

Kgetmxrrs bestmx -z:
Kbestmx bestmx
Kgethostbyname dns -RA
KBadMX regex -n -aOK ^(127\.[0-9]+\.[0-9]+\.[0-9]+|10\.[0-9]+\.[0-9]+\.[0-9]+|172\.20\.[0-9]+\.[0-9]+|192\.168\.[0-9]+\.[0-9]+)$
Kstore macro
KmatchIPv4Address regex -aMXTOIPV4 ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.|\.)$

LOCAL_RULESETS

Sreverse
R$+ $- $: $2 $>reverse $1

SLocal_check_mail
R $* $: $>canonify $1
R $* <@ $+. > $* $1 <@ $2> $3 strip trailing dot
R $* <@ $+ > $* $: $2 return host part

R <@> $@ OK no further checking for empty sender

R $* $: $(store {FQDN} $@ $1 $) $1 save workspace for further processing
R $* $: $>CheckMX $1
R $* $: $&{FQDN}
R $* $: $>CheckMXtoIPAdress $1

SCheckMX
R $+ $: $(getmxrrs $1 $: $1 $) : get list of MX RRs
R $+ : $* $: $>resolve $1 : $2 resolve all entries
R $* : $: $1 strip trailing :
R $* OK : OK $* $1 OK $2 summarize known-good
R $* OK $* $@ OK at least one ok, proceed
R $* $@ $# error $@ 5.1.8 $: “550 all MX RRs resolve to RFC 1918 IP addresses”

SCheckMXtoIPAdress
R $+ $: $(bestmx $1 $: $1 $) get best MX RR
R $* $: $(matchIPv4Address $1 $) is it an IPv4 address?
R $* MXTOIPV4 $@ $# error $@ 5.1.8 $: “550 MX RR pointing to IP addresses not allowed (RFC 2181, section 10.3)”
R $* $@ OK MX RR accepted

Sresolve
R : $* $: $1 strip off leading :
R $+ : $* $: $(gethostbyname $1 $: $1 $) : $2 resolve first entry
R $+ : $* $: $(BadMX $1 $) : $>resolve $2 recur for remaining list

DNSBL Milter

This part is using the milter developed by five-ten-sg.com. It will not only check the message headers, but also the actual message content. If it finds IP addresses from known spam sources, it will reject the message. It is quite extensive in its features, as it can deal with uuencode, base64 and html. The configuration can be quite tricky. As an example, it rejected at first the messages generated by Logwatch as they included spam reports, complete with the spammers IP addresses. The configuration had to be adapted to exclude those messages from the general check.

In sendmail.mc. the line should be:

INPUT_MAIL_FILTER(`dnsbl’, `S=local:/var/run/dnsbl/dnsbl.sock, F=T, T=C:30s;S:5m;R:5m;E:5m’)

This is of course not enough. You need to compile the milter from source. See the docs in the milter tarball for instructions.

Require correct RDNS

This hack, developed by Neil Rickert does wonders to block connections for hosts that do not comply to RFC 1912.

Acknowledgments

Most of the tricks above were found on web sites, mailing lists and newsgroups, Credit is due to their respective authors, some of whom I have not identified.

  1. Great article. I am using the dnswl.m4 and it works well, but I would like to also log a comment similar to dnsbl where the comment can include the source IP.

    Is this possible?

  2. Check for valid IP addresses how can i use above mentiond script to check validiaty of IP. in sendmail.mc file or to other file.

    lipson12@yahoo.com

    • @Kaisar

      It depends what you mean by “valid IP address”. The above-mentioned ruleset catches connections from hosts that advertise only private IP space (aka RFC1918) as MX records.

      If you want to check that the IP address matches the host name, you could use the rDNS ruleset mentioned above. Although this is mandated by RFCs, do expect a lot of false positives. Many hosters do not update rDNS records nowadays. I personally would not use that ruleset anymore.

      This being said, this is a very old post of mine. I have switched to Postfix more than 3 years ago. Hence, my experience with Sendmail is becoming outdated.

  3. Andy Lee Robinson

    Useful tips, and SPF and DKIM take up rates are still really pathetic. What more can we do to get everyone using them?

    You would think that all banks would want to protect their domains from spoofing. Paypal still use old domain keys and spf soft fails (~all).
    hmrc.gov.uk doesn’t have any SPF at all and I am sick of phishing spam. Complaining to them had no effect!
    Of course, having spf and dkim records are useless unless the receiving MTAs bother to check and reject!

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackbacks and Pingbacks: