summaryrefslogtreecommitdiffstats
path: root/html/BUILTIN_FILTER_README.html
blob: 3cd3dec89ce3cf78d4b4a808c1935054057e1dba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Postfix Built-in Content Inspection</title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>

<body>

<h1><img src="postfix-logo.jpg" width="203" height="98" alt="">
Postfix Built-in Content Inspection</h1>

<hr>

<h2>Built-in content inspection introduction </h2>

<p> Postfix supports a built-in filter mechanism that examines
message header and message body content, one line at a time, before
it is stored in the Postfix queue. The filter is usually implemented
with POSIX or PCRE regular expressions, as described in the
<a href="header_checks.5.html">header_checks(5)</a> manual page. </p>

<p>  The original purpose of the built-in filter is to stop an
outbreak of specific email worms or viruses, and it does this job
well. The filter has also helped to block bounced junk email,
bounced email from worms or viruses, and notifications from virus
detection systems.  Information about this secondary application
is given in the <a href="BACKSCATTER_README.html">BACKSCATTER_README</a> document.  </p>

<p> Because the built-in filter is optimized for stopping specific
worms and virus outbreaks, it has <a href="#limitations">limitations</a>
that make it NOT suitable for general junk email and virus detection.
For that, you should use one of the external content inspection
methods that are described in the <a href="FILTER_README.html">FILTER_README</a>, <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a>
and <a href="MILTER_README.html">MILTER_README</a> documents.  </p>

<p> The following diagram gives an over-all picture of how Postfix
built-in content inspection works: </p>

<blockquote>

<table>

<tr>

    <td colspan="4"> <td bgcolor="#f0f0ff" align="center"
    valign="middle"> Postmaster<br> notifications </td>

</tr>

<tr>

    <td colspan="4"> <td align="center"> <tt> |<br>v </tt></td>

</tr>

<tr>

        <td bgcolor="#f0f0ff" align="center" valign="middle">
        Network or<br> local users </td>

    <td align="center" valign="middle"> <tt> -&gt; </tt> </td>

        <td bgcolor="#f0f0ff" align="center" valign="middle">

        <b> Built-in<br> filter</b> </td>

    <td align="center" valign="middle"> <tt> -&gt; </tt> </td>

        <td bgcolor="#f0f0ff" align="center" valign="middle">
        Postfix<br> queue </td>

    <td align="center" valign="middle"> <tt> -&gt; </tt> </td>

        <td bgcolor="#f0f0ff" align="center" valign="middle">
        Delivery<br> agents </td>

    <td align="center" valign="middle"> <tt> -&gt; </tt> </td>

        <td bgcolor="#f0f0ff" align="center" valign="middle">
        Network or<br> local mailbox </td>

</tr>

<tr>

    <td colspan="4"> <td align="center"> ^<br> <tt> | </tt> </td>
    <td> </td> <td align="center"> <tt> |<br>v </tt> </td>

</tr>

<tr>

    <td colspan="4"> <td colspan="3" bgcolor="#f0f0ff" align="center"
    valign="middle"> Undeliverable mail<br> Forwarded mail</td>

</tr>

</table>

</blockquote>

<p> The picture makes clear that the filter works while Postfix is
receiving new mail.  This means that Postfix can reject mail from
the network without having to return undeliverable mail to the
originator address (which is often spoofed anyway). However, this
ability comes at a price:  if mail inspection takes too much time,
then the remote client will time out, and the client may send the
same message repeatedly. </p>

<p>Topics covered by this document: </p>

<ul>

<li><a href="#what">What mail is subjected to header/body checks </a>

<li><a href="#limitations">Limitations of Postfix header/body checks </a>

<li><a href="#daily">Preventing daily mail status reports from being blocked </a>

<li><a href="#remote_only">Configuring header/body checks for mail from outside users only</a>

<li><a href="#mx_submission">Configuring different header/body checks for MX service and submission service</a>

<li><a href="#domain_except">Configuring header/body checks for mail to some domains only</a>

</ul>

<h2><a name="what">What mail is subjected to header/body checks </a></h2>

<p> Postfix header/body checks are implemented by the <a href="cleanup.8.html">cleanup(8)</a>
server before it injects mail into the <a href="QSHAPE_README.html#incoming_queue">incoming queue</a>.  The diagram
below zooms in on the <a href="cleanup.8.html">cleanup(8)</a> server, and shows that this server
handles mail from many different sources. In order to keep the
diagram readable, the sources of postmaster notifications are not
shown, because they can be produced by many Postfix daemon processes.
</p>

<blockquote>

<table>

<tr> <td colspan="2"> </td> <td bgcolor="#f0f0ff" align="center"
valign="middle"> <a href="bounce.8.html">bounce(8)</a><br> (undeliverable) </td> </tr>

