This file README.postfix is part of the amavisd-new distribution,
which can be found at http://www.ijs.si/software/amavisd/

Author: Mark Martinec <Mark.Martinec@ijs.si>
Last updated: 2004-09-30


How to use amavisd-new with Postfix
***********************************

The sections labeled 'COMMENT' may be skipped on first reading.

Your Postfix must not be ancient, it must support parameter 'content_filter'.
Check for the purpose of this parameter in ./README_FILES/FILTER_README
of the Postfix distribution. This file was revised in postfix-1.1.9-20020512,
and again in postfix 20030120, you may want to read the latest version.
In the most recent Postfix documentation the setup described here is known
as 'Postfix After-Queue Content Filter', section 'Advanced content filter'.

For compatibility with previous versions of amavisd the choice of default
tcp port numbers is 10024 and 10025, in contrast to 10025 and 10026 as used
in FILTER_README examples. The service name chosen here is 'smtp-amavis'
instead of 'scan' as in the Postfix documentation.

We are assuming that Postfix is already installed, configured and is
working as expected. As a safety net during experimenting you might feel
better by setting 'soft_bounce=yes' in /etc/postfix/main.cf, and doing
a 'postfix reload'. It will turn hard errors experienced by Postfix into
temporary failures, causing failed mail operations to be retried later.
Don't forget to remove it later when things appear to be running well.


1. Install and start amavisd (as explained in INSTALL - just the daemon,
no helper programs amavis(.c) or amavisd-milter(.c) are needed)

For the first time it is best to start it interactively and keep it
attached to the terminal:

     $ /usr/local/sbin/amavisd debug

From another window check that it is listening on a
local SMTP port 10024 (the default port):

-->  $ telnet 127.0.0.1 10024
     Trying 127.0.0.1...
     Connected to 127.0.0.1.
     Escape character is '^]'.

     220 [127.0.0.1] ESMTP amavisd-new service ready

-->  quit

     221 Bye
     Connection closed by foreign host.


2. With a text editor add to the Postfix master.cf file 
the following two entries, e.g. near the end of the file:

smtp-amavis unix -	-	y/n	-	2  smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes

127.0.0.1:10025 inet n	-	y/n	-	-  smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks

COMMENT:
- Of all the options specified above in the second entry,
  the one that is essential is the '-o content_filter=' .
- The '-o smtp_send_xforward_command=yes'
  (or '-o lmtp_send_xforward_command=yes' if using LMTP)
  is optional, but recommended - amavisd-new benefits from it since V2.0.
  It does not hurt if specified even if not yet supported by the currently
  running Postfix or amavisd-new.

Change the 'y/n' to either 'y' or 'n', depending on how you prefer
the smtp and smtpd postfix services to run - either chroot-ed, or not.
See your other (normal) smtp and smtpd postfix services in this file
and use the same setting here.

COMMENT:
  If you have an entry like 'vscan unix - n n - 2 pipe user=vscan ...'
  from an ancient amavisd installation, you don't need it anymore and
  it may be removed. Keeping it does no harm.


3. Do a 'postfix reload', check its log file for any complaints,
   and test if it is listening on port 10025:

-->  $ telnet 127.0.0.1 10025
     Trying 127.0.0.1...
     Connected to 127.0.0.1.
     Escape character is '^]'.
     220 yourhost.example.com ESMTP Postfix
-->  quit
     221 Bye
     Connection closed by foreign host.


4. If you want, simulate a mail sent to amavisd and see if it gets delivered
   via Postfix to its recipient. Try first with a simple and clean message,
   then with an EICAR test virus pattern which should be recognized by all
   virus scanners (unless you have them disabled or not installed):

-->  $ telnet 127.0.0.1 10024
     Trying 127.0.0.1...
     Connected to 127.0.0.1.
     Escape character is '^]'.
     220 [127.0.0.1] ESMTP amavisd-new service ready
-->  MAIL FROM:<test@example.com>
     250 2.1.0 Sender test@example.com OK
-->  RCPT TO:<postmaster>
     250 2.1.5 Recipient postmaster OK
-->  DATA
     354 End data with <CR><LF>.<CR><LF>
