changeset 844:58d726e86650

New and improved documentation on Postfix integration.
author Ben Schmidt
date Mon, 30 Jan 2012 01:49:26 +1100
parents 2ea56ea4bd34
children b308bb25a8d1
files README.postfix
diffstat 1 files changed, 124 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/README.postfix	Wed Jan 25 22:34:57 2012 +1100
+++ b/README.postfix	Mon Jan 30 01:49:26 2012 +1100
@@ -1,146 +1,158 @@
-README.postfix                                                   Dec 16th 2009
-
-POSTFIX ISSUES
-
-    The main issue with Postfix and Mlmmj is the Mlmmj requirement that
-    the Mlmmj executables must be executed by root or the owner of the
-    list directory.
+README.postfix                                                   Jan 28th 2012
 
-    This is at odds with Postfix.  The standard local delivery mechanism
-    for Postfix is local(8) that ships with Postfix.  According to
-    local(8) delivery to external programs is done on behalf of the
-    receiving user.  But when delivering to a program without using a
-    .forward file there is no user context.  And using an alias file
-    does not provide user context.
+The main challenge to setting up Mlmmj with Postfix is that Mlmmj must be
+executed by root or the owner of the list directory, but by default Postfix
+will execute Mlmmj as 'nobody'[1].
 
-    The man page also explains that in the absence of user context the
-    local(8) daemon will use the owner of the :include: file from the
-    aliases file.  But this is a problem too.  By default :include:
-    files are disabled as a security precaution in aliases files for
-    delivering to external programs.
+There are a number of possible ways around this:
 
-    So Postfix then falls back to executing with the user specified by
-    the configuration option 'default_privs'.  The default setting for
-    this option is the user 'nobody'.  You can make Mlmmj work by having
-    your lists owned by 'nobody', but this is not recommended.  Other
-    programs and daemons may use 'nobody' as a user who should not have
-    access to anything; most notably, some NFS implementations use this
-    user when somebody connects but fails to authenticate.  Such users
-    should not be able to access your mailing lists.  Changing
-    'default_privs' to an 'mlmmj' user may open other security holes,
-    and may not be appropriate if Postfix is used for other external
-    programs besides Mlmmj.
+- Using .forward files (impractical) [2]
+- Using :include: files (possibly insecure) [3]
+- Making 'nobody' own your lists (insecure) [4]
+- Changing the Postfix default (possibly insecure or impractical) [5]
+- Using a Postfix transport (recommended)
 
-    This leaves us with a conundrum on how to execute the Mlmmj
-    executables as an 'mlmmj' user.  One answer is to use a Postfix
-    transport.
-
-    First we'll get the 'mlmmj' user setup and then move onto the
-    Postfix configuration:
-
-MLMMJ SETUP
-
-    Create a 'mlmmj' user that will own all the lists.  Use whatever
-    user creation app/script is provided by your system.  Generally
-    'useradd'.
+As you can see, the last option is recommended. Here is how to set it up using
+Postfix virtual domains (so you can host multiple domains on the same server).
+(It can also be done with regular non-virtual aliases[6].)
 
-    Create the spool directory that is owned by the 'mlmmj' user.
-    This is typically /var/spool/mlmmj but can be any directory so long
-    as it is owned by 'mlmmj'.  It can even be the home directory of the
-    'mlmmj' user.  If the spool directory is not /var/spool/mlmmj then
-    everywhere in this file replace /var/spool/mlmmj with your spool
-    directory.
-
-    Create a mailing list using mlmmj-make-ml.  Make sure to use the
-    -s flag to set the spool directory if it isn't /var/spool/mlmmj
+ 1) Add an 'mlmmj' user to your system (e.g. using 'useradd'). It usually
+    makes sense to make this a 'system' user, with no password and no shell
+    (/usr/false for the shell), and for its home directory to be
+    /var/spool/mlmmj (or wherever you want to put your Mlmmj spool directory).
 
-POSTFIX SETUP
-
-    First thing is to make sure that the postfix server accepts mail for
-    the mailing lists.  For a server that handles mail for multiple
-    domains, this is done with a 'virtual_alias_map'.  This is how I'll
-    demonstrate.
-
-    Add a virtual_alias_map file to main.cf configuration.  We'll use a
-    regular expression map since we need to be able to match all the
-    various Mlmmj delimiter addresses (list-subscribe, list-unsubscribe,
-    confsub-0123456789abcdef, etc.).
+ 2) Create your Mlmmj spool directory (we'll assume it's /var/spool/mlmmj)
+    and change its owner to the 'mlmmj' user.
 
