Creating a custom Linux Live USB environment
Published: January 27, 2015
In the advanced C programming class that I assist in teaching, we are holding interactive programming exams. That means that every student is tasked with constructing a C program in a time frame of a about 2 hours. Students have access to GCC, Valgrind, as well as the Emacs and Vim text editors (we are staunch proponents of religious freedom).
One hurdle in holding programming exams is the problem of cheating: Nowadays, every computer at any computer lab is connected to some kind of network, which would make it easy for students to share files or look up hints and answers to problems on the internet. Our solution to this problem involves booting the lab computers from a USB drive which contains a customized, lock-down version of Linux, combined with the aforementioned tools and assignment files.
After shopping around for possible distributions to base my custom exam environment on (I looked at Debian Live and Fedora), I quicky settled on Arch Linux. The linked wiki provides great documentation about the process of installing Arch on a USB drive, and since I run Arch on my personal machine, I had all of the required tools already on my machine.
Whereas my attempts to build Debian and Fedora images never quite worked out, doing things "The Arch Way ©" was a pleasure. Installation on a USB drive works the same way as installing it on a regular hard drive, with only a few minor differences.
The USB drives I am using are your standard, run-of-of-the-mill 4 GB USB drives. My plan was to finish one USB drive, test and debug it on the lab computers, and then copy the 'master' image on as many drives that I need.
I partitioned my USB key the following way:
100 MB /boot - holds the bootloader (SYSLINUX)
500 MB /work - holds assignment data
3.4 GB /root - holds the operating system
I configured the image to not use a display manager, but to instead
automatically log in the user ee264 into a terminal session. This is
archieved by changing a systemd configuration file. Then, as described here,
the user ee264 automatically executes the startx
command, which loads the
desktop environment.
I chose MATE as the DE for my custom distribution, mainly because our lab's computers run RHEL 6, which uses GNOME 2.
To disable any kind of network connectivity, I made sure to uninstall tools
like the DCHP client dhcpcd
and remote access tools like ssh
. Also, I
configured iptables
to
drop any incoming or outgoing data packets. This means absolute radio
silence. The configuration file for iptables
looks like this:
/etc/iptables/iptables.rules ---------------------------- filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] COMMIT
After setting up basic configuration files for Emacs and Vim, installing the
bootloader, and setting a long root password, the master USB drive was
done. After testing it and confirming that it actually booted up on our
computer's lab machines, I created my master image: A .img
file that contains
the entire drive, including the bootloader and all of the partitions. To do
this, I used the GNU coreutils tool
dd
(Note that in oder to
access /dev/* files, I needed to execute dd as root):
dd if=/dev/sdd of=/data/ee264exam_usb.img
Now I had a working, bootable USB image on my harddrive. But, as it is the norm with many things, making the exam OS was an iterative process, and rebooting my computer every time I wanted to make a change to the image was a massive waste of time.
To shorten my feedback loop, I explored other options, and one of them was the
utility kpartx
which is included in the AUR package
multipath-tools. With
help from
this
blog post, I was able to mount the partitions of my .img
file into my local
filesystem. A combination of the tools
arch-chroot
(a nice spin on the regular chroot) and
pacstrap
allowed me to navigate my USB filesystem from my regular Arch Linux
installation without rebooting.
Another cool trick to shorten the feedback loop was booting the .img
file
inside a virtual machine, without having to set up a new VirtualBox or VMware
machine. This was done using QEMU:
qemu-system-x86_64 -m 4096 -vga std -usbdevice tablet -hda /data/ee264exam_usb.img
Here, the -m 4096
switch gives the VM 4 GB of memory, -vga std
enables
video, -usbdevice tablet
matches the host mouse cursor to the VM cursor, and
-hda /data/ee264exam_usb.img
loads my .img
file as the VM's main harddrive.
When it was time to finalize the image and to make 20 identical copies, I was
faced with a problem: The USB drives we ordered were of the 'painfully slow'
kind. Using dd
to write the image on to drive took 17 minutes at a rate of
only 4 MB/s! In addition to that, dd
is not the most verbose of tools,
either: It does not output any status information until it's done copying
bytes. My laptop has 3 USB ports, so to be as efficient as possible, I wanted
to make use of all three ports at the same time. After searching for solutions,
I stumbled across these two hints. The first link introduced my to the tool
pv
, or "pipe viewer", which can be used to monitor the status of things
passing through its stdin and stdout, and the second link showed me a smart way
of redirecting and splitting dd
's output using the tee
command. From this,
I solved my problem of writing three USB drives from one image at the same time
with one line of bash:
dd if=ee264exam_usb,img | pv -petrb -s $(stat -c%s ee264exam_usb.img) | tee >(dd of=/dev/sdd) >(dd of=/dev/sde) | dd of=/dev/sdf
This wonderful line of bash uses dd
to read my image file, pv
to create a
progress bar, and tee
to create 3 output streams that are subsequently input
to three calls to dd
to write to the USB keys.
This is pure beauty.
It will be interesting to see how students perform during an interactive exam, but on thing is for sure: A lot of work has gone into their custom "Exam OS".