-->  Subject: test1
-->
-->  test1
-->  .

***  250 2.6.0 Ok, id=31859-01, from MTA: 250 Ok: queued as 90B7F16F

-->  MAIL FROM:<test@example.com>
     250 2.1.0 Sender test@example.com OK
-->  RCPT TO:<postmaster>
     250 2.1.5 Recipient postmaster OK
-->  DATA
     354 End data with <CR><LF>.<CR><LF>
-->  Subject: test2 - virus test pattern
-->
-->  X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
-->  .

you should get one of the following replies (or similar), depending on
your $final_virus_destiny and *virus_lovers* settings in amavisd.conf:
***  550 5.7.1 Message content rejected, id=16968-01 - VIRUS: EICAR-AV-Test
***  250 2.5.0 Ok, but 1 BOUNCE
***  250 2.7.1 Ok, discarded, id=16984-01 - VIRUS: EICAR-AV-Test
***  250 2.6.0 Ok, id=17041-01, from MTA: 250 Ok: queued as 3F1841A5F5

-->  QUIT
     221 2.0.0 [127.0.0.1] (amavisd) closing transmission channel
     Connection closed by foreign host.

You may need/want to use different sender and recipient addresses.
The test pattern must be entered exactly to be recognized, starting
at the beginning of line (without indentation).

Depending on the settings in amavisd.conf, the sender (test@example.com)
and the virus administrator may have been sent a (non-)delivery status
notification, the second message should have been quarantined, and the first
message must have been successfully delivered to the recipient. See the log
that is scrolling on the terminal (as set up at step 1) and check for possible
problems.


5. Tell Postfix to start forwarding all mail it receives to amavisd-new
   for content inspection.

To the Postfix main.cf file add a line:

  content_filter=smtp-amavis:[127.0.0.1]:10024

either with a text editor, or preferably using shell commands:
  # postconf -e 'content_filter=smtp-amavis:[127.0.0.1]:10024'

COMMENT:
  The global setting of 'content_filter' in main.cf affects any Postfix
  input service (i.e. smtpd and pickup). If a more selective approach
  is required, the option
    -o content_filter=smtp-amavis:[127.0.0.1]:10024
  may be given in master.cf to selected services only, or the option:
    -o content_filter=
  may override (i.e. clear) the global setting on selected services.


6. Do a 'postfix reload' and watch the logs - both the Postfix logs,
and the amavisd log file (on the screen or wherever you have it directed).

If you get in trouble, you only need to undo the step 5 and do a
'postfix reload'. New mail will no longer be tagged with content filter
routing.

COMMENT:
  The messages that have been received while 'content_filter' was set,
  will still try to get delivered to your old setting of content_filter,
  and will wait in the queue until successful or deleted or expired - or
  until you do:  postsuper -r ALL; postfix reload

If all is fine, you may abort (^C) the process running 'amavisd debug',
and start amavisd without a 'debug' option, making it detach and daemonize.
There is no need to stop or restart Postfix.

This completes the integration of amavisd and Postfix.
It uses the SMTP (or LMTP) protocol for Postfix->amavisd,
and uses SMTP protocol for amavisd->Postfix communication.
This is the fastest and recommended method, and simplest to set up.


ALTERNATIVE:

If you have a recent Postfix version such as 2.0, and amavisd-new-20021116
or younger, Postfix can be told to feed mail to amavisd via LMTP protocol
instead of SMTP. This brings multi-transaction session capability and
per-recipient status responses. Just replace the service name (last item)
'smtp' with 'lmtp' in the master.cf entry:         vvvv

smtp-amavis unix -	-	y/n	-	2  lmtp
    -o lmtp_data_done_timeout=1200
    -o lmtp_send_xforward_command=yes

(and change parameter names accordingly).

It works with earlier version of Postfix as well, but there are one or two
minor bugs in the Postfix lmtp client code (parsing a LMTP port number,
and unnecessarily lowercasing the addresses), so it is better to stick
to a fresh Postfix version.


OPTIONAL:

I also like to set up an e-mail addresses with Postfix to receive
all quarantined viruses, so that a mailer will deal with storing
(or forwarding) them, and you don't even have to set up a quarantine
directory directly and locally accessible to amavisd. Here is one way
of doing it, but see 'local(8)' Postfix man page for more options.

