1 - Overview ============ This document has the intent of providing simple and easy to implement instructions to deliver mail from Postfix to the CMU Cyrus IMAP/POP server, which is available from: http://asg.web.cmu.edu/cyrus/ Cyrus is a sealed server IMAP/POP implementation, Cyrus user database and mailbox spool are not shared with the usual Unix user database and mailboxes. To deliver mail to Cyrus mailboxes Postfix has to interface with the Cyrus server which does actual delivery. There are two main ways of delivering mail from postfix to Cyrus: - via LMTP protocol - via the Cyrus deliver program The first method apply to Cyrus 2.x, if you are struck with an older version of Cyrus and cannot upgrade you must go through the deliver program. 2 - Delivering mail via the LMTP protocol ========================================= A description of the LMTP protocol and other examples of using LMTP to deliver mail are documented in the LMTP_README file. 2.1 - LMTP over UNIX-domain sockets ----------------------------------- This method allows delivery from postfix to a Cyrus server running on the same machine. Check the /etc/cyrus.conf file for a section that looks like this: SERVICES { ... lmtpunix cmd="lmtpd" listen="/var/imap/socket/lmtp" prefork=1 ... } If you modified the Cyrus configuration restart it, then as root issue the following command: postconf -e "mailbox_transport=cyrus:unix:/var/imap/socket/lmtp" In this case, the Postfix local delivery agent expands aliases and .forward files, and delegates mailbox delivery to the Cyrus lmtpd server via the socket "/var/imap/socket/lmtp". NOTE: Check that the path specified in cyrus.conf is the same as in the postconf command. Make sure that both the user id that Cyrus runs under and the Postfix user id can access the socket "/var/imap/socket/lmtp". While this is implied by the example above, it is often overlooked and so warrants emphasis. 2.1.1 - Chrooted setup ---------------------- There is no good reason to run the Postfix LMTP client chrooted, but in case you still would do that, remember that postfix will consider the socket path relative to the Postfix queue directory (typically, /var/spool/postfix). In this case create the directories /var/spool/postfix/extern, user root group root and mode 755 /var/spool/postfix/extern/cyrus, user cyrus group postfix and mode 750 modify /etc/cyrus.conf so the lmtpunix line looks like lmtpunix cmd="lmtpd" listen="/var/spool/postfix/extern/cyrus/lmtp" prefork=1 Restart Cyrus, then as root issue the following command: postconf -e "mailbox_transport=cyrus:unix:extern/cyrus/lmtp" Note that the socket name is a relative pathname. This will work even if you later decide that you no longer wish to run postfix LMTP client chrooted. 2.2 - LMTP over TCP socket ----------------------------------------------------- This option is the most powerful and complex to set up, you might want to do this if the Cyrus server and the postfix server are not on the same machine. Cyrus LMTP server will ask for authentication by default when used over a TCP socket, with Cyrus 2.1.x it is possible to disable authentication, but this does not seem to be a good idea, unless you are certain that there is no way of sending unauthorized mail to Cyrus. SASL authentication concepts can be difficult to understand, so if you don't need TCP delivery, just don't use it. 2.2.1 - LMTP over TCP sockets (SASL) ---------------------------------- In the following example "cyrus.example.com" is the name of the Cyrus IMAP/POP server. It is important that you use this name consistently in the Postfix configuration settings. The first approach uses the PLAIN authentication mechanism (see the Cyrus SASL documentation.) 2.2.1.1 - Configuring Cyrus to receive mail ------------------------------------------- your /etc/cyrus.conf should look like: SERVICES { ... lmtp cmd="lmtpd" listen="lmtp" prefork=1 ... } Comment out the lmtpunix line if it is present and you don't need it, if your server is very busy change 'prefork=1' with a bigger number, you might also want to add 'maxchild=' to the end of the line to avoid the server to be overloaded in this case you would have to change master.cf to match the number of children, look for the "cyrus-inet" line and substitute the last dash "-" with the same number you used for Cyrus. You can also tell the server to listen only on a specific address writing: lmtp cmd="lmtpd" listen="cyrus.example.com:lmtp" prefork=1 You can use localhost if you don't want other systems connecting to yours, but in this case it will be easier to use an UNIX-domain socket. in your /etc/services you should have the line: lmtp 2003/tcp This setting is the Cyrus default, but can choose a different port than 2003, and even use a different name than "lmtp", just change the occurrence of "lmtp" in the listen="..." and in the postconf command at the end of this section. 2.2.1.2 Configuring authentication ---------------------------------- Creating users for Cyrus depends on which method of SASL authentication you use, please also check Cyrus documentation for this. The two main method of authentication are auxprop based (sasldb) or saslauthd based You can use the cyrus user, which you should already have configured, but it would be better to create an user that can only send mail via LMTP. To do this create the user lmtpuser and set a password for it, then add to /etc/imapd.conf the line lmtp_admins: lmtpuser Some older version of Cyrus do not support "lmtp_admins" as a setting in imapd.conf, use "admins" instead. The following example uses the auxprop sasldb to hold usernames and passwords for Cyrus. saslpasswd2 -c -u cyrus.example.com lmtpuser If you encounter difficulties with "lmtpuser" not being permitted to authenticate to the LMTP server, try the above command with the un-qualified hostname: saslpasswd2 -c -u cyrus lmtpuser Also make sure the Cyrus user has read permission of the SASL database, check your SASL documentation for this. If your system uses saslauthd it might be as simple as creating an lmtpuser entry in /etc/passwd, or it might involve creating an user in LDAP depending on how you configured authentication on the system. Incidentally, it is very likely that the Cyrus server and the Postfix server will need to use the same SASL backend databases (e.g., auxprop or saslauthd.) Currently it is not possible to assign different SASL backends for different Cyrus services. If this LMTP connection is made over an exposed network, you should protect the "lmtpuser" password using encryption. Saslauthd, being a proxy authentication service needs to receive the plain-text user password to work, this means only TLS (SSL) or STARTTLS can be used in conjunction with it. Since none of these encryption methods are available for LMTP, if you need to encrypt your LMTP connections, you will very likely have to use auxprop throughout. Compile Postfix with MD5 (CRAM or DIGEST) password support. See SASL_README for more details. Then edit the cyrus-inet entry in /etc/postfix/master.cf, changing -o lmtp_sasl_security_options=noanonymous to read -o lmtp_sasl_security_options=noanonymous,noplaintext On the Cyrus host you should also set: /etc/imapd.conf: lmtp_allowplaintext: no or change the "sasl_minimum_layer" parameter. Create the file /etc/postfix/cyrus_lmtp_sasl_pass be sure it is owned by root and not readable by others, it should contain the line cyrus.example.com lmtpuser:PASSWORD where PASSWORD is the password for lmtpuser. Now run postmap /etc/postfix/cyrus_lmtp_sasl_pass 2.2.1.3 - Route mail to Cyrus ----------------------------- Restart the Cyrus server to apply all changes, if you modified master.cf restart postfix as well, then enable the whole thing by issuing: postconf -e "mailbox_transport=cyrus-inet:inet:cyrus.example.org:lmtp" With the above settings, the Postfix local delivery agent expands aliases and .forward files, and delegates mailbox delivery to the Cyrus LMTP server. Postfix makes a connection to port 2003 on the Cyrus host, subsequently transmitting the message to the lmtpd server managed by the Cyrus master process. 2.3 - LMTP over TCP sockets (non-SASL) -------------------------------------- See the Cyrus lmtpd(8) man page to verify that the version you have supports the "-a" option. If it does not, then you must use SASL. This is similar to 2.2.1.1 above, but /etc/cyrus.conf should be configured like this: SERVICES { ... lmtp cmd="lmtpd -a" listen="lmtp" prefork=1 ... } Skip 2.2.1.2 and execute the same instructions described in 2.1.3. NOTE: Consider this approach if and only if this particular host does not allow direct user logins or user-controlled processes. Otherwise, this LMTP server may be abused! If the Cyrus lmtpd service is to listen on a network other than the local loop-back (127.0.0.1), be sure to install Cyrus with tcp_wrappers support. Then use tcp_wrappers to only allow access to the "lmtp" service from trusted hosts. Otherwise, this LMTP server may be abused! Section 7 contains an example using tcp_wrappers. For the configuration above, replace "deliver" with "lmtp" in the /etc/hosts.allow shown in that example. 3 - Local delivery via pipe to Cyrus deliver program ==================================================== The oldest way to send mail to Cyrus is using the Cyrus deliver program you can enable it by issuing as root one of these commands: for Cyrus 2.x use: postconf -e "mailbox_transport=cyrus-deliver" for Cyrus 1.6.x use: postconf -e "mailbox_transport=cyrus-old" There is not much to say about this, the only possible change is if you used an user different than "cyrus" when installing the Cyrus software, in this case you need to modify the corresponding entry in master.cf. NOTE: There is no known way of making this setup work chrooted, short of installing Cyrus into the chroot, which is insane. 4 - Local delivery via Cyrus deliver program invoked by procmail ================================================================ You can install procmail and have procmailrc invoke the Cyrus deliver program to actually deliver mail. But remember that Cyrus includes it's own filtering language 'sieve' that can easily replace procmail. The main problem in doing this would be having the Cyrus deliver program invoked as user cyrus, while preventing delivery in non-owned mailbox. 5 - Alternative delivery methods ================================== The previous Sections showed how to make the local(8) delivery agent deliver mail to Cyrus. It is also possible to route mail to Cyrus using transport maps or bypassing local(8). 5.1 - Using transport maps -------------------------- For the transport map examples see LMTP_README files, you have to consider that the provided Cyrus LMTP based entries in master.cf use 5.2 - Using fallback_transport ------------------------------ If you replace mailbox_transport in the examples above with fallback_transport, mail that resolves as local (domain is listed in $mydestination) is given to the Postfix local delivery agent. The Postfix local delivery agent processes aliases and .forward files, and delivers to /var[/spool]/mail/$user for users that have a UNIX account. Mail for other local users is delegated to the Postfix LMTP client which then sends it to the non-Postfix LMTP server. 5.3 - Using local_transport --------------------------- If you replace mailbox_transport in the examples above with local_transport, mail that resolves as local (domain is listed in $mydestination) is directly given to the Postfix LMTP client which then sends it to the non-Postfix LMTP server. The mail is not processed by the Postfix local delivery agent; therefore aliases and .forward files are not processed. You are probably not interested in .forward files, since Cyrus sieve is a far more powerful tool, but you might want to replace your alias maps with virtual_alias_maps. virtual_alias_maps, which are documented in the virtual(5) man-page have almost nothing to do the virtual delivery agent documented in virtual(8) man-page. They are just a more general alias map which is processed by cleanup instead of local, it should be fairly easy to migrate from one to the other. 6 - Single Instance Message Store =================================== Since 1.6.22, Cyrus has had the feature that if a message containing multiple recipients is received and all these recipients were on the same Cyrus partition, only one instance of this message would be written to the file system. The other recipients would then see a hard link of this single instance. Depending on your user base, this can be a considerable disk space gain. To achieve this setup we have to overcome the single user per message limitation of postfix local(8) delivery agent, delivering directly to Cyrus. Bypassing local has the side effect of ignoring your alias_maps and user .forward files. You can use a transport map or you can replace local_transport, see 5.3 above. Once you have read that simply issue: postconf -e "local_transport=cyrus:unix:/var/imap/socket/lmtp" or postconf -e "local_transport=cyrus:unix:extern/cyrus/lmtp" or postconf -e "local_transport=cyrus-inet:inet:cyrus.example.com:lmtp" depending on which delivery method you chose. You might also need to change _destination_recipient_limit to a bigger value, if unset it is taken from $default_destination_recipient_limit, which is set at 50 by default. issue: postconf -e cyrus_destination_recipient_limit=300 or postconf -e cyrus-inet_destination_recipient_limit=300 depending on which delivery method you chose. The 300 was arbitrarily chosen for this example. Be sure to pick a number that is appropriate for the capabilities of your hardware. The bigger the number, the more you can leverage the single instance message store. However, if it is too big the LMTP server will need too much time to deliver a message and Postfix will experience timeout errors. Choose this value very carefully. If you use a transport map and different transport for each Cyrus server you should set the _destination_recipient_limit for each transport defined in master.cf 7 - Older Cyrus versions ======================== First of all, if you are using a Cyrus 2.x version prior to 2.1.4, you should really consider upgrading. There have been numerous bug fixes and performance improvements since the early 2.0 releases. Further back, 1.6.24 was the last pre-2.x production release. (Actually, there was a 1.6.25-BETA, but it is uncertain whether this will be released officially as CMU is now focusing support on the 2.1.x branch.) The following discussion touches on how to configure the Postfix LMTP facilities with Cyrus 1.6.24. (deliver based configuration is shown in section 3) One of the significant differences between Cyrus 1.x and 2.x is the inclusion of the "master" process in 2.x. This "master" process is responsible for running the various components of Cyrus, such as imapd, pop3d, and lmtpd. Prior to 2.x, these services were managed by inetd, the Internet services daemon. To utilize LMTP delivery with Cyrus 1.6.24, the first thing to do is configure inetd. This involves the following file edits: /etc/services: lmtp 2003/tcp /etc/inetd.conf: lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/cyrus/bin/deliver -e -l /etc/hosts.allow: deliver : localhost : ALLOW deliver : ALL@ALL : DENY The "/usr/sbin/tcpd" is from the tcp_wrappers package, which is discussed in the example "LMTP over TCP sockets, using hosts.allow." It is important that you wrap this LMTP port to protect it from unauthorized access. On some systems, tcpd is built into inetd, so you do not have to specify tcpd in the inetd.conf file. Instead of tcpd/inetd, xinetd can do a similar job of logging and access control. Now comes the Postfix configuration. Basically, the Cyrus 2.x discussions regarding LMTP delivery over TCP are also applicable to Cyrus 1.x, with the exception of the /etc/cyrus.conf file. To route mail to setup like this follow the instructions in 2.2.1.3 above or use one of the alternative methods described in section 5. If you have read the discussion covering the Cyrus 2.x installation, you will notice the one significant difference with the Postfix configuration is the lack of mention of the UNIX-domain sockets. That is because delivery over UNIX-domain sockets is new with Cyrus 2.x, yet another reason to upgrade. :-) 8 - Miscellanea =============== 8.1 - recipient_delimiter ------------------------- Some example for Cyrus or contributed documentation refer to plus addressing. Plus addressing allows direct delivery to a particular mailbox (other than an INBOX). This is done by sending mail to user+mailbox@mail.example.com. to make this feature work with Postfix you need to define the recipient_delimiter parameter. postconf -e "recipient_delimiter=+" 8.2 - posting to shared folders ------------------------------- With Cyrus shared folders are folders placed at the root of the Cyrus mail directory, by contrast user folders are placed in user directory under the root. To have mail sent directly to these folders from postfix you need to configure a special username in /etc/imapd.conf, eg: postuser: bb Mail sent to bb+folder@mail.example.com will end in the shared folder named "folder". You have to configure recipient_delimiter as detailed above. You will probably have to create such folders and set the correct permissions on it (see Cyrus documentation) You can create aliases that point to bb+folder if you wish. 8.3 - Upper/Lower case email address ------------------------------------ Cyrus LMTP is case sensitive when parsing user and mailbox names. Postfix consider upper and lower case equivalent in mail addresses, but preserves the case when sending to cyrus. This mean postfix will accept mail for both User@example.com and user@example.com, and happily send it to cyrus unchanged. This is the correct behaviour according to rfc822. This might result in cyrus lmtpd refusing mail for apparently legitimate users, and mail getting struck in postfix queue. To solve this issue you could add the following line to /etc/imapd.conf: lmtp_downcase_rcpt: yes Note that the lowercasing applies only to the part before the recipient delimiter "+", since imap mailbox names are case sensitive. 9 - configuration examples ========================== 9.1 - master.cf ----------------- since snapshot 2.0.16-yyyymmdd Postfix includes the following definitions in master.cf, note that the "cyrus" entry changed from the pipe method to the LMTP over UNIX domain socket. # for LMTP delivery over UNIX-domain socket cyrus unix - n n - - lmtp -o lmtp_cache_connection=yes # for LMTP delivery over TCP socket cyrus-inet unix - - n - - lmtp -o lmtp_cache_connection=yes -o lmtp_sasl_auth_enable=yes -o lmtp_sasl_password_maps=hash:/etc/postfix/cyrus_lmtp_sasl_pass -o lmtp_sasl_security_options=noanonymous You might have to change the map type from hash to something else, depending on what your system uses. Use "postconf -m" to find out what map types are supported. # for delivery via cyrus deliver program # cyrus 1.6 compatible cyrus-old unix - n n - - pipe flags=R user=cyrus argv=/usr/cyrus/bin/deliver -e -m ${extension} ${user} # cyrus 2.1 compatible cyrus-deliver unix - n n - - pipe user=cyrus argv=/usr/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}