|A/I Orange Book (1.0): An how-to for the realization of a resilient network of self-managed servers|
In the introduction we stated that one of the main aims of this project was to split up mailboxes on N servers. This essentially means splitting the mailbox-related services as SMTP, IMAP and webmail. Luckily the SMTP protocol and the flexibility of Postfix configuration allow us to implement this feature without too many complications.
The choices we made for the SMTP services configuration were the following (along the guidelines described in chapter 1):
storing each user's mailbox in only one of the N servers (we can change this location but it has to be one and only one);
the N servers have to be set as MX of the domain (or domains) with the same priority as they will redirect the traffic to the correct server internally.
Since the users database is replicated on every server, each one of them knows which is the final target server of a particular email address. Through this information it is possible to build the appropriate transport tables.
The preceding picture shows the flow of incoming mail inside our network. First of all the remote SMTP client chooses randomly one of the MX servers with the same probability. The incoming mail is therefore directed and received by one of the N servers. The incoming mail can then follow two different patterns, whether the receving server is the host where the mailbox is located (the final target server) or not. In the first case the message is not forwarded to other servers and is delivered in the user local maildir. In the second case the mail is queued and forwarded to the final target server (through the VPN). In both cases the message is checked by antispam controls only once.
As regards the second case above detailed, one could thing this solution is not very wise in terms of banwidth usage. In fact the average probability of a message arriving to a server it is not destined to (and then forwarded to the correct final target server) can be represented as N-1/N, tending to 1 with N sufficiently high. . However, it must be considered that this solution offers a high resistance to temporary network lapses and ensures an even splitting of mailboxes on the various servers. This could be a sufficient advantage, balancing out the problem explained above, also considering that the mail traffic is not particularly intense (our yearly statistics of some time ago gave us a figure of 1 message a second, with peaks up to 10 messages a second).
Postfix is configured on each server to accept incoming mail for all of the virtual users. It then checks out an appropriate transport table and decides whether to deliver the message locally or to forward it to the final target server.
Since we can forward the message through our VPN, we can also spare another SSL negaotiation between the ring servers. Equally, the antivirus scanning can be carried out only once, when the message drops on the doorstep of the first server of our network. This is why we can configure two different instances of the smtpd listening on both interfaces (the external one and the VPN one, adding a couple of lines to /etc/postfix/master.cf:
# smtp on the external interface 192.168.1.1:smtp inet n - - - - smtpd -o options... # smtp on the VPN interface 172.16.1.1:smtp inet n - - - - smtpd -o different options...
By properly changing the options, we can fine tune the smtp service. For example if the antispam filter were to be setup in the main.cf, the VPN instance of the smtpd could have the following options to disable TLS communications and antispam filtering, since both tasks are already carried out by the smtpd on the external interface and we don't need to check out messages twice.
-o smtpd_use_tls=no -o content_filter= -o local_recipient_maps= -o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=172.16.0.0/16
Another possibility would be to have two different postfix daemons (and two different queues) for internal and external traffic, doubling the /etc/postfix and /var/spool/postfix directories  The scheme is the same: a two-layers mechanism (three if we consider the antispam filter as an autonomous SMTP system) where one layer handles incoming and outgoing mail (and possibly antispam and other filters) and the other only handles local delivery. The particular relay queue handling by Postfix latest versions should ensure a good enough internal throughput also with a single instance, even if the external queue is stuffed with messages. 
LDAP maps should allow to recognize all virtual users (in local_recipient_maps), to forward them to the correct final target server (in trasport_maps) and finally to deliver the message into the correct mailbox (in virtual_mailbox_maps) and perhaps alias resolution (in virtual_alias_maps).
Let's see how maps are actually defined:
in transport_maps - &(mail=%s)(objectClass=virtualMailUser)(!(host=server1)) -> host
this map works because in master.cf one can define SMTP transports with the name of the different servers and hard-coded destinations. For example the server1 transport that is defined in all servers apart from server1 itself will be the SMTP transport with a predefined destination (server1-vpn). In this way we can fully customize it with fine tuning of the concurrency, disabled TLS and other optimizations. Of course on "server1" the connection coming from the VPN will have the antispam filter disabled so that messages are only checked once.
ldaptransport_server_host = localhost ldaptransport_server_port = 389 ldaptransport_scope = sub ldaptransport_bind_dn = "cn=manager, o=anarchy" ldaptransport_bind = no ldaptransport_lookup_wildcards = no ldaptransport_search_base = ou=People, dc=infra, dc=org, o=anarchy ldaptransport_query_filter = (&(mail=%s)(!(host=amnistia))) ldaptransport_result_attribute = host ldaptransport_result_filter = relay:[%s-vpn]
Here we use also the result_filter option that allows us to modify the result of the LDAP query after having received it from the LDAP server.
in virtual_mailbox_maps - &(mail=%s)(host=server1)(objectclass=virtualMailUser)(mailMessageStore=*) -> mailMessageStore
the mailbox address map forwarding them to the destination mailbox. The paths returned by the query are considered relatively to the /home/mail directory and generally include a directory with the domain name and one with the mailbox name.
ldapmailbox_server_host = localhost ldapmailbox_server_port = 389 ldapmailbox_scope = sub ldapmailbox_bind_dn = "cn=manager, o=anarchy" ldapmailbox_bind = no ldapmailbox_lookup_wildcards = no ldapmailbox_search_base = ou=People, dc=infra, dc=org, o=anarchy ldapmailbox_query_filter = (&(mail=%s)(objectclass=virtualMailUser)(mailMessageStore=*)) ldapmailbox_result_attribute = mailMessageStore
in virtual_alias_maps - &(mailAlternateAddress=%s)(objectclass=virtualMailUser) -> mail
the local alias map specifying which destination mailbox the alias address has to be forwarded to.
ldapalias_bind_dn = cn=manager,o=anarchy ldapalias_bind = no ldapalias_search_base = ou=People, dc=infra, dc=org, o=anarchy ldapalias_query_filter = (&(mailAlternateAddress=%s)(objectclass=virtualMailUser)) ldapalias_result_attribute = mail ldapalias_lookup_wildcards = no
Antispam services are fairly easy and quite effective for the time being. We installed Postgrey on all servers managing the mail service. It is a greylisting system, that works as follows: each message received for the first time (characterized by the smtp server / sender / destinatary triple information) is bounced with a temporary delivery error for 60 seconds; after N messages delivered successfully for a particular triple this is passed onto a "whitelist" so that for a certain period of time there will be no further delays. For normal SMTP servers this temporary error is not a problem and the message is queued for a second attempt at delivery shortly after. The mechanism works because most viruses and spam tools do not use a true SMTP server and do not have the possibility to hold messages in a queue, so they generally try to deliver messages only once. Of course this means that the delivery of the message is not "instantly" carried out, but it needs a small period of time to be processed (we are talking of minutes anyway). In any case we think this system is a good compromise in terms of performance, considering the efficiency that greylisting has showed.
Postgrey is installed as a policy_service in the Postfix instance accepting mail from the outside (on the public IP):
smtpd_recipient_restrictions = ..., check_policy_service inet:127.0.0.1:60000, ...
We have been using Postgrey for some time now and we have not noticed any problem, while it has proven excellent as an antispam protection (for the moment). In any case it could turn out useful to check out the /etc/postfix/whitelist_clients file and add in it the mail servers that most frequently contact you, so as to make delivery quicker and to avoid that the temporary database used by Postgrey grows too much.
Summing up to Postgrey, our main antispam mechanism is amavis-ng, that we use disabling its internal antivirus scanning system (it is far too heavy on our CPUs shoulders). Therefore, this software is for us only a SMTP wrapper of the SpamAssassin analysis module. We put up with the absence of an antivirus with a series of regexp that checks out the body of the incoming messages, a system directly integrated into Postfix mime_header_checks and body_checks: this set of regexp is regularly update on the site www.securitysage.com and includes the most common virus fingerprints as well as other very useful rules to bounce back a good part of the spam even before it gets examined by Amavis.
Amavis is installed as content_filter in the Postfix instance managing mail coming from the outside (the public IP) and it is configured to forward the approved messages to the backend server (the one listening on localhost interface).
We advise to read as a further in depth study the article High Capacity Email that can be found at http://www.vergenet.net/linux/mail_farm/. The article also details the best ways to redistribute the load on several servers weighing the mailboxes with high and low traffic.
You can read further on the topic at http://advosys.ca/papers/postfix-instance.html, and in the official Postfix 2.1 documentation at http://www.postfix.org/FILTER_README.html
Check also the debate at http://www.postfix.org/ADDRESS_CLASS_README.html