<tr> <td bgcolor="#f0f0ff" align="center" valign="middle"> <b>
<a href="smtpd.8.html">smtpd(8)</a><br> (network)</b> </td> <td align="left" valign="bottom">
<tt> \ </tt> </td> <td align="center" valign="middle"> <tt> |<br>v
</tt> </td> </tr>

<tr> <td> </td> <td> </td> </tr>

<tr> <td bgcolor="#f0f0ff" align="center" valign="middle"> <b>
<a href="qmqpd.8.html">qmqpd(8)</a><br> (network)</b> </td> <td align="center" valign="middle">
<tt> -\<br>-/ </tt> </td> <td bgcolor="#f0f0ff" align="center"
valign="middle"> <a href="cleanup.8.html">cleanup(8)</a> </td> <td align="center" valign="middle">
<tt> -&gt; </tt> </td> <td bgcolor="#f0f0ff" align="center"
valign="middle"> <a href="QSHAPE_README.html#incoming_queue">
incoming<br> queue </a> </td> </tr>

<tr> <td bgcolor="#f0f0ff" align="center" valign="middle"> <b>
<a href="pickup.8.html">pickup(8)</a><br> (local)</b> </td> <td align="left" valign="top"> <tt>
/ </tt> </td> <td align="center" valign="middle"> ^<br> <tt> |
</tt> </td> </tr>

<tr> <td colspan="2"> </td> <td bgcolor="#f0f0ff" align="center"
valign="middle"> <a href="local.8.html">local(8)</a><br> (forwarded) </td> </tr>

</table>

</blockquote>

<p> For efficiency reasons, only mail that enters from outside of
Postfix is inspected with header/body checks. It would be inefficient
to filter already filtered mail again, and it would be undesirable
to block postmaster notifications.  The table below summarizes what
mail is and is not subject to header/body checks. </p>

<blockquote>

<table border="1">

<tr> <th> Message type </th> <th> Source </th> <th> Header/body checks? </th> </tr>

<tr> <td> Undeliverable mail </td> <td> <a href="bounce.8.html">bounce(8)</a> </td> <td> No </td> </tr>

<tr> <td> Network mail </td> <td> <a href="smtpd.8.html">smtpd(8)</a> </td> <td> Configurable </td> </tr>

<tr> <td> Network mail </td> <td> <a href="qmqpd.8.html">qmqpd(8)</a> </td>  <td> Configurable </td> </tr>

<tr> <td> Local submission </td> <td> <a href="pickup.8.html">pickup(8)</a> </td>  <td> Configurable </td> </tr>

<tr> <td> Local forwarding </td> <td> <a href="local.8.html">local(8)</a> </td> <td> No </td> </tr>

<tr> <td> Postmaster notice </td> <td> many </td> <td> No </td> </tr>

</table>

</blockquote>

<p> How does Postfix decide what mail needs to be filtered? It
would be clumsy to make the decision in the <a href="cleanup.8.html">cleanup(8)</a> server, as
this program receives mail from so many different sources.  Instead,
header/body checks are requested by the source.  Examples of how
to turn off header/body checks for mail received with <a href="smtpd.8.html">smtpd(8)</a>,
<a href="qmqpd.8.html">qmqpd(8)</a> or <a href="pickup.8.html">pickup(8)</a> are given below under "<a
href="#remote_only">Configuring header/body checks for mail from
outside users only</a>", "<a href="#mx_submission">Configuring
different header/body checks for MX service and submission
service</a>", and "<a href="#domain_except">Configuring
header/body checks for mail to some domains only</a>".  </p>

<h2><a name="limitations">Limitations of Postfix header/body checks </a></h2>

<ul> 

<li> <p> Header/body checks do not decode message headers or message
body content. For example, if text in the message body is BASE64
encoded (<a href="https://tools.ietf.org/html/rfc2045">RFC 2045</a>) then your regular expressions will have to match
the BASE64 encoded form. Likewise, message headers with encoded
non-ASCII characters (<a href="https://tools.ietf.org/html/rfc2047">RFC 2047</a>) need to be matched in their encoded
form. </p>

<li> <p> Header/body checks cannot filter on a combination of
message headers or body lines. Header/body checks examine content
one message header at a time, or one message body line at a time,
and cannot carry a decision over to the next message header or body
line.  </p>

<li> <p> Header/body checks cannot depend on the recipient of a
message.  </p>

<ul>

<li> <p> One message can have multiple recipients, and all recipients
of a message receive the same treatment. Workarounds have been
proposed that involve selectively deferring some recipients of
multi-recipient mail, but that results in poor SMTP performance
and does not work for non-SMTP mail.  </p>

