Configuring the Kolab Groupware Server

Recipient Policy

Kolab Groupware features the automated application of user metadata, such as a user’s given-, surname and user ID, to form recipient email addresses for the user.

This is called the recipient policy, and serves the following important purposes:

  1. Naming Conventions (should they exist) are applied automatically and consistently,

  2. Locale-specific Transliteration of non-ASCII characters in names is applied to form valid email addresses,

  3. Globally Unique Recipient Email Addresses are automatically guaranteed.

Naming Conventions

The recipient policy aids deployments in achieving a level of consistency throughout their recipient database by applying a naming convention, avoiding the need for Kolab Groupware administrators to need to understand and consistently apply naming convention rules.

Consistency is important when considering global address book listings and searches in different contexts, and when anticipating what a valid recipient address for a given user may be – noted that most email users do not have access to your global address book to verify a recipient address is valid, and it is likely that most communications will take place with those users.

Locale-specific Transliteration

Names can contain non-ASCII characters – for example Marie Möller –, while recipient email addresses may not contain such characters (i.e. marie.möller@example.org is an invalid email address).

Without locale specific transliteration, the email address would become marie.moller@example.org. This is considered inadequate, as many users will experience this inappropriate, inconsiderate and/or awkward – it is the user’s native tongue after all and transliteration is used in every-day life. Kolab Groupware should therefor also be able to apply the same transliteration rules specific to the user’s native tongue.

With the (presumably correct) transliteration rules for German applied, the email address would become marie.moeller@example.org.

The recipient policy takes the value(s) of a number of attributes from an entry, and executes the recipient policy for primary and secondary recipient email addresses with help of the attribute names (referred to in the policy) and their values.

It will set the primary email address attribute to a singular value, though the LDAP schema allows multiple values for the default primary mail attribute mail.

The secondary email address attribute (alias by default) is set to the value(s) returned by the recipient policy as well.

Globally Unique Recipient Email Addresses

Furthermore, the recipient policy allows these automatically recipient email addresses to be globally unique.

The primary email address attribute value is, by default, used to determine a user’s mailbox name. Further using the previous example, user Marie Möller <marie.moeller@example.org> would get a user mailbox of user/marie.moeller@example.org.

It is important to realize that, should a second user also receive a primary email address attribute value of marie.moeller@example.org, two important things would happen:

  1. Possibly only one of the two, but likely neither user would be able to authenticate,

  2. The user’s mailbox and authorization ID would no longer uniquely correspond to either user.

Unless intentionally so, it is also important to avoid entries holding the same secondary email address attribute value(s).

To this end, for each generated value of either primary or secondary mail address attributes, a check is performed and – if the address is already in use – an integer is appended and incremented if necessary.

For three people named Marie Möller for example, this would end up as:

  • user/marie.moeller@example.org

  • user/marie.moeller2@example.org

  • user/marie.moeller3@example.org

Components that Apply the Recipient Policy

By default, the recipient policy is applied by two separate applications:

  1. The optional Kolab Web Administration Panel, often used for inputting new users and editing existing ones, so that this interface reflects the results to be expected,

  2. The Kolab daemon (the kolabd service) is the primary component to apply the recipient policy.

Both components use the configuration settings primary_mail and secondary_mail as their policy configuration.

To prevent the Kolab daemon from applying the recipient policy, add the following setting to kolab.conf(5):

[kolab]
daemon_rcpt_policy = False

Having added this setting, the Kolab Web Administration Panel could be re-configured so that the mail attribute for user entries becomes Generated instead of Generated (read-only).

These configuration items consist of a Python notation for string formatting, along with a limited number of string operations.

The primary_mail setting contains a singular basestring to be used in simplistic string formatting, while the secondary_mail setting contains a numerically ordered list of singular basestrings to be used in string formatting, and additionally allow the opportunity to execute some additional string operations such as converstion to substrings and capitalization.

Application of the Default Recipient Policy Example

If an example.org user is created with given name John and surname Doe, the default recipient policy will set the primary email address to john.doe@example.org and the secondary email addresses to j.doe@example.org and doe@example.org.

Important

By default, the primary recipient email address is used as a globally unique identifier involving authentication (allowing login by email address), the user’s mailbox name, mail delivery configuration, access control and the storage of user profiles.

It is therefore very important that no two entries share a single primary recipient email address.

