Zimbra on a VPS: Tuning April 16, 2008 1 Comment
Out of the box, Zimbra is tuned for a fairly beefy machine. Zimbra will easily consume 1.5GB of ram with default settings. Some of these changes mean turning off a few Zimbra features and there are quite a few ways to do this, so you will need to evaluate memory usage given your own situation. If you simply can’t live without *all* the Zimbra features or intend to support a larger (> 15) number of users, my only suggestion is to get more ram ;). But if you are willing to make some compromises, please read on.
The changes below are ones that I found to be a reasonable compromise. With a few easy changes, you can bring the memory consumption way down, so that it will hum along just fine on a server with ~512MB of ram. Of course this reduces the amount of users that your server can support, but I’ve found that for a small installation of around 15 users, these settings will work just fine and give you the same performance as the out-of-the-box config with gobs of RAM. The server I have been testing with has 560MB of ram, and after tuning consumes all the ram and only about 80MB of swap on average. You will find that if you don’t make changes similar to the ones suggested below with ~512MB of ram, your Zimbra install will slowly creep up its consumption of swap and your Zimbra install will start to crawl. Also, > 512MB seems to be about the minimum amount of ram to get a useful/zippy server.
After tuning: Typical memory usage
Mem: 553160k total, 538580k used, 14580k free, 14616k buffers Swap: 1023992k total, 83484k used, 940508k free, 98672k cached
Tuning Steps
- Turn off anti-virus
- Turn off spell checking service
- Reduce the # of ‘amavisd’ (spam/virus) daemons
- Turn off unused services
Turning off Anti-virus
Although anti-virus checking is a “nice-to-have” feature, it is one of the largest consumers of ram on your Zimbra install. IMHO, virus checking (clamav) is necessary with a large number of users, but I’ve found that I can easily live without this feature given it’s cost to memory and the size of my installation.
Turning off spell checking service
For me, spell checking is another service that I’ve found to be unncessary. As an avid Firefox user, I already rely on the built-in real-time spellchecking services of Firefox. The Zimbra spellchecker is a submit-n-check style system, which relies on a PHP service to be run in the backend to handle to the submission. Turning this off will free up a bit more ram and your browser may already give you an easier interface to work with anyways.
Turning off anti-virus and spell checking in the admin interface
Reducing the # of amavisd processes
The last step that will really reduce the amount of ram consumed by your Zimbra install is to limit the number of ‘amavisd’ processes. This daemon stays in memory and waits for both anti-spam and anti-virus jobs. The default number of processes is 10 and each process consumes about 40MB of memory, so, by default amavisd will consume around 400MB of ram! To reduce this, make the following change in /opt/zimbra/conf/amavisd.conf.in and restart your server. There are two files, amavisd.conf and amavisd.conf.in. Make your changes to the amavisd.conf.in file as this is replicated when zimbra starts up. Note that you cannot reduce this to less than 2 as you will sorrily miss anti-spam checking and the minimum is 2:
/opt/zimbra/conf/amavisd.conf.in
$max_servers = 2; # number of pre-forked children (2..15 is common)
Turn off unused services
Lastly, turn off any services that you don’t intend to use. I don’t use IMAP(s) or POP(s) services, so for me, it’s not worth keeping them on. This is good practice, both to free up more ram and for security. These are controlled via their own tabs in the admin area under ’servers’.
Conclusion
So, with a little compromise, you can make your low-ram VPS or server work just fine with Zimbra. The key is to find those things that you are OK with disabling and reducing the amount of ‘amavisd’ processes.
Zimbra: It’s Going Places April 10, 2008 No Comments
Zimbra is a full featured email and collaboration suite that is currently rivaling and surpassing Microsoft Exchange in features. As a devoted Qmail fan, and avid do-it-yourself emailer, I was at first skeptical about the platform when I first saw it. To be honest, before I got to know it, I had pinned Zimbra to be yet another hyped-up web 2.0 app without the substance/innovation behind it to make it worthwhile. Since then, I have learned many thing that have surprised me about Zimbra Collaboration Suite, among these:
First: Zimbra is a 100% complete email and collaboration solution including MTA, web-based email, spam filtering, anti-virus, etc., i.e. it is not just a web front-end that leaves you to figure out the rest of the stuff. You start with a basic bare-bones UNIX installation and 20 minutes later you have the whole enchilada. The installation, soup-to-nuts is easy, well developed and doable by anybody with basic UNIX system administration experience.
Second: The open source version of Zimbra Collaboration Suite is full featured and has everything and more that most want or expect from an email solution including: unlimited users, multiple domain support, class of service provisioning, intelligent anti-spam/anti-virus system, aliases, calendaring (iCal, CalDav, etc.), email filters, tasks, file sharing and countless other bells and whistles. The open source version has it all, leaving no gotchas. This is really quite remarkable as Zimbra is a company that gains profits. Zimbra’s bread and butter is with corporate customers. Especially the fact that you can literally rip out your exchange server and its mandatory Active Directory servers, etc., and replace them with a pay-for version of the Zimbra Collaboration suite and Outlook/mobile users won’t know the difference.
Third: Zimbra has recently made a 15-user version of their pay-for Network Edition available to users with small installations for $399/year. The Network Edition of Zimbra adds full MAPI Outlook connectivity, hot backup, clustering and most everything else that makes their product an Exchange killer.
I’ve been running a Zimbra server for 4 months now on a cheap Linode and could not be happier. I will soon post an article about how to install and tune an open source Zimbra server on a low-cost Virtual Machine with minimal ram.
For a demo of the Zimbra front-end, visit: http://www.zimbra.com/products/hosted_demo.php
Switch vs. If January 27, 2008 3 Comments
Often, with little rhyme or reason behind it, I choose between using a switch condition or an if-else statement while coding simple condition matches in PHP. I got curious about which is actually more efficient at matching a random integer with a set of conditionals. So, I setup a script to create a set of large scripts to test the speed of these different constructs. Using the ‘time’ command, I measured the speed at which the condition could match a random number. Here is what I found:
The switch construct is generally more than twice as fast at matching a simple integer within a single large set of conditions.
There is largely little difference between the time it takes to find a condition using if,if statements or if-elseif statements. Also the size of the resulting scripts were only negligibly different compared to the difference in execution time.
At first I thought that since the switch statement breaks execution after it matches its value especially since I was using random numbers, that this could account for the time difference, but that also had no bearing. Setting $var=999999 in the below statements had little effect on the faster execution time of the switch statement.
If-Elseif
<? $var=rand(0,1000000); if ($var==0) { echo "found 0"; } elseif ($var==1) { echo "found 1"; } elseif ($var==2) { echo "found 2"; } .... ?>
If,If
<? $var=rand(0,1000000); if ($var==1) { echo "found 1"; } if ($var==2) { echo "found 2"; } if ($var==3) { echo "found 3"; } .... ?>
Switch
<? $var=rand(0,1000000); switch ($var) { case 1: echo "found 1"; break; case 2: echo "found 2"; break; case 3: echo "found 3"; break; .... ?>
Also, I wondered if the switch condition would overall be less efficient if there were a large number of switch statements within a script, instead of a single switch construct with many cases. The thinking was that the switch condition was possibly more expensive to setup than an if-else. However, in this case, many switch statements still performed faster (around 25% more) than when using many if-else or if,if conditionals instead. This was tested with 100,000 iterations of 10 conditions as opposed to a 1 iteration of 1,000,000 conditions.
Of course, the if else construct is a bit more powerful for more complex condition testing, but if you only have a simple variable to match up against a known set of values, I guess the winner is the switch condition!
Scripts as Reliable Daemons No Comments
Overview
Here is a step-by-step tutorial on one way that I’ve found to deploy simple scripts as reliable daemons using the Daemontools package and Multilog facilities provided by D. J. Bernstein.
If you are familiar with QMail, you are probably familiar with one of the many brilliant applications created by the DJB; Daemontools. In a QMail install, it keeps the various parts running and resident in memory no matter what happens, even after you explicitly kill them.
Also, Daemontools is packaged with a very nice logging package, called Multilog. The combination of Daemontools and Multilog makes a great base to deploy scripts/applications that need to react to events, either internal or external. It keeps you out of the business of creating true daemons, handles log rolling, log time-stamping and allows you manage your scripts or applications that you write as services that are started and stopped. Once your system is setup with these tools, all you have to do is write the logic to perform the task you want. Daemontools and Multilog handle the rest of the stuff.
It doesn’t really matter what language you write your “daemon” in, as long as it will execute and hopefully sleep for a few seconds between runs.
Example: /etc/passwd Monitor
As an example, I’ll show how to write a “daemon” with daemontools that will monitor your /etc/passwd file for changes and email you when it is changed. Here is what you need:
- 1) http://cr.yp.to/daemontools/daemontools-0.76.tar.gz – The Daemontools package, this includes Multilog
- 2) Exactly follow the instructions here to install it: http://cr.yp.to/daemontools/install.html
- 3) On certain types of systems, you will need to reboot in order for the installation to take effect. This is because Daemontools actually becomes part of run-level of you machine.
- 2) Exactly follow the instructions here to install it: http://cr.yp.to/daemontools/install.html
The installation process will create a directory called /service. This directory is where you define what scripts/applications you want to run as daemons. Next, create the directory structure to house the necessary shell scripts that Daemontool is looking for to manage your application.
# mkdir /usr/local/bin/daemons/supervise/ # mkdir /usr/local/bin/daemons/supervise/passwdmon/ # mkdir /usr/local/bin/daemons/supervise/passwdmon/supervise/ # mkdir /usr/local/bin/daemons/supervise/passwdmon/log/
Now, create the script that you want to run (/usr/local/bin/passwdmon.php) and make it executable. In PHP, I’ve simply used a while(1) construct to keep it in memory and sleep() function to stop it from running 24/7 and consuming too much CPU.
#!/usr/bin/bash <? $passwdfile = "/etc/passwd"; while (1) { $thismodtime = filemtime($passwdfile); if ($lastmodtime != $thismodtime) { echo "Your /etc/passwd file has changed"; mail("user@domain.com","/etc/passwd changed","/etc/passwd file changed on machine x"); } $lastmodtime = $thismodtime; sleep(10); } ?>
Now, you will need to create a run script for daemontools. Daemontools looks for a file named ‘run’ in the supervise/passwdmon/ directory that you created.
#!/bin/sh exec /usr/local/bin/passwdmon.php
Make both the script you wrote and the ‘run’ script executable:
# chmod 755 /usr/local/bin/passwdmon.php # chmod 755 /usr/local/bin/daemons/supervise/passwdmon/run
Now, create a symbolic link to your supervise directory from /service
# ln -s /usr/local/bin/daemons/supervise/passwdmon/ /service/passwdmon
Now, your daemon should be running! To stop and start it, run:
# svc -d /services/passwdmon # svc -u /services/passwdmon
You should now see your process in the process list, along with supervise, which is keeping it running.
# ps -ax|grep passwdmon 599 ?? S 0:00.03 /usr/bin/php /usr/local/bin/passwdmon.php 610 ?? S 7:02.39 supervise passwdmon
Adding Multilog Logging
Now your script is outputting text and you want to be able to see the logs for you process. You simply need to create a ‘run’ script to enable Multilog to capture that and manage a log file. Place the following in /usr/local/bin/daemons/supervise/passwdmon/log/run:
#!/bin/sh exec /usr/local/bin/setuidgid root /usr/local/bin/multilog t /var/log/passwdmon</code>
Your logs will be kept in /var/log/passwdmon.
That’s it! Now your script can’t be killed until you do a ’svc -d /services/passwdmon’ to stop it. If it dies on its own, it will restart automatically. Also, it will automatically startup when your machine starts up.
# ps -ax|grep passwdmon 13547 ?? S 0:00.03 /usr/bin/php /usr/local/bin/passwdmon.php 13561 p5 R+ 0:00.00 grep passwdmon 101 ?? R 5018:26.07 supervise passwdmon 104 ?? S 0:00.02 /usr/local/bin/multilog s1000000 n100 t /var/log/passwdmon # kill -9 13547 # ps -ax|grep passwdmon 13567 ?? S 0:00.03 /usr/bin/php /usr/local/bin/passwdmon.php 13576 p5 R+ 0:00.00 grep passwdmon 101 ?? R 5018:33.80 supervise passwdmon 104 ?? S 0:00.02 /usr/local/bin/multilog s1000000 n100 t /var/log/passwdmon #
Zimbra December 23, 2007 No Comments
Looking back a few years, I remember seeing the the fledgling Zimbra project. My first thoughts were that it was a noble effort, but had little hope for long-term survivability under the shadow of the powerful and all-consuming MS Exchange product. So, I’m absolutely ecstatic to have been proven wrong by this now very large company. The mountain to climb for innovate Email tools is huge. For years, Outlook users have been silently enjoying the group collaboration features of the Exchange/Outlook duo, to the point where it is considered the defacto standard by which other products are measured. The functionality of Outlook/Exchange has, until now, gone completely unmatched by any other packaged solution.
Amazingly, Zimbra has outgrown its “noble effort” image. The product now offers every collaboration feature that exchange does, but adds a whole new direction to email and group collaboration that is likely going to reverse the prevailing perspective and finally out-shadow Exchange.
I don’t have to list the features that make this product a winner as you already know them. Think of Zimbra as a fully functional replacement for Exchange, just to establish a baseline, then add on real innovation like uncompromising web-based collaboration tools and a devotion to implementing open standards. As of the GA release 5.0 this week, you could pull your old, tired Exchange server out and replace it with a Zimbra server and your users would not know the difference (literally). This alone is going to prove to be a direction setter for the email industry. Zimbra is a true success story and proves the viability of opensource tools for real-world business applications.
High Resolution Security Cameras December 17, 2007 No Comments
Living in Detroit, Michigan can be a trial at times. Don’t get me wrong, I love this city, it is a great place to live and more exciting than most places I have been. However, one of the mainstays of Detroit residential life is petty crime, lots of little annoying stuff. I have been the victim of petty crime on numerous occasions, including (but not limited to) garage break-ins, UPS package theft, screen-door capers and more. Sometimes, I even catch the crazy perps in the act.
One of the small things that I do to put my mind at ease and to pretend like I’m some sort bad-ass cop is adding video surveillance to my home. However, by far, the most frustrating thing about video surveillance has to do with picture quality. What good is a blurry indistinguishable image of a criminal? Here are images I caught last year of some guy that broke into my friends car while he was over. This guy stole a very nice digital camera (Nikon D90) and a few other things after smashing out the window of my friends Subaru.

