1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-23 02:39:37 +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 3bf1268ac6
commit 80edaab12f
2 changed files with 63 additions and 37 deletions

View file

@ -213,35 +213,57 @@ 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
valid paths. However, in the *impure* eval
case this gets in the way of the union
mechanism, because an invalid access in the
upper layer will *not* be caught by the union
source accessor, but instead abort the entire
lookup.
This happens when the store dir in the /* In the pure eval case, we can simply require
ambient file system has a path (e.g. because valid paths. However, in the *impure* eval
another Nix store there), but the relocated case this gets in the way of the union
store does not. mechanism, because an invalid access in the
upper layer will *not* be caught by the union
source accessor, but instead abort the entire
lookup.
TODO make the various source accessors doing This happens when the store dir in the
access control all throw the same type of ambient file system has a path (e.g. because
exception, and make union source accessor another Nix store there), but the relocated
catch it, so we don't need to do this hack. store does not.
*/
{CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)},
}))
, rootFS([&] {
auto accessor = [&]() -> decltype(rootFS) {
/* In pure eval mode, we provide a filesystem that only
contains the Nix store. */
if (settings.pureEval)
return storeFS;
TODO make the various source accessors doing
access control all throw the same type of
exception, and make union source accessor
catch it, so we don't need to do this hack.
*/
if (settings.pureEval) {
/* This is just an overkill way to make sure other store
paths get this error, and not the "doesn't exist" error
that the mounted source accessor would do on its own. */
accessor->mount(
CanonPath::root,
AllowListSourceAccessor::create(
getFSSourceAccessor(),
{},
{CanonPath::root, CanonPath(store->storeDir)},
[&](const CanonPath & path) -> RestrictedPathError {
throw RestrictedPathError(
"access to absolute path '%1%' is forbidden in pure evaluation mode (use '--impure' to override)",
CanonPath(store->storeDir) / path);
}));
/* We don't want to list store paths */
accessor->mount(CanonPath(store->storeDir), makeEmptySourceAccessor());
} else {
accessor->mount(CanonPath(store->storeDir), store->getFSAccessor(false));
}
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, /* If we have a chroot store and pure eval is not enabled,
use a union accessor to make the chroot store available use a union accessor to make the chroot store available
at its logical location while still having the underlying at its logical location while still having the underlying
@ -253,18 +275,16 @@ EvalState::EvalState(
return makeUnionSourceAccessor({getFSSourceAccessor(), storeFS}); return makeUnionSourceAccessor({getFSSourceAccessor(), storeFS});
return getFSSourceAccessor(); return getFSSourceAccessor();
}(); };
/* Apply access control if needed. */ /* Apply access control if needed. */
if (settings.restrictEval || settings.pureEval) if (settings.restrictEval)
accessor = AllowListSourceAccessor::create( return AllowListSourceAccessor::create(
accessor, {}, {}, [&settings](const CanonPath & path) -> RestrictedPathError { makeImpureAccessor(), {}, {}, [](const CanonPath & path) -> RestrictedPathError {
auto modeInformation = settings.pureEval ? "in pure evaluation mode (use '--impure' to override)" throw RestrictedPathError("access to absolute path '%1%' is forbidden in restricted mode", path);
: "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
}); });
return accessor; return makeImpureAccessor();
}()) }())
, corepkgsFS(make_ref<MemorySourceAccessor>()) , corepkgsFS(make_ref<MemorySourceAccessor>())
, internalFS(make_ref<MemorySourceAccessor>()) , internalFS(make_ref<MemorySourceAccessor>())
@ -351,13 +371,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

@ -65,7 +65,7 @@ traverseDir="${_NIX_TEST_SOURCE_DIR}/restricted-traverse-me"
ln -sfn "${_NIX_TEST_SOURCE_DIR}/restricted-secret" "${_NIX_TEST_SOURCE_DIR}/restricted-innocent" ln -sfn "${_NIX_TEST_SOURCE_DIR}/restricted-secret" "${_NIX_TEST_SOURCE_DIR}/restricted-innocent"
mkdir -p "$traverseDir" mkdir -p "$traverseDir"
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"