The primary recipient email address for an example.org domain user with given name Marie, surname Möller and preferred language of en_US is (incidentally wrongly, see further down for the “correct” way) composed as follows:

  1. The sn attribute is transliterated using the specified preferred language for the user, with the equivalent of:

    # echo "Möller" | \
        LANG=en_US /usr/bin/iconv -f UTF-8 -t ASCII//TRANSLIT -s

    and stored as the value to key surname (now “Moller”).

  2. The recipient policy is obtained from the primary_mail setting.

    By default, this setting is set to:

    [example.org]
    primary_mail = %(givenname)s.%(surname)s@%(domain)s
    

    Note

    The attribute name for a user’s surname (family name) is sn. The use of surname in the configuration is to indicate that the transliterated version of the original (sn) should be used.

  3. The equivalent of the following Python is then executed:

    >>> primary_mail = "%(givenname)s.%(surname)s@%(domain)s" % {
                "givenname": "Maria",
                "surname": "Moller",
                "preferredlanguage": "en_US"
            }
    
    >>> primary_mail
    maria.moller@example.org
    
  4. This return value is checked against the existing user database for global uniqueness, and appended an integer to, which starts at 2, and is incremented by 1 for each recipient address that would not be globally unique.

The same routine applied with a preferred language of de_DE though:

  1. The sn attribute is transliterated using the specified preferred language for the user, with the equivalent of:

    # echo "Möller" | \
        LANG=de_DE /usr/bin/iconv -f UTF-8 -t ASCII//TRANSLIT -s

    and stored as the value to key surname (now “Moeller”).

  2. The recipient policy is obtained from the primary_mail setting.

    By default, this setting is set to:

    [example.org]
    primary_mail = %(givenname)s.%(surname)s@%(domain)s
    

    Note

    The attribute name for a user’s surname (family name) is sn. The use of surname in the configuration is to indicate that the transliterated version of the original (sn) should be used.

  3. The equivalent of the following Python is then executed:

    >>> primary_mail = "%(givenname)s.%(surname)s@%(domain)s" % {
                "givenname": "Maria",
                "surname": "Moeller",
                "preferredlanguage": "de_DE"
            }
    >>> primary_mail
    maria.moeller@example.org
    
  4. This return value is checked against the existing user database for global uniqueness, and appended an integer to, which start at 2, and is incremented by 1 for each recipient address that would not be globally unique.

Secondary Email Address Recipient Policy Example

The secondary recipient email address for an example.org user with given name Marie, surname Möller and preffered language de_DE, is composed as follows:

  1. The sn attribute is transliterated using the specified preferred language for the user, with the equivalent of:

    # echo "Möller" | \
        LANG=de_DE /usr/bin/iconv -f UTF-8 -t ASCII//TRANSLIT -s

    and stored as the value to key surname (now “Moeller”).

  2. The recipient policy is obtained from the i``secondary_mail`` setting. By default, this configuration setting is set to:

    [example.org]
    secondary_mail = {
            0: {
                    "{0}.{1}@{2}": "format('%(givenname)s'[0:1].capitalize(), '%(surname)s', '%(domain)s')"
                },
            1: {
                    "{0}@{1}": "format('%(uid)s', '%(domain)s')"
                },
            2: {
                    "{0}@{1}": "format('%(givenname)s.%(surname)s', '%(domain)s')"
                }
        }
    

    As the configuration indicates, the attributes for the user that will be used are the given name, the surname and the uid.

  3. Using Python’s string formatting, the resulting primary recipient email address would become:

    >>> secondary_mail = [
                "{0}.{1}@{2}".format('Maria'[0:1].capitalize(), 'Moeller', 'example.org'),
                "{0}@{1}".format('moeller', 'example.org'),
                "{0}@{1}".format('maria.moeller', 'example.org'),
            ]
    >>> secondary_mail
    [ 'm.moeller@example.org', 'moeller@example.org', 'maria.moeller@example.org' ]
    
  4. Any secondary email address that ends up being a duplicate of the established primary email address is removed,

  5. The remainder addresses are checked against the existing user database for global uniqueness, and appended an integer to, which starts at 2, and is incremented by 1 for each recipient address that would not be globally unique.

Configuring the Recipient Policy

Setting Primary Recipient Email Address

