Add option allowed-uris

This allows network access in restricted eval mode.
This commit is contained in:
Eelco Dolstra 2017-10-30 12:39:59 +01:00
parent f1c555cef8
commit 812e027e1d
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
8 changed files with 67 additions and 7 deletions

View file

@ -563,7 +563,8 @@ password <replaceable>my-password</replaceable>
<para>If set to <literal>true</literal>, the Nix evaluator will <para>If set to <literal>true</literal>, the Nix evaluator will
not allow access to any files outside of the Nix search path (as not allow access to any files outside of the Nix search path (as
set via the <envar>NIX_PATH</envar> environment variable or the set via the <envar>NIX_PATH</envar> environment variable or the
<option>-I</option> option). The default is <option>-I</option> option), or to URIs outside of
<option>allowed-uri</option>. The default is
<literal>false</literal>.</para> <literal>false</literal>.</para>
</listitem> </listitem>
@ -571,6 +572,21 @@ password <replaceable>my-password</replaceable>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-allowed-uris"><term><literal>allowed-uris</literal></term>
<listitem>
<para>A list of URI prefixes to which access is allowed in
restricted evaluation mode. For example, when set to
<literal>https://github.com/NixOS</literal>, builtin functions
such as <function>fetchGit</function> are allowed to access
<literal>https://github.com/NixOS/patchelf.git</literal>.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term> <varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term>
<listitem> <listitem>

View file

@ -418,6 +418,14 @@ configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"
through the MELPA package repository.</para> through the MELPA package repository.</para>
</listitem> </listitem>
<listitem>
<para>In restricted evaluation mode
(<option>--restrict-eval</option>), builtin functions that
download from the network (such as <function>fetchGit</function>)
are permitted to fetch underneath the list of URI prefixes
specified in the option <option>allowed-uris</option>.</para>
</listitem>
</itemizedlist> </itemizedlist>
<para>This release has contributions from TBD.</para> <para>This release has contributions from TBD.</para>

View file

@ -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) void EvalState::addConstant(const string & name, Value & v)
{ {
Value * v2 = allocValue(); Value * v2 = allocValue();

View file

@ -110,6 +110,8 @@ public:
Path checkSourcePath(const Path & path); Path checkSourcePath(const Path & path);
void checkURI(const std::string & uri);
/* Parse a Nix expression from the specified file. */ /* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path); Expr * parseExprFromFile(const Path & path);
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv); Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);

View file

@ -1937,8 +1937,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
} else } else
url = state.forceStringNoCtx(*args[0], pos); url = state.forceStringNoCtx(*args[0], pos);
if (state.restricted) state.checkURI(url);
throw Error(format("'%1%' is not allowed in restricted mode") % who);
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash); Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
mkString(v, res, PathSet({res})); mkString(v, res, PathSet({res}));

View file

@ -113,9 +113,6 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v) 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 url;
std::string ref = "master"; std::string ref = "master";
std::string rev; std::string rev;
@ -150,6 +147,10 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
} else } else
url = state.forceStringNoCtx(*args[0], pos); 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); auto gitInfo = exportGit(state.store, url, ref, rev, name);
state.mkAttrs(v, 8); state.mkAttrs(v, 8);

View file

@ -225,7 +225,7 @@ public:
Setting<bool> restrictEval{this, false, "restrict-eval", Setting<bool> restrictEval{this, false, "restrict-eval",
"Whether to restrict file system access to paths in $NIX_PATH, " "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<size_t> buildRepeat{this, 0, "repeat", Setting<size_t> buildRepeat{this, 0, "repeat",
"The number of times to repeat a build in order to verify determinism.", "The number of times to repeat a build in order to verify determinism.",
@ -353,6 +353,8 @@ public:
Setting<uint64_t> maxFree{this, std::numeric_limits<uint64_t>::max(), "max-free", Setting<uint64_t> maxFree{this, std::numeric_limits<uint64_t>::max(), "max-free",
"Stop deleting garbage when free disk space is above the specified amount."}; "Stop deleting garbage when free disk space is above the specified amount."};
Setting<Strings> allowedUris{this, {}, "allowed-uris",
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
}; };

View file

@ -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 <foo>') (! nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>')
nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=. nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -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)