-        main.cf:
-            virtual_alias_maps = hash:/etc/postfix/virtual,
-                                 regexp:/var/spool/mlmmj/virtual.regexp
-
-        /var/spool/mlmmj/virtual.regexp:
-            /^(mlmmj-test.*)@example\.com$/          ${1}
-            /^(another-list.*)@sample\.com$/         ${1}
-
-    One line needs to be in the virtual map for each list the 'mlmmj' id
-    is to handle.  The regex formula is:
-
-        /^(list-name.*)@(domain\.com)$/              ${1}
+ 3) Add an 'mlmmj' transport which uses the pipe(8) delivery agent to execute
+    mlmmj-receive as the mlmmj user by adding something like the following to
+    master.cf (often in /etc/postfix)[7]:
 
-    If you want to host multiple domains in a hierarchical structure,
-    you can alternatively use:
-
-        /^(list-name.*)@(domain\.com)$/        domain--${1}
+        # mlmmj mailing lists
+        mlmmj   unix  -       n       n       -       -       pipe
+            flags=ORhu user=mlmmj argv=/usr/local/bin/mlmmj-receive -F -L /var/spool/mlmmj/$nexthop
 
-    Next we make sure that Postfix can invoke the mlmmj executables as
-    the 'mlmmj' user.  This is where the transport map comes in.  So we
-    add a transport map and a configuration option that instructs the
-    transport to only deliver one file at a time.  See transport(5) for
-    more information on transports.
+    Note that $nexthop is used to specify the list directory. We will return
+    to that later.
 
-        main.cf:
-            transport_maps = regexp:/var/spool/mlmmj/transport
+ 4) Integrate some necessary options in main.cf (also often in /etc/postfix):
+
+        # Only deliver one message to Mlmmj at a time
             mlmmj_destination_recipient_limit = 1
 
-        /var/spool/mlmmj/transport:
-            /^(list-test).*$/                        mlmmj:list-test
-            /^(another-list).*$/                     mlmmj:another-list
+        # Consider the part after '+' but before '@' to be an address extension
+        # i.e. addresses have the form user+extension@domain.tld
+        recipient_delimiter = +
+
+        # A map to forward mail to a dummy domain
+        virtual_alias_maps = hash:/var/spool/mlmmj/virtual
+
+	# Allow virtual alias maps to specify only the user part of the address
+	# and have the +extension part preserved when forwarding, so that
+	# list-name+subscribe, list-name+confsub012345678, etc. will all work
+        propagate_unmatched_extensions = virtual
+
+        # A map to forward mail for the dummy domain to the Mlmmj transport
+        transport_maps = hash:/var/spool/mlmmj/transport
+
+    Of course, you may need to merge these options with existing ones (e.g.
+    you probably have existing virtual_alias_maps if you run a multi-domain
+    server).
+
+    It is probably unnecessary to change propagate_unmatched_extensions because
+    it defaults to something including 'virtual'. You can check this with
+    something like 'postconf | grep propagate'.
 
-    What this transport file says, is that any message destined for an
-    email address that matches the regexp on the left, deliver it using
-    the transport 'mlmmj' and setting 'nexthop' to the value in $1.
-    Which in this case is the mailing list name.  'nexthop' is special
-    variable for transports.
+ 5) (For each list) Create a mailing list (e.g. by using mlmmj-make-ml). The
+    list directory should be like /var/spool/mlmmj/list-dir for a flat
+    structure, or /var/spool/mlmmj/domain.tld/list-name for a hierarchical
+    structure (the -s option to mlmmj-make-ml may be useful to get the list
+    created where you want it). Ensure the list directory and everything in it
+    is owned by the mlmmj user (except you may want control files to be owned
+    by your www server user in order to use web configuration interfaces; they
+    must be readable by the mlmmj user though).
+
+ 6) (For each list) Add entries to the Postfix tables to accept mail for the
+    list and forward it to the Mlmmj transport:
+
+    /var/spool/mlmmj/virtual:
+        list-name@domain.tld    domain.tld--list-name@localhost.mlmmj
 
-    For the hierarchical multi-domain solution, use this variant:
+    /var/spool/mlmmj/transport:
+        # for a flat structure
+        domain.tld--list-name@localhost.mlmmj   mlmmj:list-dir
+        # for a hierarchical structure
+        domain.tld--list-name@localhost.mlmmj   mlmmj:domain.tld/list-name
 
