Using qemu to lock down tetrinetx server
Rusty Russell
Note: the mentioned files are available in tetrinet.tar.gz.
Hi all,
I recently set up a tetrinet server inside a qemu image, for a
layer of additional security. The image is under 2MB, and below is a
mini-howto in setting up such a server in case anyone else is
interested.
You will need:
1) A qemu-compatible disk image: I have a simple script to make one,
(see file 'make_fs.sh').
2) The "vl" binary, and a qemu-compatible kernel bzImage.
I used qemu 0.4.3, modified with the -tun-fd patch and the closed
stdin patch. If you use the softmm version of qemu, any kernel
bzImage should work.
3) The root-running version of the tundev program (see file 'tundev.c')
Steps:
1) Mount the filesystem. If you've already prepended a partition
table, use
losetup -o $((63 * 512)) /dev/loop0
mount /dev/loop0 /mnt/qemu
Otherwise you can just do:
mount -o loop /mnt/qemu
2) Remove unneccessary files, add files you need, and lock down
directories. This is best done by creating an oldroot dir and
moving things into that. For tetrinet, I ended up under 2MB
(see file 'tetrinet-in-qemu-ls-R')
3) I wanted the filesystem to be read-only (too bad about the tetrinet
high scores file: I don't care), but I wanted the logs, so I made
/var/log/tetrinetx/game.log a symlink to /dev/ttyS0, and allowed
the games group to write to it.
4) I wrote a simply launcher program called run to avoid the need for
a shell (see file 'run.c')
5) Unmount the qemu image. Once I'd trimmed out most of the code, I
created a new smaller one one, mounted that, and used tar to
transfer everything across.
6) My startup script looks like so:
#! /bin/sh
# Set up forwarding and masquerading, and make sure tun module is loaded.
set -e
INTERFACE=`./tundev games /var/log/qemu-tetrinet ./vl -hda tetrinet-filesystem bzImage root=/dev/hda1 ide1=noprobe ide2=noprobe ide3=noprobe ide4=noprobe ide5=noprobe init=/run`
ifdown $INTERFACE 2>/dev/null || true
ifup $INTERFACE
You need to reconfigure the tun devices everytime they get opened,
hence the ifdown/ifup stuff.
8) You will also need to forward incoming connections to the server.
In this case, the server makes no outgoing connections, so this
works:
iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 31457 -j DNAT --to 192.168.1.2
iptables -A FORWARD -o tun0 -m state --state NEW,INVALID -j DROP
iptables -N LOGDROP
iptables -A LOGDROP -j LOG
iptables -A LOGDROP -j DROP
iptables -A FORWARD -i tun0 -m state --state NEW,INVALID -j LOGDROP
Good luck!
Rusty.