But, what good is this? Ok, I can tell that he has on a dark jacket with some sort of a light collar but that’s about it. Definitely nothing about these images could be posted on the FBI Worlds-Worst-Criminals site or showcased on Americas Most Wanted!
It’s not as if I’m not using a nice camera. The camera that I used for the above picture is a very high quality 1/3 CCD Sony camera salvaged from police cars themselves. These cameras were purchased at the Dayton, OH Hamvention for a few around $30 each, but probably worth much more. The issue I’ve had has more to do with resolution and light sensitivity. For petty crime scenes that happen at night, it does no good to take a dazzling image of nothing and for images during the day, what good does it do if the image of your crazed crack-head is 2×2 pixels?
So, I set out to find a solution to my crime-fighting dream. A security imaging system that will capture, in gory detail, the very essence of the criminal element. The first thought I had was that I wanted image quality. Most IP video cameras have sub-megapixel quality. This is largely because they all attempt to capture full-motion video. However, I don’t need video, only a single good shot with detail. I needed a true high-resolution digital camera for my CrimStopping© system.
Canon digital cameras have the ability to be controlled and used almost completely via USB. The key factor that Canon has over many manufacturers is that they provide a “preview” or digital LCD image of the what the viewfinder is displaying, which is accessible over USB.
Having been an avid fan of Canon cameras, even before my recent fascination with the Detroit criminal element, I happened to have had a number of Canon cameras laying around. The next order of business was to find some software to control these bad-boys. I immediately came around a good piece of software for the PC called “PS Remote” by Breeze Systems. This software will remotely control your Canon camera and offers an interface to work with another piece of motion detecting software by Zone Trigger. This turned out to be the Eureka of high-quality security footage. Here is an image of BreezeSys PSRemote and ZoneTrigger working in tandem to do motion detection using the live viewfinder from a Canon digital camera and capturing high-resolution images.
The quality coming from your best digital camera can’t be beat. Here is an image during the day, reduce from 5 megapixels, from my brand-new security setup.

