Iptables dependency: why we got there and how we got out

In our first ever release, we relied on iptables for port forwarding so that a regular user process could have access to privileged ports (ie, ports < 1024 can be bound only by root). Eg, our IMAP server would bind to port 7143, and iptables would be setup to forward port 143 to 7143.
The upside of port forwarding was that Zimbra server process did not have to run as root. We made the decision to use iptables a long time ago assuming Zimbra systems would only be used on Linux and that all Linux kernels would have the iptables modules available – boy were we wrong on both counts.

The downside of relying on port forwarding have been many – every platform offers a different port forwarding solution, it may add overhead, changing your IP address requires more steps now, etc. In addition, it conflicted with the Linux firewall, which made it hard for Zimbra to co-exist with other services.

ZCS 3.0.0_M2 onwards (coming soon to a nearby website), we now rely on some Java native (JNI) code that calls setuid() to drop privileges after starting out as root. Hasta la vista iptables dependency.

Here is how the new drop priveleges mechanism works:

– We have a small conservatively coded C program “zmtomcatstart” that is started as root which in turn fork()/exec()s the Tomcat Java process.

– The Java process also starts out running as root. Minimal Tomcat initialization and Zimbra server initialization are then run as root. Tomcat initialization binds to ports 80 and 443. The minimal part of Zimbra initialization is to bind to all the privileged ports configured (IMAP4/S, POP3/S). After the ports are bound, the Java process calls setuid() and friends through some JNI code to drop privileges and become a “zimbra” user process.

– The “zimbra” user is allowed to start tomcat as root. This is accomplished with sudo. The zimbra user sudo’s the zmtomcatstart program. zmtomcatstart sanitizes the Java arguments before passing them along so no arbitrary code belonging to the “zimbra” user can be loaded as root (drop LD_LIBRARY_PATH, do not allow arbitrary classpath, etc; look at the source for the details). Sanitizing the environment and the Java arguments ensures that a compromised “zimbra” user does not lead to a local root exploit.

Our installer (which edits the sudoers file), and our tomcat start/stop wrapper script both together hide all this complexity from you. The under the hood details described above will be of interest to you if you are porting Zimbra to other platforms. The new native code to do all this has been tested on a few flavors of Linux and on Mac OSX – the latter means the code should be BSD friendly as well.

Comments are closed.