How it works

To create a chroot environment you use the chroot() macro in the following fashion:
chroot(USER, XXX)

This creates a chroot environment to be started from domain user_t (patameter 1), the chroot environment has a base name of "XXX" which is used for naming the domains XXX_t, XXX_super_t, and USER_chroot_t, and the types XXX_ro_t, XXX_rw_t, XXX_dropdown_t, and XXX_super_entry_t.

The tty types used are USER_devpts_t and USER_tty_device_t, or sysadm_devpts_t and sysadm_tty_device_t if USER == initrc (for system chroot's).

The domain XXX_t is used for programs running in the chroot environment. It can read and write to the tty devices.

The type XXX_rw_t is for files/directories in the chroot that can be read and written by the domain XXX_t.

The type XXX_ro_t is for files/directories in the chroot that can be read but not written by the domain XXX_t.

The domain XXX_super_t is a domain for running in the chroot that can see and kill processes in the XXX_t domain (but not be seen or killed by them). It gets read/write/ioctl access to the tty devices.

Also it can write to XXX_ro_t, it is to be used for administration of the chroot environment. When XXX_super_t executes a program of type XXX_rw_t then it transitions to domain XXX_t. However this does not entirely prevent you from accidentally running code written by a XXX_t domain while running as XXX_super_t - watch out for scripts and configuration files containing names of programs to execute. As long as /etc is of type XXX_ro_t and XXX_dropdown_t and /lib and /usr are of XXX_ro_t then it should be fine.

The type XXX_super_entry_t is used as an entry point into the XXX_super_t domain. I suggest having a copy of the shell with this type for entering into administration mode (but a package administration program such as dselect could have it too).

The type XXX_dropdown_t is used for the contents of /etc/init.d or other files that should trigger a domain change from XXX_super_t to XXX_t. The idea is that when a package administration program such as dpkg runs a script to start a daemon then the daemon should automatically transition to XXX_t. This requires that /etc/init.d also have the type XXX_dropdown_t so that new scripts also have that type.

The domain USER_chroot_t is used for the chroot program, it has the chroot capability and has access to read directories and execute files under the chroot.

When the domain user_XXX executes the chroot program (type chroot_exec_t) then it transitions to USER_chroot_t. When USER_chroot_t executes XXX_ro_t, XXX_rw_t, or XXX_dropdown_t then it transitions to XXX_t. When it executes XXX_super_entry_t it transitions to XXX_super_t.

Also there is a domain USER_mount_t used for mounting devices under the chroot environment. A script for bind mounting /dev entries and mounting /proc is below. NB the domain USER_mount_t can not be entered from processes in XXX_t or XXX_super_t domains - this has to be setup from outside the chroot!

The general idea is that when installing a chroot you have the default type be XXX_ro_t, then you have /dev, /dev/pts, /var, /home, /tmp with type XXX_rw_t, and /var/lib/dpkg, /var/cache/apt/archives, and /var/{lib,state}/apt with type XXX_ro_t so processes in domain XXX_t can't mess with the installation of new packages.

The role used for all domains is USER_r (or system_r if USER == initrc).

The end result is that as root in the chroot environment you can create new users, delete users, manage users' processes, etc. But you can't change system files or do anything outside the chroot.

Script to mount the file systems

#!/bin/bash -e

if [ "$1" == "mount" ]; then
  cd $2
  mount -n none -t proc proc
  mount -n /dev dev --bind
fi
if [ "$1" == "umount" ]; then
  cd $2
  umount -n `pwd`/proc/
  umount -n `pwd`/dev
fi

Script to set the initial directories and permissions

#!/bin/bash -e

# the first parameter is the full path to the root directory
# the second parameter is the name of the chroot (the second parameter to
# the chroot() macro

echo mkdir -p $1
echo cd $1
IDENTITY=`id -c | cut -f1 -d:`
BASECON=$IDENTITY:object_r:$2
chcon ${BASECON}_ro_t .
mkdir bin dev etc home lib proc root sbin tmp usr var
chcon ${BASECON}_rw_t home root tmp
mkdir -p var/lib/dpkg var/tmp etc/init.d
chcon ${BASECON}_rw_t var/tmp
chcon ${BASECON}_dropdown_t etc/init.d
echo "Everything under /var other than /var/lib should be"
echo "${BASECON}_rw_t"
echo "Everything under /var/lib other than /var/lib/dpkg should be"
echo "${BASECON}_rw_t"