Repository: systemd/mkosi

Issue:
unshare --user --map-root-user mkosi qemu fails
mkosi fails to start QEMU in a user namespace with mapped UIDs:

```
$ unshare --user --map-root-user --net mkosi --debug qemu
‣ Including configuration file /home/me/q/mkosi.local.conf
‣ Including configuration file /home/me/q/mkosi.conf
‣ + timedatectl show -p Timezone --value
Bus n/a: changing state UNSET → OPENING
sd-bus: starting bus by connecting to /run/dbus/system_bus_socket...
Bus n/a: changing state OPENING → AUTHENTICATING
Bus n/a: changing state AUTHENTICATING → HELLO
Sent message type=method_call sender=n/a destination=org.freedesktop.DBus path=/org/freedesktop/DBus interface=org.freedesktop.DBus member=Hello cookie=1 reply_cookie=0 signature=n/a error-name=n/a error-message=n/a
Got message type=method_return sender=org.freedesktop.DBus destination=:1.411 path=n/a interface=n/a member=n/a  cookie=4294967295 reply_cookie=1 signature=s error-name=n/a error-message=n/a
Bus n/a: changing state HELLO → RUNNING
Sent message type=method_call sender=n/a destination=org.freedesktop.timedate1 path=/org/freedesktop/timedate1 interface=org.freedesktop.DBus.Properties member=GetAll cookie=2 reply_cookie=0 signature=s error-name=n/a error-message=n/a
Got message type=method_return sender=:1.412 destination=:1.411 path=n/a interface=n/a member=n/a  cookie=5 reply_cookie=2 signature=a{sv} error-name=n/a error-message=n/a
Bus n/a: changing state RUNNING → CLOSED
‣ + openssl x509 -in /home/me/q/mkosi.crt -pubkey -noout
‣ + ssh-keygen -f /dev/stdin -i -m PKCS8
‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-0b8b0bcc35b149c1'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-0b8b0bcc35b149c1 && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-0b8b0bcc35b149c1 /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' qemu-system-aarch64 --version
‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-9d03daaca3854dfe'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-9d03daaca3854dfe && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-9d03daaca3854dfe /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' qemu-system-aarch64 --version
‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-c7565a412a154317'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-c7565a412a154317 && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-c7565a412a154317 /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /home/me/q/var/mkosi/output/image.vmlinuz /home/me/q/var/mkosi/output/image.vmlinuz --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' bootctl kernel-identify /home/me/q/var/mkosi/output/image.vmlinuz
Failed to read $container of PID 1, ignoring: Permission denied
statfs("/sys/fs/cgroup/") failed: No such file or directory
Failed to detect cgroup namespace: No such file or directory
Found container virtualization none.
‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-0d59c09b67f947ab'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-0d59c09b67f947ab && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-0d59c09b67f947ab /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /home/me/q/var/mkosi/output/image.raw /home/me/q/var/mkosi/output/image.raw --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' systemd-repart --json=short /home/me/q/var/mkosi/output/image.raw
‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-3bda3fce071e4562'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-3bda3fce071e4562 && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-3bda3fce071e4562 /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --bind /var/tmp/mkosi-scratchbgucjzti /var/tmp/mkosi-scratchbgucjzti --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' mkfs.btrfs -L scratch /var/tmp/mkosi-scratchbgucjzti
WARNING: sectorsize 4096 does not match host CPU page size 16384, with kernels 6.x and up
	 the 4KiB sectorsize is supported on all architectures but other combinations
	 may fail the filesystem mount, use "--sectorsize 16384" to override that

‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-c4ed826e6f5e43c3'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-c4ed826e6f5e43c3 && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-c4ed826e6f5e43c3 /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /home/me/q/var/mkosi/output/image.vmlinuz /home/me/q/var/mkosi/output/image.vmlinuz --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' bootctl kernel-identify /home/me/q/var/mkosi/output/image.vmlinuz
Failed to read $container of PID 1, ignoring: Permission denied
statfs("/sys/fs/cgroup/") failed: No such file or directory
Failed to detect cgroup namespace: No such file or directory
Found container virtualization none.
‣ + sh -c 'trap '"'"'rm -rf /var/tmp/mkosi-var-tmp-fb3281872d9f4641'"'"' EXIT && mkdir --mode 1777 /var/tmp/mkosi-var-tmp-fb3281872d9f4641 && $0 "$@"' bwrap --ro-bind /usr /usr --unshare-net --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 0 --tmpfs /tmp --unshare-ipc --dev /dev --bind /var/tmp/mkosi-var-tmp-fb3281872d9f4641 /var/tmp --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /home/me/q/var/mkosi/output/image.vmlinuz /home/me/q/var/mkosi/output/image.vmlinuz --ro-bind /etc/alternatives /etc/alternatives sh -c 'chmod 1777 /dev/shm && exec $0 "$@"' bootctl kernel-identify /home/me/q/var/mkosi/output/image.vmlinuz
Failed to read $container of PID 1, ignoring: Permission denied
statfs("/sys/fs/cgroup/") failed: No such file or directory
Failed to detect cgroup namespace: No such file or directory
Found container virtualization none.
‣ + bwrap --ro-bind /usr /usr --die-with-parent --proc /proc --setenv SYSTEMD_OFFLINE 1 --bind /tmp /tmp --bind /sys /sys --bind /run /run --dev-bind /dev /dev --bind /etc /etc --bind /opt /opt --bind /srv /srv --bind /media /media --bind /mnt /mnt --bind /var /var --bind /root /root --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --setenv PATH :/home/me/.local/bin:/home/me/bin:/usr/lib/qtchooser:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/me/depot_tools:/home/me/depot_tools --ro-bind /etc/alternatives /etc/alternatives sh -c ': && exec $0 "$@"' qemu-system-aarch64 -machine type=virt -smp 1 -m 14G -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0,id=rng-device0 -nic user,model=virtio-net-pci -accel kvm -device vhost-vsock-pci,guest-cid=900813514,vhostfd=4 -cpu max -nographic -nodefaults -chardev stdio,mux=on,id=console,signal=off -serial chardev:console -mon console -fw_cfg name=opt/io.systemd.credentials/agetty.autologin,file=/tmp/mkosi-fw-cfgzthh8oa8 -fw_cfg name=opt/io.systemd.credentials/login.noauth,file=/tmp/mkosi-fw-cfghuiybd42 -fw_cfg name=opt/io.systemd.credentials/firstboot.timezone,file=/tmp/mkosi-fw-cfguj3iqu9q -fw_cfg name=opt/io.systemd.credentials/firstboot.locale,file=/tmp/mkosi-fw-cfghko06934 -fw_cfg name=opt/io.systemd.credentials/ssh.authorized_keys.root,file=/tmp/mkosi-fw-cfgk32y_ejg -kernel /home/me/q/var/mkosi/output/image.vmlinuz -device virtio-scsi-pci,id=scsi -drive if=none,id=scratch,file=/var/tmp/mkosi-scratchbgucjzti,format=raw -device scsi-hd,drive=scratch -append 'console=ttyAMA0 systemd.wants=network.target module_blacklist=vmw_vmci systemd.tty.term.ttyAMA0=xterm-256color systemd.tty.columns.ttyAMA0=254 systemd.tty.rows.ttyAMA0=60 ip=enc0:any ip=enp0s1:any ip=enp0s2:any ip=host0:any ip=none loglevel=4 SYSTEMD_SULOGIN_FORCE=1 systemd.tty.term.console=xterm-256color systemd.tty.columns.console=254 systemd.tty.rows.console=60 console=ttyAMA0 root=PARTUUID=4609b1f8-0567-46e2-932a-f37dcc36a329 systemd.mount-extra=LABEL=scratch:/var/tmp:btrfs' -initrd /home/me/q/var/mkosi/output/image.initrd -drive if=none,id=mkosi,file=/home/me/q/var/mkosi/output/image.raw,format=raw -device scsi-hd,drive=mkosi,bootindex=1
qemu-system-aarch64: -drive if=none,id=mkosi,file=/home/me/q/var/mkosi/output/image.raw,format=raw: Could not open '/home/me/q/var/mkosi/output/image.raw': No such file or directory
‣ + tput cnorm
‣ + tput smam
```

