diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index d2ff8a9b0..fa6874c43 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -2904,6 +2904,31 @@ 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 and AppArmor implementations of this +restriction, 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/AppArmor systems, you should be able to remove the restrictions by +adapting this snippet to point to your mkosi binary, copying it to +/etc/apparmor.d/mkosi, and then running `systemctl reload apparmor`: + +``` +abi , + +include + +/path/to/mkosi flags=(default_allow) { + userns, + + # Site-specific additions and overrides. See local/README for details. + include if exists +} +``` + # Frequently Asked Questions (FAQ) - Why does `mkosi qemu` with KVM not work on Debian/Kali/Ubuntu? diff --git a/mkosi/sandbox.py b/mkosi/sandbox.py index be1a0370f..ed2b6d94f 100755 --- a/mkosi/sandbox.py +++ b/mkosi/sandbox.py @@ -28,6 +28,7 @@ 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,9 @@ 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) finally: os.write(event, ctypes.c_uint64(1)) os.close(event) @@ -694,6 +698,16 @@ def execute(self, oldroot: str, newroot: str) -> None: """ +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 +820,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: