Thursday, May 6, 2010

Sendmail Config

SkyHi @ Thursday, May 06, 2010

The first layer of spam defense is sendmail itself, because that's the first piece of software to touch each message. Sendmail has a number of different config options that can help you block spam and keep your machine stable.


SMTP Phasepre-DATA
CPU Uselow
Memory Uselow
False Positiveslow
Maintenancelow
Effectivenesshigh

greet_pause

As of version 8.13, sendmail added an anti-spam feature called "greet_pause". It is both simple and clever.

In a normal SMTP transaction, first the client connects, then the server sends back a "220" greeting message, then the client sends its HELO command. Some spam programs, however, don't wait for the greeting message. They just send their commands immediately without listening.

The greet_pause feature detects this misbehavior by pausing briefly before sending out the "220" greeting message. If any commands arrive during that pause, then the connection is marked bad and anything coming over it is ignored.

This one is interesting because it actually cuts down on the number of spam attempts, not just the spam deliveries. I figure when the spammers hit the pause they are somehow getting stuck. I'll have a graph of this later - before I enabled greet_pause, I was getting a couple million spam attempts per day; after, only 600,000.

To enable the feature, you need to make two changes. First, in your sendmail.mc file:

FEATURE(access_db)dnl
FEATURE(`greet_pause',5000)
You probably already have access_db defined; it just needs to appear somewhere prior to greet_pause. The number is how many milliseconds to pause; 5000 = five seconds. Then in your access file you should add this:
GreetPause:localhost    0
The second change prevents the pause from applying to connections from your local machine, which would otherwise be annoying when you're sending mail. If you're doing this on a server which accepts mail from multiple machines, you'll want to do the same for the whole local network.

I've heard from a few folks that greet_pause can block some legitimate sites. Those sites should probably fix their mailers, but until that happens you can always whitelist them by adding more entries to your access file.



SMTP Phasepre-DATA
CPU Uselow
Memory Uselow
False Positiveslow
Maintenancelow
Effectivenessunknown

PIPELINING=0

This is the same basic idea as greet_pause: it disallows connections which slam their bytes out without waiting for responses. While greet_pause operates only at the start of the connection, PIPELINING=0 operates throughout the entire connection.

I tried this for a while and didn't see any effect, aside from some "Broken pipe" messages in the mail log. Only about one per minute, not enough to base a new blacklist on. Since I'm also using greet_pause, I figure that has already weeded out the unauthorized pipeliners. Unless there are some extra-semi-clever spammers who don't do pipelining before the 220 but do do it afterwards even if the greeting says not to, PIPELINING=0 isn't going to add anything. It's possible that if you use PIPELINING=0 and don't use greet_pause, you'll get a benefit; but why would you?

Anyway, if you want to try it, you have to re-compile sendmail. (Supposedly there's a way to disable pipelining without recompiling but I haven't figured that out yet.) Start by adding this to your devtools/Site/site.config.m4:

<a name="pipelining">APPENDDEF(`confENVDEF', `-DPIPELINING=0')</a>
Then remove your obj.* directory so that the Makefiles get regenerated with the new PIPELINING setting. (Just doing a 'make clean' is not sufficient.) Do a make, make install, and restart sendmail.

SMTP Phasepre-DATA
CPU Uselow
Memory Uselow
False Positiveslow
Maintenancelow
Effectivenessunknown

BAD_RCPT_THROTTLE

This feature tells sendmail to slow down any connections that try to send to lots of non-existent usernames. Some spammers do dictionary attacks, trying to send to thousands of different usernames on your system just to see which ones exist. This throttle tries to deter that behavior. It's unclear how effective it is, but it doesn't harm legitimate mail so why not.

To enable it, add the following code to your sendmail.mc file:

