FILTER_README 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. Introduction
  2. ============
  3. This is a very first implementation of Postfix content filtering.
  4. A Postfix content filter receives unfiltered mail from Postfix and
  5. does one of the following:
  6. - re-injects the mail back into Postfix, perhaps after changing content
  7. - rejects the mail (by sending a suitable status code back to
  8. Postfix) so that it is returned to sender.
  9. - sends the mail somewhere else
  10. This document describes two approaches to content filtering: simple
  11. and advanced. Both filter all the mail by default.
  12. At the end are examples that show how to filter only mail from
  13. users, about using different filters for different domains that
  14. you provide MX service for, and about selective filtering on the
  15. basis of message envelope and/or header/body patterns.
  16. Simple content filtering example
  17. ================================
  18. The first example is simple to set up. It uses a shell script that
  19. receives unfiltered mail from the Postfix pipe delivery agent, and
  20. that feeds filtered mail back into the Postfix sendmail command.
  21. Only mail arriving via SMTP will be content filtered.
  22. ..................................
  23. : Postfix :
  24. Unfiltered mail----->smtpd \ /local---->Filtered mail
  25. : -cleanup->queue- :
  26. ---->pickup / \smtp----->Filtered mail
  27. ^ : | :
  28. | : \pipe-----+
  29. | .................................. |
  30. | |
  31. | |
  32. +-Postfix sendmail<----filter script<--+
  33. Mail is filtered by a /some/where/filter program. This can be a
  34. simple shell script like this:
  35. #!/bin/sh
  36. # Localize these.
  37. INSPECT_DIR=/var/spool/filter
  38. SENDMAIL="/usr/sbin/sendmail -i"
  39. # Exit codes from <sysexits.h>
  40. EX_TEMPFAIL=75
  41. EX_UNAVAILABLE=69
  42. # Clean up when done or when aborting.
  43. trap "rm -f in.$$" 0 1 2 3 15
  44. # Start processing.
  45. cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; }
  46. cat >in.$$ || { echo Cannot save mail to file; exit $EX_TEMPFAIL; }
  47. # filter <in.$$ || { echo Message content rejected; exit $EX_UNAVAILABLE; }
  48. $SENDMAIL "$@" <in.$$
  49. exit $?
  50. The idea is to first capture the message to file and then run the
  51. content through a third-party content filter program.
  52. - If the mail cannot be captured to file, mail delivery is deferred
  53. by terminating with exit status 75 (EX_TEMPFAIL). Postfix will
  54. try again after some delay.
  55. - If the content filter program finds a problem, the mail is bounced
  56. by terminating with exit status 69 (EX_UNAVAILABLE). Postfix
  57. will return the message to the sender as undeliverable.
  58. - If the content is OK, it is given as input to the Postfix sendmail
  59. command, and the exit status of the filter command is whatever
  60. exit status the Postfix sendmail command produces. Postfix will
  61. deliver the message as usual.
  62. I suggest that you run this script by hand until you are satisfied
  63. with the results. Run it with a real message (headers+body) as
  64. input:
  65. % /some/where/filter -f sender recipient... <message-file
  66. Once you're satisfied with the content filtering script:
  67. 1 - Create a dedicated local user account called "filter". This
  68. user handles all potentially dangerous mail content - that is
  69. why it should be a separate account. Do not use "nobody", and
  70. most certainly do not use "root" or "postfix". The user will
  71. never log in, and can be given a "*" password and non-existent
  72. shell and home directory.
  73. 2 - Create a directory /var/spool/filter that is accessible only
  74. to the "filter" user. This is where the content filtering script
  75. is supposed to store its temporary files.
  76. 3 - Define the content filter in the Postfix master file:
  77. /etc/postfix/master.cf:
  78. filter unix - n n - - pipe
  79. flags=Rq user=filter argv=/somewhere/filter -f ${sender} -- ${recipient}
  80. To turn on content filtering for mail arriving via SMTP only, append
  81. "-o content_filter=filter:dummy" to the master.cf entry that defines
  82. the Postfix SMTP server:
  83. /etc/postfix/master.cf:
  84. smtp inet ...stuff... smtpd
  85. -o content_filter=filter:dummy
  86. The content_filter configuration parameter accepts the same syntax
  87. as the right-hand side in a Postfix transport table. Execute
  88. "postfix reload" to complete the change.
  89. To turn off content filtering, edit the master.cf file, remove the
  90. "-o content_filter=filter:dummy" text from the entry that defines
  91. the Postfix SMTP server, and execute another "postfix reload".
  92. With the shell script as shown above you will lose a factor of four
  93. in Postfix performance for transit mail that arrives and leaves
  94. via SMTP. You will lose another factor in transit performance for
  95. each additional temporary file that is created and deleted in the
  96. process of content filtering. The performance impact is less for
  97. mail that is submitted or delivered locally, because such deliveries
  98. are already slower than SMTP transit mail.
  99. Simple content filter limitations
  100. =================================
  101. The problem with content filters like the one above is that they
  102. are not very robust. The reason is that the software does not talk
  103. a well-defined protocol with Postfix. If the filter shell script
  104. aborts because the shell runs into some memory allocation problem,
  105. the script will not produce a nice exit status as defined in the
  106. file /usr/include/sysexits.h. Instead of going to the deferred
  107. queue, mail will bounce. The same lack of robustness can happen
  108. when the content filtering software itself runs into a resource
  109. problem.
  110. Advanced content filtering example
  111. ===================================
  112. The second example is more complex, but can give much better
  113. performance, and is less likely to bounce mail when the machine
  114. runs into a resource problem. This approach uses content filtering
  115. software that can receive and deliver mail via SMTP.
  116. Some Anti-virus software is built to receive and deliver mail via
  117. SMTP and is ready to use as an advanced Postfix content filter.
  118. For non-SMTP capable content filtering software, Bennett Todd's
  119. SMTP proxy implements a nice PERL/SMTP content filtering framework.
  120. See: http://bent.latency.net/smtpprox/
  121. The example given here filters all mail, including mail that arrives
  122. via SMTP and mail that is locally submitted via the Postfix sendmail
  123. command.
  124. You can expect to lose about a factor of two in Postfix performance
  125. for transit mail that arrives and leaves via SMTP, provided that
  126. the content filter creates no temporary files. Each temporary file
  127. created by the content filter adds another factor to the performance
  128. loss.
  129. We will set up a content filtering program that receives SMTP mail
  130. via localhost port 10025, and that submits SMTP mail back into
  131. Postfix via localhost port 10026.
  132. ..................................
  133. : Postfix :
  134. ----->smtpd \ /local---->
  135. : -cleanup->queue- :
  136. ---->pickup / ^ | \smtp----->
  137. : | v :
  138. : smtpd smtp :
  139. : 10026 | :
  140. ......................|...........
  141. ^ |
  142. | v
  143. ....|............
  144. : | 10025 :
  145. : filter :
  146. : :
  147. .................
  148. To enable content filtering in this manner, specify in main.cf a
  149. new parameter:
  150. /etc/postfix/main.cf:
  151. content_filter = scan:localhost:10025
  152. This causes Postfix to add one extra content filtering record to
  153. each incoming mail message, with content scan:localhost:10025.
  154. The content filtering records are added by the smtpd and pickup
  155. servers.
  156. When a queue file has content filtering information, the queue
  157. manager will deliver the mail to the specified content filter
  158. regardless of its final destination.
  159. In this example, "scan" is an instance of the Postfix SMTP client
  160. with slightly different configuration parameters. This is how
  161. one would set up the service in the Postfix master.cf file:
  162. /etc/postfix/master.cf:
  163. scan unix - - n - 10 smtp
  164. Instead of a limit of 10 concurrent processes, use whatever process
  165. limit is feasible for your machine. Content inspection software
  166. can gobble up a lot of system resources, so you don't want to have
  167. too much of it running at the same time.
  168. The content filter can be set up with the Postfix spawn service,
  169. which is the Postfix equivalent of inetd. For example, to instantiate
  170. up to 10 content filtering processes on demand:
  171. /etc/postfix/master.cf:
  172. localhost:10025 inet n n n - 10 spawn
  173. user=filter argv=/some/where/filter localhost 10026
  174. "filter" is a dedicated local user account. The user will never
  175. log in, and can be given a "*" password and non-existent shell and
  176. home directory. This user handles all potentially dangerous mail
  177. content - that is why it should be a separate account.
  178. In the above example, Postfix listens on port localhost:10025. If
  179. you want to have your filter listening on port localhost:10025
  180. instead of Postfix, then you must run your filter as a stand-alone
  181. program.
  182. Note: the localhost port 10025 SMTP server filter should announce
  183. itself as "220 localhost...". Postfix aborts delivery when it
  184. connects to an SMTP server that uses the same hostname as Postfix
  185. ("host <servername> greeted me with my own hostname"), because that
  186. normally means you have a mail delivery loop problem.
  187. The example here assumes that the /some/where/filter command is a
  188. PERL script. PERL has modules that make talking SMTP easy. The
  189. command-line specifies that mail should be sent back into Postfix
  190. via localhost port 10026.
  191. The simplest content filter just copies SMTP commands and data
  192. between its inputs and outputs. If it has a problem, all it has to
  193. do is to reply to an input of `.' with `550 content rejected', and
  194. to disconnect without sending `.' on the connection that injects
  195. mail back into Postfix.
  196. The job of the content filter is to either bounce mail with a
  197. suitable diagnostic, or to feed the mail back into Postfix through
  198. a dedicated listener on port localhost 10026:
  199. /etc/postfix/master.cf:
  200. localhost:10026 inet n - n - 10 smtpd
  201. -o content_filter=
  202. -o local_recipient_maps=
  203. -o relay_recipient_maps=
  204. -o myhostname=localhost.domain.tld
  205. -o smtpd_helo_restrictions=
  206. -o smtpd_client_restrictions=
  207. -o smtpd_sender_restrictions=
  208. -o smtpd_recipient_restrictions=permit_mynetworks,reject
  209. -o mynetworks=127.0.0.0/8
  210. Warning for Postfix version 2 users: in this SMTP server after the
  211. content filter, do not override main.cf settings for virtual_alias_maps
  212. or virtual_alias_domains. That would cause mail to be rejected with
  213. "User unknown".
  214. This SMTP server has the same process limit as the "filter" master.cf
  215. entry.
  216. The "-o content_filter=" requests no content filtering for incoming
  217. mail.
  218. The "-o local_recipient_maps=" and "-o relay_recipient_maps=" avoid
  219. unnecessary table lookups.
  220. The "-o myhostname=localhost.domain.tld" avoids false alarms ("host
  221. <servername> greeted me with my own hostname") if your content
  222. filter is based on a proxy that simply relays SMTP commands.
  223. The "-o smtpd_xxx_restrictions" and "-o mynetworks=127.0.0.0/8"
  224. turn off UCE controls that would only waste time here.
  225. Squeezing out more performance
  226. ==============================
  227. Many refinements are possible, such as running a specially-configured
  228. smtp delivery agent for feeding mail into the content filter, and
  229. turning off address rewriting before content filtering.
  230. As the example below shows, things quickly become very complex,
  231. because a lot of main.cf like information gets listed in the
  232. master.cf file. This makes the system hard to understand.
  233. Even worse, details change as Postfix evolves and different
  234. configuration parameters are implemented by different programs.
  235. If you need to squeeze out more performance, it is probably simpler
  236. to run multiple Postfix instances, one before and one after the
  237. content filter. That way, each instance can have simple main.cf
  238. and master.cf files, each instance can have its own mail queue,
  239. and the system will be easier to understand.
  240. As before, we will set up a content filtering program that receives
  241. SMTP mail via localhost port 10025, and that submits SMTP mail back
  242. into Postfix via localhost port 10026.
  243. .......................................
  244. : Postfix :
  245. ----->smtpd \ :
  246. : -pre-cleanup-\ /local---->
  247. ---->pickup / -queue- :
  248. : -cleanup-/ | \smtp----->
  249. : bounces/ ^ v :
  250. : and locally | v :
  251. : forwarded smtpd scan :
  252. : messages 10026 | :
  253. ...........................|...........
  254. ^ |
  255. | v
  256. ....|.............
  257. : | 10025 :
  258. : filter :
  259. : :
  260. ..................
  261. To enable content filtering in this manner, specify in main.cf a
  262. new parameter:
  263. /etc/postfix/main.cf:
  264. content_filter = scan:localhost:10025
  265. /etc/postfix/master.cf:
  266. #
  267. # These are the usual input "smtpd" and local "pickup" servers already
  268. # present in master.cf. We add an option to select a non-default
  269. # cleanup service (defined further below).
  270. #
  271. smtp inet n - n - - smtpd
  272. -o cleanup_service_name=pre-cleanup
  273. pickup fifo n - n 60 1 pickup
  274. -o cleanup_service_name=pre-cleanup
  275. #
  276. # ------------------------------------------------------------------
  277. #
  278. # This is the cleanup daemon that handles messages in front of
  279. # the content filter. It does header_checks and body_checks (if
  280. # any), but does no virtual alias or canonical address mapping,
  281. # so that mail passes through your content filter with the original
  282. # recipient addresses mostly intact.
  283. #
  284. # Virtual alias or canonical address mapping happens in the second
  285. # cleanup phase after the content filter. This gives the content_filter
  286. # access to *largely* unmodified addresses for maximum flexibility.
  287. #
  288. # Some sites may specifically want to perform canonical or virtual
  289. # address mapping in front of the content_filter. In that case you
  290. # still have to enable address rewriting in the after-filter cleanup
  291. # instance, in order to correctly process forwarded mail or bounced
  292. # mail.
  293. #
  294. pre-cleanup unix n - n - 0 cleanup
  295. -o canonical_maps=
  296. -o sender_canonical_maps=
  297. -o recipient_canonical_maps=
  298. -o masquerade_domains=
  299. -o virtual_alias_maps=
  300. #
  301. # ------------------------------------------------------------------
  302. #
  303. # This is the delivery agent that injects mail into the content
  304. # filter. It is tuned for low concurrency, because most content
  305. # filters burn CPU and use lots of memory. The process limit of 10
  306. # re-enforces the effect of $default_destination_concurrency_limit.
  307. # Even without an explicit process limit, the concurrency is bounded
  308. # because all messages heading into the content filter have the same
  309. # destination.
  310. #
  311. scan unix - - n - 10 smtp
  312. #
  313. # ------------------------------------------------------------------
  314. #
  315. # This is the SMTP listener that receives filtered messages from
  316. # the content filter. It *MUST* clear the content_filter
  317. # parameter to avoid loops, and use a different hostname to avoid
  318. # triggering the Postfix SMTP loop detection code.
  319. #
  320. # This "smtpd" uses the normal cleanup service which is also used
  321. # for bounces and for internally forwarded mail.
  322. #
  323. # The parameters from mynetworks onward disable all access
  324. # control other than insisting on connections from one of the IP
  325. # addresses of the host. This is typically overkill, but can
  326. # reduce resource usage, if the default restrictions use lots of
  327. # tables.
  328. #
  329. localhost:10026 inet n - n - - smtpd
  330. -o content_filter=
  331. -o myhostname=localhost.domain.tld
  332. -o local_recipient_maps=
  333. -o relay_recipient_maps=
  334. -o mynetworks=127.0.0.0/8
  335. -o mynetworks_style=host
  336. -o smtpd_restriction_classes=
  337. -o smtpd_client_restrictions=
  338. -o smtpd_helo_restrictions=
  339. -o smtpd_sender_restrictions=
  340. -o smtpd_recipient_restrictions=permit_mynetworks,reject
  341. #
  342. # Do not override main.cf settings here for virtual_alias_maps or
  343. # virtual_mailbox_maps. This causes mail to be rejected with "User
  344. # unknown in virtual (alias|mailbox) recipient table".
  345. #
  346. # ------------------------------------------------------------------
  347. #
  348. # This is the normal cleanup daemon for use after content filtering.
  349. # No header or body checks, because those have already been taken
  350. # care of by the pre-cleanup service before the content filter.
  351. #
  352. # The normal cleanup instance does all the virtual alias and canonical
  353. # address mapping that was disabled in the pre-cleanup instance before
  354. # the content filter. This rewriting must be done even when you didn't
  355. # disable address rewriting in the pre-cleanup instance, in order to
  356. # correctly process bounces and locally forwarded mail.
  357. #
  358. cleanup unix n - n - 0 cleanup
  359. -o header_checks=
  360. -o mime_header_checks=
  361. -o nested_header_checks=
  362. -o body_checks=
  363. #
  364. # ------------------------------------------------------------------
  365. #
  366. # The normal "smtp" delivery agent for contrast with "scan".
  367. #
  368. smtp unix - - n - - smtp
  369. The above example causes Postfix to add one content filtering record
  370. to each incoming mail message, with content scan:localhost:10025.
  371. You can use the same syntax as in the right-hand side of a Postfix
  372. transport table. The content filtering records are added by the
  373. smtpd and pickup servers.
  374. The "scan" transport is a dedicated instance of the "smtp" delivery
  375. agent for injecting messages into the SMTP content filter. Using
  376. a dedicated "smtp" transport allows one to tune it for the specific
  377. task of delivering mail to a local content filter (low latency,
  378. low concurrency, throughput dependent on predictably low latency).
  379. See the previous example for setting up the content filter with
  380. the Postfix spawn service; you can of course use any server that
  381. can be run stand-alone outside the Postfix environment.
  382. Filtering mail from outside users only
  383. ======================================
  384. The easiest approach is to configure ONE Postfix instance with TWO
  385. SMTP server addresses in master.cf:
  386. - One SMTP server address for inside users only that never invokes
  387. content filtering.
  388. - One SMTP server address for outside users that always invokes
  389. content filtering.
  390. /etc/postfix.master.cf:
  391. # SMTP service for internal users only, no content filtering.
  392. 1.2.3.4:smtp inet n - n - - smtpd
  393. -o smtpd_client_restrictions=permit_mynetworks,reject
  394. 127.0.0.1:smtp inet n - n - - smtpd
  395. -o smtpd_client_restrictions=permit_mynetworks,reject
  396. # SMTP service for external users, with content filtering.
  397. 1.2.3.5:smtp inet n - n - - smtpd
  398. -o content_filter=foo:bar
  399. Getting really nasty
  400. ====================
  401. The above filtering configurations are static. Mail that follows
  402. a given path is either always filtered or it is never filtered. As
  403. of Postfix 2.0 you can also turn on content filtering on the fly.
  404. The Postfix UCE features allow you to specify a filtering action
  405. on the fly:
  406. FILTER foo:bar
  407. You can do this in smtpd access maps as well as the cleanup server's
  408. header/body_checks. This feature must be used with great care:
  409. you must disable all the UCE features in the after-filter smtpd
  410. and cleanup daemons or else you will have a content filtering loop.
  411. Limitations:
  412. - There can be only one content filter action per message.
  413. - FILTER actions from smtpd access maps and header/body_checks take
  414. precedence over filters specified with the main.cf content_filter
  415. parameter.
  416. - Only the last FILTER action from smtpd access maps or from
  417. header/body_checks takes effect.
  418. - The same content filter is applied to all the recipients of a
  419. given message.