So here is a picture captured by ZoneTrigger of someone. Ok, so maybe he was here to read my electrical meter, but you never can be sure! (I think the DTE Energy jacket gave him away)

So, when the next crack-head comes along, I’ll be sure to post a nice high-resolution image of their mug-shot here for everyone to see. Not actually too sure what good it will do still, but I hope that at least I’ll be able to see their face. Just in case I see them in my neighborhood again and feel like starting something =)
Multiple User Apache Setups November 12, 2007 No Comments
One of the things I’ve recently been doing, is looking into ways of deploying Apache that can improve security in multiple user web environments. The scenario is that there are 100’s of different users who all want to develop sites under their own virtualhost configs with server side scripting abilities. Also, these same workers often need to work in groups on the same set of code. Making this work at a basic level is relatively easy, but making the environments antonymous from each other and secure is another. Here are some of the issues.
Autonomy
With a single web service running, if there are any server-side scripting packages installed such as PHP, Perl, etc., a single user can crash, hang, over-utilize or otherwise degrade the web service for everyone.
Security
Poorly written code and vulnerable server software potentially compromises the security of the entire installation and the system itself. As the number of sites increase, the possibility that this will cause a problem gets even greater as it becomes very difficult to audit the security of the all the code that is running under the different sites.
UNIX Group Limitations
Another security issue with a single server install has to do with the fact that is is necessary to allow the Apache uid/gid access to read everyone’s content. This is difficult to do in large environments while keeping the user areas separate.
While group permissions may seem like just another scriptable/auditable system administration task, there are some real limitations with respect to what is possible. The first thought may be to enforce file permissions like ‘uid=user group=www perms=rwxr—–’. This works ok, but there are still some fairly large security problems since group ‘www’ needs to read anyone’s files. Users could potentially simply write a PHP script or the like to read anyone else’s data through the permission of the web server, which still needs to be able to read anything.
Apache does deal with group membership for the www user in the /etc/group file, so this is a way to make the files between users and groups a bit more secure by doing something like the following:
www:*:100: sitea:*:101:www siteb:*:102:www
And set file permissions to look like:
uid=user, gid=site, perms=drwxr-----
However, this still does not solve the PHP/Perl execution problem and you still may run into another issue: The maximum number of groups that you can have on a UNIX machine is usually limited to somewhere between 16 and 32 groups! On some systems you can expand this number to be larger, however, this limitation is imposed because of a limitation in NFS, whereas the protocol can only deal with a limited set of groups. Of course, this is going to be an issue if you have a large number of groups, or maybe even using NFS.
One way, that avoids the /etc/group limitation, is to add a little more security by creating a couple of “trustable groups” for which you assign all new users. For example, you could do something like the following
www:*:100: groupa:*:101:www,usera,userb,userc groupb:*:102:www,userc,userd,usere,userf
And set file permissions to look like:
uid=user, gid=groupa, perms=drwxrws---
This way, virtual host directories are assigned the permissions of a specific group and users in groupa will not be able to modify any virtual hosts in other groups. This all still works since www is a member of each of the work groups. Note: Apache needs a restart to pickup new group assignments.
This seems like the best you’re going to get (at least as far as I can figure out) without doing something else that would change the architecture of your setup entirely. Here are a few options that I’ve found that do just that:
A Single Apache with FastCGI and suEXEC Wrappers
The combination of FastCGI and suEXEC wrappers for Apache is a nice way to segregate different server-side processes to run under different IDs. As opposed to standard CGI, the FastCGI interface re-uses spawned interfaces, which largely avoids the resource problems and speed issues associated with CGI.
Multiple Apache Instances with a Front-end Proxy
Another way to go about creating antonymous server sites is to create a single apache instance for each DNS name that you are hosting and create a single proxy on the port 80 front-end to deal the incoming traffic. The front-end proxy handler could be done with Apache + mod_proxy or Apache + mod_rewrite.
Summary
There does not seem to be a magic bullet for dealing with secure, antonymous Apache installations with many users. FastCGI, suEXEC and other tools offer some ways to make hosting environments relatively secure, but certainly at the cost of resources and complexity.
PHP and Solaris: getcwd() Behavior November 10, 2007 No Comments
The Solaris OS has implemented, for quite some time, a permission restriction with respect to when getcwd() will return a full/real path under certain conditions. This has at least been since SunOS 5.8 and continuing on into 5.10. Many functions within the PHP codebase relied upon a universally working getcwd() [C] call to expand paths and to find out where a script is being executed. In particular, Solaris does not assume that getcwd() is a privilege that should be granted to users in directories that don’t have ‘r’ (read) permission, even if it has ‘x’ (execute) permissions. For example, in Solaris, getcwd() will fail if a directory anywhere underneath your current path has ‘–x’.
pwd vs. getcwd()
The ‘pwd’ command under most shells cannot be used to reliably test for this behavior since it caches and keeps track of ‘cd’ operations. ‘pwd’ will let you see the name of your current location in Solaris, but it does not do this via a getcwd(). Instead, it is simply spitting back a path that it recorded and cached from when you have issued a ‘cd /directory’. Therefore, it is necessary to explicitly call getcwd() to test this behavior out. The following code can be used to test the operation of getcwd() in a directory:
#include <stdio.h> main() { char str[200]; if (getcwd(str, sizeof(str)) == 0) printf("getcwd failed!!!\n"); else printf("CWD = %s\n",str); }
Under Linux, getcwd() behaves normally with the restrictive permissions:
# uname -a Linux x.y.z 2.6.9-42.ELsmp #1 SMP Wed Jul 12 23:27:17 EDT 2006 i686 athlon i386 GNU/Linux # find ./localfs -ls 32001 8 d--x--x--x 3 nobody nobody 4096 Oct 1 13:05 ./localfs 32002 8 d--x--x--x 2 nobody nobody 4096 Oct 1 13:12 ./localfs/test123 32004 12 -rwsr-xr-x 1 nobody nobody 4878 Oct 1 13:06 ./localfs/test123/cwdtest # su nobody # cd /localfs/test123 # ./cwdtest CWD = /localfs/test123 # pwd /localfs/test123 #
Under Solaris, getcwd() does not work with the –x restrictive permissions:
# uname -a SunOS opteron 5.10 Generic_118855-14 i86pc i386 i86pc # find ./localfs -ls 449384 1 d--x--x--x 3 nobody nobody 512 Oct 1 13:13 ./localfs 449386 1 d--x--x--x 2 nobody nobody 512 Oct 1 13:13 ./localfs/test123 449388 7 -rwsr-xr-x 1 nobody nobody 6552 Oct 1 12:57 ./localfs/test123/cwdtest # su nobody # cd /localfs/test123 # ./cwdtest getcwd failed!!! # pwd /localfs/test123 #
Bug or “security” feature?
There are some who feel that this is a bug, not a feature. I tend to agree with it being termed a bug, especially since I cannot find a single other OS that implements this “feature”. Either way, it broke PHP under situations where site owners were attempting to implement tight security on websites with many different users who have their own directory for PHP content.
Changes to PHP 5.2.5 that deal with this
Prior to PHP 5.2.5, PHP would would fail for relative includes in these situations: [include("../dir/")]. Since PHP 5.2.5, we added proper handling for this Solaris quirk to the code base so that relative paths in include()/require() still work under these conditions. In addition, a patch was added to safe_mode that used to cause similar behavior when the getcwd() call failed.
[1245][root@opteron:/test/testa/test]$ ls -al
total 56150
d--x--x--x 2 root root 512 Nov 11 12:45 .
d--x--x--x 4 root root 512 Nov 11 12:42 ..
-rwxr-xr-x 1 root root 16719040 Nov 11 12:44 php-5.2.0
-rwxr-xr-x 1 root root 12007004 Nov 11 12:45 php-5.2.5
-rw-r--r-- 1 root root 35 Nov 11 12:43 test.php
[1245][root@opteron:/test/testa/test]$ cat ./test.php
<?php
include('../b/file.php');
?>
[1245][root@opteron:/test/testa/test]$ cat ../b/file.php
<?php
echo "I am a file in directory b.\n";
?>
[1245][root@opteron:/test/testa/test]$ su rob
[1246][rob@opteron:/test/testa/test]$ uname -a
SunOS opteron 5.10 Generic_118855-14 i86pc i386 i86pc
[1246][rob@opteron:/test/testa/test]$ ./php-5.2.0 ./test.php
Warning: include(../b/file.php): failed to open stream: No such file or directory in /test.php on line 2
Warning: include(): Failed opening '../b/file.php' for inclusion (include_path='.:/usr/local/lib/php') in /test.php on line 2
[1246][rob@opteron:/test/testa/test]$ ./php-5.2.5 ./test.php
I am a file in directory b.
[1246][rob@opteron:/test/testa/test]$NFS Mounts can hide the problem
The issue can initially be perplexing to pin down. I originally ran into this problem on a machine with an NFS mounted directory like: /a/b/NFS/webstuff/, where ‘NFS’ was an external FS. Looking at the directory path, all permissions looked ok (r-x), so I at first dismissed it as not being the culprit of the broken include() functionality. Upon looking into the problem, it was found that the local directory called ‘NFS’, before the mount happens had the restrictive permissions that triggered the quirky Solaris behavior (–x). This was not immediately evident since after the mount happens, the /NFS path is (to the eye) replaced with the inode of the remotely mounted directory, which was (r-x). None-the-less, the OS must walk this directory tree, including the /NFS directory inode on the local FS to get to the remotely mounted content.
Directory: /a/b/NFS/webstuff
inode(a) --> inode(b) --> inode(NFS) --> inode(NFS) --> inode(webstuff)
[local] [local] [local] [remote NFS] [remote NFS]
^
^
hidden after mountUpgrade your PHP!
So, long story short, I strongly encourage anyone using Solaris to upgrade to PHP 5.2.5 as you are likely to run into this situation if you have multiple users, NFS mounted web dirs, safe_mode=on, or another reason to implement strict directory permission in your installation.
Wapache – Win GUI Framework October 29, 2007 No Comments
Here is a project that deserves much more attention: http://wapache.sourceforge.net.
It is a modified Apache installation for Windows that links the power of apache (including PHP/Perl/etc. extensions) with the Windows GUI to create GUI windows applications simply and easily by way of custom apache Directives that link an Internet Explorer instance to the Windows GUI and to URLs in your code. It does not require a local TCP port and is very stable. Long story short, you can create Windows GUIs in your web based programming language of choice, including [File Edit Help] style menus, system tray support, right-click menus and lots of other features. Very cool. It is very well thought out and extremely powerful. I think this will eventually out-pace GTK libs and other attempts at GUI-ing PHP, Python and Perl.
Combine this with an installshield package or the like and you can develop and distribute full windows apps in just about any web language of your choice.
Here is a screenshot from the standard install:
SetGID and SetUID Shell Scripts October 22, 2007 No Comments
Under most UNIX implementations, shell scripts cannot be made to setuid. This is both a security precaution and hindrance. For example, let’s say that you have the need to allow your users to RSYNC content from one server (a development server), that they have an account on, to a production server that they don’t have direct access to. So here is a script called rolldata.sh that accomplishes just that:
#!/usr/bin/sh /opt/local/bin/rsync -avP -e "/bin/ssh -i /sshdir/rolluser-id_rsa" \ "/localdir/" "rolluser@securehost.domain.com:/remotedir/"
Now, this works, but it leaves a few glaring security problems:
1) The user needs to be able to READ the /sshdir/rolluser-id_rsa file in order for the ssh command to work without a password. If they can read this file, then they could just SSH right to the box and do lots of other stuff if they know how, which is not very hard.
2) Since ssh will only allow identity files to be -r——– in permission, every user who needs to be able to execute the above script must have their own rolluser-id_rsa file somewhere (and their own personal script) that can read by them and nobody else.
So, the obvious solution I thought would be to setgid the shell script, make it owned by root and make it only executable like:
---s--s--- 1 root cit 229 Oct 22 19:32 rolldata.sh -r-------- 1 root cit 883 Oct 22 18:59 rolluser-id_rsa
Therefore, only root would be able read the contents of the rolldata.sh script and the rolluser-id_rsa file, but any user in group cit could run it. The only problem is that most UNIX kernels (including Solaris) don’t allow you to setuid/gid a shell script!
However, this restriction obviously does not apply to compiled code, so this is what I came up with to get around this restriction. Create a C file called rolldata.c that looks like:
#include <unistd.h>; main () { setuid(0); int ret; ret = execl ("/mydir/rolldata.sh", "/mydir/rolldata.sh", "-1", (char *)0); }
This program simply runs a pre-defined script as the user root, by way of the setuid(0); call. After compiling this (gcc ./rolldata.c), call this file rolldata and set the permissions with setgid like:
---s--s--- 1 root cit 6656 Oct 22 20:05 rolldata ---x------ 1 root cit 229 Oct 22 19:32 rolldata.sh -r-------- 1 root cit 883 Oct 22 18:59 rolluser-id_rsa
Now, any user in group cit can now execute the rolldata.sh script by way of running rolldata, but cannot read the script or the ssh identity key!
[user@computer mydir]$ ./rolldata building file list ... 109 files to consider wrote 2998 bytes read 20 bytes 6036.00 bytes/sec total size is 2532169264 speedup is 839022.29 [user@computer mydir]$ cat ./rolluser-id_rsa cat: cannot open ./rolluser-id_rsa