This method of quarantining might be the only method available if amavisd
is running chrooted and quarantine should be located outside of chroot jail.

To the Postfix aliases file (or database) add an entry for an e-mail address,
e.g. 'infected', either to forward its mail to some place, or do a local
delivery to a file or directory, e.g.:

infected: /var/spool/mail/infected

(append a '/' if you prefer Maildir style mailbox),
then run 'newaliases' (or 'postalias /etc/postfix/aliases').

In your amavisd.conf file specify (note the trailing '@') :
  $virus_quarantine_to = 'infected@';  # forward to MTA for delivery

Reload amavisd (amavisd reload) if it is already running to make it
re-read its config file, and check the log file.  All set!  Send some
infected mail and watch it appear at the specified mailbox.


----------------------------------------------------------------------------
The next section is a commented setup that can be directly appended
to the Postfix master.cf file instead of the item (2.) above,
in case you need finer control or better understanding.
It describes a Postfix setup with two cleanup services, as recommended
by the new Postfix 2.0.3 ./README_FILES/FILTER_README .

Here is an overall picture (adapted from FILTER_README to match
port numbers and service name as traditionally used by amavisd-new):

      .......................................
      :                Postfix              :
   ----->smtpd \                            :
      :         -pre-cleanup-\       /local---->
   ---->pickup /              -queue-       :
      :             -cleanup-/   |   \smtp----->
      :     bounces/    ^        v          :
      : and locally     |        v          :
      :   forwarded   smtpd  smtp-amavis    :
      :    messages   10025      |          :
      ...........................|...........
                        ^        |
                        |        v
            ............|...............................
            :           |   $inet_socket_port=10024    :
            :           |                              :
            : $forward_method='smtp:[127.0.0.1]:10025' :
            : $notify_method ='smtp:[127.0.0.1]:10025' :
            :                                          :
            :    amavisd-new                           :
            ............................................


To be able to get largely unmodified e-mail addresses to content filter
(avoiding virtual_alias_maps before the content filter), one needs to
use two cleanup services:

- add new service 'pre-cleanup';
- (optionally) add options to existing service 'cleanup';
- add option 'cleanup_service_name=pre-cleanup' to existing services
  'smtp' and 'pickup';

as described below.


# Append this to the master.cf Postfix file and edit to will.
# It defaults to the standard settings as described in README.postfix .
#
# Instruct Postfix content filtering to SEND all mail TO amavisd:
# ===============================================================
#
# By default amavisd will listen to both protocols (SMTP/LMTP over TCP,
# as well as to amavis helper protocol on a Unix socket). The older method
# using the helper program amavis.c still works, but is not recommended, and
# is not described here. To disable amavisd-new unnecessarily listening on a
# Unix socket, comment out the assignment to $unix_socketname in amavisd.conf.
#
# NOTE1: match number of sending Postfix processes (the '2' below) with
#        $max_servers in amavisd.conf. Two or three per CPU should be enough.
#        Going beyond 20 just wastes memory and does not help with throughput.
# NOTE2: point the 'content_filter' hostname part to where amavisd is running,
#        and match the port number with $inet_socket_port in amavisd.conf, e.g:
#	   content_filter = smtp-amavis:my-amavisd-server.example.com:10024
# NOTE3: you should restrict amavisd to only accept connections from the
#        authorized Postfix host(s) by the @inet_acl access list in
#	 the amavisd.conf file, and/or by binding to specific interface, e.g.:
#	   $inet_socket_bind = '127.0.0.1';  # limit bind to loopback interface
# NOTE4: set the chroot field the same (y/n) as for your regular smtp service;
# NOTE5: set $child_timeout (in amavisd.conf) to a shorter time than
#        the Postfix parameter smtp_data_done_timeout - see rfc1047.
#        The value in '-o smtp_data_done_timeout=1200' must always be larger
#        (with some margin) than the value of $child_timeout in amavisd.conf
# NOTE6: the option '-o disable_dns_lookups=yes' is recommended for reducing
#	 latency with Postfix versions older than 2.0, but specify preferably
#        an IP address and not a DNS name in the content_filter specification
#        (thanks to Victor Duchovni for the suggestion).
#
# The "smtp-amavis" transport is a dedicated instance of the "smtp"
# delivery agent for injecting messages into the SMTP/LMTP content filter.
# Using a dedicated "smtp" or "lmtp" transport allows one to tune it
# for the specific task of delivering mail to a local content filter
# (low latency, low concurrency, throughput dependent on predictably
# low latency).
#
smtp-amavis unix -	-	n	-	2	smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
#some more ideas:
#   -o disable_dns_lookups=yes
#   -o smtp_bind_address=127.0.0.1
#   -o smtp_line_length_limit=0
#   -o notify_classes=protocol,resource,software
#   -o fallback_relay=backup-filter.example.com:10024

