Hi,

I've send an updated version of autopkgtest-virt-unshare as a merge request:

https://salsa.debian.org/ci-team/autopkgtest/-/merge_requests/138

Cheers Jochen

* Jochen Sprickerhof <jspri...@debian.org> [2022-04-17 22:10]:
Hi,

* Martin Pitt <mp...@debian.org> [2016-11-16 13:37]:
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.

Nice!

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
lxc-usernsexec:

$ unshare -rmU  sh -c 'whoami; mount -t tmpfs foo /mnt; touch /mnt/foo; ls -l 
/mnt/foo'
root
-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?

I have implemented this in autopkgtest-virt-unshare (attached). Would be great to get it into the autopkgtest package.

$ 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 implemented this as well.

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
 virt/autopkgtest-virt-chroot.1.

I can look into this if you are fine with the script in general.

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.

done.

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?

done.

|     # 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?

done.

|     # 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/?

done.

| 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
pipe.

done.

Cheers Jochen

Attachment: signature.asc
Description: PGP signature

Reply via email to