diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml
index 6b90083f0..fb4d8cefc 100644
--- a/doc/manual/command-ref/conf-file.xml
+++ b/doc/manual/command-ref/conf-file.xml
@@ -563,7 +563,8 @@ password my-password
If set to true, the Nix evaluator will
not allow access to any files outside of the Nix search path (as
set via the NIX_PATH environment variable or the
- option). The default is
+ option), or to URIs outside of
+ . The default is
false.
@@ -571,6 +572,21 @@ password my-password
+ allowed-uris
+
+
+
+ A list of URI prefixes to which access is allowed in
+ restricted evaluation mode. For example, when set to
+ https://github.com/NixOS, builtin functions
+ such as fetchGit are allowed to access
+ https://github.com/NixOS/patchelf.git.
+
+
+
+
+
+
pre-build-hook
diff --git a/doc/manual/release-notes/rl-1.12.xml b/doc/manual/release-notes/rl-1.12.xml
index 609dcef6b..7c9a8b75e 100644
--- a/doc/manual/release-notes/rl-1.12.xml
+++ b/doc/manual/release-notes/rl-1.12.xml
@@ -418,6 +418,14 @@ configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"
through the MELPA package repository.
+
+ In restricted evaluation mode
+ (), builtin functions that
+ download from the network (such as fetchGit)
+ are permitted to fetch underneath the list of URI prefixes
+ specified in the option .
+
+
This release has contributions from TBD.
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 548537b72..63de2d60a 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -355,6 +355,26 @@ Path EvalState::checkSourcePath(const Path & path_)
}
+void EvalState::checkURI(const std::string & uri)
+{
+ if (!restricted) return;
+
+ /* 'uri' should be equal to a prefix, or in a subdirectory of a
+ prefix. Thus, the prefix https://github.co does not permit
+ access to https://github.com. Note: this allows 'http://' and
+ 'https://' as prefixes for any http/https URI. */
+ for (auto & prefix : settings.allowedUris.get())
+ if (uri == prefix ||
+ (uri.size() > prefix.size()
+ && prefix.size() > 0
+ && hasPrefix(uri, prefix)
+ && (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
+ return;
+
+ throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
+}
+
+
void EvalState::addConstant(const string & name, Value & v)
{
Value * v2 = allocValue();
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 04a36b14c..f0ab1435b 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -110,6 +110,8 @@ public:
Path checkSourcePath(const Path & path);
+ void checkURI(const std::string & uri);
+
/* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path);
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 22925ba4d..cd0dfbc03 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1937,8 +1937,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
} else
url = state.forceStringNoCtx(*args[0], pos);
- if (state.restricted)
- throw Error(format("'%1%' is not allowed in restricted mode") % who);
+ state.checkURI(url);
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
mkString(v, res, PathSet({res}));
diff --git a/src/libexpr/primops/fetchgit.cc b/src/libexpr/primops/fetchgit.cc
index 38bffd8db..81b641900 100644
--- a/src/libexpr/primops/fetchgit.cc
+++ b/src/libexpr/primops/fetchgit.cc
@@ -113,9 +113,6 @@ GitInfo exportGit(ref store, const std::string & uri,
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- // FIXME: cut&paste from fetch().
- if (state.restricted) throw Error("'fetchGit' is not allowed in restricted mode");
-
std::string url;
std::string ref = "master";
std::string rev;
@@ -150,6 +147,10 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
} else
url = state.forceStringNoCtx(*args[0], pos);
+ // FIXME: git externals probably can be used to bypass the URI
+ // whitelist. Ah well.
+ state.checkURI(url);
+
auto gitInfo = exportGit(state.store, url, ref, rev, name);
state.mkAttrs(v, 8);
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 538273b54..a4aa842d7 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -225,7 +225,7 @@ public:
Setting restrictEval{this, false, "restrict-eval",
"Whether to restrict file system access to paths in $NIX_PATH, "
- "and to disallow fetching files from the network."};
+ "and network access to the URI prefixes listed in 'allowed-uris'."};
Setting buildRepeat{this, 0, "repeat",
"The number of times to repeat a build in order to verify determinism.",
@@ -353,6 +353,8 @@ public:
Setting maxFree{this, std::numeric_limits::max(), "max-free",
"Stop deleting garbage when free disk space is above the specified amount."};
+ Setting allowedUris{this, {}, "allowed-uris",
+ "Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
};
diff --git a/tests/restricted.sh b/tests/restricted.sh
index 19096a9f8..a297847cc 100644
--- a/tests/restricted.sh
+++ b/tests/restricted.sh
@@ -16,3 +16,15 @@ nix-instantiate --option restrict-eval true --eval -E 'builtins.readDir ../src/b
(! nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in ')
nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in ' -I src=.
+p=$(nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)")
+cmp $p restricted.sh
+
+(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval)
+
+(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/")
+
+nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh"
+
+(! nix eval --raw "(builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
+(! nix eval --raw "(builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
+(! nix eval --raw "(fetchGit git://github.com/NixOS/patchelf.git)" --restrict-eval)