My use case is to debug QEMU with a tap device. I need to run `mkosi` within a non-root network namespace and user namespace to create a tap device without a privilege.


Discussion:

Resolving patch:
diff --git a/mkosi/qemu.py b/mkosi/qemu.py
--- a/mkosi/qemu.py
+++ b/mkosi/qemu.py
@@ -308,7 +308,7 @@ def start_virtiofsd(config: Config, directory: Path, *, uidmap: bool) -> Iterato
         "--no-announce-submounts",
         "--sandbox=chroot",
         "--cache=always",
-        f"--inode-file-handles={'mandatory' if os.getuid() == 0 and not uidmap else 'never'}",
+        f"--inode-file-handles={'prefer' if os.getuid() == 0 and not uidmap else 'never'}",
     ]
 
     if not uidmap and want_selinux_relabel(config, directory, fatal=False):
diff --git a/mkosi/sandbox.py b/mkosi/sandbox.py
--- a/mkosi/sandbox.py
+++ b/mkosi/sandbox.py
@@ -142,7 +142,11 @@ def sandbox_cmd(
 
     cmdline += [
         "bwrap",
-        *(["--unshare-net"] if not network and have_effective_cap(Capability.CAP_NET_ADMIN) else []),
+        *(
+            ["--unshare-net"]
+            if not network and (os.getuid() != 0 or have_effective_cap(Capability.CAP_NET_ADMIN))
+            else []
+        ),
         "--die-with-parent",
         "--proc", "/proc",
         "--setenv", "SYSTEMD_OFFLINE", one_zero(network),
diff --git a/mkosi/user.py b/mkosi/user.py
--- a/mkosi/user.py
+++ b/mkosi/user.py
@@ -44,6 +44,9 @@ def name(cls) -> str:
     @classmethod
     @functools.lru_cache(maxsize=1)
     def home(cls) -> Path:
+        if cls.invoked_as_root and Path.cwd().is_relative_to("/home") and len(Path.cwd().parents) > 2:
+            return list(Path.cwd().parents)[-3]
+
         return Path(f"~{cls.name()}").expanduser()
 
     @classmethod


Test patch: