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. 25d4709a4f)

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
This commit is contained in:
jade 2024-05-04 00:55:15 -07:00 committed by jade
parent 9909a175bf
commit e3b702fa22

View file

@ -8,31 +8,31 @@
namespace nix { namespace nix {
bool userNamespacesSupported() static void diagnoseUserNamespaces()
{
static auto res = [&]() -> bool
{ {
if (!pathExists("/proc/self/ns/user")) { if (!pathExists("/proc/self/ns/user")) {
debug("'/proc/self/ns/user' does not exist; your kernel was likely built without CONFIG_USER_NS=y"); warn("'/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"; Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
if (!pathExists(maxUserNamespaces) || if (!pathExists(maxUserNamespaces) ||
trim(readFile(maxUserNamespaces)) == "0") trim(readFile(maxUserNamespaces)) == "0")
{ {
debug("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'"); warn("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'");
return false;
} }
Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone"; Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
if (pathExists(procSysKernelUnprivilegedUsernsClone) if (pathExists(procSysKernelUnprivilegedUsernsClone)
&& trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") && trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0")
{ {
debug("user namespaces appear to be disabled; check '/proc/sys/kernel/unprivileged_userns_clone'"); warn("user namespaces appear to be disabled for unprivileged users; check '/proc/sys/kernel/unprivileged_userns_clone'");
return false; }
} }
bool userNamespacesSupported()
{
static auto res = [&]() -> bool
{
try { try {
Pid pid = startProcess([&]() Pid pid = startProcess([&]()
{ {
@ -44,7 +44,8 @@ bool userNamespacesSupported()
auto r = pid.wait(); auto r = pid.wait();
assert(!r); assert(!r);
} catch (SysError & e) { } 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; return false;
} }