dudle

categories: code

In my quest to minimize the external third party services I rely on in my daily life, I stumbled over dudle which is an online poll system like doodle only better :)

The problems with giving your availability for a meeting or preferences over a subject to third party services like doodle are obviously the entailed privacy issues. Since I thought that doing stuff like a simple poll can also be handled by software running on my machine I firstly just wanted something like doodle running on one of my servers. This way, whenever me or anybody else would vote, they would no longer be depending on trusting someone like doodle but they would only have to trust me, who I own the server. (and hopefully they trust me more than a unknown money making instance)

But one can do even better than that! Benjamin Kellermann implemented an online poll platform where the availability or preferences of the participants are not even known by the server itself (nor by the other participants) and only when everybody voted a sum of all the votes can be calculated to decide for a timeslot or choice of subject. Hence, the user doesn't even have to trust the server he uses that runs dudle. All the server and the other participants get to see are encrypted availability vectors from which nobody can infer the original choice of options of the user. Only when combining all of them, a sum can be calculated which represents the overall availability of all users at a given time.

By its usage dudle is as simple as doodle. The only thing that might sound tedious is the private key each user has to take care of after his registration (which of course works without giving away any private detail like email) but this is greatly solved by using a bookmarklet to enter his private key into a poll field. I was showing the setup and what its advantages are to some non-CS people and was very pleased by the positive responses I got. A big kudos to Benjamin for his great work on this piece of software!

I am running thttpd on mister-muffin.de and by design it chroots into the www directory to increase security. A result of this is, that the only cgi scripts that can be run are statically compiled executables. Dudle is written in ruby and uses git as the database backend. Hence I had to setup a minimal chroot environment inside my www directory so that dynamically linked executables like git and ruby would work. I could've bothered with compiling both statically but was not up for the trouble (yet). A requirement of this environment was of course that it was very small and only included stuff that was necessary for git and ruby to run: dynamic libraries and ruby modules. Another requirement was that there were no setuid programs that could be used by an attacker to break out of the chroot environment by becoming root.

One way to do that would be to do a normal debootstrap including git and ruby and then manually removing everything that was not needed. It turned out that a normal debootstrap creates lots of overhead in retrieving lots of things that are not needed anyways and result in also having to delete lots of things afterwards, which is not worth the hassle.

My idea was to retrieve the git and ruby debian packages and all its dependencies and all the dependencies of those recursively and then just extract those packages into a directory. Since I didnt want to do the dependency resolution manually I let myself be inspired by multistrap and used apt to do that. Using apt for this task (as well as for multistrap) is possible because one can specify a custom target directory for apt in the commandline.

Since my final setup did not contain /bin/sh which ruby needed to call git, I had to patch dudle. I also proposed a way to get rid of the htdigest dependency to Benjamin and he included that and my /bin/sh patch into dudle. ☺

#!/bin/sh -ex

# check for fakeroot
if [ "$LOGNAME" = "root" ] \
|| [ "$USER" = "root" ] \
|| [ "$USERNAME" = "root" ] \
|| [ "$SUDO_COMMAND" != "" ] \
|| [ "$SUDO_USER" != "" ] \
|| [ "$SUDO_UID" != "" ] \
|| [ "$SUDO_GID" != "" ]; then
echo "don't run this script as root - there is no need to"
exit
fi

# modify these
ARCH="amd64"
DIST="squeeze"
MIRROR="http://127.0.0.1:3142/ftp.de.debian.org/debian"
DIRECTORY="`pwd`/debian-$DIST-$ARCH-ministrap"

# re-execute script in fakeroot
if [ "$FAKEROOTKEY" = "" ]; then
echo "re-executing script inside fakeroot"
fakeroot $0;
rsync -Phaze ssh $DIRECTORY/ mister-muffin.de:/var/www/
ssh mister-muffin.de "chown -R www-data:www-data /var/www/dudle.mister-muffin.de/"
exit
fi