The primary recipient email address can be changed to reflect your naming convention through this procedure:

  1. Edit /etc/kolab/kolab.conf and replace the value of the primary_mail setting in the applicable domain section.

    For example, set the policy to %(givenname)s@%(domain)s to get email addresses like john@example.org and jane@example.org.

  2. Edit the user types for any user that matches the filter used by the kolab daemon to determine which users are kolab users. For example, “Kolab Users” and “Mail-enabled POSIX users” both include “objectclass=kolabinetorgperson” (the default kolab_user_filter) and therefore the Kolab daemon would apply the recipient policy to these objects.

    For each of these user_types, make sure that the auto_form_field configuration for the primary recipient email (mail) attribute include all attributes needed to compose the address.

    If, for example, you seek to apply a recipient policy containing %(initials)s, so that a user John Frank Doe may have an email address of john.f.doe@example.org, the initials attribute will need to be added to the data list in the auto_form_field definition for the mail attribute.

Setting the Secondary Recipient Email Address

The secondary recipient email addresses can be changed to reflect your naming convention through this procedure:

  1. Edit /etc/kolab/kolab.conf and replace the value of the secondary_mail setting in the applicable domain section.

    For example, set a policy item to '{0}@{1}': "format('%(givenname)s', '%(domain)s')" to get email addresses like john@example.org and jane@example.org.

  2. Edit the user types for any user that matches the filter used by the kolab daemon to determine which users are kolab users. For example, “Kolab Users” and “Mail-enabled POSIX users” both include “objectclass=kolabinetorgperson” (the default kolab_user_filter) and therefore the Kolab daemon would apply the recipient policy to these objects.

    For each of these user_types, make sure that the auto_form_field configuration for the secondary recipient email (alias) attribute include all attributes needed to compose the addresses.

Controlling the Primary and Secondary Recipient Email Address Attributes

The attribute names that contain the primary and secondary recipient email addresses are controlled by the mail_attrs setting in the [$domain] section of /etc/kolab/kolab.conf.

Should no such section or setting exist, then the fallback in the [$auth_mechanism] section is used, where $auth_mechanism is the authentication mechanism configured using the auth_mechanism setting in the [kolab] section.

Note

Note that only ‘ldap’ is currently supported as an authentication mechanism.

This setting is a comma- and/or comma-space separated list of attribute names.

By default, mail_attrs is set to mail, alias.

The first item in the list will be used as the attribute to use for primary recipient email addresses.

The second item in the list will be used as the attribute to use for secondary recipient email addresses.

No policy applies to any further values in this list, should they exist.

Disabling the Recipient Policy

The following procedure describes how to disable the recipient policy.