# or equivalently when using lmtp:
#smtp-amavis unix -	-	n	-	2	lmtp
#   -o lmtp_data_done_timeout=1200
#   -o lmtp_send_xforward_command=yes
#...

# COMMENT:
#   To provide a backup content filter in case the primary fails,
#   either use the '-o fallback_relay' as above, or use a DNS name and
#   provide multiple MX records for it. See /etc/postfix/sample-smtp.cf
#   and smtp(8) man page for details.


# from amavisd back to Postfix:
# =============================
#
# variant 1A: via SMTP, same host (or see 1B for multihost setup)
# In amavisd.conf choose the host running Postfix and its port number, e.g.:
#   $notify_method  = 'smtp:127.0.0.1:10025';
#   $forward_method = 'smtp:127.0.0.1:10025';
#   $relayhost_is_client = 0;


# The following is the SMTP listener that receives filtered messages from the
# content filter. It *MUST* clear the content_filter parameter to avoid loops.
#
# This "smtpd" uses the normal cleanup service which is also used
# for bounces and for internally forwarded mail.
#
# Disable all access control other than insisting on connections from one
# of the IP addresses of the host. This is typically overkill, but can reduce
# resource usage if the default restrictions do lots of checks.
#
# NOTE: set the chroot field the same (y/n) as for your regular smtpd service
#
127.0.0.1:10025 inet n	-	n	-	-  smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o mynetworks_style=host
    -o strict_rfc821_envelopes=yes
#some more ideas:
#   -o always_bcc=user@example.com


# The following is the cleanup daemon that handles messages in front of
# the content filter. It does header_checks and body_checks (if any),
# but does no virtual alias or canonical address mapping,
# so that mail comes out of your content filter with the original
# recipient addresses still intact.
#
# Virtual alias or canonical address mapping happens in the second
# cleanup phase after the content filter. This gives the content_filter
# access to *largely* unmodified addresses for maximum flexibility.
#
# Note that some sites may specifically want to perform canonical or
# virtual address mapping in front of the content_filter. However,
# in that case you still have to enable address rewriting in the
# after-filter cleanup instance, in order to correctly process
# forwarded mail or bounced mail.

# handle both the canonicalization and virtual_alias_maps later
# (this will provide content filter with largely unmodified addresses)
#
pre-cleanup  unix n	-	n	-	0	cleanup
    -o virtual_alias_maps=
    -o canonical_maps=
    -o sender_canonical_maps=
    -o recipient_canonical_maps=
    -o masquerade_domains=

# ...or leave canonicalization in pre-cleanup, but do virtual_alias_maps later
# (this is useful if canonicalization is used to map internal e-mail
# addresses to external, and you want the content filter to always see
# external canonical addresses of local users, both as senders,
# and as recipients):
#
#pre-cleanup  unix n	-	n	-	0	cleanup
#   -o virtual_alias_maps=


# The following is the normal cleanup daemon. No header or body checks here,
# because these have already been taken care of by the pre-cleanup service
# before the content filter.  The normal cleanup instance does all
# the virtual alias and canonical address mapping that was disabled
# in the pre-cleanup instance before the content filter.
#
cleanup	unix	n	-	n	-	0	cleanup
    -o mime_header_checks=
    -o nested_header_checks=
    -o body_checks=
    -o header_checks=