define(`confBAD_RCPT_THROTTLE', `1')dnl
The number is how many bad recipients is takes to trigger the throttle, so 1 is the strictest setting.

SMTP Phasepre-DATA
CPU Uselow
Memory Uselow
False Positiveslow
Maintenancelow
Effectivenessunknown

MAX_RCPTS_PER_MESSAGE

This setting limits the number of recipients allowed on each message. Some spammers try to deliver messages to thousands of recipients at once; this prevents that. If a message comes in with more than the allowed number, the excess recipients are rejected. The recipients under the limit are accepted and get delivered; the excess recipients get retried later, assuming the sending system is a real standards-conforming mailer. Of course the spammers are not running real mailers, so they won't retry.

I set the limit to only ten per message:

define(`confMAX_RCPTS_PER_MESSAGE', `10')dnl
I can go this low because there's really only one user on the site: me. Most sites will probably want to use a higher limit. But again, if the sending system is working properly, the mail will eventually get through to all recipients regardless of this setting.

One downside to this setting is that it applies to outgoing mail too, so I can't send mail to more then ten people at a time because my mail program isn't smart enough to retry the excess recipients. There's probably some way to configure things so this only applies to incoming mail, but I haven't looked into it.


SMTP Phasepre-DATA
CPU Uselow
Memory Uselow
False Positiveslow
Maintenancelow
Effectivenessunknown

CONNECTION_RATE_THROTTLE

This sets a limit on the number of new connections per second. It helps protect you against mail-bombs and similar denial-of-service attacks.

define(`confCONNECTION_RATE_THROTTLE', `100')dnl
If the rate goes above the limit, new connections are rejected and the sending mailers have to retry later.

MAX_DAEMON_CHILDREN

This setting helps you keep your system from running out of memory due to too many processes. It lets you set the maximum number of child processes that sendmail will spawn. Putting a limit on the number of processes will keep you from running out of memory. If you figure that each sendmail process uses about two megabytes of memory, then decide how much total memory you want to spend on mail handling and do the division. I have two gigabytes on my current machine, and I figure I can waste half of that on mail, so one gigabyte divided by 2 megabytes is 500:

define(`confMAX_DAEMON_CHILDREN', `500')dnl
Typically I have more like 200 sendmail processes going, but I have run into the 500-process limit on occasion. When that happens, sendmail just stops accepting new connections until some of the existing processes finish.

QUEUE_LA / REFUSE_LA

These two settings help you guard your system against running out of CPU cycles. If the load-average goes above QUEUE_LA, sendmail will stop processing mail. Incoming messages will still be accepted but they'll just get put in a queue to be handled later. If the load-average goes above REFUSE_LA, then sendmail refuses all new connections until the load is lower.

Here are the settings I use:

define(`confQUEUE_LA', `5')dnl<br />define(`confREFUSE_LA', `20')dnl
Since loadav numbers are not really comparable from one system to another, you'll have to come up with your own numbers.

There's also DELAY_LA, which tells sendmail to sleep for one second on each new connection while the loadav is over the specified value. I haven't found this to be useful, especially since I'm already having sendmail sleep for five seconds on every connection regardless of the loadav (see greet_pause).


timeouts

Sendmail lets you specify timeouts for various phases of the mail transaction. Some of the default values are ridiculously high. For instance, up to an hour waiting for the next command to be issued. This can result in a bunch of sendmail processes sitting around twiddling their thumbs, using up memory. I lower the command timeout to a minute, and a lot of the other timeouts too.

My settings:

define(`confTO_ICONNECT', `15s')dnl<br />define(`confTO_CONNECT', `3m')dnl<br />define(`confTO_HELO', `2m')dnl<br />define(`confTO_MAIL', `1m')dnl<br />define(`confTO_RCPT', `1m')dnl<br />define(`confTO_DATAINIT', `1m')dnl<br />define(`confTO_DATABLOCK', `1m')dnl<br />define(`confTO_DATAFINAL', `1m')dnl<br />define(`confTO_RSET', `1m')dnl<br />define(`confTO_QUIT', `1m')dnl<br />define(`confTO_MISC', `1m')dnl<br />define(`confTO_COMMAND', `1m')dnl<br />define(`confTO_STARTTLS', `2m')dnl<br /><br /><b>REFERENCES</b><br /><a href="http://www.acme.com/mail_filtering/sendmail_config.html">http://www.acme.com/mail_filtering/sendmail_config.html</a><br />