Disabling the Recipient Policy

  1. Edit /etc/kolab/kolab.conf and navigate to the section applicable for your domain (named [$domain]).

  2. Remove the settings primary_mail and secondary_mail.

  3. Restart the Kolab daemon:

    # service kolabd restart
  4. Copy /usr/share/doc/kolab-webadmin-*/sample-insert-user_types.php to /usr/share/kolab-webadmin/:

    # cp /usr/share/doc/kolab-webadmin-*/sample-insert-user_types.php \
        /usr/share/kolab-webadmin/
  5. Open /usr/share/kolab-webadmin/sample-insert-user_types.php in an editor of your choice.

  6. Remove the values of $attributes['auto_form_fields']['mail'] and $attributes['auto_form_fields']['alias'].

    The corresponding lines should look as follows:

    "alias" => Array(
            "type" => "list",
            "data" => Array(
                    "givenname",
                    "preferredlanguage",
                    "sn",
                ),
        ),
    

    and:

    "mail" => Array(
            "data" => Array(
                    "givenname",
                    "preferredlanguage",
                    "sn",
                ),
        ),
    
  7. Edit the value of $attributes['form_fields']['alias'] and add $attributes['form_fields']['alias']['type'] => "list":

    "alias" => Array(
            "optional" => true,
            "type" => "list"
        ),
    
  8. Add a new value $attributes['form_fields']['mail'] with the following contents:

    “mail” => Array(

    “optional” => true

    ),

    Important

    Remember to remove the aforementioned $attributes['auto_form_fields'] and add or chance the ``$attributes[‘form_fields’]` for all user types that have mail attributes.

  9. Exit the editor, saving your changes, and check the PHP syntax:

    # cd /usr/share/kolab-webadmin/
    # php -l sample-insert-user_types.php
  10. Apply the changes to the database:

    # php sample-insert-user_types.php

    Note

    You may have to log out and log back in of the Kolab Web Administration Panel for the changes to take effect.

Storage Tiering of the IMAP Spool

Using Cyrus IMAP partitions, a single IMAP server can hold multiple spools in which mailboxes reside.

A deployment can choose to have, for example, the INBOX and additional folders on fast, expensive storage, while an Archive folder may need to reside on slow, cheap storage.

The configuration in imapd.conf(5) would look like:

(...)
defaultpartition: default
partition-default: /path/to/fast/storage
partition-archive: /path/to/slow/storage
(...)

To have Archive folders for new users be created on the archive partition, use the autocreate_folders setting in kolab.conf(5) and adjust:

autocreate_folders = {
        'Archive': {
                'quota': 0
            },
        (...)
    }

to:

autocreate_folders = {
        'Archive': {
                'quota': 0,
                'partition': 'archive'
            },
        (...)
    }

Adding Domains

Kolab Groupware allows a single-domain setup to easily become a multi-domain setup, by adding additional domain name spaces through the web administration panel.

Two types of additional domain name spaces exist;

  1. An alias domain name space, that is an additional namespace for an existing domain name space.

    This type of domain name space is useful for additional domain name spaces that should end up with the same organization or group of accounts that already exists.

    For example, a company with domain name space “example.us” as its primary domain may want to allow email addresses for the domain name space “example.com” (alias) to be used as well.

  2. A new domain name space, that can be viewed as a new organization or group of accounts.

    This type of domain name space creates a new, isolated space in which the accounts for the domain name space(s) are to exist. It is isolated in the sense that none of the accounts in the new domain name space will (typically) ever know about any of the accounts in any of the other isolated domain name spaces (unless you make it specifically so).

    For example, a company “redhat.com” is definitively a different organization from “sco.com”, and no information or accounts should leak across the boundaries of each respective realm.

Adding an Alias Domain Name Space

  1. Login to the web administration panel as a global LDAP administrator (for example, as “cn=Directory Manager”, but you may have delegated this authority).

  2. Navigate to “Domains”.

  3. From the left-hand pane – the list of current parent domain name spaces –, select the domain to add an alias domain name space to.

  4. In the “Domain name(s)” field, click the “[+]” to append a form field to the list.

  5. Supply the new domain name space to add in the new form field appended to the list.

  6. Press “[Submit]”.

Adding a Parent Domain Name Space

When a new domain needs to be completely isolated from the existing domain, please be aware additional configuration is necessary in a variety of locations.

See Configuration Considerations for Multiple Domain Namespaces for more information on additional configuration considerations.

  1. Login to the web administration panel as the Directory Manager user (login username “cn=Directory Manager”).

  2. Navigate to “Domains”.

    The “Add Domain” form should be displayed.

  3. Fill out the domain name space, for example: “mynewdomain.org”.

  4. Click the “[Submit]” button.

The new domain has now been created in LDAP.

Configuration Considerations for Multiple Domain Namespaces

Suppose the following diagram illustrates a number of domain names in a Kolab deployment for a single organization.

digraph tree
{

    charset="latin1";
    fixedsize=true;
    node [style="rounded", width=0, height=0, shape=box, concentrate=true]

    "cn=kolab,cn=config" [shape=box] {
            rank=same
            "path-point-holding.inc" [shape=point]
            "dc=holding,dc=inc" [color=blue];
            "associateddomain=holding.inc" -> "dc=holding,dc=inc" [color=blue]
        }

    "cn=kolab,cn=config" -> "path-point-holding.inc" [arrowhead=none]

    "path-point-holding.inc" -> "associateddomain=holding.inc"

    "associateddomain=holding.inc" {
            rank=same
            "path-point-foo.inc" [shape=point]
            "associateddomain=foo.inc"
        }

    "associateddomain=holding.inc" -> "path-point-foo.inc" [arrowhead=none]
    "path-point-foo.inc" -> "associateddomain=foo.inc" {
            rank=same
            "path-point-bar.inc" [shape=point]
            "associateddomain=bar.inc"
        }

    "path-point-foo.inc" -> "path-point-bar.inc" [arrowhead=none]
    "path-point-bar.inc" -> "associateddomain=bar.inc"
}

This deployment has been set up to serve holding.inc, to which the additional alias domain name spaces foo.inc and bar.inc have been added later.

Holding corporation Holding, Inc. clearly uses three domain name spaces:

  1. holding.inc

  2. foo.inc

  3. bar.inc

Using the Postfix LDAP lookup tables configured by default, all recipient addresses in these domain namespaces are validated against dc=holding,dc=inc.

Now consider three different organizations use the same Kolab Groupware environment, such as might be the case with hosted, or in case Holding, Inc. wishes to maintain different, isolated trees for its subsidiaries.

digraph tree
{

    charset="latin1";
    fixedsize=true;
    node [style="rounded", width=0, height=0, shape=box, concentrate=true]

    "cn=kolab,cn=config" [shape=box] {
            rank=same
            "path-point-farmer.inc" [shape=point]
            "dc=farmer,dc=inc" [color=blue]
            "associateddomain=farmer.inc" -> "dc=farmer,dc=inc" [color=blue]
        }

    "cn=kolab,cn=config" -> "path-point-farmer.inc" [arrowhead=none]

    "path-point-farmer.inc" -> "associateddomain=farmer.inc" {
            rank=same
            "path-point-baker.inc" [shape=point]
            "dc=baker,dc=inc" [color=blue]
            "associateddomain=baker.inc" -> "dc=baker,dc=inc" [color=blue]
        }

    "path-point-farmer.inc" -> "path-point-baker.inc" [arrowhead=none]

    "path-point-baker.inc" -> "associateddomain=baker.inc" {
            rank=same
            "path-point-butcher.inc" [shape=point]
            "dc=butcher,dc=inc" [color=blue]
            "associateddomain=butcher.inc" -> "dc=butcher,dc=inc" [color=blue]
        }

    "path-point-baker.inc" -> "path-point-butcher.inc" [arrowhead=none]
    "path-point-butcher.inc" -> "associateddomain=butcher.inc"
}

While the LDAP lookup table for domain name spaces will succeed as expected, local recipient maps will not, as they have initially been configured to query the primary domain (and the primary domain only).

server_host = localhost
server_port = 389
version = 3
search_base = dc=example,dc=org
scope = sub

domain = ldap:/etc/postfix/ldap/mydestination.cf

bind_dn = uid=kolab-service,ou=Special Users,dc=example,dc=org
bind_pw = pass

query_filter = (&(|(mail=%s)(alias=%s))(|(objectclass=kolabinetorgperson)(...)))
result_attribute = mail

This is intentional - the alternative scenario creates a potentially disruptive amount of overhead, and eliminates option value for customers.

If the number of parent domain name spaces hosted is relatively static, we recommend considering the use of configured lookup tables per parent domain name space.

If the number of parent domain name spaces hosted in relatively dynamic, we recommend the use of dynamic search bases.

Should you choose to use a dynamic configuration value for search bases, please take on the following points:

  • A search base template needs to be specified for all levels of domains and sub-domains.

    The example.org domain name space can be resolved to dc=example,dc=org using a search base template of dc=%2,dc=%1, but example.org.uk can not be resolved to dc=example,dc=org,dc=uk using that same template.

  • The search base template sets a standard format for root dns, that will therefore need to apply to all domains throughout the environment. If, for example, an organization Plumbers, Inc. wants you (the service provider) to synchronize their in-house LDAP tree with root dn o=plumbers,c=de, you will need to provide an extra set of custom lookup tables.

  • To reduce overhead, the domain setting should include only relevant and applicable domains (for the search base template to work).

    With domains example.org.uk and example.org, the triplet should be queried first, with domain being restricted to use a filter of:

    (&(associateddomain=%s)(associateddomain=*.*.*))
    

Example Templated Search Base

The following depicts an example /etc/postfix/ldap/hosted_triplet_local_recipient_maps.cf:

server_host = localhost
server_port = 389
version = 3
search_base = dc=%3,dc=%2,dc=%1
scope = sub

domain = ldap:/etc/postfix/ldap/hosted_triplet_mydestination.cf

bind_dn = uid=kolab-service,ou=Special Users,dc=example,dc=org
bind_pw = pass

query_filter = (&(|(mail=%s)(alias=%s))(|(objectclass=kolabinetorgperson)(...)))
result_attribute = mail

The following depicts an example /etc/postfix/ldap/hosted_duplet_local_recipient_maps.cf:

server_host = localhost
server_port = 389
version = 3
search_base = dc=%2,dc=%1
scope = sub

domain = ldap:/etc/postfix/ldap/hosted_duplet_mydestination.cf

bind_dn = uid=kolab-service,ou=Special Users,dc=example,dc=org
bind_pw = pass

query_filter = (&(|(mail=%s)(alias=%s))(|(objectclass=kolabinetorgperson)(...)))
result_attribute = mail

Example Postfix Configuration

# postconf local_recipient_maps
local_recipient_maps = \
    ldap:/etc/postfix/ldap/hosted_triplet_local_recipient_maps.cf \
    ldap:/etc/postfix/ldap/hosted_dupplet_local_recipient_maps.cf