Jan-Philipp Litza

Sandboxing processes

Yesterday, my interest in sandboxing a program I didn’t fully trust finally surpassed my laziness to look at namespaces again. And after a few hours of coding, I created a small script that uses unshare to encapsulate the newly launched process in new namespaces of all kinds (not much work there) and hardens the filesystem so that effectively, (hopefully) the only writable persistent directory is $PWD, the process sees a minimal /dev and fresh copies of temporary filesystems. In case you are interested in the script, here it is:



unshare -unpirf --mount-proc sh -c '
    hostname sandbox
    TMPNAME=$(mktemp -d)
    # replace /dev by minimal dummy version
    mount -t tmpfs -o nosuid,mode=755 none $TMPNAME
    for dev in null zero full random urandom console tty; do
        touch $TMPNAME/$dev
        mount --bind /dev/$dev $TMPNAME/$dev
    mount --move $TMPNAME /dev
    # Make some symlinks
    ln -s null /dev/kmsg
    ln -s /proc/self/fd/0 /dev/stdin
    ln -s /proc/self/fd/1 /dev/stdout
    ln -s /proc/self/fd/2 /dev/stderr
    # save a r/w bind mount of our original cwd
    if [ "${PWD#/home}" != "$PWD" ]; then
        mount --bind "$PWD" $TMPNAME
    # make /home read-only
    mount -o remount,bind,ro /home
    # restore the original r/w cwd
    if [ "${PWD#/home}" != "$PWD" ]; then
        mount --move $TMPNAME $PWD
    # shadow temporary filesystems with fresh copies
    mount -t tmpfs -o nosuid,nodev,noexec,mode=755 none /run
    mount -t tmpfs -o nosuid,nodev none /var/tmp
    if [ "${PWD#/tmp}" != "$PWD" ]; then
        mount -t tmpfs -o nosuid,nodev none $TMPNAME
        mkdir -p "$TMPNAME${PWD#/tmp}"
        mount --bind "$PWD" "$TMPNAME${PWD#/tmp}"
        mount --move $TMPNAME /tmp
        rmdir $TMPNAME
        mount -t tmpfs -o nosuid,nodev none /tmp
    # refresh cwd handle
    cd "$PWD"
    # get lost of uid 0 by starting yet another user namespace
    exec unshare -U '"$CMD"

I’m always open for feedback, either via e-mail or on Github!