diff --git a/doc/manual/rl-next/reject-flake-config.md b/doc/manual/rl-next/reject-flake-config.md new file mode 100644 index 000000000..d38edc80c --- /dev/null +++ b/doc/manual/rl-next/reject-flake-config.md @@ -0,0 +1,9 @@ +--- +synopsis: Allow automatic rejection of configuration options from flakes +cls: [1541] +credits: [alois31] +category: Improvements +--- + +Setting `accept-flake-config` to `false` now respects user choice by automatically rejecting configuration options set by flakes. +The old behaviour of asking each time is still available (and default) by setting it to the special value `ask`. diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index b330d96f9..a7664b9cf 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -51,30 +51,44 @@ void ConfigFile::apply() else assert(false); - if (!whitelist.count(baseName) && !nix::fetchSettings.acceptFlakeConfig) { - bool trusted = false; - auto trustedList = readTrustedList(); - auto tlname = get(trustedList, name); - if (auto saved = tlname ? get(*tlname, valueS) : nullptr) { - trusted = *saved; - printInfo("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name, valueS); - } else { - // FIXME: filter ANSI escapes, newlines, \r, etc. - if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') { - trusted = true; - } - if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') { - trustedList[name][valueS] = trusted; - writeTrustedList(trustedList); - } + bool trusted = whitelist.count(baseName); + if (!trusted) { + switch (nix::fetchSettings.acceptFlakeConfig) { + case AcceptFlakeConfig::True: { + trusted = true; + break; } - if (!trusted) { - warn("ignoring untrusted flake configuration setting '%s'.\nPass '%s' to trust it", name, "--accept-flake-config"); - continue; + case AcceptFlakeConfig::Ask: { + auto trustedList = readTrustedList(); + auto tlname = get(trustedList, name); + if (auto saved = tlname ? get(*tlname, valueS) : nullptr) { + trusted = *saved; + printInfo("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name, valueS); + } else { + // FIXME: filter ANSI escapes, newlines, \r, etc. + if (std::tolower(logger->ask(fmt("Do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)? This may allow the flake to gain root, see the nix.conf manual page.", name, valueS)).value_or('n')) == 'y') { + trusted = true; + } + if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') { + trustedList[name][valueS] = trusted; + writeTrustedList(trustedList); + } + } + break; + } + case nix::AcceptFlakeConfig::False: { + trusted = false; + break; + }; } } - globalConfig.set(name, valueS); + if (trusted) { + debug("accepting trusted flake configuration setting '%s'", name); + globalConfig.set(name, valueS); + } else { + warn("ignoring untrusted flake configuration setting '%s', pass '%s' to trust it (may allow the flake to gain root, see the nix.conf manual page)", name, "--accept-flake-config"); + } } } diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc index e7d5244dc..aeb3c542b 100644 --- a/src/libfetchers/fetch-settings.cc +++ b/src/libfetchers/fetch-settings.cc @@ -1,7 +1,50 @@ +#include "abstract-setting-to-json.hh" +#include "args.hh" +#include "config-impl.hh" #include "fetch-settings.hh" +#include + namespace nix { +template<> AcceptFlakeConfig BaseSetting::parse(const std::string & str) const +{ + if (str == "true") return AcceptFlakeConfig::True; + else if (str == "ask") return AcceptFlakeConfig::Ask; + else if (str == "false") return AcceptFlakeConfig::False; + else throw UsageError("option '%s' has invalid value '%s'", name, str); +} + +template<> std::string BaseSetting::to_string() const +{ + if (value == AcceptFlakeConfig::True) return "true"; + else if (value == AcceptFlakeConfig::Ask) return "ask"; + else if (value == AcceptFlakeConfig::False) return "false"; + else abort(); +} + +template<> void BaseSetting::convertToArg(Args & args, const std::string & category) +{ + args.addFlag({ + .longName = name, + .description = "Accept Lix configuration options from flakes without confirmation. This allows flakes to gain root access to your machine if you are a trusted user; see the nix.conf manual page for more details.", + .category = category, + .handler = {[this]() { override(AcceptFlakeConfig::True); }} + }); + args.addFlag({ + .longName = "ask-" + name, + .description = "Ask whether to accept Lix configuration options from flakes.", + .category = category, + .handler = {[this]() { override(AcceptFlakeConfig::Ask); }} + }); + args.addFlag({ + .longName = "no-" + name, + .description = "Reject Lix configuration options from flakes.", + .category = category, + .handler = {[this]() { override(AcceptFlakeConfig::False); }} + }); +} + FetchSettings::FetchSettings() { } diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 6fb260c3a..1702fb941 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -11,6 +11,8 @@ namespace nix { +enum class AcceptFlakeConfig { True, Ask, False }; + struct FetchSettings : public Config { FetchSettings(); @@ -86,15 +88,21 @@ struct FetchSettings : public Config "Whether to use flake registries to resolve flake references.", {}, true, Xp::Flakes}; - Setting acceptFlakeConfig{this, false, "accept-flake-config", + Setting acceptFlakeConfig{ + this, AcceptFlakeConfig::Ask, "accept-flake-config", R"( Whether to accept Lix configuration from the `nixConfig` attribute of - a flake without prompting. This is almost always a very bad idea. - - Setting this setting as a trusted user allows Nix flakes to gain root + a flake. Doing so as a trusted user allows Nix flakes to gain root access on your machine if they set one of the several trusted-user-only settings that execute commands as root. + If set to `true`, such configuration will be accepted without asking; + this is almost always a very bad idea. Setting this to `ask` will + prompt the user each time whether to allow a certain configuration + option set this way, and offer to optionally remember their choice. + When set to `false`, the configuration will be automatically + declined. + See [multi-user installations](@docroot@/installation/multi-user.md) for more details on the Lix security model. )", diff --git a/tests/functional/experimental-features.sh b/tests/functional/experimental-features.sh index 9ee4a53d4..658ae3cc4 100644 --- a/tests/functional/experimental-features.sh +++ b/tests/functional/experimental-features.sh @@ -32,7 +32,7 @@ NIX_CONFIG=' experimental-features = nix-command accept-flake-config = true ' nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -grepQuiet "false" $TEST_ROOT/stdout +grepQuiet "ask" $TEST_ROOT/stdout grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr # 'flakes' experimental-feature is disabled after, ignore and warn @@ -40,7 +40,7 @@ NIX_CONFIG=' accept-flake-config = true experimental-features = nix-command ' nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -grepQuiet "false" $TEST_ROOT/stdout +grepQuiet "ask" $TEST_ROOT/stdout grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr # 'flakes' experimental-feature is enabled before, process diff --git a/tests/functional/flakes/config.sh b/tests/functional/flakes/config.sh index d1941a6be..f7eae06d8 100644 --- a/tests/functional/flakes/config.sh +++ b/tests/functional/flakes/config.sh @@ -28,6 +28,11 @@ nix build < /dev/null (! [[ -f post-hook-ran ]]) clearStore +# likewise with no-accept-flake-config +nix build --no-accept-flake-config +(! [[ -f post-hook-ran ]]) +clearStore + nix build --accept-flake-config test -f post-hook-ran || fail "The post hook should have ran"