Merge "Fix /etc/group having desynced IDs from the actual UID in the sandbox" into main

This commit is contained in:
jade 2024-05-05 03:57:29 +00:00 committed by Gerrit Code Review
commit fb5d6f325b
3 changed files with 95 additions and 22 deletions

View file

@ -215,17 +215,6 @@ void LocalDerivationGoal::tryLocalBuild()
#endif #endif
} }
#if __linux__
if (useChroot) {
if (!mountAndPidNamespacesSupported()) {
if (!settings.sandboxFallback)
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
useChroot = false;
}
}
#endif
if (useBuildUsers()) { if (useBuildUsers()) {
if (!buildUser) if (!buildUser)
buildUser = acquireUserLock(parsedDrv->useUidRange() ? 65536 : 1, useChroot); buildUser = acquireUserLock(parsedDrv->useUidRange() ? 65536 : 1, useChroot);
@ -239,6 +228,26 @@ void LocalDerivationGoal::tryLocalBuild()
} }
} }
#if __linux__
// FIXME: should user namespaces being unsupported also require
// sandbox-fallback to be allowed? I don't think so, since they aren't a
// huge security win to have enabled.
usingUserNamespace = userNamespacesSupported();
if (useChroot) {
if (!mountAndPidNamespacesSupported()) {
if (!settings.sandboxFallback)
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing. Pass --debug for diagnostics on what is broken.");
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
useChroot = false;
}
if (!usingUserNamespace && !buildUser) {
throw Error("cannot perform a sandboxed build because user namespaces are not available.\nIn this Lix's configuration, user namespaces are required due to either being non-root, or build-users-group being disabled without also enabling auto-allocate-uids");
}
}
#endif
actLock.reset(); actLock.reset();
try { try {
@ -697,13 +706,6 @@ void LocalDerivationGoal::startBuilder()
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536)) if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name); throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name);
/* Declare the build user's group so that programs get a consistent
view of the system (e.g., "id -gn"). */
writeFile(chrootRootDir + "/etc/group",
fmt("root:x:0:\n"
"nixbld:!:%1%:\n"
"nogroup:x:65534:\n", sandboxGid()));
/* Create /etc/hosts with localhost entry. */ /* Create /etc/hosts with localhost entry. */
if (derivationType->isSandboxed()) if (derivationType->isSandboxed())
writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n"); writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n");
@ -911,8 +913,6 @@ void LocalDerivationGoal::startBuilder()
userNamespaceSync.create(); userNamespaceSync.create();
usingUserNamespace = userNamespacesSupported();
Pipe sendPid; Pipe sendPid;
sendPid.create(); sendPid.create();
@ -981,8 +981,6 @@ void LocalDerivationGoal::startBuilder()
fmt("%d %d %d", sandboxGid(), hostGid, nrIds)); fmt("%d %d %d", sandboxGid(), hostGid, nrIds));
} else { } else {
debug("note: not using a user namespace"); debug("note: not using a user namespace");
if (!buildUser)
throw Error("cannot perform a sandboxed build because user namespaces are not enabled; check /proc/sys/user/max_user_namespaces");
} }
/* Now that we now the sandbox uid, we can write /* Now that we now the sandbox uid, we can write
@ -993,6 +991,13 @@ void LocalDerivationGoal::startBuilder()
"nobody:x:65534:65534:Nobody:/:/noshell\n", "nobody:x:65534:65534:Nobody:/:/noshell\n",
sandboxUid(), sandboxGid(), settings.sandboxBuildDir)); sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
/* Declare the build user's group so that programs get a consistent
view of the system (e.g., "id -gn"). */
writeFile(chrootRootDir + "/etc/group",
fmt("root:x:0:\n"
"nixbld:!:%1%:\n"
"nogroup:x:65534:\n", sandboxGid()));
/* Save the mount- and user namespace of the child. We have to do this /* Save the mount- and user namespace of the child. We have to do this
*before* the child does a chroot. */ *before* the child does a chroot. */
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY)}; sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY)};

View file

@ -0,0 +1,66 @@
# Lix should be able to build derivations that want working NSS, even with
# broken user namespaces support
{ ... }:
let
testDerivation = builtins.toFile "test.nix" ''
{ cacheBreak }:
let pkgs = import <nixpkgs> { };
in
pkgs.runCommand "test" { } '''
# ''${cacheBreak}
id -g
id -u
echo "GROUP"
cat /etc/group
echo "PASSWD"
cat /etc/passwd
username=$(id -un)
groupname=$(id -gn)
[[ "$username" =~ nixbld* ]]
[[ "$groupname" =~ nixbld* ]]
touch $out
'''
'';
in
{
name = "broken-userns";
nodes.machine =
{
config,
lib,
pkgs,
...
}:
{
virtualisation.writableStore = true;
nix.settings.substituters = lib.mkForce [ ];
nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
virtualisation.additionalPaths = [
pkgs.stdenvNoCC
testDerivation
];
};
testScript =
{ nodes }:
''
start_all()
# Building it normally should work
machine.succeed(r"""
nix-build --argstr cacheBreak 1 --store daemon ${testDerivation}
""")
# Building it with broken userns should also work
machine.succeed(r"""
# break user ns
sysctl -w user.max_user_namespaces=0
""")
machine.systemctl("restart nix-daemon")
machine.succeed(r"""
nix-build --argstr cacheBreak 2 --store daemon ${testDerivation}
""")
'';
}

View file

@ -164,4 +164,6 @@ in
symlinkResolvconf = runNixOSTestFor "x86_64-linux" ./symlink-resolvconf.nix; symlinkResolvconf = runNixOSTestFor "x86_64-linux" ./symlink-resolvconf.nix;
rootInSandbox = runNixOSTestFor "x86_64-linux" ./root-in-sandbox; rootInSandbox = runNixOSTestFor "x86_64-linux" ./root-in-sandbox;
broken-userns = runNixOSTestFor "x86_64-linux" ./broken-userns.nix;
} }