On Sun, Jan 14, 2024 at 06:36:29PM +0100, Francesco Poli wrote:
> Of course, I have!   ;-)
> 
> For privacy reasons: I don't want other users to be able to enter my
> home directory and to read any file within it that I might have
> forgotten with world-readable permissions!

I agree that this is good practice. It's just that working with
namespaces tends to make this annoying. We're still in search for a more
satisfying solution. For now, I'm happy to have identified the cause of
your failure.

> the final touches to sid_amd64.img will be put with the file in its
> intended destination directory, which is /home/${USER}/mysubdir, since
> the command-line argument was "sid_amd64.img", a relative path to a
> file directly within the current working directory (~/mysubdir).

That command that fails is mkfs.ext4. This command is run inside the
user namespace that mmdebstrap creates for constructing the chroot. The
uids 0 to 65535 inside the user namespace correspond to your first
subuid range that is large enough. The mkfs.ext4 operation is performed
by the root user inside the user namespace. In the initial namespace
that root user is your first subuid. In particular, it is not your user
but a different user and this user has no permission to access your
output image.

> And these final touches require all parent directories (up to the root
> directory) to be world-executable.

Technically speaking, this is a sufficient but not strictly necessary
precondition. What we actually needs is your first subuid to be able to
open the output image for writing. This is difficult to achieve. The
method implemented in mmdebstrap-autopkgtest-build-qemu is to
temporarily change the ownership of the output image to the first
subuid. While chown is normally a privileged operation, you can chown
inside your namespace if both the original owner and the new owner are
mapped inside your user namespace. This is not the case for the
namespace that mmdebstrap creates, but before calling mmdebstrap, we
construct another namespace suitable for performing this chown.

What we don't do is adjust the permission of leading directory
components. The ability for opening the output image depends on both the
final component having a +w bit in an appropriate place (and since we
care fully chose the owner this is u+w) and leading directory components
being +x (and since these are neither owned by the first subuid nor the
first subgid, it amounts to o+x).

> In other words, the user would need:
> 
>   $ stat -c "%a %n" ~/mysubdir ~ /home /
>   771 /home/${USER}/mysubdir
>   701 /home/${USER}
>   755 /home
>   755 /
> 
> rather than:
> 
>   $ stat -c "%a %n" ~/mysubdir ~ /home /
>   770 /home/${USER}/mysubdir
>   700 /home/${USER}
>   755 /home
>   755 /
> 
> Is that correct?
> Or am I completely off-track?

This is one way to establish the actual precondition. Let me suggest
some alternatives. One is storing the output image outside $HOME. If you
put it into /tmp and the move it from /tmp into your home, that should
work as well. Another is using ACLs. You might add an ACL that
specifically grants your subuid range execute permission on your home
but not every user.

The available options to improve this are not super nice. A simple
workaround is creating the output image as a temporary file inside the
namespace and then copying it out. This will perform a 1GB copy
operation that we'd like to avoid (and rather construct the filesystem
in-place) for performance reasons, but maybe usability beats
performance? I'm also not sure how mmdebstrap downloads sparse files. It
might make them un-sparse and that'd be quite bad for this use case as
you'd write 25GB of zeros. Another option could be using fuse. In
principle, we could have a fuse driver running in the initial namespace
that just serves the output image. Then inside the namespace we could
mount this fuse filesystem (which becomes a regular file, not a
directory) and write to it. Having the fuse driver run in one namespace
and the fuse mount in a different namespace resolves the permission
problem. This is all non-trivial to implement though. The plumbing isn't
up to the task. Maybe there are other options that I don't see at this
time?

> It looks like it worked, but, unfortunately, it (again) fails to be
> usable with autopkgtest:
> 
>   $ autopkgtest --output-dir ./${SRCPKG}_autopkgtest.out  --summary 
> ./${SRCPKG}_autopkgtest.summary --apt-upgrade -B ./${SRCPKG}_amd64.changes -- 
> qemu --overlay-dir /dev/shm ~/Downloads/sid_amd64.img
>   autopkgtest [18:24:23]: starting date and time: 2024-01-14 18:24:23+0100
>   autopkgtest [18:24:23]: version 5.32
>   autopkgtest [18:24:23]: host ${HOST}; command line: /usr/bin/autopkgtest 
> --output-dir './${SRCPKG}_autopkgtest.out' --summary 
> './${SRCPKG}_autopkgtest.summary' --apt-upgrade -B 
> './${SRCPKG}_amd64.changes' -- qemu --overlay-dir /dev/shm 
> ${HOME}/Downloads/sid_amd64.img
>   qemu-system-x86_64: terminating on signal 15 from pid 115770 
> (/usr/bin/python3)
>   <VirtSubproc>: failure: timed out waiting for 'login prompt on serial 
> console'
>   autopkgtest [18:25:24]: ERROR: testbed failure: unexpected eof from the 
> testbed
> 
> 
> What did I fail to understand?

The difficulty resides in making a bootable image with no root
privileges. mmdebstrap-autopkgtest-build-qemu is not fully compatible
with autopkgtest-build-qemu. It specifically required you to pass
--boot=efi, right? On amd64, autopkgtest-virt-qemu defaults to bios
boot. In order to use this image, you likewise have to pass --boot=efi
to the virtualization backend.

Helmut

Reply via email to