Semi-automated, semi-secure marking

Given the wad of marking I have, I wanted to automate at least some parts of the task. Furthermore, I wanted to run submissions such that they couldn’t do much damage (caused as much by student error as maliciousness). Here is a brief summary of what I dd. What’s here is very much not suitable for use without groking it first!

I settled upon Docker, a management tool on top of Linux LXC containers. The idea was for each student’s submission to run in its own container. Since the students had to prepare multiple programmes that had to be run simultaneously (this was for a networks module), I had to be able to get in to the container in multiple windows. Having the container run sshd seemed the best way to do this.

First of all I needed the student submissions. Each submitted a .zip file to Course Resources, so I had it supply me with a .zip of all the .zips. Then I used this shell script to pull them apart:

#!/usr/bin/env zsh
source=$1
for f in $source/*100[0-9]*.((zip)|(rar)|(7z)); do
 info=(`echo $f | sed -E 's/.*_(100[0-9]*)_.*((zip)|(rar)|(7z))/\1 \2/'`)
 echo $info[1]
 mkdir $info[1]
 cd $info[1]
 case $info[2] in
   zip)
     unzip ../"$f"
     ;;
   rar)
     unrar x ../"$f"
     ;;
   7z)
     7z x ../"$f"
     ;;
 esac
 cd ..
done

This led to a bunch of directories with names corresponding to student numbers. I put them in a directory called “subs”.

I then created an image in Docker to be the “parent” of every container. I used the following Dockerfile:

FROM angelrr7702/ubuntu-13.10-sshd
RUN apt-get update
RUN apt-get upgrade
RUN apt-get install -y vim
RUN echo -e '\n\n\n\n\n\n\n\n' | adduser --disabled-password --uid 1001 --quiet me
RUN mkdir ~me/.ssh
RUN echo 'ssh-rsa [REDACTED]' > ~me/.ssh/authorized_keys
RUN echo 'alias p=python3' >> ~me/.profile

Use the public key of the SSH keypair you intend to use. I beefed up me’s .profile to include an alias for python3; do whatever is most useful to you. Then, build the image (called 5cc515 because that was the module I was marking):

docker build -t 5cc515 .

Per-student containers are made using this shell script, which I called student.sh

#!/bin/bash
id=$1
cid=`docker run -d -p 22 -v /home/dfe/markingDocker/subs/$id:/home/me/a -name 5cc515_$id 5cc515 /usr/sbin/sshd -D`
echo $cid
docker port $cid 22 | cut -d: -f2 | tee ./.portno
echo "Press enter when done."
read
docker stop $cid >& /dev/null
docker rm $cid >& /dev/null

This will create the container from the 5cc515 image and forward port 22, SSH. The resulting local port number will be printed and stashed in .portno, so you’ll need to faff with this if you want to run multiple containers simultaneously. student.sh then waits for you to press enter; after you do that, the container will be blown away.

A container for the student with ID 100999999 can then be created using

 ./student.sh 100999999

and you can use getin.sh

#!/bin/sh
ssh me@localhost -p `cat .portno`

to ssh into the container. You will be the user “me” running bash and the student’s submission will be in the “a” directory. You can ssh into the container multiple times if you like. Do your marking thing, then press enter in the shell running student.sh. The SSH sessions will be disconnected and he container deleted. You may want

StrictHostKeyChecking no

in your ~/.ssh/config to eliminate the annoying host key prompts.

It is important to note that the containers have read/write access to the submissions. I needed this for my marking (I didn’t feel like finding a workaround involving temporary per-container copies) but you may wish to alter the -v option in student.sh if you don’t like this.

Cleaning up extra containers can be done with

docker rm `docker ps -a | awk '{print $1}'`

If you’re more organised than me you should use something like Pexpect to automate more.

That’s all!

This entry was posted in David Evans. Bookmark the permalink.