<li> <p> Some sources of mail send the headers and content ahead
of the recipient information. It would be inefficient to buffer up
an entire message before deciding if it needs to be filtered, and
it would be clumsy to filter mail and to buffer up all the actions
until it is known whether those actions need to be executed.  </p>

</ul>

<li> <p> Despite warnings, some people try to use the built-in
filter feature for general junk email and/or virus blocking, using
hundreds or even thousands of regular expressions. This can result
in catastrophic performance failure. The symptoms are as follows:
</p>

<ul>

<li> <p> The <a href="cleanup.8.html">cleanup(8)</a> processes use up all available CPU time in
order to process the regular expressions, and/or they use up all
available memory so that the system begins to swap. This slows down
all incoming mail deliveries.  </p>

<li> <p> As Postfix needs more and more time to receive an email
message, the number of simultaneous SMTP sessions increases to the
point that the SMTP server process limit is reached. </p>

<li> <p> While all SMTP server processes are waiting for the
<a href="cleanup.8.html">cleanup(8)</a> servers to finish, new SMTP clients have to wait until
an SMTP server process becomes available. This causes mail deliveries
to time out before they have even begun.  </p>

</ul>

<p> The remedy for this type of performance problem is simple:
don't use header/body checks for general junk email and/or virus
blocking, and don't filter mail before it is queued.  When performance
is a concern, use an external content filter that runs after mail
is queued, as described in the <a href="FILTER_README.html">FILTER_README</a> document. </p>

</ul>

<h2><a name="daily">Preventing daily mail status reports from being blocked </a></h2>

<p>The following is quoted from Jim Seymour's Pflogsumm FAQ at
<a href="http://jimsun.linxnet.com/downloads/pflogsumm-faq.txt">http://jimsun.linxnet.com/downloads/pflogsumm-faq.txt</a>. Pflogsumm
is a program that analyzes Postfix logs, including the logging from
rejected mail. If these logs contain text that was rejected by
Postfix <a href="postconf.5.html#body_checks">body_checks</a> patterns, then the logging is also likely to
be rejected by those same <a href="postconf.5.html#body_checks">body_checks</a> patterns. This problem does
not exist with <a href="postconf.5.html#header_checks">header_checks</a> patterns, because those are not applied
to the text that is part of the mail status report. </p>

<blockquote>

<p>You configure Postfix to do body checks, Postfix does its thing,
Pflogsumm reports it and Postfix catches the same string in the
Pflogsumm report.  There are several solutions to this. </p>

<p> Wolfgang Zeikat contributed this: </p>

<blockquote>
<pre>
#!/usr/bin/perl
use MIME::Lite;

### Create a new message:
$msg = MIME::Lite-&gt;new(
    From     =&gt; 'your@send.er',
    To       =&gt; 'your@recipie.nt',
    # Cc     =&gt; 'some@other.com, some@more.com',
    Subject  =&gt; 'pflogsumm',
    Date     =&gt; `date`,
    Type     =&gt; 'text/plain',
    Encoding =&gt; 'base64',
    Path     =&gt; '/tmp/pflogg',
);

$msg-&gt;send;
</pre>
</blockquote>

<p> Where "/tmp/pflogg" is the output of Pflogsumm.  This puts Pflogsumm's
output in a base64 MIME attachment. </p>

</blockquote>

<p> Note by Wietse: if you run this on a machine that is accessible
by untrusted users, it is safer to store the Pflogsumm report in
a directory that is not world writable. </p>

<blockquote>

<p> In a follow-up to a thread in the postfix-users mailing list, Ralf
Hildebrandt noted: </p>

<blockquote> <p> "mpack does the same thing." </p> </blockquote>

</blockquote>

<p> And it does. Which tool one should use is a matter of preference.
</p>

<p> Other solutions involve additional <a href="postconf.5.html#body_checks">body_checks</a> rules that make
exceptions for daily mail status reports, but this is not recommended.
Such rules slow down all mail and complicate Postfix maintenance.
</p>

<h2><a name="remote_only">Configuring header/body checks for mail from outside users only</a></h2>

<p> The following information applies to Postfix 2.1 and later.
Earlier
Postfix versions do not support the <a href="postconf.5.html#receive_override_options">receive_override_options</a> feature.
</p>

<p> The easiest approach is to configure ONE Postfix instance with
multiple SMTP server IP addresses in <a href="master.5.html">master.cf</a>: </p>

<ul>

<li> <p> Two SMTP server IP addresses for mail from inside users
only, with header/body filtering turned off, and a local mail pickup
service with header/body filtering turned off.  </p>