# apt options
APT_OPTS="-y"
APT_OPTS=$APT_OPTS" -o Apt::Architecture=$ARCH"
APT_OPTS=$APT_OPTS" -o Dir::Etc::TrustedParts=$DIRECTORY/etc/apt/trusted.gpg.d"
APT_OPTS=$APT_OPTS" -o Dir::Etc::Trusted=$DIRECTORY/etc/apt/trusted.gpg"
APT_OPTS=$APT_OPTS" -o Apt::Get::AllowUnauthenticated=true"
APT_OPTS=$APT_OPTS" -o Apt::Get::Download-Only=true"
APT_OPTS=$APT_OPTS" -o Apt::Install-Recommends=false"
APT_OPTS=$APT_OPTS" -o Dir=$DIRECTORY/"
APT_OPTS=$APT_OPTS" -o Dir::Etc=$DIRECTORY/etc/apt/"
APT_OPTS=$APT_OPTS" -o Dir::Etc::SourceList=$DIRECTORY/etc/apt/sources.list"
APT_OPTS=$APT_OPTS" -o Dir::State=$DIRECTORY/var/lib/apt/"
APT_OPTS=$APT_OPTS" -o Dir::State::Status=$DIRECTORY/var/lib/dpkg/status"
APT_OPTS=$APT_OPTS" -o Dir::Cache=$DIRECTORY/var/cache/apt/"

# clean root directory
rm -rf $DIRECTORY

# initial setup for apt to work properly
mkdir -p $DIRECTORY
mkdir -p $DIRECTORY/etc/apt/
mkdir -p $DIRECTORY/etc/apt/sources.list.d/
mkdir -p $DIRECTORY/etc/apt/preferences.d/
mkdir -p $DIRECTORY/var/lib/apt/
mkdir -p $DIRECTORY/var/lib/apt/lists/partial/
mkdir -p $DIRECTORY/var/lib/dpkg/
mkdir -p $DIRECTORY/var/cache/apt/
# apt somehow needs this file to be present
touch $DIRECTORY/var/lib/dpkg/status

# fill sources.list
echo deb $MIRROR $DIST main > $DIRECTORY/etc/apt/sources.list

# update and install git and ruby
apt-get $APT_OPTS update
apt-get $APT_OPTS install ruby git-core libgettext-ruby1.8 libjson-ruby1.8

# unpack downloaded archives
for deb in $DIRECTORY/var/cache/apt/archives/*.deb; do
dpkg -x $deb $DIRECTORY
done

# delete obsolete directories
rm -rf $DIRECTORY/usr/share/
rm -rf $DIRECTORY/usr/lib/perl/
rm -rf $DIRECTORY/usr/lib/gconv/
rm -rf $DIRECTORY/usr/lib/git-core/
rm -rf $DIRECTORY/usr/sbin/
rm -rf $DIRECTORY/var/
rm -rf $DIRECTORY/bin/
rm -rf $DIRECTORY/sbin/
rm -rf $DIRECTORY/selinux/
rm -rf $DIRECTORY/etc/*

# delete all setuid programs
find $DIRECTORY -perm -4000 -delete

# delete all binaries except for "git" and "ruby"
find $DIRECTORY/usr/bin/ -type f -o -type l | egrep -v "ruby|git$" | xargs rm -rf

# git needs /etc/passwd otherwise git says: "You dont't exist, go away!"
cat > $DIRECTORY/etc/passwd << __END__
www-data:x:33:33:www-data:/var/www:/bin/sh
__END__

# dont forget to create /tmp directory for dudle
mkdir -m 777 $DIRECTORY/tmp

# get latest dudle
bzr branch https://dudle.inf.tu-dresden.de/unstable/ $DIRECTORY/dudle.mister-muffin.de
( cd $DIRECTORY/dudle.mister-muffin.de; make; )
bzr branch https://dudle.inf.tu-dresden.de/unstable/extensions/dc-net/ $DIRECTORY/dudle.mister-muffin.de/extensions/dc-net/
( cd $DIRECTORY/dudle.mister-muffin.de/extensions/dc-net/; make; )

# fix shebang
find $DIRECTORY/dudle.mister-muffin.de/ -type f -regex ".*\.cgi\|.*\.rb" \
| xargs sed -i 's/#!\/usr\/bin\/env ruby/#!\/usr\/bin\/ruby/'

The above code will compile a minimal chroot environment, delete everything that is not needed, fetch dudle and deploy it to my server. The comments in the code should explain everything.

View Comments
blog comments powered by Disqus