]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
sandbox: Spit out some info when initial unshare gets EPERM
authorBrendan Jackman <jackmanb@chromium.org>
Sun, 8 Dec 2024 12:03:57 +0000 (13:03 +0100)
committerJörg Behrmann <behrmann@physik.fu-berlin.de>
Mon, 9 Dec 2024 20:18:10 +0000 (21:18 +0100)
To try and minimise the pain of this issue
(https://github.com/systemd/mkosi/issues/3265), dump some info that might help
users resolve it.

I had a quick look around expecting to find a document from Red Hat discussing
this topic much like the Ubuntu one I've linked here, but I didn't find it.
Hopefully if it exists someone else can add it later.

I'm doing this via a direct write to stderr because of the comment at the top of
sandbox.py saying to avoid imports. If this is highly undesirable it looks like
log.log_notice would  be the right choice here (then you don't need the
annoying ANSI codes).

mkosi/resources/man/mkosi.1.md
mkosi/sandbox.py

index d2ff8a9b05f25dc4900e50127cb2646d9e292e25..ab842ec1f2059508c94bedc7bd71b45283192677 100644 (file)
@@ -2904,6 +2904,29 @@ images you want to build.
 
 Note that the minimum required Python version is 3.9.
 
+mkosi needs unrestricted abilities to create and act within namespaces. Some
+distros restrict creation of, or capabilities within, user namespaces, which
+breaks mkosi.
+
+For information about Ubuntu, that implements such restrictions using AppArmor, see
+https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces.
+For other systems, try researching the `kernel.unprivileged_userns_clone` or
+`user.max.user_namespace` sysctls.
+
+For Ubuntu systems, you can remove the restrictions for mkosi by
+adapting this snippet to point to your mkosi binary, copying it to
+`/etc/apparmor.d/path.to.mkosi`, and then running `systemctl reload apparmor`:
+
+```
+abi <abi/4.0>,
+
+include <tunables/global>
+
+/path/to/mkosi flags=(default_allow) {
+  userns,
+}
+```
+
 # Frequently Asked Questions (FAQ)
 
 - Why does `mkosi qemu` with KVM not work on Debian/Kali/Ubuntu?
index be1a0370f95aba307e0afe4090c6dc58d1e87fed..51ae6a3cf7c0e990a0a17719b974b7accd1e9690 100755 (executable)
@@ -28,6 +28,7 @@ CLONE_NEWIPC = 0x08000000
 CLONE_NEWNET = 0x40000000
 CLONE_NEWNS = 0x00020000
 CLONE_NEWUSER = 0x10000000
+EPERM = 1
 ENOENT = 2
 LINUX_CAPABILITY_U32S_3 = 2
 LINUX_CAPABILITY_VERSION_3 = 0x20080522
@@ -342,6 +343,10 @@ def become_user(uid: int, gid: int) -> None:
 
     try:
         unshare(CLONE_NEWUSER)
+    except OSError as e:
+        if e.errno == EPERM:
+            print(UNSHARE_EPERM_MSG, file=sys.stderr)
+        raise
     finally:
         os.write(event, ctypes.c_uint64(1))
         os.close(event)
@@ -694,6 +699,16 @@ See the mkosi-sandbox(1) man page for details.\
 """
 
 
+UNSHARE_EPERM_MSG = """
+mkosi was forbidden to unshare namespaces.
+
+This probably means your distribution has restricted unprivileged user namespaces.
+
+Please consult the REQUIREMENTS section of the mkosi man page, e.g. via "mkosi
+documentation", for workarounds.
+"""
+
+
 def main() -> None:
     # We don't use argparse as it takes +- 10ms to import and since this is purely for internal
     # use, it's not necessary to have good UX for this CLI interface so it's trivial to write
@@ -806,7 +821,14 @@ def main() -> None:
     if suppress_chown and (userns or userns_has_single_user()):
         seccomp_suppress_chown()
 
-    unshare(namespaces)
+    try:
+        unshare(namespaces)
+    except OSError as e:
+        # This can happen here as well as in become_user, it depends on exactly
+        # how the userns restrictions are implemented.
+        if e.errno == EPERM:
+            print(UNSHARE_EPERM_MSG, file=sys.stderr)
+        raise
 
     # If we unshared the user namespace the mount propagation of root is changed to slave automatically.
     if not userns: