From e3b702fa2211c05ead6f030e63fc869c1b69413e Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Sat, 4 May 2024 00:55:15 -0700 Subject: [PATCH] Actually try making a userns before assuming they don't work If unprivileged userns are *believed* to be disabled (such as with "kernel.unprivileged_userns_clone = 0"), Lix would previously *give up* on trying to use a user namespace before actually trying it, even if, in cases such as unprivileged_userns_clone, it would actually be allowed since Nix has CAP_SYS_ADMIN when running as daemon. (see, e.g. https://github.com/archlinux/linux/commit/25d4709a4fc0e4f3b432c24b60dd508fb84f0cb2) We changed it to actually try it first, and then diagnose possible causes, and also to be more loud about the whole thing, using warnings instead of debugs. These warnings will only print on the first build run by the daemon, which is, tbh, eh, shrug. This is what led to us realizing that no-userns was a poorly exercised condition. Change-Id: I8e4f21afc89c574020dc7e89a560cc740ce6573a --- src/libutil/namespaces.cc | 45 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/libutil/namespaces.cc b/src/libutil/namespaces.cc index f66accb10..dec3a7189 100644 --- a/src/libutil/namespaces.cc +++ b/src/libutil/namespaces.cc @@ -8,31 +8,31 @@ namespace nix { +static void diagnoseUserNamespaces() +{ + if (!pathExists("/proc/self/ns/user")) { + warn("'/proc/self/ns/user' does not exist; your kernel was likely built without CONFIG_USER_NS=y"); + } + + Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces"; + if (!pathExists(maxUserNamespaces) || + trim(readFile(maxUserNamespaces)) == "0") + { + warn("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'"); + } + + Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone"; + if (pathExists(procSysKernelUnprivilegedUsernsClone) + && trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") + { + warn("user namespaces appear to be disabled for unprivileged users; check '/proc/sys/kernel/unprivileged_userns_clone'"); + } +} + bool userNamespacesSupported() { static auto res = [&]() -> bool { - if (!pathExists("/proc/self/ns/user")) { - debug("'/proc/self/ns/user' does not exist; your kernel was likely built without CONFIG_USER_NS=y"); - return false; - } - - Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces"; - if (!pathExists(maxUserNamespaces) || - trim(readFile(maxUserNamespaces)) == "0") - { - debug("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'"); - return false; - } - - Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone"; - if (pathExists(procSysKernelUnprivilegedUsernsClone) - && trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") - { - debug("user namespaces appear to be disabled; check '/proc/sys/kernel/unprivileged_userns_clone'"); - return false; - } - try { Pid pid = startProcess([&]() { @@ -44,7 +44,8 @@ bool userNamespacesSupported() auto r = pid.wait(); assert(!r); } catch (SysError & e) { - debug("user namespaces do not work on this system: %s", e.msg()); + warn("user namespaces do not work on this system: %s", e.msg()); + diagnoseUserNamespaces(); return false; }