mirror of
https://github.com/NixOS/nix
synced 2025-06-25 10:41:16 +02:00
Merge pull request #12658 from obsidiansystems/local-derivation-goal-hide-and-split
Move `RestrictedStore` into its own file+header
This commit is contained in:
commit
d10f9488fe
4 changed files with 413 additions and 281 deletions
|
@ -234,6 +234,7 @@ sources = files(
|
||||||
'realisation.cc',
|
'realisation.cc',
|
||||||
'remote-fs-accessor.cc',
|
'remote-fs-accessor.cc',
|
||||||
'remote-store.cc',
|
'remote-store.cc',
|
||||||
|
'restricted-store.cc',
|
||||||
's3-binary-cache-store.cc',
|
's3-binary-cache-store.cc',
|
||||||
'serve-protocol-connection.cc',
|
'serve-protocol-connection.cc',
|
||||||
'serve-protocol.cc',
|
'serve-protocol.cc',
|
||||||
|
@ -305,6 +306,7 @@ headers = [config_h] + files(
|
||||||
'remote-fs-accessor.hh',
|
'remote-fs-accessor.hh',
|
||||||
'remote-store-connection.hh',
|
'remote-store-connection.hh',
|
||||||
'remote-store.hh',
|
'remote-store.hh',
|
||||||
|
'restricted-store.hh',
|
||||||
's3-binary-cache-store.hh',
|
's3-binary-cache-store.hh',
|
||||||
's3.hh',
|
's3.hh',
|
||||||
'ssh-store.hh',
|
'ssh-store.hh',
|
||||||
|
|
341
src/libstore/restricted-store.cc
Normal file
341
src/libstore/restricted-store.cc
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
#include "restricted-store.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
#include "callback.hh"
|
||||||
|
#include "realisation.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
static StorePath pathPartOfReq(const SingleDerivedPath & req)
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
overloaded{
|
||||||
|
[&](const SingleDerivedPath::Opaque & bo) { return bo.path; },
|
||||||
|
[&](const SingleDerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
|
||||||
|
},
|
||||||
|
req.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
static StorePath pathPartOfReq(const DerivedPath & req)
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
overloaded{
|
||||||
|
[&](const DerivedPath::Opaque & bo) { return bo.path; },
|
||||||
|
[&](const DerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
|
||||||
|
},
|
||||||
|
req.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestrictionContext::isAllowed(const DerivedPath & req)
|
||||||
|
{
|
||||||
|
return isAllowed(pathPartOfReq(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RestrictedStoreConfig : virtual LocalFSStoreConfig
|
||||||
|
{
|
||||||
|
using LocalFSStoreConfig::LocalFSStoreConfig;
|
||||||
|
const std::string name() override
|
||||||
|
{
|
||||||
|
return "Restricted Store";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around LocalStore that only allows building/querying of
|
||||||
|
* paths that are in the input closures of the build or were added via
|
||||||
|
* recursive Nix calls.
|
||||||
|
*/
|
||||||
|
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual IndirectRootStore, public virtual GcStore
|
||||||
|
{
|
||||||
|
ref<LocalStore> next;
|
||||||
|
|
||||||
|
RestrictionContext & goal;
|
||||||
|
|
||||||
|
RestrictedStore(const Params & params, ref<LocalStore> next, RestrictionContext & goal)
|
||||||
|
: StoreConfig(params)
|
||||||
|
, LocalFSStoreConfig(params)
|
||||||
|
, RestrictedStoreConfig(params)
|
||||||
|
, Store(params)
|
||||||
|
, LocalFSStore(params)
|
||||||
|
, next(next)
|
||||||
|
, goal(goal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Path getRealStoreDir() override
|
||||||
|
{
|
||||||
|
return next->realStoreDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getUri() override
|
||||||
|
{
|
||||||
|
return next->getUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePathSet queryAllValidPaths() override;
|
||||||
|
|
||||||
|
void queryPathInfoUncached(
|
||||||
|
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;
|
||||||
|
|
||||||
|
void queryReferrers(const StorePath & path, StorePathSet & referrers) override;
|
||||||
|
|
||||||
|
std::map<std::string, std::optional<StorePath>>
|
||||||
|
queryPartialDerivationOutputMap(const StorePath & path, Store * evalStore = nullptr) override;
|
||||||
|
|
||||||
|
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
||||||
|
{
|
||||||
|
throw Error("queryPathFromHashPart");
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePath addToStore(
|
||||||
|
std::string_view name,
|
||||||
|
const SourcePath & srcPath,
|
||||||
|
ContentAddressMethod method,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
PathFilter & filter,
|
||||||
|
RepairFlag repair) override
|
||||||
|
{
|
||||||
|
throw Error("addToStore");
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToStore(
|
||||||
|
const ValidPathInfo & info,
|
||||||
|
Source & narSource,
|
||||||
|
RepairFlag repair = NoRepair,
|
||||||
|
CheckSigsFlag checkSigs = CheckSigs) override;
|
||||||
|
|
||||||
|
StorePath addToStoreFromDump(
|
||||||
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
FileSerialisationMethod dumpMethod,
|
||||||
|
ContentAddressMethod hashMethod,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair) override;
|
||||||
|
|
||||||
|
void narFromPath(const StorePath & path, Sink & sink) override;
|
||||||
|
|
||||||
|
void ensurePath(const StorePath & path) override;
|
||||||
|
|
||||||
|
void registerDrvOutput(const Realisation & info) override;
|
||||||
|
|
||||||
|
void queryRealisationUncached(
|
||||||
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
|
void
|
||||||
|
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
||||||
|
|
||||||
|
std::vector<KeyedBuildResult> buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
BuildMode buildMode = bmNormal,
|
||||||
|
std::shared_ptr<Store> evalStore = nullptr) override;
|
||||||
|
|
||||||
|
BuildResult
|
||||||
|
buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal) override
|
||||||
|
{
|
||||||
|
unsupported("buildDerivation");
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTempRoot(const StorePath & path) override {}
|
||||||
|
|
||||||
|
void addIndirectRoot(const Path & path) override {}
|
||||||
|
|
||||||
|
Roots findRoots(bool censor) override
|
||||||
|
{
|
||||||
|
return Roots();
|
||||||
|
}
|
||||||
|
|
||||||
|
void collectGarbage(const GCOptions & options, GCResults & results) override {}
|
||||||
|
|
||||||
|
void addSignatures(const StorePath & storePath, const StringSet & sigs) override
|
||||||
|
{
|
||||||
|
unsupported("addSignatures");
|
||||||
|
}
|
||||||
|
|
||||||
|
void queryMissing(
|
||||||
|
const std::vector<DerivedPath> & targets,
|
||||||
|
StorePathSet & willBuild,
|
||||||
|
StorePathSet & willSubstitute,
|
||||||
|
StorePathSet & unknown,
|
||||||
|
uint64_t & downloadSize,
|
||||||
|
uint64_t & narSize) override;
|
||||||
|
|
||||||
|
virtual std::optional<std::string> getBuildLogExact(const StorePath & path) override
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void addBuildLog(const StorePath & path, std::string_view log) override
|
||||||
|
{
|
||||||
|
unsupported("addBuildLog");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TrustedFlag> isTrustedClient() override
|
||||||
|
{
|
||||||
|
return NotTrusted;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<Store> makeRestrictedStore(const Store::Params & params, ref<LocalStore> next, RestrictionContext & context)
|
||||||
|
{
|
||||||
|
return make_ref<RestrictedStore>(params, next, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePathSet RestrictedStore::queryAllValidPaths()
|
||||||
|
{
|
||||||
|
StorePathSet paths;
|
||||||
|
for (auto & p : goal.originalPaths())
|
||||||
|
paths.insert(p);
|
||||||
|
for (auto & p : goal.addedPaths)
|
||||||
|
paths.insert(p);
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::queryPathInfoUncached(
|
||||||
|
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
||||||
|
{
|
||||||
|
if (goal.isAllowed(path)) {
|
||||||
|
try {
|
||||||
|
/* Censor impure information. */
|
||||||
|
auto info = std::make_shared<ValidPathInfo>(*next->queryPathInfo(path));
|
||||||
|
info->deriver.reset();
|
||||||
|
info->registrationTime = 0;
|
||||||
|
info->ultimate = false;
|
||||||
|
info->sigs.clear();
|
||||||
|
callback(info);
|
||||||
|
} catch (InvalidPath &) {
|
||||||
|
callback(nullptr);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
callback(nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
void RestrictedStore::queryReferrers(const StorePath & path, StorePathSet & referrers) {}
|
||||||
|
|
||||||
|
std::map<std::string, std::optional<StorePath>>
|
||||||
|
RestrictedStore::queryPartialDerivationOutputMap(const StorePath & path, Store * evalStore)
|
||||||
|
{
|
||||||
|
if (!goal.isAllowed(path))
|
||||||
|
throw InvalidPath("cannot query output map for unknown path '%s' in recursive Nix", printStorePath(path));
|
||||||
|
return next->queryPartialDerivationOutputMap(path, evalStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::addToStore(
|
||||||
|
const ValidPathInfo & info, Source & narSource, RepairFlag repair, CheckSigsFlag checkSigs)
|
||||||
|
{
|
||||||
|
next->addToStore(info, narSource, repair, checkSigs);
|
||||||
|
goal.addDependency(info.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePath RestrictedStore::addToStoreFromDump(
|
||||||
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
FileSerialisationMethod dumpMethod,
|
||||||
|
ContentAddressMethod hashMethod,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair)
|
||||||
|
{
|
||||||
|
auto path = next->addToStoreFromDump(dump, name, dumpMethod, hashMethod, hashAlgo, references, repair);
|
||||||
|
goal.addDependency(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::narFromPath(const StorePath & path, Sink & sink)
|
||||||
|
{
|
||||||
|
if (!goal.isAllowed(path))
|
||||||
|
throw InvalidPath("cannot dump unknown path '%s' in recursive Nix", printStorePath(path));
|
||||||
|
LocalFSStore::narFromPath(path, sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::ensurePath(const StorePath & path)
|
||||||
|
{
|
||||||
|
if (!goal.isAllowed(path))
|
||||||
|
throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", printStorePath(path));
|
||||||
|
/* Nothing to be done; 'path' must already be valid. */
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::registerDrvOutput(const Realisation & info)
|
||||||
|
// XXX: This should probably be allowed as a no-op if the realisation
|
||||||
|
// corresponds to an allowed derivation
|
||||||
|
{
|
||||||
|
throw Error("registerDrvOutput");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::queryRealisationUncached(
|
||||||
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
|
// XXX: This should probably be allowed if the realisation corresponds to
|
||||||
|
// an allowed derivation
|
||||||
|
{
|
||||||
|
if (!goal.isAllowed(id))
|
||||||
|
callback(nullptr);
|
||||||
|
next->queryRealisation(id, std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::buildPaths(
|
||||||
|
const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
for (auto & result : buildPathsWithResults(paths, buildMode, evalStore))
|
||||||
|
if (!result.success())
|
||||||
|
result.rethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<KeyedBuildResult> RestrictedStore::buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
assert(!evalStore);
|
||||||
|
|
||||||
|
if (buildMode != bmNormal)
|
||||||
|
throw Error("unsupported build mode");
|
||||||
|
|
||||||
|
StorePathSet newPaths;
|
||||||
|
std::set<Realisation> newRealisations;
|
||||||
|
|
||||||
|
for (auto & req : paths) {
|
||||||
|
if (!goal.isAllowed(req))
|
||||||
|
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto results = next->buildPathsWithResults(paths, buildMode);
|
||||||
|
|
||||||
|
for (auto & result : results) {
|
||||||
|
for (auto & [outputName, output] : result.builtOutputs) {
|
||||||
|
newPaths.insert(output.outPath);
|
||||||
|
newRealisations.insert(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePathSet closure;
|
||||||
|
next->computeFSClosure(newPaths, closure);
|
||||||
|
for (auto & path : closure)
|
||||||
|
goal.addDependency(path);
|
||||||
|
for (auto & real : Realisation::closure(*next, newRealisations))
|
||||||
|
goal.addedDrvOutputs.insert(real.id);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestrictedStore::queryMissing(
|
||||||
|
const std::vector<DerivedPath> & targets,
|
||||||
|
StorePathSet & willBuild,
|
||||||
|
StorePathSet & willSubstitute,
|
||||||
|
StorePathSet & unknown,
|
||||||
|
uint64_t & downloadSize,
|
||||||
|
uint64_t & narSize)
|
||||||
|
{
|
||||||
|
/* This is slightly impure since it leaks information to the
|
||||||
|
client about what paths will be built/substituted or are
|
||||||
|
already present. Probably not a big deal. */
|
||||||
|
|
||||||
|
std::vector<DerivedPath> allowed;
|
||||||
|
for (auto & req : targets) {
|
||||||
|
if (goal.isAllowed(req))
|
||||||
|
allowed.emplace_back(req);
|
||||||
|
else
|
||||||
|
unknown.insert(pathPartOfReq(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
next->queryMissing(allowed, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
60
src/libstore/restricted-store.hh
Normal file
60
src/libstore/restricted-store.hh
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "local-store.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A restricted store has a pointer to one of these, which manages the
|
||||||
|
* restrictions that are in place.
|
||||||
|
*
|
||||||
|
* This is a separate data type so the whitelists can be mutated before
|
||||||
|
* the restricted store is created: put differently, someones we don't
|
||||||
|
* know whether we will in fact create a restricted store, but we need
|
||||||
|
* to prepare the whitelists just in case.
|
||||||
|
*
|
||||||
|
* It is possible there are other ways to solve this problem. This was
|
||||||
|
* just the easiest place to begin, when this was extracted from
|
||||||
|
* `LocalDerivationGoal`.
|
||||||
|
*/
|
||||||
|
struct RestrictionContext
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Paths that are already allowed to begin with
|
||||||
|
*/
|
||||||
|
virtual const StorePathSet & originalPaths() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paths that were added via recursive Nix calls.
|
||||||
|
*/
|
||||||
|
StorePathSet addedPaths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realisations that were added via recursive Nix calls.
|
||||||
|
*/
|
||||||
|
std::set<DrvOutput> addedDrvOutputs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive Nix calls are only allowed to build or realize paths
|
||||||
|
* in the original input closure or added via a recursive Nix call
|
||||||
|
* (so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
||||||
|
* /nix/store/<bla> is some arbitrary path in a binary cache).
|
||||||
|
*/
|
||||||
|
virtual bool isAllowed(const StorePath &) = 0;
|
||||||
|
virtual bool isAllowed(const DrvOutput & id) = 0;
|
||||||
|
bool isAllowed(const DerivedPath & id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 'path' to the set of paths that may be referenced by the
|
||||||
|
* outputs, and make it appear in the sandbox.
|
||||||
|
*/
|
||||||
|
virtual void addDependency(const StorePath & path) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a shared pointer to a restricted store.
|
||||||
|
*/
|
||||||
|
ref<Store> makeRestrictedStore(const Store::Params & params, ref<LocalStore> next, RestrictionContext & context);
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
#include "unix-domain-socket.hh"
|
#include "unix-domain-socket.hh"
|
||||||
#include "posix-fs-canonicalise.hh"
|
#include "posix-fs-canonicalise.hh"
|
||||||
#include "posix-source-accessor.hh"
|
#include "posix-source-accessor.hh"
|
||||||
|
#include "restricted-store.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -75,7 +76,7 @@ extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags,
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct LocalDerivationGoal : public DerivationGoal
|
struct LocalDerivationGoal : DerivationGoal, RestrictionContext
|
||||||
{
|
{
|
||||||
LocalStore & getLocalStore();
|
LocalStore & getLocalStore();
|
||||||
|
|
||||||
|
@ -208,27 +209,16 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
*/
|
*/
|
||||||
std::vector<std::thread> daemonWorkerThreads;
|
std::vector<std::thread> daemonWorkerThreads;
|
||||||
|
|
||||||
/**
|
const StorePathSet & originalPaths() override
|
||||||
* Paths that were added via recursive Nix calls.
|
{
|
||||||
*/
|
return inputPaths;
|
||||||
StorePathSet addedPaths;
|
}
|
||||||
|
|
||||||
/**
|
bool isAllowed(const StorePath & path) override
|
||||||
* Realisations that were added via recursive Nix calls.
|
|
||||||
*/
|
|
||||||
std::set<DrvOutput> addedDrvOutputs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursive Nix calls are only allowed to build or realize paths
|
|
||||||
* in the original input closure or added via a recursive Nix call
|
|
||||||
* (so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
|
||||||
* /nix/store/<bla> is some arbitrary path in a binary cache).
|
|
||||||
*/
|
|
||||||
bool isAllowed(const StorePath & path)
|
|
||||||
{
|
{
|
||||||
return inputPaths.count(path) || addedPaths.count(path);
|
return inputPaths.count(path) || addedPaths.count(path);
|
||||||
}
|
}
|
||||||
bool isAllowed(const DrvOutput & id)
|
bool isAllowed(const DrvOutput & id) override
|
||||||
{
|
{
|
||||||
return addedDrvOutputs.count(id);
|
return addedDrvOutputs.count(id);
|
||||||
}
|
}
|
||||||
|
@ -287,11 +277,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
*/
|
*/
|
||||||
void stopDaemon();
|
void stopDaemon();
|
||||||
|
|
||||||
/**
|
void addDependency(const StorePath & path) override;
|
||||||
* Add 'path' to the set of paths that may be referenced by the
|
|
||||||
* outputs, and make it appear in the sandbox.
|
|
||||||
*/
|
|
||||||
void addDependency(const StorePath & path);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a file owned by the builder.
|
* Make a file owned by the builder.
|
||||||
|
@ -1618,263 +1604,6 @@ void LocalDerivationGoal::writeStructuredAttrs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static StorePath pathPartOfReq(const SingleDerivedPath & req)
|
|
||||||
{
|
|
||||||
return std::visit(overloaded {
|
|
||||||
[&](const SingleDerivedPath::Opaque & bo) {
|
|
||||||
return bo.path;
|
|
||||||
},
|
|
||||||
[&](const SingleDerivedPath::Built & bfd) {
|
|
||||||
return pathPartOfReq(*bfd.drvPath);
|
|
||||||
},
|
|
||||||
}, req.raw());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static StorePath pathPartOfReq(const DerivedPath & req)
|
|
||||||
{
|
|
||||||
return std::visit(overloaded {
|
|
||||||
[&](const DerivedPath::Opaque & bo) {
|
|
||||||
return bo.path;
|
|
||||||
},
|
|
||||||
[&](const DerivedPath::Built & bfd) {
|
|
||||||
return pathPartOfReq(*bfd.drvPath);
|
|
||||||
},
|
|
||||||
}, req.raw());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool LocalDerivationGoal::isAllowed(const DerivedPath & req)
|
|
||||||
{
|
|
||||||
return this->isAllowed(pathPartOfReq(req));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct RestrictedStoreConfig : virtual LocalFSStoreConfig
|
|
||||||
{
|
|
||||||
using LocalFSStoreConfig::LocalFSStoreConfig;
|
|
||||||
const std::string name() override { return "Restricted Store"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* A wrapper around LocalStore that only allows building/querying of
|
|
||||||
paths that are in the input closures of the build or were added via
|
|
||||||
recursive Nix calls. */
|
|
||||||
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual IndirectRootStore, public virtual GcStore
|
|
||||||
{
|
|
||||||
ref<LocalStore> next;
|
|
||||||
|
|
||||||
LocalDerivationGoal & goal;
|
|
||||||
|
|
||||||
RestrictedStore(const Params & params, ref<LocalStore> next, LocalDerivationGoal & goal)
|
|
||||||
: StoreConfig(params)
|
|
||||||
, LocalFSStoreConfig(params)
|
|
||||||
, RestrictedStoreConfig(params)
|
|
||||||
, Store(params)
|
|
||||||
, LocalFSStore(params)
|
|
||||||
, next(next), goal(goal)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Path getRealStoreDir() override
|
|
||||||
{ return next->realStoreDir; }
|
|
||||||
|
|
||||||
std::string getUri() override
|
|
||||||
{ return next->getUri(); }
|
|
||||||
|
|
||||||
StorePathSet queryAllValidPaths() override
|
|
||||||
{
|
|
||||||
StorePathSet paths;
|
|
||||||
for (auto & p : goal.inputPaths) paths.insert(p);
|
|
||||||
for (auto & p : goal.addedPaths) paths.insert(p);
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
void queryPathInfoUncached(const StorePath & path,
|
|
||||||
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override
|
|
||||||
{
|
|
||||||
if (goal.isAllowed(path)) {
|
|
||||||
try {
|
|
||||||
/* Censor impure information. */
|
|
||||||
auto info = std::make_shared<ValidPathInfo>(*next->queryPathInfo(path));
|
|
||||||
info->deriver.reset();
|
|
||||||
info->registrationTime = 0;
|
|
||||||
info->ultimate = false;
|
|
||||||
info->sigs.clear();
|
|
||||||
callback(info);
|
|
||||||
} catch (InvalidPath &) {
|
|
||||||
callback(nullptr);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
callback(nullptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
void queryReferrers(const StorePath & path, StorePathSet & referrers) override
|
|
||||||
{ }
|
|
||||||
|
|
||||||
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(
|
|
||||||
const StorePath & path,
|
|
||||||
Store * evalStore = nullptr) override
|
|
||||||
{
|
|
||||||
if (!goal.isAllowed(path))
|
|
||||||
throw InvalidPath("cannot query output map for unknown path '%s' in recursive Nix", printStorePath(path));
|
|
||||||
return next->queryPartialDerivationOutputMap(path, evalStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
|
||||||
{ throw Error("queryPathFromHashPart"); }
|
|
||||||
|
|
||||||
StorePath addToStore(
|
|
||||||
std::string_view name,
|
|
||||||
const SourcePath & srcPath,
|
|
||||||
ContentAddressMethod method,
|
|
||||||
HashAlgorithm hashAlgo,
|
|
||||||
const StorePathSet & references,
|
|
||||||
PathFilter & filter,
|
|
||||||
RepairFlag repair) override
|
|
||||||
{ throw Error("addToStore"); }
|
|
||||||
|
|
||||||
void addToStore(const ValidPathInfo & info, Source & narSource,
|
|
||||||
RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) override
|
|
||||||
{
|
|
||||||
next->addToStore(info, narSource, repair, checkSigs);
|
|
||||||
goal.addDependency(info.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePath addToStoreFromDump(
|
|
||||||
Source & dump,
|
|
||||||
std::string_view name,
|
|
||||||
FileSerialisationMethod dumpMethod,
|
|
||||||
ContentAddressMethod hashMethod,
|
|
||||||
HashAlgorithm hashAlgo,
|
|
||||||
const StorePathSet & references,
|
|
||||||
RepairFlag repair) override
|
|
||||||
{
|
|
||||||
auto path = next->addToStoreFromDump(dump, name, dumpMethod, hashMethod, hashAlgo, references, repair);
|
|
||||||
goal.addDependency(path);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void narFromPath(const StorePath & path, Sink & sink) override
|
|
||||||
{
|
|
||||||
if (!goal.isAllowed(path))
|
|
||||||
throw InvalidPath("cannot dump unknown path '%s' in recursive Nix", printStorePath(path));
|
|
||||||
LocalFSStore::narFromPath(path, sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ensurePath(const StorePath & path) override
|
|
||||||
{
|
|
||||||
if (!goal.isAllowed(path))
|
|
||||||
throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", printStorePath(path));
|
|
||||||
/* Nothing to be done; 'path' must already be valid. */
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerDrvOutput(const Realisation & info) override
|
|
||||||
// XXX: This should probably be allowed as a no-op if the realisation
|
|
||||||
// corresponds to an allowed derivation
|
|
||||||
{ throw Error("registerDrvOutput"); }
|
|
||||||
|
|
||||||
void queryRealisationUncached(const DrvOutput & id,
|
|
||||||
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
|
||||||
// XXX: This should probably be allowed if the realisation corresponds to
|
|
||||||
// an allowed derivation
|
|
||||||
{
|
|
||||||
if (!goal.isAllowed(id))
|
|
||||||
callback(nullptr);
|
|
||||||
next->queryRealisation(id, std::move(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
|
|
||||||
{
|
|
||||||
for (auto & result : buildPathsWithResults(paths, buildMode, evalStore))
|
|
||||||
if (!result.success())
|
|
||||||
result.rethrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<KeyedBuildResult> buildPathsWithResults(
|
|
||||||
const std::vector<DerivedPath> & paths,
|
|
||||||
BuildMode buildMode = bmNormal,
|
|
||||||
std::shared_ptr<Store> evalStore = nullptr) override
|
|
||||||
{
|
|
||||||
assert(!evalStore);
|
|
||||||
|
|
||||||
if (buildMode != bmNormal) throw Error("unsupported build mode");
|
|
||||||
|
|
||||||
StorePathSet newPaths;
|
|
||||||
std::set<Realisation> newRealisations;
|
|
||||||
|
|
||||||
for (auto & req : paths) {
|
|
||||||
if (!goal.isAllowed(req))
|
|
||||||
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto results = next->buildPathsWithResults(paths, buildMode);
|
|
||||||
|
|
||||||
for (auto & result : results) {
|
|
||||||
for (auto & [outputName, output] : result.builtOutputs) {
|
|
||||||
newPaths.insert(output.outPath);
|
|
||||||
newRealisations.insert(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePathSet closure;
|
|
||||||
next->computeFSClosure(newPaths, closure);
|
|
||||||
for (auto & path : closure)
|
|
||||||
goal.addDependency(path);
|
|
||||||
for (auto & real : Realisation::closure(*next, newRealisations))
|
|
||||||
goal.addedDrvOutputs.insert(real.id);
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
|
||||||
BuildMode buildMode = bmNormal) override
|
|
||||||
{ unsupported("buildDerivation"); }
|
|
||||||
|
|
||||||
void addTempRoot(const StorePath & path) override
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void addIndirectRoot(const Path & path) override
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Roots findRoots(bool censor) override
|
|
||||||
{ return Roots(); }
|
|
||||||
|
|
||||||
void collectGarbage(const GCOptions & options, GCResults & results) override
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void addSignatures(const StorePath & storePath, const StringSet & sigs) override
|
|
||||||
{ unsupported("addSignatures"); }
|
|
||||||
|
|
||||||
void queryMissing(const std::vector<DerivedPath> & targets,
|
|
||||||
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
|
|
||||||
uint64_t & downloadSize, uint64_t & narSize) override
|
|
||||||
{
|
|
||||||
/* This is slightly impure since it leaks information to the
|
|
||||||
client about what paths will be built/substituted or are
|
|
||||||
already present. Probably not a big deal. */
|
|
||||||
|
|
||||||
std::vector<DerivedPath> allowed;
|
|
||||||
for (auto & req : targets) {
|
|
||||||
if (goal.isAllowed(req))
|
|
||||||
allowed.emplace_back(req);
|
|
||||||
else
|
|
||||||
unknown.insert(pathPartOfReq(req));
|
|
||||||
}
|
|
||||||
|
|
||||||
next->queryMissing(allowed, willBuild, willSubstitute,
|
|
||||||
unknown, downloadSize, narSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual std::optional<std::string> getBuildLogExact(const StorePath & path) override
|
|
||||||
{ return std::nullopt; }
|
|
||||||
|
|
||||||
virtual void addBuildLog(const StorePath & path, std::string_view log) override
|
|
||||||
{ unsupported("addBuildLog"); }
|
|
||||||
|
|
||||||
std::optional<TrustedFlag> isTrustedClient() override
|
|
||||||
{ return NotTrusted; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void LocalDerivationGoal::startDaemon()
|
void LocalDerivationGoal::startDaemon()
|
||||||
{
|
{
|
||||||
experimentalFeatureSettings.require(Xp::RecursiveNix);
|
experimentalFeatureSettings.require(Xp::RecursiveNix);
|
||||||
|
@ -1886,7 +1615,7 @@ void LocalDerivationGoal::startDaemon()
|
||||||
params["root"] = *optRoot;
|
params["root"] = *optRoot;
|
||||||
params["state"] = "/no-such-path";
|
params["state"] = "/no-such-path";
|
||||||
params["log"] = "/no-such-path";
|
params["log"] = "/no-such-path";
|
||||||
auto store = make_ref<RestrictedStore>(params,
|
auto store = makeRestrictedStore(params,
|
||||||
ref<LocalStore>(std::dynamic_pointer_cast<LocalStore>(worker.store.shared_from_this())),
|
ref<LocalStore>(std::dynamic_pointer_cast<LocalStore>(worker.store.shared_from_this())),
|
||||||
*this);
|
*this);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue