Bug#844459: autopkgtest: Please add autopkgtest-virt-uchroot

Martin Pitt mpitt at debian.org
Wed Nov 16 12:37:50 UTC 2016

Hello Josch,

Johannes Schauer [2016-11-16  1:12 +0100]:
> in the context of #833407 I told you about my plan of adding a
> virtualization backend which would allow completely unprivileged chroot
> operation by using linux user namespaces.


> In contrast to what I thought was required back then, I now managed
> to write that backend using just lxc-usernsexec and lxc-unshare.
> Thus, I was able to get it to work using the existing Python
> modules. You can find the script attached.  As you can see, it is
> extremely simple, which I find makes the beauty of it all. All you
> need is:

>  - the lxc package installed for lxc-usernsexec and lxc-unshare

I'd like to eliminate this even. util-linux' unshare has known about
--user/-U for a while now, and thus replaces lxc-unshare and

  $ unshare -rmU  sh -c 'whoami; mount -t tmpfs foo /mnt; touch /mnt/foo; ls -l /mnt/foo'
  -rw-r--r-- 1 root root 0 Nov 16 12:59 /mnt/foo

And you can use util-linux' nsenter to enter an existing namespace.

These lxc-* tools were written before util-linux learned about those,
and I'm not sure if they are going to stick around forever as they are
basically obsolete. It would also avoid the lxc dependency.

Would you be willing to try this?

>  - sbuild from git (a tiny fix to its autopkgtest backend is required)

OOI, is that required for running autopkgtest with uchroot itself, or
"just" sbuild's integration with automatically running autopkgtest
after a package build?

> $ sbuild --chroot-mode=autopkgtest --autopkgtest-virt-server=uchroot \
>     --autopkgtest-virt-server-opts="-- /srv/chroot/%r-%a-sbuild.tar.gz /tmp/rootfs"
> The path /tmp/rootfs is the path that the rootfs will be extracted to
> and can be at any location that the user has access to.

I think it would be more comfortable to use mkdtemp() by default, and
provide --unpack-dir as an option?

> I don't think there is an existing backend which allows unprivileged
> package building with so little overhead in terms of configuration.

lxd is quite nice and similarly easy (and a bit more powerful too),
but unfortunately not available in Debian right now.

> It would be great if this backend could be added to autopkgtest itself.
> If you think that it is not a good fit for autopkgtest, then I can
> maintain it in a separate package.

I think it would be a great fit, but in order to accept it I have some
stricter requirements:

 * tests/autopkgtest should run at least the standard
   DebTestsVirtContainer tests. Look at classes LxcRunner and LxdRunner, should
   be a fairly simple extension.

   This will show the limits of what the backend can do, uncover
   possible encoding/locale/whatever issues, and ensure that this will
   keep working over time.

 * It should get a manpage, probably starting from

As building such a chroot tarball doesn't require new tools, that
should be it (the manpage should just explain how to build them, with
sbuild-createchroot or mk-sbuild).

I actually have wanted to deprecate the "chroot" backend for a long
time, as it's inherently insecure and I never use it myself any more.
I wonder if uschroot could completely replace that? At first sight it
should have the same isolation and robustness capabilities like
lxc/lxd (at least wrt. the file system and mounting), except with a
lot fewer dependencies.

| tarball = None
| rootdir = None
| def parse_args():
|     global tarball, rootdir
|     parser = argparse.ArgumentParser()
|     parser.add_argument('-d', '--debug', action='store_true',
|                         help='Enable debugging output')
|     parser.add_argument('tarball', help='path to rootfs tarball')
| def hook_open():
|     global tarball, rootdir
|     # We want to find out our user and group id inside the chroot but we want
|     # to avoid having to parse /etc/subuid and /etc/subgid. We solve the
|     # situation by creating a temporary file from inside the user namespace
|     # and then checking its user and group ids from outside the user namespace.
|     probe = VirtSubproc.check_exec(['lxc-usernsexec', 'mktemp',
|                                     '/tmp/uchroot.XXXXXX'], outp=True)
|     inner_uid = os.stat(probe)[stat.ST_UID]
|     inner_gid = os.stat(probe)[stat.ST_GID]
|     VirtSubproc.check_exec(['lxc-usernsexec', 'rm', probe])
|     outer_uid = os.getuid()
|     outer_gid = os.getgid()

This dance wouldn't even be necessary with unshare -rU -- you know
that the outside uid/gid is just the normal user, and the inside one
is root/root.

I'm not sure if there is something to be gained from the UID shift --
that isolates the chroot test better, but also makes it much harder to
clean up after a failed tests, as your normal user cannot touch/rm the
temporary directories? But if you want this, there's newuidmap(1).

|     # Unpack the tarball into the new directory.
|     # Make sure not to extract any character special files because we cannot
|     # mknod.
|     VirtSubproc.check_exec(['lxc-usernsexec', '--', 'tar',
|                             '--exclude=./dev/urandom',

Eek, do chroot tarballs regularly have /dev in them? Might be easier
and safer to exclude /dev/ wholesale, as you provide a minimal /dev
later on anyway?

|     # A shell script that prepares the environment by bind-mounting all the
|     # important things.
|     # The chmod is done such that somebody accidentally using the chroot
|     # without the right bind-mounts will not fill up their disk.
|     shellcommand = """
|     mkdir -p {rootdir}/dev
|     touch {rootdir}/dev/null
|     chmod -rwx {rootdir}/dev/null
|     mount -o bind /dev/null {rootdir}/dev/null
|     touch {rootdir}/dev/zero
|     chmod -rwx {rootdir}/dev/zero
|     mount -o bind /dev/zero {rootdir}/dev/zero
|     touch {rootdir}/dev/full
|     chmod -rwx {rootdir}/dev/full
|     mount -o bind /dev/full {rootdir}/dev/full
|     touch {rootdir}/dev/random
|     chmod -rwx {rootdir}/dev/random
|     mount -o bind /dev/random {rootdir}/dev/random
|     touch {rootdir}/dev/urandom
|     chmod -rwx {rootdir}/dev/urandom
|     mount -o bind /dev/urandom {rootdir}/dev/urandom
|     touch {rootdir}/dev/tty
|     chmod -rwx {rootdir}/dev/tty
|     mount -o bind /dev/tty {rootdir}/dev/tty

Would you mind putting this into a loop?

|     # Test whether the auxverb is able to successfully run /bin/true
|     status = VirtSubproc.execute_timeout(None, 5,
|                                          VirtSubproc.auxverb + ['true'])[0]
|     if status != 0:
|         VirtSubproc.bomb('failed to connect to VM')

s/connect to VM/enter user chroot/?

| def hook_capabilities():
|     return ['revert', 'root-on-testbed']

Please arrange for downtmp-host= to be set (like in virt-chroot). A
shared directory should be fairly simple to do for this runner, and
it's much more efficient than squeezing everything through tar and a

Thanks for working on this!

Martin Pitt                        | http://www.piware.de
Ubuntu Developer (www.ubuntu.com)  | Debian Developer  (www.debian.org)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.alioth.debian.org/pipermail/autopkgtest-devel/attachments/20161116/8a5f5b75/attachment.sig>

More information about the autopkgtest-devel mailing list