Cyrus IMAP Murder Topologies

A Cyrus IMAP Murder is an aggregate of multiple Cyrus IMAP servers, providing transparent access to mailboxes throughout the murder.

For the purpose of this documentation, the following terminology needs to be outlined:

frontend

A Cyrus IMAP frontend is a functional component that receives connections from clients, and proxies those to the backend on which the relevant mailbox resides.

Note

Please note that a frontend proxies the connection on a per-mailbox level (as needed). As such, the client connection is terminated at the frontend, the frontend interprets the protocol, and decides what backend to create a new connection to (if needed).

backend

A Cyrus IMAP backend is the functional component that actually holds and maintains a mail spool containing mailboxes and messages.

mupdate

To aggregate various backends, compose a single authoritative list of mailboxes available throughout the murder, and communicate these to the frontends, a Cyrus IMAP murder requires a server to be the mupdate master.

Discrete Murder

A Cyrus IMAP Discrete Murder topology separates frontends from backends.

As illustrated in the next diagram, the Mail User Agent (MUA) connects to a frontend, and is proxied to backend1 for the user’s own mailbox, or backend2 for a shared mailbox that resides on that server.

digraph murder_discrete {
        "MUA" -> "frontend" [ label = "client connection" ];
        "frontend" -> "INBOX" [ label = "proxied connection" ];
        "frontend" -> "shared/memo" [ label = "proxied connection" ];
    }

In such a deployment scenario, the daemon responsible for synchronizing changes made to LDAP with IMAP, called kolabd, recognizes the topology of a murder and assures the LDAP mailHost attribute is set to the correct value – the FQDN [1] of the backend system the mailbox is hosted on.

Deployment Considerations for a Discrete Cyrus IMAP Murder Topology

In a Cyrus IMAP Discrete Murder topology, inbound as well as outbound [2] email messages need to be routed to and from the IMAP backend server, that hosts the mailbox to which the email is to be delivered.

Similarly, backends need to be configured such that they do not find themselves authoritative for the entire domain – as part of the recipients is hosted on other backends – and use the ‘’smart’’ internal MTA to relay messages to.

digraph murder_discrete {
        subgraph cluster_backend1 {
                label = "backend1";
                "John";
            }

        subgraph cluster_backend2 {
                label = "backend2";
                "Jane";
            }

        "MTA (Internal)" -> "John", "Jane" [label="inbound"];
        "John", "Jane" -> "MTA (Internal)" [label="outbound"];
        "MTA (Internal)" -> "MTA (Internet)" [dir=both];
    }

The MTA (Internal) can use a lookup table for local recipients (valid recipient addresses in any of the mydestination or relay_domains domain name spaces), that routes (instead of delivers) the message through to the correct backend (called a transport map), using the LDAP mailHost attribute value for entries.

To configure such transport map lookup table, adjust the contents of the following snippet to suite your deployment and save it to /etc/postfix/ldap/transport_maps.cf – this file could exist already, likely with a result_attribute value of mail, and a result_format value of lmtp:unix:/var/lib/imap/socket/lmtp:

server_host = ldap.example.org
server_port = 389
version = 3
search_base = dc=example,dc=org
scope = sub
domain = example.org
bind_dn = uid=kolab-service,ou=Special Users,dc=example,dc=org
bind_pw = Welcome2KolabSystems
query_filter = (&(mail=%s)(|(objectclass=kolabinetorgperson)(objectclass=kolabsharedfolder)))
result_attribute = mailHost
result_format = smtp:[%s]:25

Note

By the time the MTA (Internal) queries the transport map, any secondary email address should have already been translated to a final recipient email address (primary email address), for which Kolab uses virtual alias maps by default.

The MTA (Internal) now needs to be configured to use this transport map:

# postconf -e transport_maps=ldap:/etc/postfix/ldap/transport_maps.cf
# service postfix reload

The MTA (Internal) will now attempt delivery for John to backend1, and for Jane to backend2.

The backends’ MTA now needs to be configured to consider part of mydestination local – the local mailboxes – and part of mydestination remote – the mailboxes on the other backend(s). This consists of three parts:

  1. Setting the local_recipient_maps, line-breaks for legibility:

    server_host = ldap.example.org
    server_port = 389
    version = 3
    search_base = dc=example,dc=org
    scope = sub
    domain = example.org
    bind_dn = uid=kolab-service,ou=Special Users,dc=example,dc=org
    bind_pw = Welcome2KolabSystems
    
    query_filter = (& \
            (|(mail=%s)(alias=%s)) \
            (|(objectclass=kolabinetorgperson)(|(objectclass=kolabgroupofuniquenames)(objectclass=kolabgroupofurls))(|(|(objectclass=groupofuniquenames)(objectclass=groupofurls))(objectclass=kolabsharedfolder))(objectclass=kolabsharedfolder)) \
            (mailHost=<%= fqdn -%>) \
        )
    
    result_attribute = mail

    Note

    The most important to take away from this is to make local recipient maps for the backend only include those LDAP entries for which the mailHost attribute is the same value as the system’s FQDN.

  2. Setting the transport_maps, in (for example) /etc/postfix/ldap/transport_maps.cf:

    server_host = ldap.example.org
    server_port = 389
    version = 3
    search_base = dc=example,dc=org
    scope = sub
    domain = example.org
    bind_dn = uid=kolab-service,ou=Special Users,dc=example,dc=org
    bind_pw = Welcome2KolabSystems
    query_filter = (&(|(alias=%s)(mail=%s))(objectclass=kolabinetorgperson)(mailhost=<%= fqdn -%>))
    result_attribute = mail
    result_format = lmtp:unix:/var/lib/imap/socket/lmtp

    Note

    Here too the most important part is to only transfer over the local LMTP socket, only those messages intended for recipients with mailboxes locally hosted – Those LDAP entries for which the mailHost attribute is the same value as the system’s FQDN.

    For delivery to shared folders, an additional lookup table for transport maps is needed (save as /etc/postfix/transport):

    shared@example.org  lmtp:unix:/var/lib/imap/socket/lmtp
    

    Execute the following commands to activate:

    # postmap /etc/postfix/transport
    # postconf -e transport_maps=ldap:/etc/postfix/ldap/transport_maps.cf,hash:/etc/postfix/transport
    # service postfix reload
  3. Setting the relayhost, and redirect all mailboxes for locally hosted domains not hosted on the local server to the smart host(s):

    # postconf -e local_transport=relay:[smtp.example.org]:25
    # postconf -e relayhost=[smtp.example.org]
    # service postfix reload

Footnotes