<pre>
/etc/postfix.<a href="master.5.html">master.cf</a>:
    # ==================================================================
    # service      type  private unpriv  chroot  wakeup  maxproc command
    #                    (yes)   (yes)   (yes)   (never) (100)
    # ==================================================================
    1.2.3.4:smtp   inet  n       -       n       -       -       smtpd
        -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>
    127.0.0.1:smtp inet  n       -       n       -       -       smtpd
        -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>
    pickup         fifo  n       -       n       60      1       pickup
        -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>
</pre>

<li> <p> Add some firewall rule to prevent access to 1.2.3.4:smtp
from the outside world. </p>

<li> <p> One SMTP server address for mail from outside users with
header/body filtering turned on via <a href="postconf.5.html">main.cf</a>. </p>

<pre>
/etc/postfix.<a href="master.5.html">master.cf</a>:
    # =================================================================
    # service     type  private unpriv  chroot  wakeup  maxproc command
    #                   (yes)   (yes)   (yes)   (never) (100)
    # =================================================================
    1.2.3.5:smtp  inet  n       -       n       -       -       smtpd
</pre>

</ul>

<h2><a name="mx_submission">Configuring different header/body checks for MX service and submission service</a></h2>

<p> If authorized user submissions require different header/body
checks than mail from remote MTAs, then this is possible as long
as you have separate mail streams for authorized users and for MX
service. </p>

<p> The example below assumes that authorized users connect to TCP
port 587 (submission) or 465 (smtps), and that remote MTAs connect
to TCP port 25 (smtp). </p>

<p> First, we define a few "user-defined" parameters that will
override settings for the submission and smtps services. </p>

<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
    msa_cleanup_service_name = msa_cleanup
    msa_header_checks = <a href="pcre_table.5.html">pcre</a>:/etc/postfix/msa_header_checks
    msa_body_checks = <a href="pcre_table.5.html">pcre</a>:/etc/postfix/msa_body_checks
</pre>
</blockquote>

<p> Next, we define msa_cleanup as a dedicated cleanup service that
will be used only by the submission and smtps services.  This service
uses the <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a> overrides that were defined
above. </p>

<blockquote>
<pre>
/etc/postfix.<a href="master.5.html">master.cf</a>:
    # =================================================================
    # service     type  private unpriv  chroot  wakeup  maxproc command
    #                   (yes)   (yes)   (yes)   (never) (100)
    # =================================================================
    smtp          inet  n       -       n       -       -       smtpd
    msa_cleanup   unix  n       -       n       -       0       cleanup
        -o <a href="postconf.5.html#header_checks">header_checks</a>=$msa_header_checks
        -o <a href="postconf.5.html#body_checks">body_checks</a>=$msa_body_checks
    submission    inet  n       -       n       -       -       smtpd
        -o <a href="postconf.5.html#cleanup_service_name">cleanup_service_name</a>=$msa_cleanup_service_name
        -o <a href="postconf.5.html#syslog_name">syslog_name</a>=postfix/submission
        <i>...[see sample <a href="master.5.html">master.cf</a> file for more]...</i>
    smtps         inet  n       -       n       -       -       smtpd
        -o <a href="postconf.5.html#cleanup_service_name">cleanup_service_name</a>=$msa_cleanup_service_name
        -o <a href="postconf.5.html#syslog_name">syslog_name</a>=postfix/smtps
        -o <a href="postconf.5.html#smtpd_tls_wrappermode">smtpd_tls_wrappermode</a>=yes
        <i>...[see sample <a href="master.5.html">master.cf</a> file for more]...</i>
</pre>
</blockquote>

<p> By keeping the "msa_xxx" parameter settings in <a href="postconf.5.html">main.cf</a>, you
keep your <a href="master.5.html">master.cf</a> file simple, and you minimize the amount
of duplication. </p>

<h2><a name="domain_except">Configuring header/body checks for mail to some domains only</a></h2>

<p> The following information applies to Postfix 2.1. Earlier
Postfix versions do not support the <a href="postconf.5.html#receive_override_options">receive_override_options</a> feature.
</p>

<p> If you are an MX service provider and want to enable header/body
checks only for some domains, you can configure ONE Postfix
instance with multiple SMTP server IP addresses in <a href="master.5.html">master.cf</a>. Each
address provides a different service. </p>

<blockquote>

<pre>
/etc/postfix.<a href="master.5.html">master.cf</a>:
    # =================================================================
    # service     type  private unpriv  chroot  wakeup  maxproc command
    #                   (yes)   (yes)   (yes)   (never) (100)
    # =================================================================
    # SMTP service for domains with header/body checks turned on.
    1.2.3.4:smtp  inet  n       -       n       -       -       smtpd

    # SMTP service for domains with header/body checks turned off.
    1.2.3.5:smtp  inet  n       -       n       -       -       smtpd
        -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>
</pre>
</blockquote>

<p> Once this is set up you can configure MX records in the DNS
that route each domain to the proper SMTP server instance. </p>

</body>

</html>