1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-08 10:01:01 +01:00

Mount allowed paths on storeFS with pure eval

No `AllowListSourceAccessor` for pure eval --- not needed anymore!
This commit is contained in:
Eelco Dolstra 2025-09-22 23:04:58 +02:00 committed by John Ericson
parent 750306234d
commit 77e988eb27
3 changed files with 77 additions and 44 deletions

View file

@ -242,8 +242,9 @@ EvalState::EvalState(
, settings{settings} , settings{settings}
, symbols(StaticEvalSymbols::staticSymbolTable()) , symbols(StaticEvalSymbols::staticSymbolTable())
, repair(NoRepair) , repair(NoRepair)
, storeFS(makeMountedSourceAccessor({ , storeFS([&] {
{CanonPath::root, makeEmptySourceAccessor()}, auto accessor = makeMountedSourceAccessor({{CanonPath::root, makeEmptySourceAccessor()}});
/* In the pure eval case, we can simply require /* In the pure eval case, we can simply require
valid paths. However, in the *impure* eval valid paths. However, in the *impure* eval
case this gets in the way of the union case this gets in the way of the union
@ -262,32 +263,58 @@ EvalState::EvalState(
exception, and make union source accessor exception, and make union source accessor
catch it, so we don't need to do this hack. catch it, so we don't need to do this hack.
*/ */
{CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)}, if (settings.pureEval) {
})) /* This is just an overkill way to make sure other store
, rootFS([&] { paths get this error, and not the "doesn't exist" error
/* In pure eval mode, we provide a filesystem that only that the mounted source accessor would do on its own. */
contains the Nix store. accessor->mount(
CanonPath::root,
Otherwise, use a union accessor to make the augmented store AllowListSourceAccessor::create(
available at its logical location while still having the getFSSourceAccessor(),
underlying directory available. This is necessary for {},
instance if we're evaluating a file from the physical {CanonPath::root, CanonPath(store->storeDir)},
/nix/store while using a chroot store, and also for lazy [&](const CanonPath & path) -> RestrictedPathError {
mounted fetchTree. */ throw RestrictedPathError(
auto accessor = settings.pureEval ? storeFS.cast<SourceAccessor>() "access to absolute path '%1%' is forbidden in pure evaluation mode (use '--impure' to override)",
: makeUnionSourceAccessor({getFSSourceAccessor(), storeFS}); CanonPath(store->storeDir) / path);
}));
/* Apply access control if needed. */ /* We don't want to list store paths */
if (settings.restrictEval || settings.pureEval) accessor->mount(CanonPath(store->storeDir), makeEmptySourceAccessor());
accessor = AllowListSourceAccessor::create( } else {
accessor, {}, {}, [&settings](const CanonPath & path) -> RestrictedPathError { accessor->mount(CanonPath(store->storeDir), store->getFSAccessor(false));
auto modeInformation = settings.pureEval ? "in pure evaluation mode (use '--impure' to override)" }
: "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
});
return accessor; return accessor;
}()) }())
, rootFS([&] -> decltype(rootFS) {
/* In pure eval mode, we provide a filesystem that only
contains the Nix store. */
if (settings.pureEval)
return storeFS;
auto makeImpureAccessor = [&]() -> decltype(rootFS) {
/* If we have a chroot store and pure eval is not enabled,
use a union accessor to make the chroot store available
at its logical location while still having the underlying
directory available. This is necessary for instance if
we're evaluating a file from the physical /nix/store
while using a chroot store. */
auto realStoreDir = dirOf(store->toRealPath(StorePath::dummy));
if (store->storeDir != realStoreDir)
return makeUnionSourceAccessor({getFSSourceAccessor(), storeFS});
return getFSSourceAccessor();
}();
/* Apply access control if needed. */
if (settings.restrictEval)
return AllowListSourceAccessor::create(
makeImpureAccessor(), {}, {}, [](const CanonPath & path) -> RestrictedPathError {
throw RestrictedPathError("access to absolute path '%1%' is forbidden in restricted mode", path);
});
return makeImpureAccessor();
}())
, corepkgsFS(make_ref<MemorySourceAccessor>()) , corepkgsFS(make_ref<MemorySourceAccessor>())
, internalFS(make_ref<MemorySourceAccessor>()) , internalFS(make_ref<MemorySourceAccessor>())
, derivationInternal{internalFS->addFile( , derivationInternal{internalFS->addFile(
@ -373,13 +400,19 @@ void EvalState::allowPathLegacy(const Path & path)
void EvalState::allowPath(const StorePath & storePath) void EvalState::allowPath(const StorePath & storePath)
{ {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>()) if (settings.pureEval) {
storeFS->mount(CanonPath(store->printStorePath(storePath)), ref{store->getFSAccessor(storePath)});
}
if (settings.restrictEval) {
auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>();
assert(rootFS2);
rootFS2->allowPrefix(CanonPath(store->printStorePath(storePath))); rootFS2->allowPrefix(CanonPath(store->printStorePath(storePath)));
}
} }
void EvalState::allowClosure(const StorePath & storePath) void EvalState::allowClosure(const StorePath & storePath)
{ {
if (!rootFS.dynamic_pointer_cast<AllowListSourceAccessor>()) if (!settings.pureEval && !settings.restrictEval)
return; return;
StorePathSet closure; StorePathSet closure;

View file

@ -17,7 +17,7 @@ SourcePath EvalState::rootPath(PathView path)
SourcePath EvalState::storePath(const StorePath & path) SourcePath EvalState::storePath(const StorePath & path)
{ {
return {rootFS, CanonPath{store->printStorePath(path)}}; return {storeFS, CanonPath{path.to_str()}};
} }
StorePath StorePath

View file

@ -67,7 +67,7 @@ ln -sfn "${_NIX_TEST_SOURCE_DIR}/restricted-secret" "${_NIX_TEST_SOURCE_DIR}/res
mkdir -p "$traverseDir" mkdir -p "$traverseDir"
# shellcheck disable=SC2001 # shellcheck disable=SC2001
goUp="..$(echo "$traverseDir" | sed -e 's,[^/]\+,..,g')" goUp="..$(echo "$traverseDir" | sed -e 's,[^/]\+,..,g')"
output="$(nix eval --raw --restrict-eval -I "$traverseDir" \ output="$(nix eval --raw --impure --restrict-eval -I "$traverseDir" \
--expr "builtins.readFile \"$traverseDir/$goUp${_NIX_TEST_SOURCE_DIR}/restricted-innocent\"" \ --expr "builtins.readFile \"$traverseDir/$goUp${_NIX_TEST_SOURCE_DIR}/restricted-innocent\"" \
2>&1 || :)" 2>&1 || :)"
echo "$output" | grep "is forbidden" echo "$output" | grep "is forbidden"