# or use second-stage header checks, to be able to place mail bombs on HOLD
#   -o header_checks=pcre:/etc/postfix/header_checks2

# Place the following line (without the leading # and space) into file
# /etc/postfix/header_checks2, and use the -o header_checks=pcre:... above,
# if you need trouble mail (e.g. mail bombs) to be placed on hold by Postfix,
# instead of being passed to recipients:
#
# /^X-Amavis-Hold:/	HOLD


# These are the usual input "smtpd" and local "pickup" servers already
# present in master.cf. We add an option to select a non-default
# cleanup service.
#
smtp      inet  n       -       n       -       -       smtpd
    -o cleanup_service_name=pre-cleanup
pickup    fifo  n       -       n       60      1       pickup
    -o cleanup_service_name=pre-cleanup



# variant 1B: via SMTP - with amavisd on a different host,
# also good for the case of several MTAs using the same amavisd server.
# - re-injection (forwarding) if $forward_method is smtp:...
# - notification messages     if $notify_method  is smtp:...
# - quarantine                if $virus_quarantine_to contains '@'
# In amavisd.conf set port number where Postfix (one or more) is
# listening for re-injected mail and notifications, and optionally set 
# $relayhost_is_client to 1, if you want to have host field in the
# $forward_method and $notify_method specification dynamically replaced
# (re-injection port number is automatically set to one higher than the
# port number on which message came in to amavisd, making possible for
# several MTA pairs on the same host to independently use amavisd, e.g.
# separately for incoming and outgoing mail). To prevent unauthorized use
# of the service you SHOULD restrict the set of IP addresses from which
# amavisd is willing to accept mail by specifying authorized Postfix host(s)
# with the access list @inet_acl in the amavisd.conf file. Bind must not be
# restricted to the loopback interface, so set $inet_socket_bind to undef.
#
# NOTE1: you SHOULD also restrict Postfix to only accept connections
#        on port 10025 from the amavisd host by '-o mynetworks = ...'
# NOTE2: set the chroot field the same (y/n) as for your regular smtp service.

#10025		inet n	-	n	-	-  smtpd
#   -o content_filter=
#   -o local_recipient_maps=
#   -o relay_recipient_maps=
#   -o smtpd_restriction_classes=
#   -o smtpd_client_restrictions=
#   -o smtpd_helo_restrictions=
#   -o smtpd_sender_restrictions=
#   -o smtpd_recipient_restrictions=permit_mynetworks,reject
#   -o mynetworks=127.0.0.0/8,10.0.0.0/8,192.168.1.1
#   -o mynetworks_style=host
#   -o strict_rfc821_envelopes=yes
#   -o smtpd_error_sleep_time=0
#   -o smtpd_soft_error_limit=1001
#   -o smtpd_hard_error_limit=1000
#   -o smtpd_client_connection_count_limit=0
#   -o smtpd_client_connection_rate_limit=0
#   -o receive_override_options=no_header_body_checks
#some more ideas:
#   -o smtpd_authorized_xforward_hosts=0.0.0.0/0
#   -o always_bcc=user@example.com


A tip from Wietse Venema (2002-12-12):

|  If you want to filter inbound SMTP mail only, then:
|
|  /etc/postfix/main.cf:
|     smtpd_recipient_restrictions = 
|        check_recipient_access hash:/etc/postfix/recipient_access
|        ...the usual stuff here...
|        reject_unauth_destination
|
|  /etc/postfix/recipient_access:
|     my.domain   FILTER foo:bar
|
|  That filters all the mail that has at least one recipient in your
|  domain, and does not filter mail with external recipients only.

(comment: the 'foo:bar' is what you would traditionally specify
in content_filter option, i.e. smtp-amavis:[127.0.0.1]:10024 )


Since Postfix snapshot 2.0.13-20030706 there is a new parameter
'receive_override_options' that eliminates the need for two cleanup
services in some more straightforward cases (not all features of
having two cleanup services are available). The idea is to use:
  -o receive_override_options=no_address_mappings
for main incoming smtpd service, and the:
  -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
for the post-content-filter smtpd service on port 10025. See SMTPD(8)
man page and the README_FILES/FILTER_README in the Postfix documentation. 