-        /^(domain--list-name).*$/              mlmmj:domain/list-name
+    Note that we have used a dummy domain 'localhost.mlmmj' to connect the
+    virtual alias with the Mlmmj transport. This could be anything as long as
+    it isn't a real domain. The user part of the address could also be
+    anything; as long as the address matches in both tables it should work.
+
+    Also note that the text after 'mlmmj:' becomes $nexthop which was mentioned
+    earlier, so it is used to specify the list directory when executing
+    mlmmj-receive.
 
-    Now we setup the 'mlmmj' transport.  The 'mlmmj' in mlmmj:$1 above
-    indicates a transport listed in the Postfix master.cf file.  We are
-    just going to create a transport called 'mlmmj' but it is nothing
-    more than a pipe(8) to the mlmmj-receive program that is invoked as
-    the 'mlmmj' user.
+ 7) Refresh your postfix tables and reload your configuration so it takes
+    effect.
+
+        postmap /var/spool/mlmmj/virtual
+        postmap /var/spool/mlmmj/transport
+        postfix reload
+
+    Enjoy your new lists!
+
+
 
-        master.cf:
-            # mlmmj mailing lists
-            mlmmj   unix  -       n       n       -       -       pipe
-                flags=DORhu user=mlmmj argv=/usr/local/bin/mlmmj-receive -F -L /var/spool/mlmmj/$nexthop/
+[1] Actually, the standard local(8) delivery agent will execute external
+    programs (such as Mlmmj) as the 'receiving user'. However, unless you
+    direct your mail to Mlmmj using a .forward file (see local(8)) or an
+    :include: file (see aliases(5)), there is no 'receiving user'. Without a
+    'receiving user', Postfix uses the user from the configuration option
+    'default_privs', which defaults to 'nobody'.
+
+[2] Using .forward files is not practical, as it requires a user to be created
+    for every mailing list.
+
+[3] Using :include: files would require delivery to commands to be enabled in
+    :include: files, which is not recommended for security reasons.
 
-    This takes the pipe(8) Postfix delivery agent and tells it to invoke
-    '/usr/local/bin/mlmmj-receive' as the 'mlmmj' user and pipe the
-    email to it on stdin.  This mode of transportation is given the name
-    'mlmmj'.
+[4] Other programs and daemons rely on 'nobody' not owning any files or having
+    access to anything; they use 'nobody' as a way of denying access and
+    keeping all your files and system secure. Most notably, some NFS
+    implementations use 'nobody' when somebody connects but fails to
+    authenticate. Your mailing lists should not be accessible in such
+    situations, but they may be if they are owned by 'nobody'.
+
+[5] Changing 'default_privs' to an 'mlmmj' user may open other security holes,
+    and may not be appropriate if Postfix is used for other external programs
+    besides Mlmmj.
+
+[6] To do this, at step 4, you'll need to incorporate:
+ 
+        alias_maps = hash:/var/spool/mlmmj/aliases
+        propagate_unmatched_extensions = alias
 
-    The 'flags' parameter to pipe(8) is pretty critical here. In
-    particular if the 'R' option is not used mlmmj-receive fails to
-    receive the mail correctly. The options mean:
+    You probably will need to adjust propagate_unmatched_extensions in this
+    case, probably by adding 'alias' to the existing value rather than using
+    'alias' alone.
+
+    At step 6, entries in /var/spool/mlmmj/aliases should look something like:
+
+        list-name:    list-name@localhost.mlmmj
 
-        D - Prepend a 'Delivered-To: recipient' header
+    And of course you can omit the virtual stuff if you're not using it.
+
+    Note that this has not been tested, but we believe it should work.
+
+[7] The flags are pretty critical here. In particular if the 'R' option is not
+    used mlmmj-receive fails to receive the mail correctly. The options mean:
+
+        D - Prepend a 'Delivered-To: recipient' header (not used)
         O - Prepend an 'X-Original-To: recipient' header
         R - Prepend a 'Return-Path:'. header
         h - fold $nexthop to lowercase
         u - fold $recipient to lowercase
 
-    $nexthop gets set to what was on the right had side of the ':' in
-    the transport file.  The way we have that configured is that
-    $nexthop will get set to the name of the mailing list (or domain
-    and name).  Your list directories, then, should be at
-    /var/spool/mlmmj/list-name as usual, or for the hierarchical
-    multi-domain version, in /var/spool/mlmmj/domain/list-name.
-
-    Restart Postfix and enjoy your new lists.