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.
#!/bin/sh
cd /var/www/vhosts
# repeat for each web server
sudo zipsync.bin \
-av --delete \
--exclude=".svn" \
--exclude="logs" \
--exclude="tmp" \
--exclude="cache" \
--exclude="*.swp" \
* 192.168.1.101:/var/www/vhosts
cd -
And the C program, zipsync.bin
.
#include
int main(int argc, char** argv)
{
*argv = "rsync";
return execvp(*argv, argv);
}