diff options
author | Alyssa Ross <hi@alyssa.is> | 2021-07-23 09:26:00 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2021-07-23 09:26:00 +0000 |
commit | ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d (patch) | |
tree | 504b28a058661f6c1cbb7d3f580020e50367ca7f /nixpkgs/nixos/modules | |
parent | 55cc63c079f49e81d695a25bc2f5b3902f2bd290 (diff) | |
parent | b09661d41fb93562fd53f31574dbf781b130ac44 (diff) | |
download | nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.gz nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.bz2 nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.lz nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.xz nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.zst nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.zip |
Merge commit 'b09661d41fb93562fd53f31574dbf781b130ac44'
Diffstat (limited to 'nixpkgs/nixos/modules')
55 files changed, 1782 insertions, 924 deletions
diff --git a/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix index 801e28cec44a..e3576074a5b7 100644 --- a/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix +++ b/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix @@ -1,6 +1,7 @@ { - x86_64-linux = "/nix/store/d1ppfhjhdwcsb4npfzyifv5z8i00fzsk-nix-2.3.11"; - i686-linux = "/nix/store/c6ikndcrzwpfn2sb5b9xb1f17p9b8iga-nix-2.3.11"; - aarch64-linux = "/nix/store/fb0lfrn0m8s197d264jzd64vhz9c8zbx-nix-2.3.11"; - x86_64-darwin = "/nix/store/qvb86ffv08q3r66qbd6nqifz425lyyhf-nix-2.3.11"; + x86_64-linux = "/nix/store/qsgz2hhn6mzlzp53a7pwf9z2pq3l5z6h-nix-2.3.14"; + i686-linux = "/nix/store/1yw40bj04lykisw2jilq06lir3k9ga4a-nix-2.3.14"; + aarch64-linux = "/nix/store/32yzwmynmjxfrkb6y6l55liaqdrgkj4a-nix-2.3.14"; + x86_64-darwin = "/nix/store/06j0vi2d13w4l0p3jsigq7lk4x6gkycj-nix-2.3.14"; + aarch64-darwin = "/nix/store/77wi7vpbrghw5rgws25w30bwb8yggnk9-nix-2.3.14"; } diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt b/nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt deleted file mode 100644 index e5834598c4fd..000000000000 --- a/nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project (nixos-option) - -add_executable(nixos-option nixos-option.cc libnix-copy-paste.cc) -target_link_libraries(nixos-option PRIVATE -lnixmain -lnixexpr -lnixstore -lnixutil) -target_compile_features(nixos-option PRIVATE cxx_std_17) - -install (TARGETS nixos-option DESTINATION bin) diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix b/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix index 72eec3a38363..061460f38a3b 100644 --- a/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix +++ b/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix @@ -1,11 +1 @@ -{lib, stdenv, boost, cmake, pkg-config, nix, ... }: -stdenv.mkDerivation rec { - name = "nixos-option"; - src = ./.; - nativeBuildInputs = [ cmake pkg-config ]; - buildInputs = [ boost nix ]; - meta = with lib; { - license = licenses.lgpl2Plus; - maintainers = with maintainers; [ chkno ]; - }; -} +{ pkgs, ... }: pkgs.nixos-option diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc b/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc deleted file mode 100644 index 875c07da6399..000000000000 --- a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc +++ /dev/null @@ -1,83 +0,0 @@ -// These are useful methods inside the nix library that ought to be exported. -// Since they are not, copy/paste them here. -// TODO: Delete these and use the ones in the library as they become available. - -#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM - -#include "libnix-copy-paste.hh" -#include <boost/format/alt_sstream.hpp> // for basic_altstringbuf... -#include <boost/format/alt_sstream_impl.hpp> // for basic_altstringbuf... -#include <boost/format/format_class.hpp> // for basic_format -#include <boost/format/format_fwd.hpp> // for format -#include <boost/format/format_implementation.hpp> // for basic_format::basi... -#include <boost/optional/optional.hpp> // for get_pointer -#include <iostream> // for operator<<, basic_... -#include <nix/types.hh> // for Strings, Error -#include <string> // for string, basic_string - -using boost::format; -using nix::Error; -using nix::Strings; -using std::string; - -// From nix/src/libexpr/attr-path.cc -Strings parseAttrPath(const string & s) -{ - Strings res; - string cur; - string::const_iterator i = s.begin(); - while (i != s.end()) { - if (*i == '.') { - res.push_back(cur); - cur.clear(); - } else if (*i == '"') { - ++i; - while (1) { - if (i == s.end()) - throw Error(format("missing closing quote in selection path '%1%'") % s); - if (*i == '"') - break; - cur.push_back(*i++); - } - } else - cur.push_back(*i); - ++i; - } - if (!cur.empty()) - res.push_back(cur); - return res; -} - -// From nix/src/nix/repl.cc -bool isVarName(const string & s) -{ - if (s.size() == 0) - return false; - char c = s[0]; - if ((c >= '0' && c <= '9') || c == '-' || c == '\'') - return false; - for (auto & i : s) - if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-' || - i == '\'')) - return false; - return true; -} - -// From nix/src/nix/repl.cc -std::ostream & printStringValue(std::ostream & str, const char * string) -{ - str << "\""; - for (const char * i = string; *i; i++) - if (*i == '\"' || *i == '\\') - str << "\\" << *i; - else if (*i == '\n') - str << "\\n"; - else if (*i == '\r') - str << "\\r"; - else if (*i == '\t') - str << "\\t"; - else - str << *i; - str << "\""; - return str; -} diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh b/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh deleted file mode 100644 index 2274e9a0f853..000000000000 --- a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include <iostream> -#include <nix/types.hh> -#include <string> - -nix::Strings parseAttrPath(const std::string & s); -bool isVarName(const std::string & s); -std::ostream & printStringValue(std::ostream & str, const char * string); diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc b/nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc deleted file mode 100644 index f779d82edbd6..000000000000 --- a/nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc +++ /dev/null @@ -1,643 +0,0 @@ -#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM - -#include <exception> // for exception_ptr, current_exception -#include <functional> // for function -#include <iostream> // for operator<<, basic_ostream, ostrin... -#include <iterator> // for next -#include <list> // for _List_iterator -#include <memory> // for allocator, unique_ptr, make_unique -#include <new> // for operator new -#include <nix/args.hh> // for argvToStrings, UsageError -#include <nix/attr-path.hh> // for findAlongAttrPath -#include <nix/attr-set.hh> // for Attr, Bindings, Bindings::iterator -#include <nix/common-eval-args.hh> // for MixEvalArgs -#include <nix/eval-inline.hh> // for EvalState::forceValue -#include <nix/eval.hh> // for EvalState, initGC, operator<< -#include <nix/globals.hh> // for initPlugins, Settings, settings -#include <nix/nixexpr.hh> // for Pos -#include <nix/shared.hh> // for getArg, LegacyArgs, printVersion -#include <nix/store-api.hh> // for openStore -#include <nix/symbol-table.hh> // for Symbol, SymbolTable -#include <nix/types.hh> // for Error, Path, Strings, PathSet -#include <nix/util.hh> // for absPath, baseNameOf -#include <nix/value.hh> // for Value, Value::(anonymous), Value:... -#include <string> // for string, operator+, operator== -#include <utility> // for move -#include <variant> // for get, holds_alternative, variant -#include <vector> // for vector<>::iterator, vector - -#include "libnix-copy-paste.hh" - -using nix::absPath; -using nix::Bindings; -using nix::Error; -using nix::EvalError; -using nix::EvalState; -using nix::Path; -using nix::PathSet; -using nix::Strings; -using nix::Symbol; -using nix::tAttrs; -using nix::ThrownError; -using nix::tLambda; -using nix::tString; -using nix::UsageError; -using nix::Value; - -// An ostream wrapper to handle nested indentation -class Out -{ - public: - class Separator - {}; - const static Separator sep; - enum LinePolicy - { - ONE_LINE, - MULTI_LINE - }; - explicit Out(std::ostream & ostream) : ostream(ostream), policy(ONE_LINE), writeSinceSep(true) {} - Out(Out & o, const std::string & start, const std::string & end, LinePolicy policy); - Out(Out & o, const std::string & start, const std::string & end, int count) - : Out(o, start, end, count < 2 ? ONE_LINE : MULTI_LINE) - {} - Out(const Out &) = delete; - Out(Out &&) = default; - Out & operator=(const Out &) = delete; - Out & operator=(Out &&) = delete; - ~Out() { ostream << end; } - - private: - std::ostream & ostream; - std::string indentation; - std::string end; - LinePolicy policy; - bool writeSinceSep; - template <typename T> friend Out & operator<<(Out & o, T thing); -}; - -template <typename T> Out & operator<<(Out & o, T thing) -{ - if (!o.writeSinceSep && o.policy == Out::MULTI_LINE) { - o.ostream << o.indentation; - } - o.writeSinceSep = true; - o.ostream << thing; - return o; -} - -template <> Out & operator<<<Out::Separator>(Out & o, Out::Separator /* thing */) -{ - o.ostream << (o.policy == Out::ONE_LINE ? " " : "\n"); - o.writeSinceSep = false; - return o; -} - -Out::Out(Out & o, const std::string & start, const std::string & end, LinePolicy policy) - : ostream(o.ostream), indentation(policy == ONE_LINE ? o.indentation : o.indentation + " "), - end(policy == ONE_LINE ? end : o.indentation + end), policy(policy), writeSinceSep(true) -{ - o << start; - *this << Out::sep; -} - -// Stuff needed for evaluation -struct Context -{ - Context(EvalState & state, Bindings & autoArgs, Value optionsRoot, Value configRoot) - : state(state), autoArgs(autoArgs), optionsRoot(optionsRoot), configRoot(configRoot), - underscoreType(state.symbols.create("_type")) - {} - EvalState & state; - Bindings & autoArgs; - Value optionsRoot; - Value configRoot; - Symbol underscoreType; -}; - -Value evaluateValue(Context & ctx, Value & v) -{ - ctx.state.forceValue(v); - if (ctx.autoArgs.empty()) { - return v; - } - Value called{}; - ctx.state.autoCallFunction(ctx.autoArgs, v, called); - return called; -} - -bool isOption(Context & ctx, const Value & v) -{ - if (v.type != tAttrs) { - return false; - } - const auto & actualType = v.attrs->find(ctx.underscoreType); - if (actualType == v.attrs->end()) { - return false; - } - try { - Value evaluatedType = evaluateValue(ctx, *actualType->value); - if (evaluatedType.type != tString) { - return false; - } - return static_cast<std::string>(evaluatedType.string.s) == "option"; - } catch (Error &) { - return false; - } -} - -// Add quotes to a component of a path. -// These are needed for paths like: -// fileSystems."/".fsType -// systemd.units."dbus.service".text -std::string quoteAttribute(const std::string & attribute) -{ - if (isVarName(attribute)) { - return attribute; - } - std::ostringstream buf; - printStringValue(buf, attribute.c_str()); - return buf.str(); -} - -const std::string appendPath(const std::string & prefix, const std::string & suffix) -{ - if (prefix.empty()) { - return quoteAttribute(suffix); - } - return prefix + "." + quoteAttribute(suffix); -} - -bool forbiddenRecursionName(std::string name) { return (!name.empty() && name[0] == '_') || name == "haskellPackages"; } - -void recurse(const std::function<bool(const std::string & path, std::variant<Value, std::exception_ptr>)> & f, - Context & ctx, Value v, const std::string & path) -{ - std::variant<Value, std::exception_ptr> evaluated; - try { - evaluated = evaluateValue(ctx, v); - } catch (Error &) { - evaluated = std::current_exception(); - } - if (!f(path, evaluated)) { - return; - } - if (std::holds_alternative<std::exception_ptr>(evaluated)) { - return; - } - const Value & evaluated_value = std::get<Value>(evaluated); - if (evaluated_value.type != tAttrs) { - return; - } - for (const auto & child : evaluated_value.attrs->lexicographicOrder()) { - if (forbiddenRecursionName(child->name)) { - continue; - } - recurse(f, ctx, *child->value, appendPath(path, child->name)); - } -} - -bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType) -{ - try { - const auto & typeLookup = v.attrs->find(ctx.state.sType); - if (typeLookup == v.attrs->end()) { - return false; - } - Value type = evaluateValue(ctx, *typeLookup->value); - if (type.type != tAttrs) { - return false; - } - const auto & nameLookup = type.attrs->find(ctx.state.sName); - if (nameLookup == type.attrs->end()) { - return false; - } - Value name = evaluateValue(ctx, *nameLookup->value); - if (name.type != tString) { - return false; - } - return name.string.s == soughtType; - } catch (Error &) { - return false; - } -} - -bool isAggregateOptionType(Context & ctx, Value & v) -{ - return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf"); -} - -MakeError(OptionPathError, EvalError); - -Value getSubOptions(Context & ctx, Value & option) -{ - Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option)); - if (getSubOptions.type != tLambda) { - throw OptionPathError("Option's type.getSubOptions isn't a function"); - } - Value emptyString{}; - nix::mkString(emptyString, ""); - Value v; - ctx.state.callFunction(getSubOptions, emptyString, v, nix::Pos{}); - return v; -} - -// Carefully walk an option path, looking for sub-options when a path walks past -// an option value. -struct FindAlongOptionPathRet -{ - Value option; - std::string path; -}; -FindAlongOptionPathRet findAlongOptionPath(Context & ctx, const std::string & path) -{ - Strings tokens = parseAttrPath(path); - Value v = ctx.optionsRoot; - std::string processedPath; - for (auto i = tokens.begin(); i != tokens.end(); i++) { - const auto & attr = *i; - try { - bool lastAttribute = std::next(i) == tokens.end(); - v = evaluateValue(ctx, v); - if (attr.empty()) { - throw OptionPathError("empty attribute name"); - } - if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) { - v = getSubOptions(ctx, v); - } - if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) { - auto subOptions = getSubOptions(ctx, v); - if (lastAttribute && subOptions.attrs->empty()) { - break; - } - v = subOptions; - // Note that we've consumed attr, but didn't actually use it. This is the path component that's looked - // up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name". - } else if (v.type != tAttrs) { - throw OptionPathError("Value is %s while a set was expected", showType(v)); - } else { - const auto & next = v.attrs->find(ctx.state.symbols.create(attr)); - if (next == v.attrs->end()) { - throw OptionPathError("Attribute not found", attr, path); - } - v = *next->value; - } - processedPath = appendPath(processedPath, attr); - } catch (OptionPathError & e) { - throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg()); - } - } - return {v, processedPath}; -} - -// Calls f on all the option names at or below the option described by `path`. -// Note that "the option described by `path`" is not trivial -- if path describes a value inside an aggregate -// option (such as users.users.root), the *option* described by that path is one path component shorter -// (eg: users.users), which results in f being called on sibling-paths (eg: users.users.nixbld1). If f -// doesn't want these, it must do its own filtering. -void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, const std::string & path) -{ - auto root = findAlongOptionPath(ctx, path); - recurse( - [f, &ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) { - bool isOpt = std::holds_alternative<std::exception_ptr>(v) || isOption(ctx, std::get<Value>(v)); - if (isOpt) { - f(path); - } - return !isOpt; - }, - ctx, root.option, root.path); -} - -// Calls f on all the config values inside one option. -// Simple options have one config value inside, like sound.enable = true. -// Compound options have multiple config values. For example, the option -// "users.users" has about 1000 config values inside it: -// users.users.avahi.createHome = false; -// users.users.avahi.cryptHomeLuks = null; -// users.users.avahi.description = "`avahi-daemon' privilege separation user"; -// ... -// users.users.avahi.openssh.authorizedKeys.keyFiles = [ ]; -// users.users.avahi.openssh.authorizedKeys.keys = [ ]; -// ... -// users.users.avahi.uid = 10; -// users.users.avahi.useDefaultShell = false; -// users.users.cups.createHome = false; -// ... -// users.users.cups.useDefaultShell = false; -// users.users.gdm = ... ... ... -// users.users.messagebus = ... .. ... -// users.users.nixbld1 = ... .. ... -// ... -// users.users.systemd-timesync = ... .. ... -void mapConfigValuesInOption( - const std::function<void(const std::string & path, std::variant<Value, std::exception_ptr> v)> & f, - const std::string & path, Context & ctx) -{ - Value * option; - try { - option = findAlongAttrPath(ctx.state, path, ctx.autoArgs, ctx.configRoot); - } catch (Error &) { - f(path, std::current_exception()); - return; - } - recurse( - [f, ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) { - bool leaf = std::holds_alternative<std::exception_ptr>(v) || std::get<Value>(v).type != tAttrs || - ctx.state.isDerivation(std::get<Value>(v)); - if (!leaf) { - return true; // Keep digging - } - f(path, v); - return false; - }, - ctx, *option, path); -} - -std::string describeError(const Error & e) { return "«error: " + e.msg() + "»"; } - -void describeDerivation(Context & ctx, Out & out, Value v) -{ - // Copy-pasted from nix/src/nix/repl.cc :( - Bindings::iterator i = v.attrs->find(ctx.state.sDrvPath); - PathSet pathset; - try { - Path drvPath = i != v.attrs->end() ? ctx.state.coerceToPath(*i->pos, *i->value, pathset) : "???"; - out << "«derivation " << drvPath << "»"; - } catch (Error & e) { - out << describeError(e); - } -} - -Value parseAndEval(EvalState & state, const std::string & expression, const std::string & path) -{ - Value v{}; - state.eval(state.parseExprFromString(expression, absPath(path)), v); - return v; -} - -void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr> maybeValue, const std::string & path); - -void printList(Context & ctx, Out & out, Value & v) -{ - Out listOut(out, "[", "]", v.listSize()); - for (unsigned int n = 0; n < v.listSize(); ++n) { - printValue(ctx, listOut, *v.listElems()[n], ""); - listOut << Out::sep; - } -} - -void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path) -{ - Out attrsOut(out, "{", "}", v.attrs->size()); - for (const auto & a : v.attrs->lexicographicOrder()) { - std::string name = a->name; - if (!forbiddenRecursionName(name)) { - attrsOut << name << " = "; - printValue(ctx, attrsOut, *a->value, appendPath(path, name)); - attrsOut << ";" << Out::sep; - } - } -} - -void multiLineStringEscape(Out & out, const std::string & s) -{ - int i; - for (i = 1; i < s.size(); i++) { - if (s[i - 1] == '$' && s[i] == '{') { - out << "''${"; - i++; - } else if (s[i - 1] == '\'' && s[i] == '\'') { - out << "'''"; - i++; - } else { - out << s[i - 1]; - } - } - if (i == s.size()) { - out << s[i - 1]; - } -} - -void printMultiLineString(Out & out, const Value & v) -{ - std::string s = v.string.s; - Out strOut(out, "''", "''", Out::MULTI_LINE); - std::string::size_type begin = 0; - while (begin < s.size()) { - std::string::size_type end = s.find('\n', begin); - if (end == std::string::npos) { - multiLineStringEscape(strOut, s.substr(begin, s.size() - begin)); - break; - } - multiLineStringEscape(strOut, s.substr(begin, end - begin)); - strOut << Out::sep; - begin = end + 1; - } -} - -void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr> maybeValue, const std::string & path) -{ - try { - if (auto ex = std::get_if<std::exception_ptr>(&maybeValue)) { - std::rethrow_exception(*ex); - } - Value v = evaluateValue(ctx, std::get<Value>(maybeValue)); - if (ctx.state.isDerivation(v)) { - describeDerivation(ctx, out, v); - } else if (v.isList()) { - printList(ctx, out, v); - } else if (v.type == tAttrs) { - printAttrs(ctx, out, v, path); - } else if (v.type == tString && std::string(v.string.s).find('\n') != std::string::npos) { - printMultiLineString(out, v); - } else { - ctx.state.forceValueDeep(v); - out << v; - } - } catch (ThrownError & e) { - if (e.msg() == "The option `" + path + "' is used but not defined.") { - // 93% of errors are this, and just letting this message through would be - // misleading. These values may or may not actually be "used" in the - // config. The thing throwing the error message assumes that if anything - // ever looks at this value, it is a "use" of this value. But here in - // nixos-option, we are looking at this value only to print it. - // In order to avoid implying that this undefined value is actually - // referenced, eat the underlying error message and emit "«not defined»". - out << "«not defined»"; - } else { - out << describeError(e); - } - } catch (Error & e) { - out << describeError(e); - } -} - -void printConfigValue(Context & ctx, Out & out, const std::string & path, std::variant<Value, std::exception_ptr> v) -{ - out << path << " = "; - printValue(ctx, out, std::move(v), path); - out << ";\n"; -} - -// Replace with std::starts_with when C++20 is available -bool starts_with(const std::string & s, const std::string & prefix) -{ - return s.size() >= prefix.size() && - std::equal(s.begin(), std::next(s.begin(), prefix.size()), prefix.begin(), prefix.end()); -} - -void printRecursive(Context & ctx, Out & out, const std::string & path) -{ - mapOptions( - [&ctx, &out, &path](const std::string & optionPath) { - mapConfigValuesInOption( - [&ctx, &out, &path](const std::string & configPath, std::variant<Value, std::exception_ptr> v) { - if (starts_with(configPath, path)) { - printConfigValue(ctx, out, configPath, v); - } - }, - optionPath, ctx); - }, - ctx, path); -} - -void printAttr(Context & ctx, Out & out, const std::string & path, Value & root) -{ - try { - printValue(ctx, out, *findAlongAttrPath(ctx.state, path, ctx.autoArgs, root), path); - } catch (Error & e) { - out << describeError(e); - } -} - -bool hasExample(Context & ctx, Value & option) -{ - try { - findAlongAttrPath(ctx.state, "example", ctx.autoArgs, option); - return true; - } catch (Error &) { - return false; - } -} - -void printOption(Context & ctx, Out & out, const std::string & path, Value & option) -{ - out << "Value:\n"; - printAttr(ctx, out, path, ctx.configRoot); - - out << "\n\nDefault:\n"; - printAttr(ctx, out, "default", option); - - out << "\n\nType:\n"; - printAttr(ctx, out, "type.description", option); - - if (hasExample(ctx, option)) { - out << "\n\nExample:\n"; - printAttr(ctx, out, "example", option); - } - - out << "\n\nDescription:\n"; - printAttr(ctx, out, "description", option); - - out << "\n\nDeclared by:\n"; - printAttr(ctx, out, "declarations", option); - - out << "\n\nDefined by:\n"; - printAttr(ctx, out, "files", option); - out << "\n"; -} - -void printListing(Out & out, Value & v) -{ - out << "This attribute set contains:\n"; - for (const auto & a : v.attrs->lexicographicOrder()) { - std::string name = a->name; - if (!name.empty() && name[0] != '_') { - out << name << "\n"; - } - } -} - -void printOne(Context & ctx, Out & out, const std::string & path) -{ - try { - auto result = findAlongOptionPath(ctx, path); - Value & option = result.option; - option = evaluateValue(ctx, option); - if (path != result.path) { - out << "Note: showing " << result.path << " instead of " << path << "\n"; - } - if (isOption(ctx, option)) { - printOption(ctx, out, result.path, option); - } else { - printListing(out, option); - } - } catch (Error & e) { - std::cerr << "error: " << e.msg() - << "\nAn error occurred while looking for attribute names. Are " - "you sure that '" - << path << "' exists?\n"; - } -} - -int main(int argc, char ** argv) -{ - bool recursive = false; - std::string path = "."; - std::string optionsExpr = "(import <nixpkgs/nixos> {}).options"; - std::string configExpr = "(import <nixpkgs/nixos> {}).config"; - std::vector<std::string> args; - - struct MyArgs : nix::LegacyArgs, nix::MixEvalArgs - { - using nix::LegacyArgs::LegacyArgs; - }; - - MyArgs myArgs(nix::baseNameOf(argv[0]), [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") { - nix::showManPage("nixos-option"); - } else if (*arg == "--version") { - nix::printVersion("nixos-option"); - } else if (*arg == "-r" || *arg == "--recursive") { - recursive = true; - } else if (*arg == "--path") { - path = nix::getArg(*arg, arg, end); - } else if (*arg == "--options_expr") { - optionsExpr = nix::getArg(*arg, arg, end); - } else if (*arg == "--config_expr") { - configExpr = nix::getArg(*arg, arg, end); - } else if (!arg->empty() && arg->at(0) == '-') { - return false; - } else { - args.push_back(*arg); - } - return true; - }); - - myArgs.parseCmdline(nix::argvToStrings(argc, argv)); - - nix::initPlugins(); - nix::initGC(); - nix::settings.readOnlyMode = true; - auto store = nix::openStore(); - auto state = std::make_unique<EvalState>(myArgs.searchPath, store); - - Value optionsRoot = parseAndEval(*state, optionsExpr, path); - Value configRoot = parseAndEval(*state, configExpr, path); - - Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot}; - Out out(std::cout); - - auto print = recursive ? printRecursive : printOne; - if (args.empty()) { - print(ctx, out, ""); - } - for (const auto & arg : args) { - print(ctx, out, arg); - } - - ctx.state.printStats(); - - return 0; -} diff --git a/nixpkgs/nixos/modules/installer/tools/tools.nix b/nixpkgs/nixos/modules/installer/tools/tools.nix index 1dc0578daca6..f79ed3493dfb 100644 --- a/nixpkgs/nixos/modules/installer/tools/tools.nix +++ b/nixpkgs/nixos/modules/installer/tools/tools.nix @@ -42,7 +42,7 @@ let nixos-option = if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre" then null - else pkgs.callPackage ./nixos-option { }; + else pkgs.nixos-option; nixos-version = makeProg { name = "nixos-version"; diff --git a/nixpkgs/nixos/modules/misc/ids.nix b/nixpkgs/nixos/modules/misc/ids.nix index b08c3cacf3a3..fe6e563d7ee5 100644 --- a/nixpkgs/nixos/modules/misc/ids.nix +++ b/nixpkgs/nixos/modules/misc/ids.nix @@ -229,7 +229,7 @@ in grafana = 196; skydns = 197; # ripple-rest = 198; # unused, removed 2017-08-12 - nix-serve = 199; + # nix-serve = 199; # unused, removed 2020-12-12 tvheadend = 200; uwsgi = 201; gitit = 202; diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix index 9ea1c3fb62e1..0ae556cea138 100644 --- a/nixpkgs/nixos/modules/module-list.nix +++ b/nixpkgs/nixos/modules/module-list.nix @@ -389,6 +389,7 @@ ./services/hardware/bolt.nix ./services/hardware/brltty.nix ./services/hardware/evscript.nix + ./services/hardware/ddccontrol.nix ./services/hardware/fancontrol.nix ./services/hardware/freefall.nix ./services/hardware/fwupd.nix @@ -476,6 +477,7 @@ ./services/misc/calibre-server.nix ./services/misc/cfdyndns.nix ./services/misc/clipmenu.nix + ./services/misc/clipcat.nix ./services/misc/cpuminer-cryptonight.nix ./services/misc/cgminer.nix ./services/misc/confd.nix @@ -728,6 +730,7 @@ ./services/networking/iwd.nix ./services/networking/jicofo.nix ./services/networking/jitsi-videobridge.nix + ./services/networking/kea.nix ./services/networking/keepalived/default.nix ./services/networking/keybase.nix ./services/networking/kippo.nix @@ -849,6 +852,7 @@ ./services/networking/ucarp.nix ./services/networking/unbound.nix ./services/networking/unifi.nix + ./services/video/unifi-video.nix ./services/networking/v2ray.nix ./services/networking/vsftpd.nix ./services/networking/wakeonlan.nix @@ -885,6 +889,7 @@ ./services/security/fprot.nix ./services/security/haka.nix ./services/security/haveged.nix + ./services/security/hockeypuck.nix ./services/security/hologram-server.nix ./services/security/hologram-agent.nix ./services/security/munge.nix @@ -970,6 +975,7 @@ ./services/web-apps/trilium.nix ./services/web-apps/selfoss.nix ./services/web-apps/shiori.nix + ./services/web-apps/vikunja.nix ./services/web-apps/virtlyst.nix ./services/web-apps/wiki-js.nix ./services/web-apps/whitebophir.nix @@ -1103,6 +1109,7 @@ ./tasks/network-interfaces-systemd.nix ./tasks/network-interfaces-scripted.nix ./tasks/scsi-link-power-management.nix + ./tasks/snapraid.nix ./tasks/swraid.nix ./tasks/trackpoint.nix ./tasks/powertop.nix diff --git a/nixpkgs/nixos/modules/programs/udevil.nix b/nixpkgs/nixos/modules/programs/udevil.nix index ba5670f9dfe9..25975d88ec86 100644 --- a/nixpkgs/nixos/modules/programs/udevil.nix +++ b/nixpkgs/nixos/modules/programs/udevil.nix @@ -10,5 +10,8 @@ in { config = mkIf cfg.enable { security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil"; + + systemd.packages = [ pkgs.udevil ]; + systemd.services."devmon@".wantedBy = [ "multi-user.target" ]; }; } diff --git a/nixpkgs/nixos/modules/programs/xwayland.nix b/nixpkgs/nixos/modules/programs/xwayland.nix index 7e9a424a7150..cb3c9c5b156c 100644 --- a/nixpkgs/nixos/modules/programs/xwayland.nix +++ b/nixpkgs/nixos/modules/programs/xwayland.nix @@ -10,14 +10,16 @@ in { options.programs.xwayland = { - enable = mkEnableOption '' - Xwayland X server allows running X programs on a Wayland compositor. - ''; + enable = mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)"; defaultFontPath = mkOption { type = types.str; default = optionalString config.fonts.fontDir.enable "/run/current-system/sw/share/X11/fonts"; + defaultText = literalExample '' + optionalString config.fonts.fontDir.enable + "/run/current-system/sw/share/X11/fonts"; + ''; description = '' Default font path. Setting this option causes Xwayland to be rebuilt. ''; @@ -25,7 +27,15 @@ in package = mkOption { type = types.path; - description = "The Xwayland package"; + default = pkgs.xwayland.override (oldArgs: { + inherit (cfg) defaultFontPath; + }); + defaultText = literalExample '' + pkgs.xwayland.override (oldArgs: { + inherit (config.programs.xwayland) defaultFontPath; + }); + ''; + description = "The Xwayland package to use."; }; }; @@ -37,9 +47,5 @@ in environment.systemPackages = [ cfg.package ]; - programs.xwayland.package = pkgs.xwayland.override (oldArgs: { - inherit (cfg) defaultFontPath; - }); - }; } diff --git a/nixpkgs/nixos/modules/programs/zsh/zsh.nix b/nixpkgs/nixos/modules/programs/zsh/zsh.nix index 48638fda28da..6c824a692b75 100644 --- a/nixpkgs/nixos/modules/programs/zsh/zsh.nix +++ b/nixpkgs/nixos/modules/programs/zsh/zsh.nix @@ -53,7 +53,7 @@ in }; shellAliases = mkOption { - default = {}; + default = { }; description = '' Set of aliases for zsh shell, which overrides <option>environment.shellAliases</option>. See <option>environment.shellAliases</option> for an option format description. @@ -118,7 +118,9 @@ in setOptions = mkOption { type = types.listOf types.str; default = [ - "HIST_IGNORE_DUPS" "SHARE_HISTORY" "HIST_FCNTL_LOCK" + "HIST_IGNORE_DUPS" + "SHARE_HISTORY" + "HIST_FCNTL_LOCK" ]; example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ]; description = '' @@ -278,15 +280,29 @@ in environment.etc.zinputrc.source = ./zinputrc; - environment.systemPackages = [ pkgs.zsh ] - ++ optional cfg.enableCompletion pkgs.nix-zsh-completions; + environment.systemPackages = + let + completions = + if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre" + then + pkgs.nix-zsh-completions.overrideAttrs + (_: { + postInstall = '' + rm $out/share/zsh/site-functions/_nix + ''; + }) + else pkgs.nix-zsh-completions; + in + [ pkgs.zsh ] + ++ optional cfg.enableCompletion completions; environment.pathsToLink = optional cfg.enableCompletion "/share/zsh"; #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh"; environment.shells = - [ "/run/current-system/sw/bin/zsh" + [ + "/run/current-system/sw/bin/zsh" "${pkgs.zsh}/bin/zsh" ]; diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix index 9da2d522a68d..f658eb756f7b 100644 --- a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix +++ b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix @@ -14,15 +14,21 @@ let requires = [ "postgresql.service" ]; + path = [ pkgs.coreutils pkgs.gzip config.services.postgresql.package ]; + script = '' + set -e -o pipefail + umask 0077 # ensure backup is only readable by postgres user if [ -e ${cfg.location}/${db}.sql.gz ]; then - ${pkgs.coreutils}/bin/mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz + mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz fi ${dumpCmd} | \ - ${pkgs.gzip}/bin/gzip -c > ${cfg.location}/${db}.sql.gz + gzip -c > ${cfg.location}/${db}.in-progress.sql.gz + + mv ${cfg.location}/${db}.in-progress.sql.gz ${cfg.location}/${db}.sql.gz ''; serviceConfig = { @@ -113,12 +119,12 @@ in { }) (mkIf (cfg.enable && cfg.backupAll) { systemd.services.postgresqlBackup = - postgresqlBackupService "all" "${config.services.postgresql.package}/bin/pg_dumpall"; + postgresqlBackupService "all" "pg_dumpall"; }) (mkIf (cfg.enable && !cfg.backupAll) { systemd.services = listToAttrs (map (db: let - cmd = "${config.services.postgresql.package}/bin/pg_dump ${cfg.pgdumpOptions} ${db}"; + cmd = "pg_dump ${cfg.pgdumpOptions} ${db}"; in { name = "postgresqlBackup-${db}"; value = postgresqlBackupService db cmd; diff --git a/nixpkgs/nixos/modules/services/backup/sanoid.nix b/nixpkgs/nixos/modules/services/backup/sanoid.nix index be44a43b6d3f..abc4def1c61f 100644 --- a/nixpkgs/nixos/modules/services/backup/sanoid.nix +++ b/nixpkgs/nixos/modules/services/backup/sanoid.nix @@ -108,8 +108,8 @@ in { type = types.attrsOf (types.submodule ({config, options, ...}: { freeformType = datasetSettingsType; options = commonOptions // datasetOptions; - config.use_template = mkAliasDefinitions (options.useTemplate or {}); - config.process_children_only = mkAliasDefinitions (options.processChildrenOnly or {}); + config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or {}); + config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or {}); })); default = {}; description = "Datasets to snapshot."; diff --git a/nixpkgs/nixos/modules/services/cluster/k3s/default.nix b/nixpkgs/nixos/modules/services/cluster/k3s/default.nix index 300c182406ce..e5c51441690a 100644 --- a/nixpkgs/nixos/modules/services/cluster/k3s/default.nix +++ b/nixpkgs/nixos/modules/services/cluster/k3s/default.nix @@ -35,10 +35,20 @@ in token = mkOption { type = types.str; - description = "The k3s token to use when connecting to the server. This option only makes sense for an agent."; + description = '' + The k3s token to use when connecting to the server. This option only makes sense for an agent. + WARNING: This option will expose store your token unencrypted world-readable in the nix store. + If this is undesired use the tokenFile option instead. + ''; default = ""; }; + tokenFile = mkOption { + type = types.nullOr types.path; + description = "File path containing k3s token to use when connecting to the server. This option only makes sense for an agent."; + default = null; + }; + docker = mkOption { type = types.bool; default = false; @@ -57,6 +67,12 @@ in default = false; description = "Only run the server. This option only makes sense for a server."; }; + + configPath = mkOption { + type = types.nullOr types.path; + default = null; + description = "File path containing the k3s YAML config. This is useful when the config is generated (for example on boot)."; + }; }; # implementation @@ -64,12 +80,12 @@ in config = mkIf cfg.enable { assertions = [ { - assertion = cfg.role == "agent" -> cfg.serverAddr != ""; - message = "serverAddr should be set if role is 'agent'"; + assertion = cfg.role == "agent" -> (cfg.configPath != null || cfg.serverAddr != ""); + message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'"; } { - assertion = cfg.role == "agent" -> cfg.token != ""; - message = "token should be set if role is 'agent'"; + assertion = cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != ""; + message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'"; } ]; @@ -105,7 +121,10 @@ in "${cfg.package}/bin/k3s ${cfg.role}" ] ++ (optional cfg.docker "--docker") ++ (optional cfg.disableAgent "--disable-agent") - ++ (optional (cfg.role == "agent") "--server ${cfg.serverAddr} --token ${cfg.token}") + ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}") + ++ (optional (cfg.token != "") "--token ${cfg.token}") + ++ (optional (cfg.tokenFile != null) "--token-file ${cfg.tokenFile}") + ++ (optional (cfg.configPath != null) "--config ${cfg.configPath}") ++ [ cfg.extraFlags ] ); }; diff --git a/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix b/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix index 2f0b573e8721..70d85a97f3b7 100644 --- a/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix +++ b/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix @@ -105,7 +105,7 @@ let pkgs.stdenv.mkDerivation { name = "hercules-ci-check-system-nix-src"; inherit (config.nix.package) src patches; - configurePhase = ":"; + dontConfigure = true; buildPhase = '' echo "Checking in-memory pathInfoCache expiry" if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then diff --git a/nixpkgs/nixos/modules/services/hardware/ddccontrol.nix b/nixpkgs/nixos/modules/services/hardware/ddccontrol.nix new file mode 100644 index 000000000000..766bf12ee9f0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/hardware/ddccontrol.nix @@ -0,0 +1,36 @@ +{ config +, lib +, pkgs +, ... +}: + +let + cfg = config.services.ddccontrol; +in + +{ + ###### interface + + options = { + services.ddccontrol = { + enable = lib.mkEnableOption "ddccontrol for controlling displays"; + }; + }; + + ###### implementation + + config = lib.mkIf cfg.enable { + # Give users access to the "gddccontrol" tool + environment.systemPackages = [ + pkgs.ddccontrol + ]; + + services.dbus.packages = [ + pkgs.ddccontrol + ]; + + systemd.packages = [ + pkgs.ddccontrol + ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix b/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix index 556f6bbb419a..9d083a615a2c 100644 --- a/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix +++ b/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix @@ -54,8 +54,7 @@ stdenv.mkDerivation { ${addAllNetDev netDevices} ''; - installPhase = ":"; - + dontInstall = true; dontStrip = true; dontPatchELF = true; diff --git a/nixpkgs/nixos/modules/services/misc/clipcat.nix b/nixpkgs/nixos/modules/services/misc/clipcat.nix new file mode 100644 index 000000000000..128bb9a89d69 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/clipcat.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.clipcat; +in { + + options.services.clipcat= { + enable = mkEnableOption "Clipcat clipboard daemon"; + + package = mkOption { + type = types.package; + default = pkgs.clipcat; + defaultText = "pkgs.clipcat"; + description = "clipcat derivation to use."; + }; + }; + + config = mkIf cfg.enable { + systemd.user.services.clipcat = { + enable = true; + description = "clipcat daemon"; + wantedBy = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + serviceConfig.ExecStart = "${cfg.package}/bin/clipcatd --no-daemon"; + }; + + environment.systemPackages = [ cfg.package ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix index 40e35ed1cd16..1514cc0665df 100644 --- a/nixpkgs/nixos/modules/services/misc/gitlab.nix +++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix @@ -911,7 +911,7 @@ in { } { assertion = versionAtLeast postgresqlPackage.version "12.0.0"; - message = "PostgreSQL >=12 is required to run GitLab 14."; + message = "PostgreSQL >=12 is required to run GitLab 14. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading"; } ]; diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix index d68d7b05c173..dcd825bba433 100644 --- a/nixpkgs/nixos/modules/services/misc/home-assistant.nix +++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix @@ -313,6 +313,7 @@ in { "w800rf32" "xbee" "zha" + "zwave" ]; in { ExecStart = "${package}/bin/hass --runner --config '${cfg.configDir}'"; diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix index 2f04c011a650..4930648ba8e3 100644 --- a/nixpkgs/nixos/modules/services/misc/klipper.nix +++ b/nixpkgs/nixos/modules/services/misc/klipper.nix @@ -2,7 +2,6 @@ with lib; let cfg = config.services.klipper; - package = pkgs.klipper; format = pkgs.formats.ini { mkKeyValue = generators.mkKeyValueDefault {} ":"; }; in { @@ -11,12 +10,51 @@ in services.klipper = { enable = mkEnableOption "Klipper, the 3D printer firmware"; + package = mkOption { + type = types.package; + default = pkgs.klipper; + description = "The Klipper package."; + }; + + inputTTY = mkOption { + type = types.path; + default = "/run/klipper/tty"; + description = "Path of the virtual printer symlink to create."; + }; + + apiSocket = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/klipper/api"; + description = "Path of the API socket to create."; + }; + octoprintIntegration = mkOption { type = types.bool; default = false; description = "Allows Octoprint to control Klipper."; }; + user = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + User account under which Klipper runs. + + If null is specified (default), a temporary user will be created by systemd. + ''; + }; + + group = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Group account under which Klipper runs. + + If null is specified (default), a temporary user will be created by systemd. + ''; + }; + settings = mkOption { type = format.type; default = { }; @@ -30,26 +68,40 @@ in ##### implementation config = mkIf cfg.enable { - assertions = [{ - assertion = cfg.octoprintIntegration -> config.services.octoprint.enable; - message = "Option klipper.octoprintIntegration requires Octoprint to be enabled on this system. Please enable services.octoprint to use it."; - }]; + assertions = [ + { + assertion = cfg.octoprintIntegration -> config.services.octoprint.enable; + message = "Option klipper.octoprintIntegration requires Octoprint to be enabled on this system. Please enable services.octoprint to use it."; + } + { + assertion = cfg.user != null -> cfg.group != null; + message = "Option klipper.group is not set when a user is specified."; + } + ]; environment.etc."klipper.cfg".source = format.generate "klipper.cfg" cfg.settings; - systemd.services.klipper = { + services.klipper = mkIf cfg.octoprintIntegration { + user = config.services.octoprint.user; + group = config.services.octoprint.group; + }; + + systemd.services.klipper = let + klippyArgs = "--input-tty=${cfg.inputTTY}" + + optionalString (cfg.apiSocket != null) " --api-server=${cfg.apiSocket}"; + in { description = "Klipper 3D Printer Firmware"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; serviceConfig = { - ExecStart = "${package}/lib/klipper/klippy.py --input-tty=/run/klipper/tty /etc/klipper.cfg"; + ExecStart = "${cfg.package}/lib/klipper/klippy.py ${klippyArgs} /etc/klipper.cfg"; RuntimeDirectory = "klipper"; SupplementaryGroups = [ "dialout" ]; - WorkingDirectory = "${package}/lib"; - } // (if cfg.octoprintIntegration then { - Group = config.services.octoprint.group; - User = config.services.octoprint.user; + WorkingDirectory = "${cfg.package}/lib"; + } // (if cfg.user != null then { + Group = cfg.group; + User = cfg.user; } else { DynamicUser = true; User = "klipper"; diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix index 8fe689ef3dbb..3be247ffb24e 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix @@ -323,15 +323,13 @@ let HTTP username ''; }; - password = mkOption { - type = types.str; - description = '' - HTTP password - ''; - }; + password = mkOpt types.str "HTTP password"; + password_file = mkOpt types.str "HTTP password file"; }; }) '' - Optional http login credentials for metrics scraping. + Sets the `Authorization` header on every scrape request with the + configured username and password. + password and password_file are mutually exclusive. ''; bearer_token = mkOpt types.str '' diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix index 46015c9ec1ef..d648de6a4148 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -27,6 +27,7 @@ let "bird" "bitcoin" "blackbox" + "buildkite-agent" "collectd" "dnsmasq" "domain" @@ -180,7 +181,7 @@ let serviceConfig.PrivateTmp = mkDefault true; serviceConfig.WorkingDirectory = mkDefault /tmp; serviceConfig.DynamicUser = mkDefault enableDynamicUser; - serviceConfig.User = conf.user; + serviceConfig.User = mkDefault conf.user; serviceConfig.Group = conf.group; } serviceOpts ]); }; diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix new file mode 100644 index 000000000000..7557480ac062 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.buildkite-agent; +in +{ + port = 9876; + extraOpts = { + tokenPath = mkOption { + type = types.nullOr types.path; + apply = final: if final == null then null else toString final; + description = '' + The token from your Buildkite "Agents" page. + + A run-time path to the token file, which is supposed to be provisioned + outside of Nix store. + ''; + }; + interval = mkOption { + type = types.str; + default = "30s"; + example = "1min"; + description = '' + How often to update metrics. + ''; + }; + endpoint = mkOption { + type = types.str; + default = "https://agent.buildkite.com/v3"; + description = '' + The Buildkite Agent API endpoint. + ''; + }; + queues = mkOption { + type = with types; nullOr (listOf str); + default = null; + example = literalExample ''[ "my-queue1" "my-queue2" ]''; + description = '' + Which specific queues to process. + ''; + }; + }; + serviceOpts = { + script = + let + queues = concatStringsSep " " (map (q: "-queue ${q}") cfg.queues); + in + '' + export BUILDKITE_AGENT_TOKEN="$(cat ${toString cfg.tokenPath})" + exec ${pkgs.buildkite-agent-metrics}/bin/buildkite-agent-metrics \ + -backend prometheus \ + -interval ${cfg.interval} \ + -endpoint ${cfg.endpoint} \ + ${optionalString (cfg.queues != null) queues} \ + -prometheus-addr "${cfg.listenAddress}:${toString cfg.port}" ${concatStringsSep " " cfg.extraFlags} + ''; + serviceConfig = { + DynamicUser = false; + RuntimeDirectory = "buildkite-agent-metrics"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix index b6cd89c3866b..9677281f8772 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix @@ -26,6 +26,7 @@ in { }; serviceOpts = { serviceConfig = { + User = "kea"; ExecStart = '' ${pkgs.prometheus-kea-exporter}/bin/kea-exporter \ --address ${cfg.listenAddress} \ diff --git a/nixpkgs/nixos/modules/services/monitoring/telegraf.nix b/nixpkgs/nixos/modules/services/monitoring/telegraf.nix index bc30ca3b77cf..4046260c1649 100644 --- a/nixpkgs/nixos/modules/services/monitoring/telegraf.nix +++ b/nixpkgs/nixos/modules/services/monitoring/telegraf.nix @@ -25,10 +25,9 @@ in { default = []; example = "/run/keys/telegraf.env"; description = '' - File to load as environment file. Environment variables - from this file will be interpolated into the config file - using envsubst with this syntax: - <literal>$ENVIRONMENT ''${VARIABLE}</literal> + File to load as environment file. Environment variables from this file + will be interpolated into the config file using envsubst with this + syntax: <literal>$ENVIRONMENT</literal> or <literal>''${VARIABLE}</literal>. This is useful to avoid putting secrets into the nix store. ''; }; @@ -73,6 +72,7 @@ in { ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID"; RuntimeDirectory = "telegraf"; User = "telegraf"; + Group = "telegraf"; Restart = "on-failure"; # for ping probes AmbientCapabilities = [ "CAP_NET_RAW" ]; @@ -81,7 +81,10 @@ in { users.users.telegraf = { uid = config.ids.uids.telegraf; + group = "telegraf"; description = "telegraf daemon user"; }; + + users.groups.telegraf = {}; }; } diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix index 20eef2c3455b..480d5a184f25 100644 --- a/nixpkgs/nixos/modules/services/networking/bind.nix +++ b/nixpkgs/nixos/modules/services/networking/bind.nix @@ -6,6 +6,8 @@ let cfg = config.services.bind; + bindPkg = config.services.bind.package; + bindUser = "named"; bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; })); @@ -59,7 +61,7 @@ let blackhole { badnetworks; }; forward first; forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} }; - directory "/run/named"; + directory "${cfg.directory}"; pid-file "/run/named/named.pid"; ${cfg.extraOptions} }; @@ -104,6 +106,14 @@ in enable = mkEnableOption "BIND domain name server"; + + package = mkOption { + type = types.package; + default = pkgs.bind; + defaultText = "pkgs.bind"; + description = "The BIND package to use."; + }; + cacheNetworks = mkOption { default = [ "127.0.0.0/24" ]; type = types.listOf types.str; @@ -156,6 +166,12 @@ in "; }; + directory = mkOption { + type = types.str; + default = "/run/named"; + description = "Working directory of BIND."; + }; + zones = mkOption { default = [ ]; type = with types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (types.submodule bindZoneOptions)); @@ -225,17 +241,20 @@ in preStart = '' mkdir -m 0755 -p /etc/bind if ! [ -f "/etc/bind/rndc.key" ]; then - ${pkgs.bind.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null + ${bindPkg.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null fi ${pkgs.coreutils}/bin/mkdir -p /run/named chown ${bindUser} /run/named + + ${pkgs.coreutils}/bin/mkdir -p ${cfg.directory} + chown ${bindUser} ${cfg.directory} ''; serviceConfig = { - ExecStart = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; - ExecReload = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' reload"; - ExecStop = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop"; + ExecStart = "${bindPkg.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; + ExecReload = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' reload"; + ExecStop = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' stop"; }; unitConfig.Documentation = "man:named(8)"; diff --git a/nixpkgs/nixos/modules/services/networking/corerad.nix b/nixpkgs/nixos/modules/services/networking/corerad.nix index 4acdd1d69cc4..e76ba9a2d00d 100644 --- a/nixpkgs/nixos/modules/services/networking/corerad.nix +++ b/nixpkgs/nixos/modules/services/networking/corerad.nix @@ -37,7 +37,7 @@ in { } ''; description = '' - Configuration for CoreRAD, see <link xlink:href="https://github.com/mdlayher/corerad/blob/master/internal/config/default.toml"/> + Configuration for CoreRAD, see <link xlink:href="https://github.com/mdlayher/corerad/blob/main/internal/config/reference.toml"/> for supported values. Ignored if configFile is set. ''; }; diff --git a/nixpkgs/nixos/modules/services/networking/ddclient.nix b/nixpkgs/nixos/modules/services/networking/ddclient.nix index 053efe712709..7820eedd9327 100644 --- a/nixpkgs/nixos/modules/services/networking/ddclient.nix +++ b/nixpkgs/nixos/modules/services/networking/ddclient.nix @@ -18,6 +18,7 @@ let ${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"} ssl=${boolToStr cfg.ssl} wildcard=YES + ipv6=${boolToStr cfg.ipv6} quiet=${boolToStr cfg.quiet} verbose=${boolToStr cfg.verbose} ${cfg.extraConfig} @@ -116,7 +117,15 @@ with lib; default = true; type = bool; description = '' - Whether to use to use SSL/TLS to connect to dynamic DNS provider. + Whether to use SSL/TLS to connect to dynamic DNS provider. + ''; + }; + + ipv6 = mkOption { + default = false; + type = bool; + description = '' + Whether to use IPv6. ''; }; diff --git a/nixpkgs/nixos/modules/services/networking/kea.nix b/nixpkgs/nixos/modules/services/networking/kea.nix new file mode 100644 index 000000000000..72773b83a496 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/kea.nix @@ -0,0 +1,361 @@ +{ config +, lib +, pkgs +, ... +}: + +with lib; + +let + cfg = config.services.kea; + + format = pkgs.formats.json {}; + + ctrlAgentConfig = format.generate "kea-ctrl-agent.conf" { + Control-agent = cfg.ctrl-agent.settings; + }; + dhcp4Config = format.generate "kea-dhcp4.conf" { + Dhcp4 = cfg.dhcp4.settings; + }; + dhcp6Config = format.generate "kea-dhcp6.conf" { + Dhcp6 = cfg.dhcp6.settings; + }; + dhcpDdnsConfig = format.generate "kea-dhcp-ddns.conf" { + DhcpDdns = cfg.dhcp-ddns.settings; + }; + + package = pkgs.kea; +in +{ + options.services.kea = with types; { + ctrl-agent = mkOption { + description = '' + Kea Control Agent configuration + ''; + default = {}; + type = submodule { + options = { + enable = mkEnableOption "Kea Control Agent"; + + extraArgs = mkOption { + type = listOf str; + default = []; + description = '' + List of additonal arguments to pass to the daemon. + ''; + }; + + settings = mkOption { + type = format.type; + default = null; + description = '' + Kea Control Agent configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html"/>. + ''; + }; + }; + }; + }; + + dhcp4 = mkOption { + description = '' + DHCP4 Server configuration + ''; + default = {}; + type = submodule { + options = { + enable = mkEnableOption "Kea DHCP4 server"; + + extraArgs = mkOption { + type = listOf str; + default = []; + description = '' + List of additonal arguments to pass to the daemon. + ''; + }; + + settings = mkOption { + type = format.type; + default = null; + example = { + valid-lifetime = 4000; + renew-timer = 1000; + rebind-timer = 2000; + interfaces-config = { + interfaces = [ + "eth0" + ]; + }; + lease-database = { + type = "memfile"; + persist = true; + name = "/var/lib/kea/dhcp4.leases"; + }; + subnet4 = [ { + subnet = "192.0.2.0/24"; + pools = [ { + pool = "192.0.2.100 - 192.0.2.240"; + } ]; + } ]; + }; + description = '' + Kea DHCP4 configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html"/>. + ''; + }; + }; + }; + }; + + dhcp6 = mkOption { + description = '' + DHCP6 Server configuration + ''; + default = {}; + type = submodule { + options = { + enable = mkEnableOption "Kea DHCP6 server"; + + extraArgs = mkOption { + type = listOf str; + default = []; + description = '' + List of additonal arguments to pass to the daemon. + ''; + }; + + settings = mkOption { + type = format.type; + default = null; + example = { + valid-lifetime = 4000; + renew-timer = 1000; + rebind-timer = 2000; + preferred-lifetime = 3000; + interfaces-config = { + interfaces = [ + "eth0" + ]; + }; + lease-database = { + type = "memfile"; + persist = true; + name = "/var/lib/kea/dhcp6.leases"; + }; + subnet6 = [ { + subnet = "2001:db8:1::/64"; + pools = [ { + pool = "2001:db8:1::1-2001:db8:1::ffff"; + } ]; + } ]; + }; + description = '' + Kea DHCP6 configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html"/>. + ''; + }; + }; + }; + }; + + dhcp-ddns = mkOption { + description = '' + Kea DHCP-DDNS configuration + ''; + default = {}; + type = submodule { + options = { + enable = mkEnableOption "Kea DDNS server"; + + extraArgs = mkOption { + type = listOf str; + default = []; + description = '' + List of additonal arguments to pass to the daemon. + ''; + }; + + settings = mkOption { + type = format.type; + default = null; + example = { + ip-address = "127.0.0.1"; + port = 53001; + dns-server-timeout = 100; + ncr-protocol = "UDP"; + ncr-format = "JSON"; + tsig-keys = [ ]; + forward-ddns = { + ddns-domains = [ ]; + }; + reverse-ddns = { + ddns-domains = [ ]; + }; + }; + description = '' + Kea DHCP-DDNS configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html"/>. + ''; + }; + }; + }; + }; + }; + + config = let + commonServiceConfig = { + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + DynamicUser = true; + User = "kea"; + ConfigurationDirectory = "kea"; + RuntimeDirectory = "kea"; + StateDirectory = "kea"; + UMask = "0077"; + }; + in mkIf (cfg.ctrl-agent.enable || cfg.dhcp4.enable || cfg.dhcp6.enable || cfg.dhcp-ddns.enable) (mkMerge [ + { + environment.systemPackages = [ package ]; + } + + (mkIf cfg.ctrl-agent.enable { + + environment.etc."kea/ctrl-agent.conf".source = ctrlAgentConfig; + + systemd.services.kea-ctrl-agent = { + description = "Kea Control Agent"; + documentation = [ + "man:kea-ctrl-agent(8)" + "https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html" + ]; + + after = [ + "network-online.target" + "time-sync.target" + ]; + wantedBy = [ + "kea-dhcp4-server.service" + "kea-dhcp6-server.service" + "kea-dhcp-ddns-server.service" + ]; + + environment = { + KEA_PIDFILE_DIR = "/run/kea"; + }; + + serviceConfig = { + ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}"; + KillMode = "process"; + Restart = "on-failure"; + } // commonServiceConfig; + }; + }) + + (mkIf cfg.dhcp4.enable { + + environment.etc."kea/dhcp4-server.conf".source = dhcp4Config; + + systemd.services.kea-dhcp4-server = { + description = "Kea DHCP4 Server"; + documentation = [ + "man:kea-dhcp4(8)" + "https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html" + ]; + + after = [ + "network-online.target" + "time-sync.target" + ]; + wantedBy = [ + "multi-user.target" + ]; + + environment = { + KEA_PIDFILE_DIR = "/run/kea"; + }; + + serviceConfig = { + ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}"; + # Kea does not request capabilities by itself + AmbientCapabilities = [ + "CAP_NET_BIND_SERVICE" + "CAP_NET_RAW" + ]; + CapabilityBoundingSet = [ + "CAP_NET_BIND_SERVICE" + "CAP_NET_RAW" + ]; + } // commonServiceConfig; + }; + }) + + (mkIf cfg.dhcp6.enable { + + environment.etc."kea/dhcp6-server.conf".source = dhcp6Config; + + systemd.services.kea-dhcp6-server = { + description = "Kea DHCP6 Server"; + documentation = [ + "man:kea-dhcp6(8)" + "https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html" + ]; + + after = [ + "network-online.target" + "time-sync.target" + ]; + wantedBy = [ + "multi-user.target" + ]; + + environment = { + KEA_PIDFILE_DIR = "/run/kea"; + }; + + serviceConfig = { + ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}"; + # Kea does not request capabilities by itself + AmbientCapabilities = [ + "CAP_NET_BIND_SERVICE" + ]; + CapabilityBoundingSet = [ + "CAP_NET_BIND_SERVICE" + ]; + } // commonServiceConfig; + }; + }) + + (mkIf cfg.dhcp-ddns.enable { + + environment.etc."kea/dhcp-ddns.conf".source = dhcpDdnsConfig; + + systemd.services.kea-dhcp-ddns-server = { + description = "Kea DHCP-DDNS Server"; + documentation = [ + "man:kea-dhcp-ddns(8)" + "https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html" + ]; + + after = [ + "network-online.target" + "time-sync.target" + ]; + wantedBy = [ + "multi-user.target" + ]; + + environment = { + KEA_PIDFILE_DIR = "/run/kea"; + }; + + serviceConfig = { + ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}"; + AmbientCapabilites = [ + "CAP_NET_BIND_SERVICE" + ]; + CapabilityBoundingSet = [ + "CAP_NET_BIND_SERVICE" + ]; + } // commonServiceConfig; + }; + }) + + ]); + + meta.maintainers = with maintainers; [ hexa ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/matterbridge.nix b/nixpkgs/nixos/modules/services/networking/matterbridge.nix index b8b4f37c84a8..9186eee26abf 100644 --- a/nixpkgs/nixos/modules/services/networking/matterbridge.nix +++ b/nixpkgs/nixos/modules/services/networking/matterbridge.nix @@ -38,8 +38,8 @@ in # Use services.matterbridge.configPath instead. [irc] - [irc.freenode] - Server="irc.freenode.net:6667" + [irc.libera] + Server="irc.libera.chat:6667" Nick="matterbot" [mattermost] @@ -55,7 +55,7 @@ in name="gateway1" enable=true [[gateway.inout]] - account="irc.freenode" + account="irc.libera" channel="#testing" [[gateway.inout]] diff --git a/nixpkgs/nixos/modules/services/networking/nix-serve.nix b/nixpkgs/nixos/modules/services/networking/nix-serve.nix index b17f35c769bb..7fc145f2303d 100644 --- a/nixpkgs/nixos/modules/services/networking/nix-serve.nix +++ b/nixpkgs/nixos/modules/services/networking/nix-serve.nix @@ -69,13 +69,9 @@ in ExecStart = "${pkgs.nix-serve}/bin/nix-serve " + "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}"; User = "nix-serve"; - Group = "nogroup"; + Group = "nix-serve"; + DynamicUser = true; }; }; - - users.users.nix-serve = { - description = "Nix-serve user"; - uid = config.ids.uids.nix-serve; - }; }; } diff --git a/nixpkgs/nixos/modules/services/networking/pppd.nix b/nixpkgs/nixos/modules/services/networking/pppd.nix index c1cbdb461765..37f44f07ac46 100644 --- a/nixpkgs/nixos/modules/services/networking/pppd.nix +++ b/nixpkgs/nixos/modules/services/networking/pppd.nix @@ -82,13 +82,21 @@ in LD_PRELOAD = "${pkgs.libredirect}/lib/libredirect.so"; NIX_REDIRECTS = "/var/run=/run/pppd"; }; - serviceConfig = { + serviceConfig = let + capabilities = [ + "CAP_BPF" + "CAP_SYS_TTY_CONFIG" + "CAP_NET_ADMIN" + "CAP_NET_RAW" + ]; + in + { ExecStart = "${getBin cfg.package}/sbin/pppd call ${peerCfg.name} nodetach nolog"; Restart = "always"; RestartSec = 5; - AmbientCapabilities = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN"; - CapabilityBoundingSet = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN"; + AmbientCapabilities = capabilities; + CapabilityBoundingSet = capabilities; KeyringMode = "private"; LockPersonality = true; MemoryDenyWriteExecute = true; @@ -103,7 +111,17 @@ in ProtectKernelTunables = false; ProtectSystem = "strict"; RemoveIPC = true; - RestrictAddressFamilies = "AF_PACKET AF_UNIX AF_PPPOX AF_ATMPVC AF_ATMSVC AF_INET AF_INET6 AF_IPX"; + RestrictAddressFamilies = [ + "AF_ATMPVC" + "AF_ATMSVC" + "AF_INET" + "AF_INET6" + "AF_IPX" + "AF_NETLINK" + "AF_PACKET" + "AF_PPPOX" + "AF_UNIX" + ]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; diff --git a/nixpkgs/nixos/modules/services/networking/smartdns.nix b/nixpkgs/nixos/modules/services/networking/smartdns.nix index f1888af70416..f84c727f0343 100644 --- a/nixpkgs/nixos/modules/services/networking/smartdns.nix +++ b/nixpkgs/nixos/modules/services/networking/smartdns.nix @@ -54,6 +54,7 @@ in { systemd.packages = [ pkgs.smartdns ]; systemd.services.smartdns.wantedBy = [ "multi-user.target" ]; + systemd.services.smartdns.restartTriggers = [ confFile ]; environment.etc."smartdns/smartdns.conf".source = confFile; environment.etc."default/smartdns".source = "${pkgs.smartdns}/etc/default/smartdns"; diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix index 09aef9a1dcf1..6d7178047ea8 100644 --- a/nixpkgs/nixos/modules/services/networking/unbound.nix +++ b/nixpkgs/nixos/modules/services/networking/unbound.nix @@ -21,7 +21,15 @@ let )) else throw (traceSeq v "services.unbound.settings: unexpected type"); - confFile = pkgs.writeText "unbound.conf" (concatStringsSep "\n" ((mapAttrsToList (toConf "") cfg.settings) ++ [""])); + confNoServer = concatStringsSep "\n" ((mapAttrsToList (toConf "") (builtins.removeAttrs cfg.settings [ "server" ])) ++ [""]); + confServer = concatStringsSep "\n" (mapAttrsToList (toConf " ") (builtins.removeAttrs cfg.settings.server [ "define-tag" ])); + + confFile = pkgs.writeText "unbound.conf" '' + server: + ${optionalString (cfg.settings.server.define-tag != "") (toOption " " "define-tag" cfg.settings.server.define-tag)} + ${confServer} + ${confNoServer} + ''; rootTrustAnchorFile = "${cfg.stateDir}/root.key"; @@ -170,6 +178,7 @@ in { # prevent race conditions on system startup when interfaces are not yet # configured ip-freebind = mkDefault true; + define-tag = mkDefault ""; }; remote-control = { control-enable = mkDefault false; diff --git a/nixpkgs/nixos/modules/services/networking/znc/default.nix b/nixpkgs/nixos/modules/services/networking/znc/default.nix index aa79ed27dcef..b872b99976ce 100644 --- a/nixpkgs/nixos/modules/services/networking/znc/default.nix +++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix @@ -133,8 +133,8 @@ in Nick = "paul"; AltNick = "paul1"; LoadModule = [ "chansaver" "controlpanel" ]; - Network.freenode = { - Server = "chat.freenode.net +6697"; + Network.libera = { + Server = "irc.libera.chat +6697"; LoadModule = [ "simple_away" ]; Chan = { "#nixos" = { Detached = false; }; diff --git a/nixpkgs/nixos/modules/services/networking/znc/options.nix b/nixpkgs/nixos/modules/services/networking/znc/options.nix index 7a43b45fabba..be9dc78c86d9 100644 --- a/nixpkgs/nixos/modules/services/networking/znc/options.nix +++ b/nixpkgs/nixos/modules/services/networking/znc/options.nix @@ -11,7 +11,7 @@ let server = mkOption { type = types.str; - example = "chat.freenode.net"; + example = "irc.libera.chat"; description = '' IRC server address. ''; @@ -150,8 +150,8 @@ in ''; example = literalExample '' { - "freenode" = { - server = "chat.freenode.net"; + "libera" = { + server = "irc.libera.chat"; port = 6697; useSSL = true; modules = [ "simple_away" ]; diff --git a/nixpkgs/nixos/modules/services/security/hockeypuck.nix b/nixpkgs/nixos/modules/services/security/hockeypuck.nix new file mode 100644 index 000000000000..686634c8add8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/security/hockeypuck.nix @@ -0,0 +1,104 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.hockeypuck; + settingsFormat = pkgs.formats.toml { }; +in { + meta.maintainers = with lib.maintainers; [ etu ]; + + options.services.hockeypuck = { + enable = lib.mkEnableOption "Hockeypuck OpenPGP Key Server"; + + port = lib.mkOption { + default = 11371; + type = lib.types.port; + description = "HKP port to listen on."; + }; + + settings = lib.mkOption { + type = settingsFormat.type; + default = { }; + example = lib.literalExample '' + { + hockeypuck = { + loglevel = "INFO"; + logfile = "/var/log/hockeypuck/hockeypuck.log"; + indexTemplate = "''${pkgs.hockeypuck-web}/share/templates/index.html.tmpl"; + vindexTemplate = "''${pkgs.hockeypuck-web}/share/templates/index.html.tmpl"; + statsTemplate = "''${pkgs.hockeypuck-web}/share/templates/stats.html.tmpl"; + webroot = "''${pkgs.hockeypuck-web}/share/webroot"; + + hkp.bind = ":''${toString cfg.port}"; + + openpgp.db = { + driver = "postgres-jsonb"; + dsn = "database=hockeypuck host=/var/run/postgresql sslmode=disable"; + }; + }; + } + ''; + description = '' + Configuration file for hockeypuck, here you can override + certain settings (<literal>loglevel</literal> and + <literal>openpgp.db.dsn</literal>) by just setting those values. + + For other settings you need to use lib.mkForce to override them. + + This service doesn't provision or enable postgres on your + system, it rather assumes that you enable postgres and create + the database yourself. + + Example: + <literal> + services.postgresql = { + enable = true; + ensureDatabases = [ "hockeypuck" ]; + ensureUsers = [{ + name = "hockeypuck"; + ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES"; + }]; + }; + </literal> + ''; + }; + }; + + config = lib.mkIf cfg.enable { + services.hockeypuck.settings.hockeypuck = { + loglevel = lib.mkDefault "INFO"; + logfile = "/var/log/hockeypuck/hockeypuck.log"; + indexTemplate = "${pkgs.hockeypuck-web}/share/templates/index.html.tmpl"; + vindexTemplate = "${pkgs.hockeypuck-web}/share/templates/index.html.tmpl"; + statsTemplate = "${pkgs.hockeypuck-web}/share/templates/stats.html.tmpl"; + webroot = "${pkgs.hockeypuck-web}/share/webroot"; + + hkp.bind = ":${toString cfg.port}"; + + openpgp.db = { + driver = "postgres-jsonb"; + dsn = lib.mkDefault "database=hockeypuck host=/var/run/postgresql sslmode=disable"; + }; + }; + + users.users.hockeypuck = { + isSystemUser = true; + description = "Hockeypuck user"; + }; + + systemd.services.hockeypuck = { + description = "Hockeypuck OpenPGP Key Server"; + after = [ "network.target" "postgresql.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + WorkingDirectory = "/var/lib/hockeypuck"; + User = "hockeypuck"; + ExecStart = "${pkgs.hockeypuck}/bin/hockeypuck -config ${settingsFormat.generate "config.toml" cfg.settings}"; + Restart = "always"; + RestartSec = "5s"; + LogsDirectory = "hockeypuck"; + LogsDirectoryMode = "0755"; + StateDirectory = "hockeypuck"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix index 940ac7832dae..d28ea61e66aa 100644 --- a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix @@ -26,12 +26,12 @@ let if value != null then [ (nameValuePair (nameToEnvVar name) (if isBool value then boolToString value else toString value)) ] else [] ) cfg.config)); in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") { - WEB_VAULT_FOLDER = "${pkgs.vaultwarden-vault}/share/vaultwarden/vault"; + WEB_VAULT_FOLDER = "${cfg.webVaultPackage}/share/vaultwarden/vault"; } // configEnv; configFile = pkgs.writeText "vaultwarden.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv)); - vaultwarden = pkgs.vaultwarden.override { inherit (cfg) dbBackend; }; + vaultwarden = cfg.package.override { inherit (cfg) dbBackend; }; in { imports = [ @@ -102,6 +102,20 @@ in { <literal>vaultwarden</literal> is running. ''; }; + + package = mkOption { + type = package; + default = pkgs.vaultwarden; + defaultText = "pkgs.vaultwarden"; + description = "Vaultwarden package to use."; + }; + + webVaultPackage = mkOption { + type = package; + default = pkgs.vaultwarden-vault; + defaultText = "pkgs.vaultwarden-vault"; + description = "Web vault package to use."; + }; }; config = mkIf cfg.enable { diff --git a/nixpkgs/nixos/modules/services/ttys/getty.nix b/nixpkgs/nixos/modules/services/ttys/getty.nix index 8345dfabeb7e..7cf2ff87da26 100644 --- a/nixpkgs/nixos/modules/services/ttys/getty.nix +++ b/nixpkgs/nixos/modules/services/ttys/getty.nix @@ -6,7 +6,7 @@ let cfg = config.services.getty; baseArgs = [ - "--login-program" "${pkgs.shadow}/bin/login" + "--login-program" "${cfg.loginProgram}" ] ++ optionals (cfg.autologinUser != null) [ "--autologin" cfg.autologinUser ] ++ optionals (cfg.loginOptions != null) [ @@ -39,6 +39,14 @@ in ''; }; + loginProgram = mkOption { + type = types.path; + default = "${pkgs.shadow}/bin/login"; + description = '' + Path to the login binary executed by agetty. + ''; + }; + loginOptions = mkOption { type = types.nullOr types.str; default = null; diff --git a/nixpkgs/nixos/modules/services/video/unifi-video.nix b/nixpkgs/nixos/modules/services/video/unifi-video.nix new file mode 100644 index 000000000000..d4c0268ed66c --- /dev/null +++ b/nixpkgs/nixos/modules/services/video/unifi-video.nix @@ -0,0 +1,265 @@ +{ config, lib, pkgs, utils, ... }: +with lib; +let + cfg = config.services.unifi-video; + mainClass = "com.ubnt.airvision.Main"; + cmd = '' + ${pkgs.jsvc}/bin/jsvc \ + -cwd ${stateDir} \ + -debug \ + -verbose:class \ + -nodetach \ + -user unifi-video \ + -home ${cfg.jrePackage}/lib/openjdk \ + -cp ${pkgs.commonsDaemon}/share/java/commons-daemon-1.2.4.jar:${stateDir}/lib/airvision.jar \ + -pidfile ${cfg.pidFile} \ + -procname unifi-video \ + -Djava.security.egd=file:/dev/./urandom \ + -Xmx${cfg.maximumJavaHeapSize}M \ + -Xss512K \ + -XX:+UseG1GC \ + -XX:+UseStringDeduplication \ + -XX:MaxMetaspaceSize=768M \ + -Djava.library.path=${stateDir}/lib \ + -Djava.awt.headless=true \ + -Djavax.net.ssl.trustStore=${stateDir}/etc/ufv-truststore \ + -Dfile.encoding=UTF-8 \ + -Dav.tempdir=/var/cache/unifi-video + ''; + + mongoConf = pkgs.writeTextFile { + name = "mongo.conf"; + executable = false; + text = '' + # for documentation of all options, see http://docs.mongodb.org/manual/reference/configuration-options/ + + storage: + dbPath: ${cfg.dataDir}/db + journal: + enabled: true + syncPeriodSecs: 60 + + systemLog: + destination: file + logAppend: true + path: ${stateDir}/logs/mongod.log + + net: + port: 7441 + bindIp: 127.0.0.1 + http: + enabled: false + + operationProfiling: + slowOpThresholdMs: 500 + mode: off + ''; + }; + + + mongoWtConf = pkgs.writeTextFile { + name = "mongowt.conf"; + executable = false; + text = '' + # for documentation of all options, see: + # http://docs.mongodb.org/manual/reference/configuration-options/ + + storage: + dbPath: ${cfg.dataDir}/db-wt + journal: + enabled: true + wiredTiger: + engineConfig: + cacheSizeGB: 1 + + systemLog: + destination: file + logAppend: true + path: logs/mongod.log + + net: + port: 7441 + bindIp: 127.0.0.1 + + operationProfiling: + slowOpThresholdMs: 500 + mode: off + ''; + }; + + stateDir = "/var/lib/unifi-video"; + +in + { + + options.services.unifi-video = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable the unifi-video service. + ''; + }; + + jrePackage = mkOption { + type = types.package; + default = pkgs.jre8; + defaultText = "pkgs.jre8"; + description = '' + The JRE package to use. Check the release notes to ensure it is supported. + ''; + }; + + unifiVideoPackage = mkOption { + type = types.package; + default = pkgs.unifi-video; + defaultText = "pkgs.unifi-video"; + description = '' + The unifi-video package to use. + ''; + }; + + mongodbPackage = mkOption { + type = types.package; + default = pkgs.mongodb-4_0; + defaultText = "pkgs.mongodb"; + description = '' + The mongodb package to use. + ''; + }; + + logDir = mkOption { + type = types.str; + default = "${stateDir}/logs"; + description = '' + Where to store the logs. + ''; + }; + + dataDir = mkOption { + type = types.str; + default = "${stateDir}/data"; + description = '' + Where to store the database and other data. + ''; + }; + + openPorts = mkOption { + type = types.bool; + default = true; + description = '' + Whether or not to open the required ports on the firewall. + ''; + }; + + maximumJavaHeapSize = mkOption { + type = types.nullOr types.int; + default = 1024; + example = 4096; + description = '' + Set the maximimum heap size for the JVM in MB. + ''; + }; + + pidFile = mkOption { + type = types.path; + default = "${cfg.dataDir}/unifi-video.pid"; + description = "Location of unifi-video pid file."; + }; + +}; + +config = mkIf cfg.enable { + users = { + users.unifi-video = { + description = "UniFi Video controller daemon user"; + home = stateDir; + group = "unifi-video"; + isSystemUser = true; + }; + groups.unifi-video = {}; + }; + + networking.firewall = mkIf cfg.openPorts { + # https://help.ui.com/hc/en-us/articles/217875218-UniFi-Video-Ports-Used + allowedTCPPorts = [ + 7080 # HTTP portal + 7443 # HTTPS portal + 7445 # Video over HTTP (mobile app) + 7446 # Video over HTTPS (mobile app) + 7447 # RTSP via the controller + 7442 # Camera management from cameras to NVR over WAN + ]; + allowedUDPPorts = [ + 6666 # Inbound camera streams sent over WAN + ]; + }; + + systemd.tmpfiles.rules = [ + "d '${stateDir}' 0700 unifi-video unifi-video - -" + "d '/var/cache/unifi-video' 0700 unifi-video unifi-video - -" + + "d '${stateDir}/logs' 0700 unifi-video unifi-video - -" + "C '${stateDir}/etc' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/etc" + "C '${stateDir}/webapps' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/webapps" + "C '${stateDir}/email' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/email" + "C '${stateDir}/fw' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/fw" + "C '${stateDir}/lib' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/lib" + + "d '${stateDir}/data' 0700 unifi-video unifi-video - -" + "d '${stateDir}/data/db' 0700 unifi-video unifi-video - -" + "C '${stateDir}/data/system.properties' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/etc/system.properties" + + "d '${stateDir}/bin' 0700 unifi-video unifi-video - -" + "f '${stateDir}/bin/evostreamms' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/evostreamms" + "f '${stateDir}/bin/libavcodec.so.54' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/libavcodec.so.54" + "f '${stateDir}/bin/libavformat.so.54' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/libavformat.so.54" + "f '${stateDir}/bin/libavutil.so.52' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/libavutil.so.52" + "f '${stateDir}/bin/ubnt.avtool' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/ubnt.avtool" + "f '${stateDir}/bin/ubnt.updater' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/ubnt.updater" + "C '${stateDir}/bin/mongo' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongo" + "C '${stateDir}/bin/mongod' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongod" + "C '${stateDir}/bin/mongoperf' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongoperf" + "C '${stateDir}/bin/mongos' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongos" + + "d '${stateDir}/conf' 0700 unifi-video unifi-video - -" + "C '${stateDir}/conf/evostream' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/evostream" + "Z '${stateDir}/conf/evostream' 0700 unifi-video unifi-video - -" + "L+ '${stateDir}/conf/mongodv3.0+.conf' 0700 unifi-video unifi-video - ${mongoConf}" + "L+ '${stateDir}/conf/mongodv3.6+.conf' 0700 unifi-video unifi-video - ${mongoConf}" + "L+ '${stateDir}/conf/mongod-wt.conf' 0700 unifi-video unifi-video - ${mongoWtConf}" + "L+ '${stateDir}/conf/catalina.policy' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/catalina.policy" + "L+ '${stateDir}/conf/catalina.properties' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/catalina.properties" + "L+ '${stateDir}/conf/context.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/context.xml" + "L+ '${stateDir}/conf/logging.properties' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/logging.properties" + "L+ '${stateDir}/conf/server.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/server.xml" + "L+ '${stateDir}/conf/tomcat-users.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/tomcat-users.xml" + "L+ '${stateDir}/conf/web.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/web.xml" + + ]; + + systemd.services.unifi-video = { + description = "UniFi Video NVR daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ] ; + unitConfig.RequiresMountsFor = stateDir; + # Make sure package upgrades trigger a service restart + restartTriggers = [ cfg.unifiVideoPackage cfg.mongodbPackage ]; + path = with pkgs; [ gawk coreutils busybox which jre8 lsb-release libcap util-linux ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${(removeSuffix "\n" cmd)} ${mainClass} start"; + ExecStop = "${(removeSuffix "\n" cmd)} stop ${mainClass} stop"; + Restart = "on-failure"; + UMask = "0077"; + User = "unifi-video"; + WorkingDirectory = "${stateDir}"; + }; + }; + + }; + + meta = { + maintainers = with lib.maintainers; [ rsynnest ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix index d3ae072f86a8..8d5302ba267b 100644 --- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix +++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix @@ -475,21 +475,16 @@ in plugins = lib.mkOption { type = lib.types.listOf lib.types.package; default = []; - example = '' - [ - (pkgs.fetchFromGitHub { - owner = "discourse"; - repo = "discourse-spoiler-alert"; - rev = "e200cfa571d252cab63f3d30d619b370986e4cee"; - sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33"; - }) + example = lib.literalExample '' + with config.services.discourse.package.plugins; [ + discourse-canned-replies + discourse-github ]; ''; description = '' - <productname>Discourse</productname> plugins to install as a - list of derivations. As long as a plugin supports the - standard install method, packaging it should only require - fetching its source with an appropriate fetcher. + Plugins to install as part of + <productname>Discourse</productname>, expressed as a list of + derivations. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.xml b/nixpkgs/nixos/modules/services/web-apps/discourse.xml index bae562423213..1d6866e7b352 100644 --- a/nixpkgs/nixos/modules/services/web-apps/discourse.xml +++ b/nixpkgs/nixos/modules/services/web-apps/discourse.xml @@ -262,9 +262,31 @@ services.discourse = { <para> You can install <productname>Discourse</productname> plugins using the <xref linkend="opt-services.discourse.plugins" /> - option. As long as a plugin supports the standard install - method, packaging it should only require fetching its source - with an appropriate fetcher. + option. Pre-packaged plugins are provided in + <literal><your_discourse_package_here>.plugins</literal>. If + you want the full suite of plugins provided through + <literal>nixpkgs</literal>, you can also set the <xref + linkend="opt-services.discourse.package" /> option to + <literal>pkgs.discourseAllPlugins</literal>. + </para> + + <para> + Plugins can be built with the + <literal><your_discourse_package_here>.mkDiscoursePlugin</literal> + function. Normally, it should suffice to provide a + <literal>name</literal> and <literal>src</literal> attribute. If + the plugin has Ruby dependencies, however, they need to be + packaged in accordance with the <link + xlink:href="https://nixos.org/manual/nixpkgs/stable/#developing-with-ruby">Developing + with Ruby</link> section of the Nixpkgs manual and the + appropriate gem options set in <literal>bundlerEnvArgs</literal> + (normally <literal>gemdir</literal> is sufficient). A plugin's + Ruby dependencies are listed in its + <filename>plugin.rb</filename> file as function calls to + <literal>gem</literal>. To construct the corresponding + <filename>Gemfile</filename>, run <command>bundle + init</command>, then add the <literal>gem</literal> lines to it + verbatim. </para> <para> @@ -280,7 +302,10 @@ services.discourse = { <para> For example, to add the <link xlink:href="https://github.com/discourse/discourse-spoiler-alert">discourse-spoiler-alert</link> - plugin and disable it by default: + and <link + xlink:href="https://github.com/discourse/discourse-solved">discourse-solved</link> + plugins, and disable <literal>discourse-spoiler-alert</literal> + by default: <programlisting> services.discourse = { @@ -301,13 +326,9 @@ services.discourse = { <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file"; }; <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true; - <link linkend="opt-services.discourse.mail.incoming.enable">plugins</link> = [ - (pkgs.fetchFromGitHub { - owner = "discourse"; - repo = "discourse-spoiler-alert"; - rev = "e200cfa571d252cab63f3d30d619b370986e4cee"; - sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33"; - }) + <link linkend="opt-services.discourse.mail.incoming.enable">plugins</link> = with config.services.discourse.package.plugins; [ + discourse-spoiler-alert + discourse-solved ]; <link linkend="opt-services.discourse.siteSettings">siteSettings</link> = { plugins = { diff --git a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix index eea49bda283b..f8f0854f1bcb 100644 --- a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix +++ b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix @@ -23,6 +23,16 @@ in { ''; }; + libraryPaths = mkOption { + type = attrsOf package; + default = { }; + description = '' + Libraries to add to the Icingaweb2 library path. + The name of the attribute is the name of the library, the value + is the package to add. + ''; + }; + virtualHost = mkOption { type = nullOr str; default = "icingaweb2"; @@ -167,6 +177,9 @@ in { services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") { ${poolName} = { user = "icingaweb2"; + phpEnv = { + ICINGAWEB_LIBDIR = toString (pkgs.linkFarm "icingaweb2-libdir" (mapAttrsToList (name: path: { inherit name path; }) cfg.libraryPaths)); + }; phpPackage = pkgs.php.withExtensions ({ enabled, all }: [ all.imagick ] ++ enabled); phpOptions = '' date.timezone = "${cfg.timezone}" @@ -184,6 +197,11 @@ in { }; }; + services.icingaweb2.libraryPaths = { + ipl = pkgs.icingaweb2-ipl; + thirdparty = pkgs.icingaweb2-thirdparty; + }; + systemd.services."phpfpm-${poolName}".serviceConfig.ReadWritePaths = [ "/etc/icingaweb2" ]; services.nginx = { diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix index 545deaa905f2..111b31734696 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix @@ -92,7 +92,7 @@ in { package = mkOption { type = types.package; description = "Which package to use for the Nextcloud instance."; - relatedPackages = [ "nextcloud19" "nextcloud20" "nextcloud21" ]; + relatedPackages = [ "nextcloud20" "nextcloud21" "nextcloud22" ]; }; maxUploadSize = mkOption { @@ -385,7 +385,7 @@ in { ]; warnings = let - latest = 21; + latest = 22; upgradeWarning = major: nixos: '' A legacy Nextcloud install (from before NixOS ${nixos}) may be installed. @@ -403,9 +403,9 @@ in { Using config.services.nextcloud.poolConfig is deprecated and will become unsupported in a future release. Please migrate your configuration to config.services.nextcloud.poolSettings. '') - ++ (optional (versionOlder cfg.package.version "19") (upgradeWarning 18 "20.09")) ++ (optional (versionOlder cfg.package.version "20") (upgradeWarning 19 "21.05")) - ++ (optional (versionOlder cfg.package.version "21") (upgradeWarning 20 "21.05")); + ++ (optional (versionOlder cfg.package.version "21") (upgradeWarning 20 "21.05")) + ++ (optional (versionOlder cfg.package.version "22") (upgradeWarning 21 "21.11")); services.nextcloud.package = with pkgs; mkDefault ( @@ -415,13 +415,13 @@ in { nextcloud defined in an overlay, please set `services.nextcloud.package` to `pkgs.nextcloud`. '' - else if versionOlder stateVersion "20.09" then nextcloud18 # 21.03 will not be an official release - it was instead 21.05. # This versionOlder statement remains set to 21.03 for backwards compatibility. # See https://github.com/NixOS/nixpkgs/pull/108899 and # https://github.com/NixOS/rfcs/blob/master/rfcs/0080-nixos-release-schedule.md. else if versionOlder stateVersion "21.03" then nextcloud19 - else nextcloud21 + else if versionOlder stateVersion "21.11" then nextcloud21 + else nextcloud22 ); } @@ -616,9 +616,7 @@ in { services.nginx.enable = mkDefault true; - services.nginx.virtualHosts.${cfg.hostName} = let - major = toInt (versions.major cfg.package.version); - in { + services.nginx.virtualHosts.${cfg.hostName} = { root = cfg.package; locations = { "= /robots.txt" = { diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml index 83a6f68edcbf..3af37b15dd56 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml @@ -11,7 +11,7 @@ desktop client is packaged at <literal>pkgs.nextcloud-client</literal>. </para> <para> - The current default by NixOS is <package>nextcloud21</package> which is also the latest + The current default by NixOS is <package>nextcloud22</package> which is also the latest major version available. </para> <section xml:id="module-services-nextcloud-basic-usage"> diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix index caf5ba466dfe..b56848b79d21 100644 --- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix +++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix @@ -7,10 +7,15 @@ let # FIXME consider using LoadCredential as soon as it actually works. envSecrets = '' - export ADMIN_USER_PWD="$(<${cfg.adminUser.passwordFile})" - export SECRET_KEY_BASE="$(<${cfg.server.secretKeybaseFile})" + ADMIN_USER_PWD="$(<${cfg.adminUser.passwordFile})" + export ADMIN_USER_PWD # separate export to make `set -e` work + + SECRET_KEY_BASE="$(<${cfg.server.secretKeybaseFile})" + export SECRET_KEY_BASE # separate export to make `set -e` work + ${optionalString (cfg.mail.smtp.passwordFile != null) '' - export SMTP_USER_PWD="$(<${cfg.mail.smtp.passwordFile})" + SMTP_USER_PWD="$(<${cfg.mail.smtp.passwordFile})" + export SMTP_USER_PWD # separate export to make `set -e` work ''} ''; in { @@ -102,6 +107,11 @@ in { type = types.str; description = '' Public URL where plausible is available. + + Note that <literal>/path</literal> components are currently ignored: + <link xlink:href="https://github.com/plausible/analytics/issues/1182"> + https://github.com/plausible/analytics/issues/1182 + </link>. ''; }; }; @@ -228,6 +238,7 @@ in { WorkingDirectory = "/var/lib/plausible"; StateDirectory = "plausible"; ExecStartPre = "@${pkgs.writeShellScript "plausible-setup" '' + set -eu -o pipefail ${envSecrets} ${pkgs.plausible}/createdb.sh ${pkgs.plausible}/migrate.sh @@ -238,6 +249,7 @@ in { ''} ''} plausible-setup"; ExecStart = "@${pkgs.writeShellScript "plausible" '' + set -eu -o pipefail ${envSecrets} plausible start ''} plausible"; diff --git a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix new file mode 100644 index 000000000000..b0b6eb6df17e --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix @@ -0,0 +1,145 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.services.vikunja; + format = pkgs.formats.yaml {}; + configFile = format.generate "config.yaml" cfg.settings; + useMysql = cfg.database.type == "mysql"; + usePostgresql = cfg.database.type == "postgres"; +in { + options.services.vikunja = with lib; { + enable = mkEnableOption "vikunja service"; + package-api = mkOption { + default = pkgs.vikunja-api; + type = types.package; + defaultText = "pkgs.vikunja-api"; + description = "vikunja-api derivation to use."; + }; + package-frontend = mkOption { + default = pkgs.vikunja-frontend; + type = types.package; + defaultText = "pkgs.vikunja-frontend"; + description = "vikunja-frontend derivation to use."; + }; + environmentFiles = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + List of environment files set in the vikunja systemd service. + For example passwords should be set in one of these files. + ''; + }; + setupNginx = mkOption { + type = types.bool; + default = config.services.nginx.enable; + defaultText = "config.services.nginx.enable"; + description = '' + Whether to setup NGINX. + Further nginx configuration can be done by changing + <option>services.nginx.virtualHosts.<frontendHostname></option>. + This does not enable TLS or ACME by default. To enable this, set the + <option>services.nginx.virtualHosts.<frontendHostname>.enableACME</option> to + <literal>true</literal> and if appropriate do the same for + <option>services.nginx.virtualHosts.<frontendHostname>.forceSSL</option>. + ''; + }; + frontendScheme = mkOption { + type = types.enum [ "http" "https" ]; + description = '' + Whether the site is available via http or https. + This does not configure https or ACME in nginx! + ''; + }; + frontendHostname = mkOption { + type = types.str; + description = "The Hostname under which the frontend is running."; + }; + + settings = mkOption { + type = format.type; + default = {}; + description = '' + Vikunja configuration. Refer to + <link xlink:href="https://vikunja.io/docs/config-options/"/> + for details on supported values. + ''; + }; + database = { + type = mkOption { + type = types.enum [ "sqlite" "mysql" "postgres" ]; + example = "postgres"; + default = "sqlite"; + description = "Database engine to use."; + }; + host = mkOption { + type = types.str; + default = "localhost"; + description = "Database host address. Can also be a socket."; + }; + user = mkOption { + type = types.str; + default = "vikunja"; + description = "Database user."; + }; + database = mkOption { + type = types.str; + default = "vikunja"; + description = "Database name."; + }; + path = mkOption { + type = types.str; + default = "/var/lib/vikunja/vikunja.db"; + description = "Path to the sqlite3 database file."; + }; + }; + }; + config = lib.mkIf cfg.enable { + services.vikunja.settings = { + database = { + inherit (cfg.database) type host user database path; + }; + service = { + frontendurl = "${cfg.frontendScheme}://${cfg.frontendHostname}/"; + }; + files = { + basepath = "/var/lib/vikunja/files"; + }; + }; + + systemd.services.vikunja-api = { + description = "vikunja-api"; + after = [ "network.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service"; + wantedBy = [ "multi-user.target" ]; + path = [ cfg.package-api ]; + restartTriggers = [ configFile ]; + + serviceConfig = { + Type = "simple"; + DynamicUser = true; + StateDirectory = "vikunja"; + ExecStart = "${cfg.package-api}/bin/vikunja"; + Restart = "always"; + EnvironmentFile = cfg.environmentFiles; + }; + }; + + services.nginx.virtualHosts."${cfg.frontendHostname}" = mkIf cfg.setupNginx { + locations = { + "/" = { + root = cfg.package-frontend; + tryFiles = "try_files $uri $uri/ /"; + }; + "~* ^/(api|dav|\\.well-known)/" = { + proxyPass = "http://localhost:3456"; + extraConfig = '' + client_max_body_size 20M; + ''; + }; + }; + }; + + environment.etc."vikunja/config.yaml".source = configFile; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix index 775ecb3acaf0..6f1ef815bc46 100644 --- a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix +++ b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix @@ -3,13 +3,18 @@ let inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types; inherit (lib) any attrValues concatMapStringsSep flatten literalExample; - inherit (lib) mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString; + inherit (lib) filterAttrs mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString; - eachSite = config.services.wordpress; + cfg = migrateOldAttrs config.services.wordpress; + eachSite = cfg.sites; user = "wordpress"; - group = config.services.httpd.group; + webserver = config.services.${cfg.webserver}; stateDir = hostName: "/var/lib/wordpress/${hostName}"; + # Migrate config.services.wordpress.<hostName> to config.services.wordpress.sites.<hostName> + oldSites = filterAttrs (o: _: o != "sites" && o != "webserver"); + migrateOldAttrs = cfg: cfg // { sites = cfg.sites // oldSites cfg; }; + pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec { pname = "wordpress-${hostName}"; version = src.version; @@ -261,21 +266,48 @@ in # interface options = { services.wordpress = mkOption { - type = types.attrsOf (types.submodule siteOpts); + type = types.submodule { + # Used to support old interface + freeformType = types.attrsOf (types.submodule siteOpts); + + # New interface + options.sites = mkOption { + type = types.attrsOf (types.submodule siteOpts); + default = {}; + description = "Specification of one or more WordPress sites to serve"; + }; + + options.webserver = mkOption { + type = types.enum [ "httpd" "nginx" ]; + default = "httpd"; + description = '' + Whether to use apache2 or nginx for virtual host management. + + Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.<name></literal>. + See <xref linkend="opt-services.nginx.virtualHosts"/> for further information. + + Further apache2 configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>. + See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. + ''; + }; + }; default = {}; - description = "Specification of one or more WordPress sites to serve via Apache."; + description = "Wordpress configuration"; }; + }; # implementation - config = mkIf (eachSite != {}) { + config = mkIf (eachSite != {}) (mkMerge [{ assertions = mapAttrsToList (hostName: cfg: { assertion = cfg.database.createLocally -> cfg.database.user == user; - message = "services.wordpress.${hostName}.database.user must be ${user} if the database is to be automatically provisioned"; + message = ''services.wordpress.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned''; } ) eachSite; + warnings = mapAttrsToList (hostName: _: ''services.wordpress."${hostName}" is deprecated use services.wordpress.sites."${hostName}"'') (oldSites cfg); + services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) { enable = true; package = mkDefault pkgs.mariadb; @@ -289,14 +321,18 @@ in services.phpfpm.pools = mapAttrs' (hostName: cfg: ( nameValuePair "wordpress-${hostName}" { - inherit user group; + inherit user; + group = webserver.group; settings = { - "listen.owner" = config.services.httpd.user; - "listen.group" = config.services.httpd.group; + "listen.owner" = webserver.user; + "listen.group" = webserver.group; } // cfg.poolConfig; } )) eachSite; + } + + (mkIf (cfg.webserver == "httpd") { services.httpd = { enable = true; extraModules = [ "proxy_fcgi" ]; @@ -332,11 +368,13 @@ in ''; } ]) eachSite; }; + }) + { systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [ - "d '${stateDir hostName}' 0750 ${user} ${group} - -" - "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -" - "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -" + "d '${stateDir hostName}' 0750 ${user} ${webserver.group} - -" + "d '${cfg.uploadsDir}' 0750 ${user} ${webserver.group} - -" + "Z '${cfg.uploadsDir}' 0750 ${user} ${webserver.group} - -" ]) eachSite); systemd.services = mkMerge [ @@ -350,7 +388,7 @@ in serviceConfig = { Type = "oneshot"; User = user; - Group = group; + Group = webserver.group; }; })) eachSite) @@ -360,9 +398,65 @@ in ]; users.users.${user} = { - group = group; + group = webserver.group; isSystemUser = true; }; + } - }; + (mkIf (cfg.webserver == "nginx") { + services.nginx = { + enable = true; + virtualHosts = mapAttrs (hostName: cfg: { + serverName = mkDefault hostName; + root = "${pkg hostName cfg}/share/wordpress"; + extraConfig = '' + index index.php; + ''; + locations = { + "/" = { + priority = 200; + extraConfig = '' + try_files $uri $uri/ /index.php$is_args$args; + ''; + }; + "~ \\.php$" = { + priority = 500; + extraConfig = '' + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket}; + fastcgi_index index.php; + include "${config.services.nginx.package}/conf/fastcgi.conf"; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; + # Mitigate https://httpoxy.org/ vulnerabilities + fastcgi_param HTTP_PROXY ""; + fastcgi_intercept_errors off; + fastcgi_buffer_size 16k; + fastcgi_buffers 4 16k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + ''; + }; + "~ /\\." = { + priority = 800; + extraConfig = "deny all;"; + }; + "~* /(?:uploads|files)/.*\\.php$" = { + priority = 900; + extraConfig = "deny all;"; + }; + "~* \\.(js|css|png|jpg|jpeg|gif|ico)$" = { + priority = 1000; + extraConfig = '' + expires max; + log_not_found off; + ''; + }; + }; + }) eachSite; + }; + }) + + ]); } diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix index 1d6f46f1d17f..adc226d89913 100644 --- a/nixpkgs/nixos/modules/system/boot/systemd.nix +++ b/nixpkgs/nixos/modules/system/boot/systemd.nix @@ -755,7 +755,7 @@ in default = []; example = [ "d /tmp 1777 root root 10d" ]; description = '' - Rules for creating and cleaning up temporary files + Rules for creation, deletion and cleaning of volatile and temporary files automatically. See <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> for the exact format. diff --git a/nixpkgs/nixos/modules/tasks/snapraid.nix b/nixpkgs/nixos/modules/tasks/snapraid.nix new file mode 100644 index 000000000000..4529009930fc --- /dev/null +++ b/nixpkgs/nixos/modules/tasks/snapraid.nix @@ -0,0 +1,230 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.snapraid; +in +{ + options.snapraid = with types; { + enable = mkEnableOption "SnapRAID"; + dataDisks = mkOption { + default = { }; + example = { + d1 = "/mnt/disk1/"; + d2 = "/mnt/disk2/"; + d3 = "/mnt/disk3/"; + }; + description = "SnapRAID data disks."; + type = attrsOf str; + }; + parityFiles = mkOption { + default = [ ]; + example = [ + "/mnt/diskp/snapraid.parity" + "/mnt/diskq/snapraid.2-parity" + "/mnt/diskr/snapraid.3-parity" + "/mnt/disks/snapraid.4-parity" + "/mnt/diskt/snapraid.5-parity" + "/mnt/disku/snapraid.6-parity" + ]; + description = "SnapRAID parity files."; + type = listOf str; + }; + contentFiles = mkOption { + default = [ ]; + example = [ + "/var/snapraid.content" + "/mnt/disk1/snapraid.content" + "/mnt/disk2/snapraid.content" + ]; + description = "SnapRAID content list files."; + type = listOf str; + }; + exclude = mkOption { + default = [ ]; + example = [ "*.unrecoverable" "/tmp/" "/lost+found/" ]; + description = "SnapRAID exclude directives."; + type = listOf str; + }; + touchBeforeSync = mkOption { + default = true; + example = false; + description = + "Whether <command>snapraid touch</command> should be run before <command>snapraid sync</command>."; + type = bool; + }; + sync.interval = mkOption { + default = "01:00"; + example = "daily"; + description = "How often to run <command>snapraid sync</command>."; + type = str; + }; + scrub = { + interval = mkOption { + default = "Mon *-*-* 02:00:00"; + example = "weekly"; + description = "How often to run <command>snapraid scrub</command>."; + type = str; + }; + plan = mkOption { + default = 8; + example = 5; + description = + "Percent of the array that should be checked by <command>snapraid scrub</command>."; + type = int; + }; + olderThan = mkOption { + default = 10; + example = 20; + description = + "Number of days since data was last scrubbed before it can be scrubbed again."; + type = int; + }; + }; + extraConfig = mkOption { + default = ""; + example = '' + nohidden + blocksize 256 + hashsize 16 + autosave 500 + pool /pool + ''; + description = "Extra config options for SnapRAID."; + type = lines; + }; + }; + + config = + let + nParity = builtins.length cfg.parityFiles; + mkPrepend = pre: s: pre + s; + in + mkIf cfg.enable { + assertions = [ + { + assertion = nParity <= 6; + message = "You can have no more than six SnapRAID parity files."; + } + { + assertion = builtins.length cfg.contentFiles >= nParity + 1; + message = + "There must be at least one SnapRAID content file for each SnapRAID parity file plus one."; + } + ]; + + environment = { + systemPackages = with pkgs; [ snapraid ]; + + etc."snapraid.conf" = { + text = with cfg; + let + prependData = mkPrepend "data "; + prependContent = mkPrepend "content "; + prependExclude = mkPrepend "exclude "; + in + concatStringsSep "\n" + (map prependData + ((mapAttrsToList (name: value: name + " " + value)) dataDisks) + ++ zipListsWith (a: b: a + b) + ([ "parity " ] ++ map (i: toString i + "-parity ") (range 2 6)) + parityFiles ++ map prependContent contentFiles + ++ map prependExclude exclude) + "\n" + extraConfig; + }; + }; + + systemd.services = with cfg; { + snapraid-scrub = { + description = "Scrub the SnapRAID array"; + startAt = scrub.interval; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.snapraid}/bin/snapraid scrub -p ${ + toString scrub.plan + } -o ${toString scrub.olderThan}"; + Nice = 19; + IOSchedulingPriority = 7; + CPUSchedulingPolicy = "batch"; + + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "none"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + SystemCallErrorNumber = "EPERM"; + CapabilityBoundingSet = "CAP_DAC_OVERRIDE"; + + ProtectSystem = "strict"; + ProtectHome = "read-only"; + ReadWritePaths = + # scrub requires access to directories containing content files + # to remove them if they are stale + let + contentDirs = map dirOf contentFiles; + in + unique ( + attrValues dataDisks ++ contentDirs + ); + }; + unitConfig.After = "snapraid-sync.service"; + }; + snapraid-sync = { + description = "Synchronize the state of the SnapRAID array"; + startAt = sync.interval; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.snapraid}/bin/snapraid sync"; + Nice = 19; + IOSchedulingPriority = 7; + CPUSchedulingPolicy = "batch"; + + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "none"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + SystemCallErrorNumber = "EPERM"; + CapabilityBoundingSet = "CAP_DAC_OVERRIDE"; + + ProtectSystem = "strict"; + ProtectHome = "read-only"; + ReadWritePaths = + # sync requires access to directories containing content files + # to remove them if they are stale + let + contentDirs = map dirOf contentFiles; + in + unique ( + attrValues dataDisks ++ parityFiles ++ contentDirs + ); + } // optionalAttrs touchBeforeSync { + ExecStartPre = "${pkgs.snapraid}/bin/snapraid touch"; + }; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/virtualisation/cri-o.nix b/nixpkgs/nixos/modules/virtualisation/cri-o.nix index 8d352e36ef99..c135081959a6 100644 --- a/nixpkgs/nixos/modules/virtualisation/cri-o.nix +++ b/nixpkgs/nixos/modules/virtualisation/cri-o.nix @@ -6,6 +6,9 @@ let crioPackage = (pkgs.cri-o.override { inherit (cfg) extraPackages; }); + format = pkgs.formats.toml { }; + + cfgFile = format.generate "00-default.conf" cfg.settings; in { imports = [ @@ -13,7 +16,7 @@ in ]; meta = { - maintainers = lib.teams.podman.members; + maintainers = teams.podman.members; }; options.virtualisation.cri-o = { @@ -55,7 +58,7 @@ in extraPackages = mkOption { type = with types; listOf package; default = [ ]; - example = lib.literalExample '' + example = literalExample '' [ pkgs.gvisor ] @@ -65,7 +68,7 @@ in ''; }; - package = lib.mkOption { + package = mkOption { type = types.package; default = crioPackage; internal = true; @@ -80,6 +83,15 @@ in description = "Override the network_dir option."; internal = true; }; + + settings = mkOption { + type = format.type; + default = { }; + description = '' + Configuration for cri-o, see + <link xlink:href="https://github.com/cri-o/cri-o/blob/master/docs/crio.conf.5.md"/>. + ''; + }; }; config = mkIf cfg.enable { @@ -87,36 +99,38 @@ in environment.etc."crictl.yaml".source = utils.copyFile "${pkgs.cri-o-unwrapped.src}/crictl.yaml"; - environment.etc."crio/crio.conf.d/00-default.conf".text = '' - [crio] - storage_driver = "${cfg.storageDriver}" - - [crio.image] - ${optionalString (cfg.pauseImage != null) ''pause_image = "${cfg.pauseImage}"''} - ${optionalString (cfg.pauseCommand != null) ''pause_command = "${cfg.pauseCommand}"''} - - [crio.network] - plugin_dirs = ["${pkgs.cni-plugins}/bin/"] - ${optionalString (cfg.networkDir != null) ''network_dir = "${cfg.networkDir}"''} - - [crio.runtime] - cgroup_manager = "systemd" - log_level = "${cfg.logLevel}" - pinns_path = "${cfg.package}/bin/pinns" - hooks_dir = [ - ${lib.optionalString config.virtualisation.containers.ociSeccompBpfHook.enable - ''"${config.boot.kernelPackages.oci-seccomp-bpf-hook}",''} - ] - - ${optionalString (cfg.runtime != null) '' - default_runtime = "${cfg.runtime}" - [crio.runtime.runtimes] - [crio.runtime.runtimes.${cfg.runtime}] - ''} - ''; + virtualisation.cri-o.settings.crio = { + storage_driver = cfg.storageDriver; + + image = { + pause_image = mkIf (cfg.pauseImage != null) cfg.pauseImage; + pause_command = mkIf (cfg.pauseCommand != null) cfg.pauseCommand; + }; + + network = { + plugin_dirs = [ "${pkgs.cni-plugins}/bin" ]; + network_dir = mkIf (cfg.networkDir != null) cfg.networkDir; + }; + + runtime = { + cgroup_manager = "systemd"; + log_level = cfg.logLevel; + manage_ns_lifecycle = true; + pinns_path = "${cfg.package}/bin/pinns"; + hooks_dir = + optional (config.virtualisation.containers.ociSeccompBpfHook.enable) + config.boot.kernelPackages.oci-seccomp-bpf-hook; + + default_runtime = mkIf (cfg.runtime != null) cfg.runtime; + runtimes = mkIf (cfg.runtime != null) { + "${cfg.runtime}" = { }; + }; + }; + }; environment.etc."cni/net.d/10-crio-bridge.conf".source = utils.copyFile "${pkgs.cri-o-unwrapped.src}/contrib/cni/10-crio-bridge.conf"; environment.etc."cni/net.d/99-loopback.conf".source = utils.copyFile "${pkgs.cri-o-unwrapped.src}/contrib/cni/99-loopback.conf"; + environment.etc."crio/crio.conf.d/00-default.conf".source = cfgFile; # Enable common /etc/containers configuration virtualisation.containers.enable = true; @@ -139,6 +153,7 @@ in TimeoutStartSec = "0"; Restart = "on-abnormal"; }; + restartTriggers = [ cfgFile ]; }; }; } diff --git a/nixpkgs/nixos/modules/virtualisation/oci-containers.nix b/nixpkgs/nixos/modules/virtualisation/oci-containers.nix index 65b63cebc79c..a4a92f22506c 100644 --- a/nixpkgs/nixos/modules/virtualisation/oci-containers.nix +++ b/nixpkgs/nixos/modules/virtualisation/oci-containers.nix @@ -31,6 +31,30 @@ let example = literalExample "pkgs.dockerTools.buildDockerImage {...};"; }; + login = { + + username = mkOption { + type = with types; nullOr str; + default = null; + description = "Username for login."; + }; + + passwordFile = mkOption { + type = with types; nullOr str; + default = null; + description = "Path to file containing password."; + example = "/etc/nixos/dockerhub-password.txt"; + }; + + registry = mkOption { + type = with types; nullOr str; + default = null; + description = "Registry where to login to."; + example = "https://docker.pkg.github.com"; + }; + + }; + cmd = mkOption { type = with types; listOf str; default = []; @@ -220,6 +244,8 @@ let }; }; + isValidLogin = login: login.username != null && login.passwordFile != null && login.registry != null; + mkService = name: container: let dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn; in { @@ -235,6 +261,13 @@ let preStart = '' ${cfg.backend} rm -f ${name} || true + ${optionalString (isValidLogin container.login) '' + cat ${container.login.passwordFile} | \ + ${cfg.backend} login \ + ${container.login.registry} \ + --username ${container.login.username} \ + --password-stdin + ''} ${optionalString (container.imageFile != null) '' ${cfg.backend} load -i ${container.imageFile} ''} diff --git a/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix b/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix index 962a9059ea47..9465a8d6800d 100644 --- a/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix +++ b/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix @@ -56,5 +56,7 @@ in ${open-vm-tools}/bin/vmware-user-suid-wrapper ''; }; + + services.udev.packages = [ open-vm-tools ]; }; } |