As much as I wish we deployed builds from our continuous integration server, all but one of our products is deployed with good ol’ `svn up`.  Developers generally have access to only one web server, so I needed an rsync command to propagate new code to the rest of the web servers.  I wanted normal user accounts to be able to run it at any time in any directory with one command.  Then developers would be instructed to run this command after updating any files.

So I whipped up an shell script that called rsync with some predefined options and targets.  Unfortunately, in order to preserve ownership and permissions in the destination, rsync needed to be run as root.

At first, I looked at the setuid bit. By changing the ownership of the rsync shell script and running `chmod u+s` on the script, setting the setuid, any user could execute it and it would run as root. Well, it turns out that the kernel will not honor setuid on shell scripts for security reasons. But what if I wrote a C program instead of a shell script? That actually worked, and ran with root privileges, but it still did not rsync as root for some reason. So that was out.

The second solution was to insert sudo before the rsync command in the script. I modified /etc/sudoers to allow the users group to run rsync under sudo. That worked perfectly. So if I put this script in /usr/local/bin, I would be done. But I had already written this magnificent (two-line) C program.  Why not make it even more secure (sudo does not work on shell scripts either)?  Instead of allowing all users to run rsync under sudo, I could limit them to running only my C program under sudo, instead of rsync in general. Then, in my script, I could replace rsync with my C program. So that’s what I did. I again modified /etc/sudoers and my shell script, threw both the script and C executable in /usr/local/bin and I was done.

I named the final command `zipsync`. Here is the shell script for that, anonymized a bit.

cd /var/www/vhosts

# repeat for each web server
sudo zipsync.bin \
   -av --delete \
   --exclude=".svn" \
   --exclude="logs" \
   --exclude="tmp" \
   --exclude="cache" \
   --exclude="*.swp" \

cd -

And the C program, zipsync.bin.

int main(int argc, char** argv)
   *argv = "rsync";
   return execvp(*argv, argv);