A chroot environment seems to be rare these days. Everything is virtualized, load-balanced and what have you. I recently found myself trying to deploy into a chroot’ed Lighttpd environment with Capistrano and immediately ran over several pitfalls. The biggest problem is that Capistrano uses absolute links for directories like current
and the links to the log
directory.
A base directory for your app could be /var/www/rails
and your Lighttpd runs in a chroot environment in /srv/lighty. In the application directory Capistrano creates the directories releases
and shared
. After a successful deployment it also creates a symbolic link named current
pointing to the latest release, and several links to directories in system
.
In this scenario the link current would point to the directory
/srv/lighty/var/www/rails/releases/20070506135654
. Now, since Lighttpd doesn’t know the directory structure above /srv/lighty that’s a bit of a problem and it won’t find the path when you point it to the dispatcher in the directory current
. This is true if you launch your Rails application through FastCGI. In a Mongrel scenario it would pretty much result in the same problems. Additionally, your Rails application won’t find its log
and other directories (if you’re up for it, these are public/system
and tmp/pids
).
Apparently not many people seem to use an environment like this. It’s pretty old-fashioned in this highly virtualized world, but you run across it from time to time. So what can you do?
Hacking the Symbolic Links
This isn’t going to be pretty. To get the thing to work somehow I created a filter for after_update_code
and removed the links created by Capistrano to replace them with new ones, only this time they wouldn’t use absolute paths, but relative ones.
I’m not proud of this solution, but I had to come up with something pretty quickly, and it works for the moment, and was only supposed to do so. It will be replaced with a production-like deployment soon. I’ve replaced the task :symlink
with my own which looks like this:
desc "Overwriting symlink task to
task :symlink do
on_rollback {
run "cd #{deploy_to} && ln -nfs releases/#{File.basename previous_release} current"
}
run "cd #{deploy_to} && ln -nfs releases/#{File.basename current_release} current"
run <<-CMD
cd #{deploy_to} &&
rm -f current/log current/public/system &&
ln -nfs ../../shared/log current/log &&
ln -nfs ../../../shared/system current/public/system
CMD
run <<-CMD
test -d #{shared_path}/pids &&
rm -rf #{release_path}/tmp/pids &&
cd #{deploy_to} &&
ln -nfs ../../../#{File.basename shared_path}/pids current/tmp/pids; true
CMD
end
One remark: I remove some symlinks in the code, because Capistrano creates them in the update_code
task.
As I said, it’s not pretty but it works. Here’s what it does: It removes the symbolic links created by Capistraon and replaces them with new ones using relative paths.
Putting the user in a cage
The better solution is to directly deploy into your chroot environment. Create a user that’s directly being send into it by SSH or by its login shell.
This scenario requires that all the tools needed for a deployment must be installed in the chroot environment, including Subversion. If that’s not a problem, this might be the ideal situation.
One thing you can’t do is restart your web server from here. A scenario like this would mean that you can only restart your FastCGI or Mongrel processes. This is a scenario I’ll use in the long run.
So is this hassle worth it? I’m not sure myself. It’s questionable, if the little added security is worth the effort you have to put in to get your applications working. In the end it depends on what your client’s environment looks like, and if you have any control over it. If guidelines require chroot’ed services, then there’s not much you can do. Other than that I’d consider breaking the service out of the chroot and trying to find a better way of securing it. Xen and the like come to mind.