Merge remote-tracking branch 'remotes/upstream/master'wip-nanopi-m4v2
| @@ -14,7 +14,9 @@ | |||
| /lib @edolstra @nbp @infinisil | |||
| /lib/systems @nbp @ericson2314 @matthewbauer | |||
| /lib/generators.nix @edolstra @nbp @Profpatsch | |||
| /lib/cli.nix @edolstra @nbp @Profpatsch | |||
| /lib/debug.nix @edolstra @nbp @Profpatsch | |||
| /lib/asserts.nix @edolstra @nbp @Profpatsch | |||
| # Nixpkgs Internals | |||
| /default.nix @nbp | |||
| @@ -162,7 +164,7 @@ | |||
| /pkgs/top-level/emacs-packages.nix @adisbladis | |||
| # VimPlugins | |||
| /pkgs/misc/vim-plugins @jonringer | |||
| /pkgs/misc/vim-plugins @jonringer @softinio | |||
| # VsCode Extensions | |||
| /pkgs/misc/vscode-extensions @jonringer | |||
| @@ -48,6 +48,15 @@ In addition to writing properly formatted commit messages, it's important to inc | |||
| For package version upgrades and such a one-line commit message is usually sufficient. | |||
| ## Backporting changes | |||
| To [backport a change into a release branch](https://nixos.org/nixpkgs/manual/#submitting-changes-stable-release-branches): | |||
| 1. Take note of the commit in which the change was introduced into `master`. | |||
| 2. Check out the target _release branch_, e.g. `release-19.09`. Do not use a _channel branch_ like `nixos-19.09` or `nixpkgs-19.09`. | |||
| 3. Use `git cherry-pick -x <original commit>`. | |||
| 4. Open your backport PR. Make sure to select the release branch (e.g. `release-19.09`) as the target branch of the PR, and link to the PR in which the original change was made to `master`. | |||
| ## Reviewing contributions | |||
| See the nixpkgs manual for more details on how to [Review contributions](https://nixos.org/nixpkgs/manual/#chap-reviewing-contributions). | |||
| @@ -80,7 +80,7 @@ appimageTools.wrapType2 { # or wrapType1 | |||
| <varname>src</varname> specifies the AppImage file to extract. | |||
| </para> | |||
| </callout> | |||
| <callout arearefs='ex-appimageTools-wrapping-2'> | |||
| <callout arearefs='ex-appimageTools-wrapping-3'> | |||
| <para> | |||
| <varname>extraPkgs</varname> allows you to pass a function to include additional packages inside the FHS environment your AppImage is going to run in. There are a few ways to learn which dependencies an application needs: | |||
| <itemizedlist> | |||
| @@ -496,8 +496,8 @@ and in this case the `python35` interpreter is automatically used. | |||
| ### Interpreters | |||
| Versions 2.7, 3.5, 3.6 and 3.7 of the CPython interpreter are available as | |||
| respectively `python27`, `python35`, `python36` and `python37`. The aliases | |||
| Versions 2.7, 3.5, 3.6, 3.7 and 3.8 of the CPython interpreter are available as | |||
| respectively `python27`, `python35`, `python36`, `python37` and `python38`. The aliases | |||
| `python2` and `python3` correspond to respectively `python27` and | |||
| `python37`. The default interpreter, `python`, maps to `python2`. The PyPy | |||
| interpreters compatible with Python 2.7 and 3 are available as `pypy27` and | |||
| @@ -833,6 +833,7 @@ used in `buildPythonPackage`. | |||
| - `pythonRemoveBinBytecode` to remove bytecode from the `/bin` folder. | |||
| - `setuptoolsBuildHook` to build a wheel using `setuptools`. | |||
| - `setuptoolsCheckHook` to run tests with `python setup.py test`. | |||
| - `venvShellHook` to source a Python 3 `venv` at the `venvDir` location. A `venv` is created if it does not yet exist. | |||
| - `wheelUnpackHook` to move a wheel to the correct folder so it can be installed with the `pipInstallHook`. | |||
| ### Development mode | |||
| @@ -1028,36 +1029,41 @@ If you want to create a Python environment for development, then the recommended | |||
| method is to use `nix-shell`, either with or without the `python.buildEnv` | |||
| function. | |||
| ### How to consume python modules using pip in a virtualenv like I am used to on other Operating Systems ? | |||
| ### How to consume python modules using pip in a virtual environment like I am used to on other Operating Systems? | |||
| This is an example of a `default.nix` for a `nix-shell`, which allows to consume a `virtualenv` environment, | |||
| While this approach is not very idiomatic from Nix perspective, it can still be useful when dealing with pre-existing | |||
| projects or in situations where it's not feasible or desired to write derivations for all required dependencies. | |||
| This is an example of a `default.nix` for a `nix-shell`, which allows to consume a virtual environment created by `venv`, | |||
| and install python modules through `pip` the traditional way. | |||
| Create this `default.nix` file, together with a `requirements.txt` and simply execute `nix-shell`. | |||
| ```nix | |||
| with import <nixpkgs> {}; | |||
| with import <nixpkgs> { }; | |||
| let | |||
| pythonPackages = python27Packages; | |||
| in | |||
| stdenv.mkDerivation { | |||
| pythonPackages = python3Packages; | |||
| in pkgs.mkShell rec { | |||
| name = "impurePythonEnv"; | |||
| src = null; | |||
| venvDir = "./.venv"; | |||
| buildInputs = [ | |||
| # these packages are required for virtualenv and pip to work: | |||
| # | |||
| pythonPackages.virtualenv | |||
| pythonPackages.pip | |||
| # the following packages are related to the dependencies of your python | |||
| # project. | |||
| # In this particular example the python modules listed in the | |||
| # requirements.txt require the following packages to be installed locally | |||
| # in order to compile any binary extensions they may require. | |||
| # | |||
| # A python interpreter including the 'venv' module is required to bootstrap | |||
| # the environment. | |||
| pythonPackages.python | |||
| # This execute some shell code to initialize a venv in $venvDir before | |||
| # dropping into the shell | |||
| pythonPackages.venvShellHook | |||
| # Those are dependencies that we would like to use from nixpkgs, which will | |||
| # add them to PYTHONPATH and thus make them accessible from within the venv. | |||
| pythonPackages.numpy | |||
| pythonPackages.requests | |||
| # In this particular example, in order to compile any binary extensions they may | |||
| # require, the python modules listed in the hypothetical requirements.txt need | |||
| # the following packages to be installed locally: | |||
| taglib | |||
| openssl | |||
| git | |||
| @@ -1067,11 +1073,54 @@ stdenv.mkDerivation { | |||
| zlib | |||
| ]; | |||
| # Now we can execute any commands within the virtual environment. | |||
| # This is optional and can be left out to run pip manually. | |||
| postShellHook = '' | |||
| pip install -r requirements.txt | |||
| ''; | |||
| } | |||
| ``` | |||
| In case the supplied venvShellHook is insufficient, or when python 2 support is needed, | |||
| you can define your own shell hook and adapt to your needs like in the following example: | |||
| ```nix | |||
| with import <nixpkgs> { }; | |||
| let | |||
| venvDir = "./.venv"; | |||
| pythonPackages = python3Packages; | |||
| in pkgs.mkShell rec { | |||
| name = "impurePythonEnv"; | |||
| buildInputs = [ | |||
| pythonPackages.python | |||
| # Needed when using python 2.7 | |||
| # pythonPackages.virtualenv | |||
| # ... | |||
| ]; | |||
| # This is very close to how venvShellHook is implemented, but | |||
| # adapted to use 'virtualenv' | |||
| shellHook = '' | |||
| # set SOURCE_DATE_EPOCH so that we can use python wheels | |||
| SOURCE_DATE_EPOCH=$(date +%s) | |||
| virtualenv --python=${pythonPackages.python.interpreter} --no-setuptools venv | |||
| export PATH=$PWD/venv/bin:$PATH | |||
| if [ -d "${venvDir}" ]; then | |||
| echo "Skipping venv creation, '${venvDir}' already exists" | |||
| else | |||
| echo "Creating new venv environment in path: '${venvDir}'" | |||
| # Note that the module venv was only introduced in python 3, so for 2.7 | |||
| # this needs to be replaced with a call to virtualenv | |||
| ${pythonPackages.python.interpreter} -m venv "${venvDir}" | |||
| fi | |||
| # Under some circumstances it might be necessary to add your virtual | |||
| # environment to PYTHONPATH, which you can do here too; | |||
| # PYTHONPATH=$PWD/${venvDir}/${pythonPackages.python.sitePackages}/:$PYTHONPATH | |||
| source "${venvDir}/bin/activate" | |||
| # As in the previous example, this is optional. | |||
| pip install -r requirements.txt | |||
| ''; | |||
| } | |||
| @@ -16,12 +16,6 @@ cargo | |||
| into the `environment.systemPackages` or bring them into | |||
| scope with `nix-shell -p rustc cargo`. | |||
| > If you are using NixOS and you want to use rust without a nix expression you | |||
| > probably want to add the following in your `configuration.nix` to build | |||
| > crates with C dependencies. | |||
| > | |||
| > environment.systemPackages = [binutils gcc gnumake openssl pkgconfig] | |||
| For daily builds (beta and nightly) use either rustup from | |||
| nixpkgs or use the [Rust nightlies | |||
| overlay](#using-the-rust-nightlies-overlay). | |||
| @@ -1,9 +1,22 @@ | |||
| .docbook .xref img[src^=images\/callouts\/], | |||
| .screen img, | |||
| .programlisting img { | |||
| .programlisting img, | |||
| .literallayout img, | |||
| .synopsis img { | |||
| width: 1em; | |||
| } | |||
| .calloutlist img { | |||
| width: 1.5em; | |||
| } | |||
| .prompt, | |||
| .screen img, | |||
| .programlisting img, | |||
| .literallayout img, | |||
| .synopsis img { | |||
| -moz-user-select: none; | |||
| -webkit-user-select: none; | |||
| -ms-user-select: none; | |||
| user-select: none; | |||
| } | |||
| @@ -60,7 +60,7 @@ rec { | |||
| [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ]; | |||
| /* Like `getAttrPath' without a default value. If it doesn't find the | |||
| /* Like `attrByPath' without a default value. If it doesn't find the | |||
| path it will throw. | |||
| Example: | |||
| @@ -0,0 +1,83 @@ | |||
| { lib }: | |||
| rec { | |||
| /* Automatically convert an attribute set to command-line options. | |||
| This helps protect against malformed command lines and also to reduce | |||
| boilerplate related to command-line construction for simple use cases. | |||
| `toGNUCommandLine` returns a list of nix strings. | |||
| `toGNUCommandLineShell` returns an escaped shell string. | |||
| Example: | |||
| cli.toGNUCommandLine {} { | |||
| data = builtins.toJSON { id = 0; }; | |||
| X = "PUT"; | |||
| retry = 3; | |||
| retry-delay = null; | |||
| url = [ "https://example.com/foo" "https://example.com/bar" ]; | |||
| silent = false; | |||
| verbose = true; | |||
| } | |||
| => [ | |||
| "-X" "PUT" | |||
| "--data" "{\"id\":0}" | |||
| "--retry" "3" | |||
| "--url" "https://example.com/foo" | |||
| "--url" "https://example.com/bar" | |||
| "--verbose" | |||
| ] | |||
| cli.toGNUCommandLineShell {} { | |||
| data = builtins.toJSON { id = 0; }; | |||
| X = "PUT"; | |||
| retry = 3; | |||
| retry-delay = null; | |||
| url = [ "https://example.com/foo" "https://example.com/bar" ]; | |||
| silent = false; | |||
| verbose = true; | |||
| } | |||
| => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; | |||
| */ | |||
| toGNUCommandLineShell = | |||
| options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs); | |||
| toGNUCommandLine = { | |||
| # how to string-format the option name; | |||
| # by default one character is a short option (`-`), | |||
| # more than one characters a long option (`--`). | |||
| mkOptionName ? | |||
| k: if builtins.stringLength k == 1 | |||
| then "-${k}" | |||
| else "--${k}", | |||
| # how to format a boolean value to a command list; | |||
| # by default it’s a flag option | |||
| # (only the option name if true, left out completely if false). | |||
| mkBool ? k: v: lib.optional v (mkOptionName k), | |||
| # how to format a list value to a command list; | |||
| # by default the option name is repeated for each value | |||
| # and `mkOption` is applied to the values themselves. | |||
| mkList ? k: v: lib.concatMap (mkOption k) v, | |||
| # how to format any remaining value to a command list; | |||
| # on the toplevel, booleans and lists are handled by `mkBool` and `mkList`, | |||
| # though they can still appear as values of a list. | |||
| # By default, everything is printed verbatim and complex types | |||
| # are forbidden (lists, attrsets, functions). `null` values are omitted. | |||
| mkOption ? | |||
| k: v: if v == null | |||
| then [] | |||
| else [ (mkOptionName k) (lib.generators.mkValueStringDefault {} v) ] | |||
| }: | |||
| options: | |||
| let | |||
| render = k: v: | |||
| if builtins.isBool v then mkBool k v | |||
| else if builtins.isList v then mkList k v | |||
| else mkOption k v; | |||
| in | |||
| builtins.concatLists (lib.mapAttrsToList render options); | |||
| } | |||
| @@ -37,10 +37,13 @@ let | |||
| licenses = callLibs ./licenses.nix; | |||
| systems = callLibs ./systems; | |||
| # serialization | |||
| cli = callLibs ./cli.nix; | |||
| generators = callLibs ./generators.nix; | |||
| # misc | |||
| asserts = callLibs ./asserts.nix; | |||
| debug = callLibs ./debug.nix; | |||
| generators = callLibs ./generators.nix; | |||
| misc = callLibs ./deprecated.nix; | |||
| # domain-specific | |||
| @@ -100,7 +103,7 @@ let | |||
| inherit (sources) pathType pathIsDirectory cleanSourceFilter | |||
| cleanSource sourceByRegex sourceFilesBySuffices | |||
| commitIdFromGitRepo cleanSourceWith pathHasContext | |||
| canCleanSource; | |||
| canCleanSource pathIsRegularFile pathIsGitRepo; | |||
| inherit (modules) evalModules unifyModuleSyntax | |||
| applyIfFunction mergeModules | |||
| mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions | |||
| @@ -46,7 +46,10 @@ rec { | |||
| else if isList v then err "lists" v | |||
| # same as for lists, might want to replace | |||
| else if isAttrs v then err "attrsets" v | |||
| # functions can’t be printed of course | |||
| else if isFunction v then err "functions" v | |||
| # let’s not talk about floats. There is no sensible `toString` for them. | |||
| else if isFloat v then err "floats" v | |||
| else err "this value is" (toString v); | |||
| @@ -536,11 +536,6 @@ lib.mapAttrs (n: v: v // { shortName = n; }) { | |||
| fullName = "University of Illinois/NCSA Open Source License"; | |||
| }; | |||
| notion_lgpl = { | |||
| url = "https://raw.githubusercontent.com/raboof/notion/master/LICENSE"; | |||
| fullName = "Notion modified LGPL"; | |||
| }; | |||
| nposl3 = spdx { | |||
| spdxId = "NPOSL-3.0"; | |||
| fullName = "Non-Profit Open Software License 3.0"; | |||
| @@ -764,12 +764,15 @@ rec { | |||
| fromOpt = getAttrFromPath from options; | |||
| toOf = attrByPath to | |||
| (abort "Renaming error: option `${showOption to}' does not exist."); | |||
| toType = let opt = attrByPath to {} options; in opt.type or null; | |||
| in | |||
| { | |||
| options = setAttrByPath from (mkOption { | |||
| inherit visible; | |||
| description = "Alias of <option>${showOption to}</option>."; | |||
| apply = x: use (toOf config); | |||
| } // optionalAttrs (toType != null) { | |||
| type = toType; | |||
| }); | |||
| config = mkMerge [ | |||
| { | |||
| @@ -9,6 +9,9 @@ rec { | |||
| # Returns true if the path exists and is a directory, false otherwise | |||
| pathIsDirectory = p: if builtins.pathExists p then (pathType p) == "directory" else false; | |||
| # Returns true if the path exists and is a regular file, false otherwise | |||
| pathIsRegularFile = p: if builtins.pathExists p then (pathType p) == "regular" else false; | |||
| # Bring in a path as a source, filtering out all Subversion and CVS | |||
| # directories, as well as backup files (*~). | |||
| cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! ( | |||
| @@ -102,6 +105,7 @@ rec { | |||
| in type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts; | |||
| in cleanSourceWith { inherit filter; src = path; }; | |||
| pathIsGitRepo = path: (builtins.tryEval (commitIdFromGitRepo path)).success; | |||
| # Get the commit id of a git repo | |||
| # Example: commitIdFromGitRepo <nixpkgs/.git> | |||
| @@ -110,24 +114,45 @@ rec { | |||
| with builtins; | |||
| let fileName = toString path + "/" + file; | |||
| packedRefsName = toString path + "/packed-refs"; | |||
| in if lib.pathExists fileName | |||
| absolutePath = base: path: | |||
| if lib.hasPrefix "/" path | |||
| then path | |||
| else toString (/. + "${base}/${path}"); | |||
| in if pathIsRegularFile path | |||
| # Resolve git worktrees. See gitrepository-layout(5) | |||
| then | |||
| let m = match "^gitdir: (.*)$" (lib.fileContents path); | |||
| in if m == null | |||
| then throw ("File contains no gitdir reference: " + path) | |||
| else | |||
| let gitDir = absolutePath (dirOf path) (lib.head m); | |||
| commonDir' = if pathIsRegularFile "${gitDir}/commondir" | |||
| then lib.fileContents "${gitDir}/commondir" | |||
| else gitDir; | |||
| commonDir = absolutePath gitDir commonDir'; | |||
| refFile = lib.removePrefix "${commonDir}/" "${gitDir}/${file}"; | |||
| in readCommitFromFile refFile commonDir | |||
| else if pathIsRegularFile fileName | |||
| # Sometimes git stores the commitId directly in the file but | |||
| # sometimes it stores something like: «ref: refs/heads/branch-name» | |||
| then | |||
| let fileContent = lib.fileContents fileName; | |||
| # Sometimes git stores the commitId directly in the file but | |||
| # sometimes it stores something like: «ref: refs/heads/branch-name» | |||
| matchRef = match "^ref: (.*)$" fileContent; | |||
| in if matchRef == null | |||
| in if matchRef == null | |||
| then fileContent | |||
| else readCommitFromFile (lib.head matchRef) path | |||
| else if pathIsRegularFile packedRefsName | |||
| # Sometimes, the file isn't there at all and has been packed away in the | |||
| # packed-refs file, so we have to grep through it: | |||
| else if lib.pathExists packedRefsName | |||
| then | |||
| let fileContent = readFile packedRefsName; | |||
| matchRef = match (".*\n([^\n ]*) " + file + "\n.*") fileContent; | |||
| in if matchRef == null | |||
| in if matchRef == null | |||
| then throw ("Could not find " + file + " in " + packedRefsName) | |||
| else lib.head matchRef | |||
| else throw ("Not a .git directory: " + path); | |||
| in readCommitFromFile "HEAD"; | |||
| @@ -244,7 +244,7 @@ rec { | |||
| Also note that Nix treats strings as a list of bytes and thus doesn't | |||
| handle unicode. | |||
| Type: stringtoCharacters :: string -> [string] | |||
| Type: stringToCharacters :: string -> [string] | |||
| Example: | |||
| stringToCharacters "" | |||
| @@ -84,7 +84,7 @@ rec { | |||
| else final.parsed.cpu.name; | |||
| qemuArch = | |||
| if final.isArm then "arm" | |||
| if final.isAarch32 then "arm" | |||
| else if final.isx86_64 then "x86_64" | |||
| else if final.isx86 then "i386" | |||
| else { | |||
| @@ -170,8 +170,8 @@ rec { | |||
| iphone64 = { | |||
| config = "aarch64-apple-ios"; | |||
| # config = "aarch64-apple-darwin14"; | |||
| sdkVer = "10.2"; | |||
| xcodeVer = "8.2"; | |||
| sdkVer = "12.4"; | |||
| xcodeVer = "10.3"; | |||
| xcodePlatform = "iPhoneOS"; | |||
| useiOSPrebuilt = true; | |||
| platform = {}; | |||
| @@ -180,8 +180,8 @@ rec { | |||
| iphone32 = { | |||
| config = "armv7a-apple-ios"; | |||
| # config = "arm-apple-darwin10"; | |||
| sdkVer = "10.2"; | |||
| xcodeVer = "8.2"; | |||
| sdkVer = "12.4"; | |||
| xcodeVer = "10.3"; | |||
| xcodePlatform = "iPhoneOS"; | |||
| useiOSPrebuilt = true; | |||
| platform = {}; | |||
| @@ -190,8 +190,8 @@ rec { | |||
| iphone64-simulator = { | |||
| config = "x86_64-apple-ios"; | |||
| # config = "x86_64-apple-darwin14"; | |||
| sdkVer = "10.2"; | |||
| xcodeVer = "8.2"; | |||
| sdkVer = "12.4"; | |||
| xcodeVer = "10.3"; | |||
| xcodePlatform = "iPhoneSimulator"; | |||
| useiOSPrebuilt = true; | |||
| platform = {}; | |||
| @@ -200,8 +200,8 @@ rec { | |||
| iphone32-simulator = { | |||
| config = "i686-apple-ios"; | |||
| # config = "i386-apple-darwin11"; | |||
| sdkVer = "10.2"; | |||
| xcodeVer = "8.2"; | |||
| sdkVer = "12.4"; | |||
| xcodeVer = "10.3"; | |||
| xcodePlatform = "iPhoneSimulator"; | |||
| useiOSPrebuilt = true; | |||
| platform = {}; | |||
| @@ -55,9 +55,6 @@ rec { | |||
| isEfi = map (family: { cpu.family = family; }) | |||
| [ "x86" "arm" "aarch64" ]; | |||
| # Deprecated after 18.03 | |||
| isArm = isAarch32; | |||
| }; | |||
| matchAnyAttrs = patterns: | |||
| @@ -441,4 +441,41 @@ runTests { | |||
| expected = "«foo»"; | |||
| }; | |||
| # CLI | |||
| testToGNUCommandLine = { | |||
| expr = cli.toGNUCommandLine {} { | |||
| data = builtins.toJSON { id = 0; }; | |||
| X = "PUT"; | |||
| retry = 3; | |||
| retry-delay = null; | |||
| url = [ "https://example.com/foo" "https://example.com/bar" ]; | |||
| silent = false; | |||
| verbose = true; | |||
| }; | |||
| expected = [ | |||
| "-X" "PUT" | |||
| "--data" "{\"id\":0}" | |||
| "--retry" "3" | |||
| "--url" "https://example.com/foo" | |||
| "--url" "https://example.com/bar" | |||
| "--verbose" | |||
| ]; | |||
| }; | |||
| testToGNUCommandLineShell = { | |||
| expr = cli.toGNUCommandLineShell {} { | |||
| data = builtins.toJSON { id = 0; }; | |||
| X = "PUT"; | |||
| retry = 3; | |||
| retry-delay = null; | |||
| url = [ "https://example.com/foo" "https://example.com/bar" ]; | |||
| silent = false; | |||
| verbose = true; | |||
| }; | |||
| expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; | |||
| }; | |||
| } | |||
| @@ -191,7 +191,7 @@ rec { | |||
| let | |||
| revisionFile = "${toString ./..}/.git-revision"; | |||
| gitRepo = "${toString ./..}/.git"; | |||
| in if lib.pathIsDirectory gitRepo | |||
| in if lib.pathIsGitRepo gitRepo | |||
| then lib.commitIdFromGitRepo gitRepo | |||
| else if lib.pathExists revisionFile then lib.fileContents revisionFile | |||
| else default; | |||
| @@ -367,18 +367,6 @@ rec { | |||
| { path = [ "services" "geoclue2" "appConfig" ]; | |||
| name = "desktopID"; | |||
| } | |||
| { path = [ "home-manager" "users" anyString "programs" "ssh" "matchBlocks" ]; | |||
| name = "host"; # https://github.com/rycee/home-manager/blob/e8dbc3561373b68d12decb3c0d7c1ba245f138f7/modules/programs/ssh.nix#L265 | |||
| } | |||
| { path = [ "home-manager" "users" anyString "home" "file" ]; | |||
| name = "target"; # https://github.com/rycee/home-manager/blob/0e9b7aab3c6c27bf020402e0e2ef20b65c040552/modules/files.nix#L33 | |||
| } | |||
| { path = [ "home-manager" "users" anyString "xdg" "configFile" ]; | |||
| name = "target"; # https://github.com/rycee/home-manager/blob/54de0e1d79a1370e57a8f23bef89f99f9b92ab67/modules/misc/xdg.nix#L41 | |||
| } | |||
| { path = [ "home-manager" "users" anyString "xdg" "dataFile" ]; | |||
| name = "target"; # https://github.com/rycee/home-manager/blob/54de0e1d79a1370e57a8f23bef89f99f9b92ab67/modules/misc/xdg.nix#L58 | |||
| } | |||
| ]; | |||
| matched = let | |||
| equals = a: b: b == anyString || a == b; | |||
| @@ -418,7 +406,7 @@ rec { | |||
| In file ${def.file} | |||
| a list is being assigned to the option config.${option}. | |||
| This will soon be an error as type loaOf is deprecated. | |||
| See https://git.io/fj2zm for more information. | |||
| See https://github.com/NixOS/nixpkgs/pull/63103 for more information. | |||
| Do | |||
| ${option} = | |||
| { ${set}${more}} | |||
| @@ -602,7 +590,7 @@ rec { | |||
| tail' = tail ts; | |||
| in foldl' either head' tail'; | |||
| # Either value of type `finalType` or `coercedType`, the latter is | |||
| # Either value of type `coercedType` or `finalType`, the former is | |||
| # converted to `finalType` using `coerceFunc`. | |||
| coercedTo = coercedType: coerceFunc: finalType: | |||
| assert lib.assertMsg (coercedType.getSubModules == null) | |||
| @@ -611,12 +599,12 @@ rec { | |||
| mkOptionType rec { | |||
| name = "coercedTo"; | |||
| description = "${finalType.description} or ${coercedType.description} convertible to it"; | |||
| check = x: finalType.check x || (coercedType.check x && finalType.check (coerceFunc x)); | |||
| check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x; | |||
| merge = loc: defs: | |||
| let | |||
| coerceVal = val: | |||
| if finalType.check val then val | |||
| else coerceFunc val; | |||
| if coercedType.check val then coerceFunc val | |||
| else val; | |||
| in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); | |||
| emptyValue = finalType.emptyValue; | |||
| getSubOptions = finalType.getSubOptions; | |||
| @@ -40,12 +40,6 @@ | |||
| See `./scripts/check-maintainer-github-handles.sh` for an example on how to work with this data. | |||
| */ | |||
| { | |||
| "00-matt" = { | |||
| name = "Matt Smith"; | |||
| email = "matt@offtopica.uk"; | |||
| github = "00-matt"; | |||
| githubId = 48835712; | |||
| }; | |||
| "0x4A6F" = { | |||
| email = "0x4A6F@shackspace.de"; | |||
| name = "Joachim Ernst"; | |||
| @@ -517,6 +511,12 @@ | |||
| githubId = 5327697; | |||
| name = "Anatolii Prylutskyi"; | |||
| }; | |||
| antoinerg = { | |||
| email = "roygobeil.antoine@gmail.com"; | |||
| github = "antoinerg"; | |||
| githubId = 301546; | |||
| name = "Antoine Roy-Gobeil"; | |||
| }; | |||
| anton-dessiatov = { | |||
| email = "anton.dessiatov@gmail.com"; | |||
| github = "anton-dessiatov"; | |||
| @@ -594,6 +594,12 @@ | |||
| githubId = 1296771; | |||
| name = "Anders Riutta"; | |||
| }; | |||
| arnoldfarkas = { | |||
| email = "arnold.farkas@gmail.com"; | |||
| github = "arnoldfarkas"; | |||
| githubId = 59696216; | |||
| name = "Arnold Farkas"; | |||
| }; | |||
| arobyn = { | |||
| email = "shados@shados.net"; | |||
| github = "shados"; | |||
| @@ -676,6 +682,12 @@ | |||
| githubId = 192147; | |||
| name = "aszlig"; | |||
| }; | |||
| atemu = { | |||
| name = "Atemu"; | |||
| email = "atemu.main+nixpkgs@gmail.com"; | |||
| github = "Atemu"; | |||
| githubId = 18599032; | |||
| }; | |||
| athas = { | |||
| email = "athas@sigkill.dk"; | |||
| github = "athas"; | |||
| @@ -951,6 +963,12 @@ | |||
| githubId = 5718007; | |||
| name = "Bastian Köcher"; | |||
| }; | |||
| blanky0230 = { | |||
| email = "blanky0230@gmail.com"; | |||
| github = "blanky0230"; | |||
| githubId = 5700358; | |||
| name = "Thomas Blank"; | |||
| }; | |||
| blitz = { | |||
| email = "js@alien8.de"; | |||
| github = "blitz"; | |||
| @@ -1893,6 +1911,12 @@ | |||
| email = "burkett.andrew@gmail.com"; | |||
| name = "Andrew Burkett"; | |||
| }; | |||
| drewrisinger = { | |||
| email = "drisinger+nixpkgs@gmail.com"; | |||
| github = "drewrisinger"; | |||
| gitHubId = 10198051; | |||
| name = "Drew Risinger"; | |||
| }; | |||
| dsferruzza = { | |||
| email = "david.sferruzza@gmail.com"; | |||
| github = "dsferruzza"; | |||
| @@ -1919,6 +1943,12 @@ | |||
| fingerprint = "5DD7 C6F6 0630 F08E DAE7 4711 1525 585D 1B43 C62A"; | |||
| }]; | |||
| }; | |||
| dwarfmaster = { | |||
| email = "nixpkgs@dwarfmaster.net"; | |||
| github = "dwarfmaster"; | |||
| githubId = 2025623; | |||
| name = "Luc Chabassier"; | |||
| }; | |||
| dxf = { | |||
| email = "dingxiangfei2009@gmail.com"; | |||
| github = "dingxiangfei2009"; | |||
| @@ -2393,6 +2423,12 @@ | |||
| githubId = 415760; | |||
| name = "Jonas Höglund"; | |||
| }; | |||
| fishi0x01 = { | |||
| email = "fishi0x01@gmail.com"; | |||
| github = "fishi0x01"; | |||
| githubId = 10799507; | |||
| name = "Karl Fischer"; | |||
| }; | |||
| Flakebi = { | |||
| email = "flakebi@t-online.de"; | |||
| github = "Flakebi"; | |||
| @@ -2439,6 +2475,12 @@ | |||
| githubId = 844574; | |||
| name = "Daniel Austin"; | |||
| }; | |||
| flyfloh = { | |||
| email = "nix@halbmastwurf.de"; | |||
| github = "flyfloh"; | |||
| githubId = 74379; | |||
| name = "Florian Pester"; | |||
| }; | |||
| fmthoma = { | |||
| email = "f.m.thoma@googlemail.com"; | |||
| github = "fmthoma"; | |||
| @@ -2520,6 +2562,12 @@ | |||
| githubId = 1943632; | |||
| name = "fro_ozen"; | |||
| }; | |||
| Frostman = { | |||
| email = "me@slukjanov.name"; | |||
| github = "Frostman"; | |||
| githubId = 134872; | |||
| name = "Sergei Lukianov"; | |||
| }; | |||
| frontsideair = { | |||
| email = "photonia@gmail.com"; | |||
| github = "frontsideair"; | |||
| @@ -2992,6 +3040,12 @@ | |||
| githubId = 4401220; | |||
| name = "Michael Eden"; | |||
| }; | |||
| ilya-fedin = { | |||
| email = "fedin-ilja2010@ya.ru"; | |||
| github = "ilya-fedin"; | |||
| githubId = 17829319; | |||
| name = "Ilya Fedin"; | |||
| }; | |||
| ilya-kolpakov = { | |||
| email = "ilya.kolpakov@gmail.com"; | |||
| github = "ilya-kolpakov"; | |||
| @@ -3474,6 +3528,12 @@ | |||
| github = "jorsn"; | |||
| githubId = 4646725; | |||
| }; | |||
| jpas = { | |||
| name = "Jarrod Pas"; | |||
| email = "jarrod@jarrodpas.com"; | |||
| github = "jpas"; | |||
| githubId = 5689724; | |||
| }; | |||
| jpdoyle = { | |||
| email = "joethedoyle@gmail.com"; | |||
| github = "jpdoyle"; | |||
| @@ -3504,6 +3564,16 @@ | |||
| githubId = 4611077; | |||
| name = "Raymond Gauthier"; | |||
| }; | |||
| jtcoolen = { | |||
| email = "jtcoolen@pm.me"; | |||
| name = "Julien Coolen"; | |||
| github = "jtcoolen"; | |||
| githubId = 54635632; | |||
| keys = [{ | |||
| longkeyid = "rsa4096/0x19642151C218F6F5"; | |||
| fingerprint = "4C68 56EE DFDA 20FB 77E8 9169 1964 2151 C218 F6F5"; | |||
| }]; | |||
| }; | |||
| jtobin = { | |||
| email = "jared@jtobin.io"; | |||
| github = "jtobin"; | |||
| @@ -3790,6 +3860,12 @@ | |||
| githubId = 787421; | |||
| name = "Kevin Quick"; | |||
| }; | |||
| kraem = { | |||
| email = "me@kraem.xyz"; | |||
| github = "kraem"; | |||
| githubId = 26622971; | |||
| name = "Ronnie Ebrin"; | |||
| }; | |||
| kragniz = { | |||
| email = "louis@kragniz.eu"; | |||
| github = "kragniz"; | |||
| @@ -3808,6 +3884,12 @@ | |||
| githubId = 17659803; | |||
| name = "Matthias Axel Kröll"; | |||
| }; | |||
| kristian-brucaj = { | |||
| email = "kbrucaj@gmail.com"; | |||
| github = "kristian-brucaj"; | |||
| githubID = "8893110"; | |||
| name = "Kristian Brucaj"; | |||
| }; | |||
| kristoff3r = { | |||
| email = "k.soeholm@gmail.com"; | |||
| github = "kristoff3r"; | |||
| @@ -3838,6 +3920,12 @@ | |||
| githubId = 449813; | |||
| name = "Roman Kuznetsov"; | |||
| }; | |||
| kwohlfahrt = { | |||
| email = "kai.wohlfahrt@gmail.com"; | |||
| github = "kwohlfahrt"; | |||
| githubId = 2422454; | |||
| name = "Kai Wohlfahrt"; | |||
| }; | |||
| kylesferrazza = { | |||
| name = "Kyle Sferrazza"; | |||
| email = "kyle.sferrazza@gmail.com"; | |||
| @@ -3903,6 +3991,12 @@ | |||
| githubId = 32152; | |||
| name = "Luka Blaskovic"; | |||
| }; | |||
| ldelelis = { | |||
| email = "ldelelis@est.frba.utn.edu.ar"; | |||
| github = "ldelelis"; | |||
| githubId = 20250323; | |||
| name = "Lucio Delelis"; | |||
| }; | |||
| ldesgoui = { | |||
| email = "ldesgoui@gmail.com"; | |||
| github = "ldesgoui"; | |||
| @@ -4147,12 +4241,6 @@ | |||
| github = "ltavard"; | |||
| name = "Laure Tavard"; | |||
| }; | |||
| lucas8 = { | |||
| email = "luc.linux@mailoo.org"; | |||
| github = "lucas8"; | |||
| githubId = 2025623; | |||
| name = "Luc Chabassier"; | |||
| }; | |||
| lucus16 = { | |||
| email = "lars.jellema@gmail.com"; | |||
| github = "Lucus16"; | |||
| @@ -5088,6 +5176,12 @@ | |||
| githubId = 7588406; | |||
| name = "Andrew R. M."; | |||
| }; | |||
| nloomans = { | |||
| email = "noah@nixos.noahloomans.com"; | |||
| github = "nloomans"; | |||
| githubId = 7829481; | |||
| name = "Noah Loomans"; | |||
| }; | |||
| nmattia = { | |||
| email = "nicolas@nmattia.com"; | |||
| github = "nmattia"; | |||
| @@ -5403,6 +5497,12 @@ | |||
| githubId = 3250809; | |||
| name = "Milan Pässler"; | |||
| }; | |||
| petercommand = { | |||
| email = "petercommand@gmail.com"; | |||
| github = "petercommand"; | |||
| githubId = 1260660; | |||
| name = "petercommand"; | |||
| }; | |||
| peterhoeg = { | |||
| email = "peter@hoeg.com"; | |||
| github = "peterhoeg"; | |||
| @@ -6147,6 +6247,16 @@ | |||
| githubId = 6022042; | |||
| name = "Sam Parkinson"; | |||
| }; | |||
| samlich = { | |||
| email = "nixos@samli.ch"; | |||
| github = "samlich"; | |||
| githubId = 1349989; | |||
| name = "samlich"; | |||
| keys = [{ | |||
| longkeyid = "rsa4096/B1568953B1939F1C"; | |||
| fingerprint = "AE8C 0836 FDF6 3FFC 9580 C588 B156 8953 B193 9F1C"; | |||
| }]; | |||
| }; | |||
| samrose = { | |||
| email = "samuel.rose@gmail.com"; | |||
| github = "samrose"; | |||
| @@ -6254,6 +6364,12 @@ | |||
| github = "scubed2"; | |||
| name = "Sterling Stein"; | |||
| }; | |||
| sdier = { | |||
| email = "scott@dier.name"; | |||
| github = "sdier"; | |||
| githubId = 11613056; | |||
| name = "Scott Dier"; | |||
| }; | |||
| sdll = { | |||
| email = "sasha.delly@gmail.com"; | |||
| github = "sdll"; | |||
| @@ -6841,6 +6957,12 @@ | |||
| githubId = 870673; | |||
| name = "Takuo Yonezawa"; | |||
| }; | |||
| talkara = { | |||
| email = "taito.horiuchi@relexsolutions.com"; | |||
| github = "talkara"; | |||
| githubId = 51232929; | |||
| name = "Taito Horiuchi"; | |||
| }; | |||
| talyz = { | |||
| email = "kim.lindberger@gmail.com"; | |||
| github = "talyz"; | |||
| @@ -7032,6 +7154,11 @@ | |||
| github = "timbertson"; | |||
| name = "Tim Cuthbertson"; | |||
| }; | |||
| timma = { | |||
| email = "kunduru.it.iitb@gmail.com"; | |||
| github = "ktrsoft"; | |||
| name = "Timma"; | |||
| }; | |||
| timokau = { | |||
| email = "timokau@zoho.com"; | |||
| github = "timokau"; | |||
| @@ -7823,6 +7950,12 @@ | |||
| githubId = 1069303; | |||
| name = "Kim Simmons"; | |||
| }; | |||
| zowoq = { | |||
| email = "59103226+zowoq@users.noreply.github.com"; | |||
| github = "zowoq"; | |||
| githubId = 59103226; | |||
| name = "zowoq"; | |||
| }; | |||
| zraexy = { | |||
| email = "zraexy@gmail.com"; | |||
| github = "zraexy"; | |||
| @@ -7895,4 +8028,16 @@ | |||
| githubId = 8686360; | |||
| name = "Illia Shestakov"; | |||
| }; | |||
| foxit64 = { | |||
| email = "o4nsxy05@gmail.com"; | |||
| github = "foxit64"; | |||
| githubId = 56247270; | |||
| name = "Foxit"; | |||
| }; | |||
| masaeedu = { | |||
| email = "masaeedu@gmail.com"; | |||
| github = "masaeedu"; | |||
| githubId = 3674056; | |||
| name = "Asad Saeeduddin"; | |||
| }; | |||
| } | |||
| @@ -28,7 +28,8 @@ find . -type f | while read src; do | |||
| done | |||
| cat >"$SRCS" <<EOF | |||
| # DO NOT EDIT! This file is generated automatically by fetch-kde-qt.sh | |||
| # DO NOT EDIT! This file is generated automatically. | |||
| # Command: $0 $@ | |||
| { fetchurl, mirror }: | |||
| { | |||
| @@ -11,13 +11,14 @@ compat53,,,,,vcunat | |||
| coxpcall,,,1.17.0-1,, | |||
| cqueues,,,,,vcunat | |||
| cyrussasl,,,,,vcunat | |||
| digestif,,http://luarocks.org/dev,,lua5_3, | |||
| digestif,,,,lua5_3, | |||
| dkjson,,,,, | |||
| fifo,,,,, | |||
| http,,,,,vcunat | |||
| inspect,,,,, | |||
| ldoc,,,,, | |||
| lgi,,,,, | |||
| linenoise,,,,, | |||
| ljsyscall,,,,lua5_1,lblasc | |||
| lpeg,,,,,vyp | |||
| lpeg_patterns,,,,, | |||
| @@ -43,6 +44,7 @@ luadbi-mysql,,,,, | |||
| luadbi-postgresql,,,,, | |||
| luadbi-sqlite3,,,,, | |||
| luadoc,,,,, | |||
| luaepnf,,,,, | |||
| luaevent,,,,, | |||
| luaexpat,,,1.3.0-1,,arobyn flosse | |||
| luaffi,,http://luarocks.org/dev,,, | |||
| @@ -50,6 +52,7 @@ luafilesystem,,,1.7.0-2,,flosse vcunat | |||
| lualogging,,,,, | |||
| luaossl,,,,lua5_1,vcunat | |||
| luaposix,,,,,vyp lblasc | |||
| luarepl,,,,, | |||
| luasec,,,,,flosse | |||
| luasocket,,,,, | |||
| luasql-sqlite3,,,,,vyp | |||
| @@ -72,3 +75,4 @@ std__debug,std._debug,,,, | |||
| std_normalize,std.normalize,,,, | |||
| stdlib,,,,,vyp | |||
| pulseaudio,,,,,doronbehar | |||
| vstruct,,,,, | |||
| @@ -19,7 +19,7 @@ export LUAROCKS_CONFIG="$NIXPKGS_PATH/maintainers/scripts/luarocks-config.lua" | |||
| # 10 is a pretty arbitrary number of simultaneous jobs, but it is generally | |||
| # impolite to hit a webserver with *too* many simultaneous connections :) | |||
| PARALLEL_JOBS=10 | |||
| PARALLEL_JOBS=1 | |||
| exit_trap() { | |||
| local lc="$BASH_COMMAND" rc=$? | |||
| @@ -19,6 +19,12 @@ | |||
| <command>nixos-rebuild switch</command>. | |||
| </para> | |||
| <note> | |||
| <para> | |||
| Some packages require additional global configuration such as D-Bus or systemd service registration so adding them to <xref linkend="opt-environment.systemPackages"/> might not be sufficient. You are advised to check the <link xlink:href="#ch-options">list of options</link> whether a NixOS module for the package does not exist. | |||
| </para> | |||
| </note> | |||
| <para> | |||
| You can get a list of the available packages as follows: | |||
| <screen> | |||
| @@ -37,4 +37,38 @@ Enter passphrase for /dev/disk/by-uuid/3f6b0024-3a44-4fde-a43a-767b872abe5d: *** | |||
| on an encrypted partition, it is necessary to add the following grub option: | |||
| <programlisting><xref linkend="opt-boot.loader.grub.enableCryptodisk"/> = true;</programlisting> | |||
| </para> | |||
| <section xml:id="sec-luks-file-systems-fido2"> | |||
| <title>FIDO2</title> | |||
| <para> | |||
| NixOS also supports unlocking your LUKS-Encrypted file system using a FIDO2 compatible token. In the following example, we will create a new FIDO2 credential | |||
| and add it as a new key to our existing device <filename>/dev/sda2</filename>: | |||
| <screen> | |||
| # export FIDO2_LABEL="/dev/sda2 @ $HOSTNAME" | |||
| # fido2luks credential "$FIDO2_LABEL" | |||
| f1d00200108b9d6e849a8b388da457688e3dd653b4e53770012d8f28e5d3b269865038c346802f36f3da7278b13ad6a3bb6a1452e24ebeeaa24ba40eef559b1b287d2a2f80b7 | |||
| # fido2luks -i add-key /dev/sda2 f1d00200108b9d6e849a8b388da457688e3dd653b4e53770012d8f28e5d3b269865038c346802f36f3da7278b13ad6a3bb6a1452e24ebeeaa24ba40eef559b1b287d2a2f80b7 | |||
| Password: | |||
| Password (again): | |||
| Old password: | |||
| Old password (again): | |||
| Added to key to device /dev/sda2, slot: 2 | |||
| </screen> | |||
| To ensure that this file system is decrypted using the FIDO2 compatible key, add the following to <filename>configuration.nix</filename>: | |||
| <programlisting> | |||
| <link linkend="opt-boot.initrd.luks.fido2Support">boot.initrd.luks.fido2Support</link> = true; | |||
| <link linkend="opt-boot.initrd.luks.devices._name__.fido2.credential">boot.initrd.luks.devices."/dev/sda2".fido2.credential</link> = "f1d00200108b9d6e849a8b388da457688e3dd653b4e53770012d8f28e5d3b269865038c346802f36f3da7278b13ad6a3bb6a1452e24ebeeaa24ba40eef559b1b287d2a2f80b7"; | |||
| </programlisting> | |||
| You can also use the FIDO2 passwordless setup, but for security reasons, you might want to enable it only when your device is PIN protected, such as <link xlink:href="https://trezor.io/">Trezor</link>. | |||
| <programlisting> | |||
| <link linkend="opt-boot.initrd.luks.devices._name__.fido2.passwordLess">boot.initrd.luks.devices."/dev/sda2".fido2.passwordLess</link> = true; | |||
| </programlisting> | |||
| </para> | |||
| </section> | |||
| </section> | |||
| @@ -28,17 +28,21 @@ | |||
| <command>nmtui</command> (curses-based terminal user interface). See their | |||
| manual pages for details on their usage. Some desktop environments (GNOME, | |||
| KDE) have their own configuration tools for NetworkManager. On XFCE, there is | |||
| no configuration tool for NetworkManager by default: by adding | |||
| <code>networkmanagerapplet</code> to the list of system packages, the | |||
| graphical applet will be installed and will launch automatically when XFCE is | |||
| starting (and will show in the status tray). | |||
| no configuration tool for NetworkManager by default: by enabling <xref linkend="opt-programs.nm-applet.enable"/>, the | |||
| graphical applet will be installed and will launch automatically when the graphical session is started. | |||
| </para> | |||
| <note> | |||
| <para> | |||
| <code>networking.networkmanager</code> and <code>networking.wireless</code> | |||
| (WPA Supplicant) cannot be enabled at the same time: you can still connect | |||
| to the wireless networks using NetworkManager. | |||
| (WPA Supplicant) can be used together if desired. To do this you need to instruct | |||
| NetworkManager to ignore those interfaces like: | |||
| <programlisting> | |||
| <xref linkend="opt-networking.networkmanager.unmanaged"/> = [ | |||
| "*" "except:type:wwan" "except:type:gsm" | |||
| ]; | |||
| </programlisting> | |||
| Refer to the option description for the exact syntax and references to external documentation. | |||
| </para> | |||
| </note> | |||
| </section> | |||
| @@ -85,11 +85,14 @@ | |||
| <programlisting> | |||
| <xref linkend="opt-services.xserver.displayManager.defaultSession"/> = "none+i3"; | |||
| </programlisting> | |||
| And, finally, to enable auto-login for a user <literal>johndoe</literal>: | |||
| Every display manager in NixOS supports auto-login, here is an example | |||
| using lightdm for a user <literal>alice</literal>: | |||
| <programlisting> | |||
| <xref linkend="opt-services.xserver.displayManager.auto.enable"/> = true; | |||
| <xref linkend="opt-services.xserver.displayManager.auto.user"/> = "johndoe"; | |||
| <xref linkend="opt-services.xserver.displayManager.lightdm.enable"/> = true; | |||
| <xref linkend="opt-services.xserver.displayManager.lightdm.autoLogin.enable"/> = true; | |||
| <xref linkend="opt-services.xserver.displayManager.lightdm.autoLogin.user"/> = "alice"; | |||
| </programlisting> | |||
| The options are named identically for all other display managers. | |||
| </para> | |||
| </simplesect> | |||
| <simplesect xml:id="sec-x11-graphics-cards-nvidia"> | |||
| @@ -28,25 +28,14 @@ | |||
| <para> | |||
| Some Xfce programs are not installed automatically. To install them manually | |||
| (system wide), put them into your | |||
| <xref linkend="opt-environment.systemPackages"/>. | |||
| <xref linkend="opt-environment.systemPackages"/> from <literal>pkgs.xfce</literal>. | |||
| </para> | |||
| <simplesect xml:id="sec-xfce-thunar-volumes"> | |||
| <title>Thunar Volume Support</title> | |||
| <simplesect xml:id="sec-xfce-thunar-plugins"> | |||
| <title>Thunar Plugins</title> | |||
| <para> | |||
| To enable <emphasis>Thunar</emphasis> volume support, put | |||
| <programlisting> | |||
| <xref linkend="opt-services.xserver.desktopManager.xfce.enable"/> = true; | |||
| </programlisting> | |||
| into your <emphasis>configuration.nix</emphasis>. | |||
| </para> | |||
| </simplesect> | |||
| <simplesect xml:id="sec-xfce-polkit"> | |||
| <title>Polkit Authentication Agent</title> | |||
| <para> | |||
| There is no authentication agent automatically installed alongside Xfce. To | |||
| allow mounting of local (non-removable) filesystems, you will need to | |||
| install one. Installing <emphasis>polkit_gnome</emphasis>, a rebuild, logout | |||
| and login did the trick. | |||
| If you'd like to add extra plugins to Thunar, add them to | |||
| <xref linkend="opt-services.xserver.desktopManager.xfce.thunarPlugins"/>. | |||
| You shouldn't just add them to <xref linkend="opt-environment.systemPackages"/>. | |||
| </para> | |||
| </simplesect> | |||
| <simplesect xml:id="sec-xfce-troubleshooting"> | |||
| @@ -187,7 +187,7 @@ | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| Update "Chapter 4. Upgrading NixOS" section of the manual to match | |||
| Update "Chapter 4. Upgrading NixOS" section of the manual to match | |||
| new stable release version. | |||
| </para> | |||
| </listitem> | |||
| @@ -236,6 +236,10 @@ | |||
| introduced to their role, making it easier to pass on knowledge and | |||
| experience. | |||
| </para> | |||
| <para> | |||
| Release managers for the current NixOS release are tracked by GitHub team | |||
| <link xlink:href="https://github.com/orgs/NixOS/teams/nixos-release-managers/members"><literal>@NixOS/nixos-release-managers</literal></link>. | |||
| </para> | |||
| <para> | |||
| A release manager's role and responsibilities are: | |||
| </para> | |||
| @@ -120,12 +120,17 @@ nixos https://nixos.org/channels/nixos-unstable | |||
| to <filename>configuration.nix</filename>: | |||
| <programlisting> | |||
| <xref linkend="opt-system.autoUpgrade.enable"/> = true; | |||
| <xref linkend="opt-system.autoUpgrade.allowReboot"/> = true; | |||
| </programlisting> | |||
| This enables a periodically executed systemd service named | |||
| <literal>nixos-upgrade.service</literal>. It runs <command>nixos-rebuild | |||
| switch --upgrade</command> to upgrade NixOS to the latest version in the | |||
| current channel. (To see when the service runs, see <command>systemctl | |||
| list-timers</command>.) You can also specify a channel explicitly, e.g. | |||
| <literal>nixos-upgrade.service</literal>. If the <literal>allowReboot</literal> | |||
| option is <literal>false</literal>, it runs <command>nixos-rebuild switch | |||
| --upgrade</command> to upgrade NixOS to the latest version in the current | |||
| channel. (To see when the service runs, see <command>systemctl list-timers</command>.) | |||
| If <literal>allowReboot</literal> is <literal>true</literal>, then the | |||
| system will automatically reboot if the new generation contains a different | |||
| kernel, initrd or kernel modules. | |||
| You can also specify a channel explicitly, e.g. | |||
| <programlisting> | |||
| <xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-19.09; | |||
| </programlisting> | |||
| @@ -210,7 +210,7 @@ | |||
| The closure must be an appropriately configured NixOS system, with boot | |||
| loader and partition configuration that fits the target host. Such a | |||
| closure is typically obtained with a command such as <command>nix-build | |||
| -I nixos-config=./configuration.nix '<nixos>' -A system | |||
| -I nixos-config=./configuration.nix '<nixpkgs/nixos>' -A system | |||
| --no-out-link</command> | |||
| </para> | |||
| </listitem> | |||
| @@ -14,12 +14,16 @@ | |||
| <refsynopsisdiv> | |||
| <cmdsynopsis> | |||
| <command>nixos-option</command> | |||
| <arg> | |||
| <option>-I</option> <replaceable>path</replaceable> | |||
| <group choice='req'> | |||
| <arg choice='plain'><option>-r</option></arg> | |||
| <arg choice='plain'><option>--recursive</option></arg> | |||
| </group> | |||
| </arg> | |||
| <arg> | |||
| <option>--all</option> | |||
| <option>-I</option> <replaceable>path</replaceable> | |||
| </arg> | |||
| <arg> | |||
| @@ -46,23 +50,22 @@ | |||
| </para> | |||
| <variablelist> | |||
| <varlistentry> | |||
| <term> | |||
| <option>-I</option> <replaceable>path</replaceable> | |||
| </term> | |||
| <term><option>-r</option></term> | |||
| <term><option>--recursive</option></term> | |||
| <listitem> | |||
| <para> | |||
| This option is passed to the underlying | |||
| <command>nix-instantiate</command> invocation. | |||
| Print all the values at or below the specified path recursively. | |||
| </para> | |||
| </listitem> | |||
| </varlistentry> | |||
| <varlistentry> | |||
| <term> | |||
| <option>--all</option> | |||
| <option>-I</option> <replaceable>path</replaceable> | |||
| </term> | |||
| <listitem> | |||
| <para> | |||
| Print the values of all options. | |||
| This option is passed to the underlying | |||
| <command>nix-instantiate</command> invocation. | |||
| </para> | |||
| </listitem> | |||
| </varlistentry> | |||
| @@ -6,7 +6,7 @@ | |||
| <author><personname><firstname>Eelco</firstname><surname>Dolstra</surname></personname> | |||
| <contrib>Author</contrib> | |||
| </author> | |||
| <copyright><year>2007-2019</year><holder>Eelco Dolstra</holder> | |||
| <copyright><year>2007-2020</year><holder>Eelco Dolstra</holder> | |||
| </copyright> | |||
| </info> | |||
| <xi:include href="man-configuration.xml" /> | |||
| @@ -23,6 +23,13 @@ | |||
| Support is planned until the end of October 2020, handing over to 20.09. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| Linux kernel is updated to branch 5.4 by default (from 4.19). | |||
| Users of Intel GPUs may prefer to explicitly set branch to 4.19 to avoid some regressions. | |||
| <programlisting>boot.kernelPackages = pkgs.linuxPackages_4_19;</programlisting> | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| Postgresql for NixOS service now defaults to v11. | |||
| @@ -52,7 +59,7 @@ | |||
| <listitem> | |||
| <para> | |||
| <command>nixos-option</command> has been rewritten in C++, speeding it up, improving correctness, | |||
| and adding a <option>--all</option> option which prints all options and their values. | |||
| and adding a <option>-r</option> option which prints all options and their values recursively. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| @@ -96,6 +103,13 @@ services.xserver.displayManager.defaultSession = "xfce+icewm"; | |||
| via <option>services.upower</option>. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| To use Geary you should enable <xref linkend="opt-programs.geary.enable"/> instead of | |||
| just adding it to <xref linkend="opt-environment.systemPackages"/>. | |||
| It was created so Geary could function properly outside of GNOME. | |||
| </para> | |||
| </listitem> | |||
| </itemizedlist> | |||
| </section> | |||
| @@ -126,7 +140,7 @@ services.xserver.displayManager.defaultSession = "xfce+icewm"; | |||
| <listitem> | |||
| <para> | |||
| The <literal>dynamicHosts</literal> option has been removed from the | |||
| <link linkend="opt-networking.networkmanager.enable">networkd</link> | |||
| <link linkend="opt-networking.networkmanager.enable">NetworkManager</link> | |||
| module. Allowing (multiple) regular users to override host entries | |||
| affecting the whole system opens up a huge attack vector. | |||
| There seem to be very rare cases where this might be useful. | |||
| @@ -168,6 +182,12 @@ services.xserver.displayManager.defaultSession = "xfce+icewm"; | |||
| SDDM, GDM, or using the startx module which uses Xinitrc. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The Way Cooler wayland compositor has been removed, as the project has been officially canceled. | |||
| There are no more <literal>way-cooler</literal> attribute and <literal>programs.way-cooler</literal> options. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The BEAM package set has been deleted. You will only find there the different interpreters. | |||
| @@ -226,6 +246,23 @@ services.xserver.displayManager.defaultSession = "xfce+icewm"; | |||
| upstream issue</link> for more information. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <literal>roundcube</literal> module has been hardened. | |||
| <itemizedlist> | |||
| <listitem> | |||
| <para> | |||
| The password of the database is not written world readable in the store any more. If <literal>database.host</literal> is set to <literal>localhost</literal>, then a unix user of the same name as the database will be created and PostreSQL peer authentication will be used, removing the need for a password. Otherwise, a password is still needed and can be provided with the new option <literal>database.passwordFile</literal>, which should be set to the path of a file containing the password and readable by the user <literal>nginx</literal> only. The <literal>database.password</literal> option is insecure and deprecated. Usage of this option will print a warning. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| A random <literal>des_key</literal> is set by default in the configuration of roundcube, instead of using the hardcoded and insecure default. To ensure a clean migration, all users will be logged out when you upgrade to this release. | |||
| </para> | |||
| </listitem> | |||
| </itemizedlist> | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The packages <literal>openobex</literal> and <literal>obexftp</literal> | |||
| @@ -401,6 +438,183 @@ users.users.me = | |||
| the type to <literal>either path (submodule ...)</literal>. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <link linkend="opt-services.buildkite-agent.enable">Buildkite Agent</link> | |||
| module and corresponding packages have been updated to 3.x. | |||
| While doing so, the following options have been changed: | |||
| </para> | |||
| <itemizedlist> | |||
| <listitem> | |||
| <para> | |||
| <literal>services.buildkite-agent.meta-data</literal> has been renamed to | |||
| <link linkend="opt-services.buildkite-agent.tags">services.buildkite-agent.tags</link>, | |||
| to match upstreams naming for 3.x. | |||
| Its type has also changed - it now accepts an attrset of strings. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The<literal>services.buildkite-agent.openssh.publicKeyPath</literal> option | |||
| has been removed, as it's not necessary to deploy public keys to clone private | |||
| repositories. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| <literal>services.buildkite-agent.openssh.privateKeyPath</literal> | |||
| has been renamed to | |||
| <link linkend="opt-services.buildkite-agent.privateSshKeyPath">buildkite-agent.privateSshKeyPath</link>, | |||
| as the whole <literal>openssh</literal> now only contained that single option. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| <link linkend="opt-services.buildkite-agent.shell">services.buildkite-agent.shell</link> | |||
| has been introduced, allowing to specify a custom shell to be used. | |||
| </para> | |||
| </listitem> | |||
| </itemizedlist> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <literal>citrix_workspace_19_3_0</literal> package has been removed as | |||
| it will be EOLed within the lifespan of 20.03. For further information, | |||
| please refer to the <link xlink:href="https://www.citrix.com/de-de/support/product-lifecycle/milestones/receiver.html">support and maintenance information</link> from upstream. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <literal>gcc5</literal> and <literal>gfortran5</literal> packages have been removed. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <option>services.xserver.displayManager.auto</option> module has been removed. | |||
| It was only intended for use in internal NixOS tests, and gave the false impression | |||
| of it being a special display manager when it's actually LightDM. | |||
| Please use the <xref linkend="opt-services.xserver.displayManager.lightdm.autoLogin"/> options instead, | |||
| or any other display manager in NixOS as they all support auto-login. If you used this module specifically | |||
| because it permitted root auto-login you can override the lightdm-autologin pam module like: | |||
| <programlisting> | |||
| <link xlink:href="#opt-security.pam.services._name__.text">security.pam.services.lightdm-autologin.text</link> = lib.mkForce '' | |||
| auth requisite pam_nologin.so | |||
| auth required pam_succeed_if.so quiet | |||
| auth required pam_permit.so | |||
| account include lightdm | |||
| password include lightdm | |||
| session include lightdm | |||
| ''; | |||
| </programlisting> | |||
| The difference is the: | |||
| <programlisting> | |||
| auth required pam_succeed_if.so quiet | |||
| </programlisting> | |||
| line, where default it's: | |||
| <programlisting> | |||
| auth required pam_succeed_if.so uid >= 1000 quiet | |||
| </programlisting> | |||
| not permitting users with uid's below 1000 (like root). | |||
| All other display managers in NixOS are configured like this. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| There have been lots of improvements to the Mailman module. As | |||
| a result, | |||
| </para> | |||
| <itemizedlist> | |||
| <listitem> | |||
| <para> | |||
| The <option>services.mailman.hyperkittyBaseUrl</option> | |||
| option has been renamed to <xref | |||
| linkend="opt-services.mailman.hyperkitty.baseUrl"/>. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <option>services.mailman.hyperkittyApiKey</option> | |||
| option has been removed. This is because having an option | |||
| for the Hyperkitty API key meant that the API key would be | |||
| stored in the world-readable Nix store, which was a | |||
| security vulnerability. A new Hyperkitty API key will be | |||
| generated the first time the new Hyperkitty service is run, | |||
| and it will then be persisted outside of the Nix store. To | |||
| continue using Hyperkitty, you must set <xref | |||
| linkend="opt-services.mailman.hyperkitty.enable"/> to | |||
| <literal>true</literal>. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| Additionally, some Postfix configuration must now be set | |||
| manually instead of automatically by the Mailman module: | |||
| <programlisting> | |||
| <xref linkend="opt-services.postfix.relayDomains"/> = [ "hash:/var/lib/mailman/data/postfix_domains" ]; | |||
| <xref linkend="opt-services.postfix.config"/>.transport_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ]; | |||
| <xref linkend="opt-services.postfix.config"/>.local_recipient_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ]; | |||
| </programlisting> | |||
| This is because some users may want to include other values | |||
| in these lists as well, and this was not possible if they | |||
| were set automatically by the Mailman module. It would not | |||
| have been possible to just concatenate values from multiple | |||
| modules each setting the values they needed, because the | |||
| order of elements in the list is significant. | |||
| </para> | |||
| </listitem> | |||
| </itemizedlist> | |||
| </listitem> | |||
| <listitem> | |||
| <para>The LLVM versions 3.5, 3.9 and 4 (including the corresponding CLang versions) have been dropped.</para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <option>networking.interfaces.*.preferTempAddress</option> option has | |||
| been replaced by <option>networking.interfaces.*.tempAddress</option>. | |||
| The new option allows better control of the IPv6 temporary addresses, | |||
| including completely disabling them for interfaces where they are not | |||
| needed. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| Rspamd was updated to version 2.2. Read | |||
| <link xlink:href="https://rspamd.com/doc/migration.html#migration-to-rspamd-20"> | |||
| the upstream migration notes</link> carefully. Please be especially | |||
| aware that some modules were removed and the default Bayes backend is | |||
| now Redis. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <literal>*psu</literal> versions of <package>oraclejdk8</package> have been removed | |||
| as they aren't provided by upstream anymore. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| The <option>services.dnscrypt-proxy</option> module has been removed | |||
| as it used the deprecated version of dnscrypt-proxy. We've added | |||
| <xref linkend="opt-services.dnscrypt-proxy2.enable"/> to use the supported version. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| <literal>qesteidutil</literal> has been deprecated in favor of <literal>qdigidoc</literal>. | |||
| </para> | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| <package>sqldeveloper_18</package> has been removed as it's not maintained anymore, | |||
| <package>sqldeveloper</package> has been updated to version <literal>19.4</literal>. | |||
| Please note that this means that this means that the <package>oraclejdk</package> is now | |||
| required. For further information please read the | |||
| <link xlink:href="https://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-relnotes-194-5908846.html">release notes</link>. | |||
| </para> | |||
| </listitem> | |||
| </itemizedlist> | |||
| </section> | |||
| @@ -452,9 +666,14 @@ users.users.me = | |||
| As well as this, the options <literal>security.acme.acceptTerms</literal> and either | |||
| <literal>security.acme.email</literal> or <literal>security.acme.certs.<name>.email</literal> | |||
| must be set in order to use the ACME module. | |||
| Certificates will be regenerated from new on the next renewal date. The credentials for simp-le are | |||
| Certificates will be regenerated anew on the next renewal date. The credentials for simp-le are | |||
| preserved and thus it is possible to roll back to previous versions without breaking certificate | |||
| generation. | |||
| </listitem> | |||
| <listitem> | |||
| <para> | |||
| It is now possible to unlock LUKS-Encrypted file systems using a FIDO2 token | |||
| via <option>boot.initrd.luks.fido2Support</option>. | |||
| </para> | |||
| </listitem> | |||
| </itemizedlist> | |||
| @@ -17,9 +17,9 @@ in | |||
| else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'"; | |||
| qemuBinary = qemuPkg: { | |||
| x86_64-linux = "${qemuPkg}/bin/qemu-kvm -cpu kvm64"; | |||
| x86_64-linux = "${qemuPkg}/bin/qemu-kvm -cpu host"; | |||
| armv7l-linux = "${qemuPkg}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host"; | |||
| aarch64-linux = "${qemuPkg}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host"; | |||
| x86_64-darwin = "${qemuPkg}/bin/qemu-kvm -cpu kvm64"; | |||
| x86_64-darwin = "${qemuPkg}/bin/qemu-kvm -cpu host"; | |||
| }.${pkgs.stdenv.hostPlatform.system} or "${qemuPkg}/bin/qemu-kvm"; | |||
| } | |||
| @@ -1,13 +1,17 @@ | |||
| #! /somewhere/python3 | |||
| from contextlib import contextmanager, _GeneratorContextManager | |||
| from queue import Queue, Empty | |||
| from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List | |||
| from xml.sax.saxutils import XMLGenerator | |||
| import _thread | |||
| import atexit | |||
| import base64 | |||
| import os | |||
| import pathlib | |||
| import ptpython.repl | |||
| import pty | |||
| from queue import Queue, Empty | |||
| import re | |||
| import shlex | |||
| import shutil | |||
| import socket | |||
| import subprocess | |||
| @@ -15,9 +19,6 @@ import sys | |||
| import tempfile | |||
| import time | |||
| import unicodedata | |||
| from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List | |||
| import shlex | |||
| import pathlib | |||
| CHAR_TO_KEY = { | |||
| "A": "shift-a", | |||
| @@ -84,7 +85,7 @@ CHAR_TO_KEY = { | |||
| # Forward references | |||
| nr_tests: int | |||
| nr_succeeded: int | |||
| failed_tests: list | |||
| log: "Logger" | |||
| machines: "List[Machine]" | |||
| @@ -221,7 +222,7 @@ class Machine: | |||
| return path | |||
| self.state_dir = create_dir("vm-state-{}".format(self.name)) | |||
| self.shared_dir = create_dir("{}/xchg".format(self.state_dir)) | |||
| self.shared_dir = create_dir("shared-xchg") | |||
| self.booted = False | |||
| self.connected = False | |||
| @@ -395,7 +396,7 @@ class Machine: | |||
| status_code_pattern = re.compile(r"(.*)\|\!EOF\s+(\d+)") | |||
| while True: | |||
| chunk = self.shell.recv(4096).decode() | |||
| chunk = self.shell.recv(4096).decode(errors="ignore") | |||
| match = status_code_pattern.match(chunk) | |||
| if match: | |||
| output += match[1] | |||
| @@ -566,6 +567,41 @@ class Machine: | |||
| if ret.returncode != 0: | |||
| raise Exception("Cannot convert screenshot") | |||
| def copy_from_host_via_shell(self, source: str, target: str) -> None: | |||
| """Copy a file from the host into the guest by piping it over the | |||
| shell into the destination file. Works without host-guest shared folder. | |||
| Prefer copy_from_host for whenever possible. | |||
| """ | |||
| with open(source, "rb") as fh: | |||
| content_b64 = base64.b64encode(fh.read()).decode() | |||
| self.succeed( | |||
| f"mkdir -p $(dirname {target})", | |||
| f"echo -n {content_b64} | base64 -d > {target}", | |||
| ) | |||
| def copy_from_host(self, source: str, target: str) -> None: | |||
| """Copy a file from the host into the guest via the `shared_dir` shared | |||
| among all the VMs (using a temporary directory). | |||
| """ | |||
| host_src = pathlib.Path(source) | |||
| vm_target = pathlib.Path(target) | |||
| with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td: | |||
| shared_temp = pathlib.Path(shared_td) | |||
| host_intermediate = shared_temp / host_src.name | |||
| vm_shared_temp = pathlib.Path("/tmp/shared") / shared_temp.name | |||
| vm_intermediate = vm_shared_temp / host_src.name | |||
| self.succeed(make_command(["mkdir", "-p", vm_shared_temp])) | |||
| if host_src.is_dir(): | |||
| shutil.copytree(host_src, host_intermediate) | |||
| else: | |||
| shutil.copy(host_src, host_intermediate) | |||
| self.succeed("sync") | |||
| self.succeed(make_command(["mkdir", "-p", vm_target.parent])) | |||
| self.succeed(make_command(["cp", "-r", vm_intermediate, vm_target])) | |||
| # Make sure the cleanup is synced into VM | |||
| self.succeed("sync") | |||
| def copy_from_vm(self, source: str, target_dir: str = "") -> None: | |||
| """Copy a file from the VM (specified by an in-VM source path) to a path | |||
| relative to `$out`. The file is copied via the `shared_dir` shared among | |||
| @@ -576,7 +612,7 @@ class Machine: | |||
| vm_src = pathlib.Path(source) | |||
| with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td: | |||
| shared_temp = pathlib.Path(shared_td) | |||
| vm_shared_temp = pathlib.Path("/tmp/xchg") / shared_temp.name | |||
| vm_shared_temp = pathlib.Path("/tmp/shared") / shared_temp.name | |||
| vm_intermediate = vm_shared_temp / vm_src.name | |||
| intermediate = shared_temp / vm_src.name | |||
| # Copy the file to the shared directory inside VM | |||
| @@ -704,7 +740,8 @@ class Machine: | |||
| def process_serial_output() -> None: | |||
| for _line in self.process.stdout: | |||
| line = _line.decode("unicode_escape").replace("\r", "").rstrip() | |||
| # Ignore undecodable bytes that may occur in boot menus | |||
| line = _line.decode(errors="ignore").replace("\r", "").rstrip() | |||
| eprint("{} # {}".format(self.name, line)) | |||
| self.logger.enqueue({"msg": line, "machine": self.name}) | |||
| @@ -841,23 +878,31 @@ def run_tests() -> None: | |||
| machine.execute("sync") | |||
| if nr_tests != 0: | |||
| nr_succeeded = nr_tests - len(failed_tests) | |||
| eprint("{} out of {} tests succeeded".format(nr_succeeded, nr_tests)) | |||
| if nr_tests > nr_succeeded: | |||
| if len(failed_tests) > 0: | |||
| eprint( | |||
| "The following tests have failed:\n - {}".format( | |||
| "\n - ".join(failed_tests) | |||
| ) | |||
| ) | |||
| sys.exit(1) | |||
| @contextmanager | |||
| def subtest(name: str) -> Iterator[None]: | |||
| global nr_tests | |||
| global nr_succeeded | |||
| global failed_tests | |||
| with log.nested(name): | |||
| nr_tests += 1 | |||
| try: | |||
| yield | |||
| nr_succeeded += 1 | |||
| return True | |||
| except Exception as e: | |||
| failed_tests.append( | |||
| 'Test "{}" failed with error: "{}"'.format(name, str(e)) | |||
| ) | |||
| log.log("error: {}".format(str(e))) | |||
| return False | |||
| @@ -879,7 +924,7 @@ if __name__ == "__main__": | |||
| exec("\n".join(machine_eval)) | |||
| nr_tests = 0 | |||
| nr_succeeded = 0 | |||
| failed_tests = [] | |||
| @atexit.register | |||
| def clean_up() -> None: | |||
| @@ -155,7 +155,7 @@ in rec { | |||
| --add-flags "''${vms[*]}" \ | |||
| ${lib.optionalString enableOCR | |||
| "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \ | |||
| --run "export testScript=\"\$(cat $out/test-script)\"" \ | |||
| --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \ | |||
| --set VLANS '${toString vlans}' | |||
| ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms | |||
| wrapProgram $out/bin/nixos-run-vms \ | |||
| @@ -4,7 +4,7 @@ stdenv.mkDerivation rec { | |||
| name = "jquery-ui-1.11.4"; | |||
| src = fetchurl { | |||
| url = "http://jqueryui.com/resources/download/${name}.zip"; | |||
| url = "https://jqueryui.com/resources/download/${name}.zip"; | |||
| sha256 = "0ciyaj1acg08g8hpzqx6whayq206fvf4whksz2pjgxlv207lqgjh"; | |||
| }; | |||
| @@ -17,7 +17,7 @@ stdenv.mkDerivation rec { | |||
| ''; | |||
| meta = { | |||
| homepage = http://jqueryui.com/; | |||
| homepage = https://jqueryui.com/; | |||
| description = "A library of JavaScript widgets and effects"; | |||
| platforms = stdenv.lib.platforms.all; | |||
| }; | |||
| @@ -28,8 +28,6 @@ let | |||
| }; | |||
| nslcdConfig = writeText "nslcd.conf" '' | |||
| uid nslcd | |||
| gid nslcd | |||
| uri ${cfg.server} | |||
| base ${cfg.base} | |||
| timelimit ${toString cfg.timeLimit} | |||
| @@ -282,6 +280,7 @@ in | |||
| Group = "nslcd"; | |||
| RuntimeDirectory = [ "nslcd" ]; | |||
| PIDFile = "/run/nslcd/nslcd.pid"; | |||
| AmbientCapabilities = "CAP_SYS_RESOURCE"; | |||
| }; | |||
| }; | |||
| @@ -248,6 +248,9 @@ in { | |||
| security.rtkit.enable = true; | |||
| systemd.packages = [ overriddenPackage ]; | |||
| # PulseAudio is packaged with udev rules to handle various audio device quirks | |||
| services.udev.packages = [ overriddenPackage ]; | |||
| }) | |||
| (mkIf (cfg.extraModules != []) { | |||
| @@ -38,6 +38,7 @@ in | |||
| (mkRenamedOptionModule [ "networking" "dnsExtensionMechanism" ] [ "networking" "resolvconf" "dnsExtensionMechanism" ]) | |||
| (mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ]) | |||
| (mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ]) | |||
| (mkRemovedOptionModule [ "networking" "resolvconf" "useHostResolvConf" ] "This option was never used for anything anyways") | |||
| ]; | |||
| options = { | |||
| @@ -53,15 +54,6 @@ in | |||
| ''; | |||
| }; | |||
| useHostResolvConf = mkOption { | |||
| type = types.bool; | |||
| default = false; | |||
| description = '' | |||
| In containers, whether to use the | |||
| <filename>resolv.conf</filename> supplied by the host. | |||
| ''; | |||
| }; | |||
| dnsSingleRequest = lib.mkOption { | |||
| type = types.bool; | |||
| default = false; | |||
| @@ -43,11 +43,11 @@ in | |||
| description = '' | |||
| Whether to enable OpenGL drivers. This is needed to enable | |||
| OpenGL support in X11 systems, as well as for Wayland compositors | |||
| like sway, way-cooler and Weston. It is enabled by default | |||
| like sway and Weston. It is enabled by default | |||
| by the corresponding modules, so you do not usually have to | |||
| set it yourself, only if there is no module for your wayland | |||
| compositor of choice. See services.xserver.enable, | |||
| programs.sway.enable, and programs.way-cooler.enable. | |||
| compositor of choice. See services.xserver.enable and | |||
| programs.sway.enable. | |||
| ''; | |||
| type = types.bool; | |||
| default = false; | |||
| @@ -49,7 +49,7 @@ in | |||
| { | |||
| options = { | |||
| hardware.openrazer = { | |||
| enable = mkEnableOption "OpenRazer drivers and userspace daemon."; | |||
| enable = mkEnableOption "OpenRazer drivers and userspace daemon"; | |||
| verboseLogging = mkOption { | |||
| type = types.bool; | |||
| @@ -0,0 +1,35 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.hardware.tuxedo-keyboard; | |||
| tuxedo-keyboard = config.boot.kernelPackages.tuxedo-keyboard; | |||
| in | |||
| { | |||
| options.hardware.tuxedo-keyboard = { | |||
| enable = mkEnableOption '' | |||
| Enables the tuxedo-keyboard driver. | |||
| To configure the driver, pass the options to the <option>boot.kernelParams</option> configuration. | |||
| There are several parameters you can change. It's best to check at the source code description which options are supported. | |||
| You can find all the supported parameters at: <link xlink:href="https://github.com/tuxedocomputers/tuxedo-keyboard#kernelparam" /> | |||
| In order to use the <literal>custom</literal> lighting with the maximumg brightness and a color of <literal>0xff0a0a</literal> one would put pass <option>boot.kernelParams</option> like this: | |||
| <programlisting> | |||
| boot.kernelParams = [ | |||
| "tuxedo_keyboard.mode=0" | |||
| "tuxedo_keyboard.brightness=255" | |||
| "tuxedo_keyboard.color_left=0xff0a0a" | |||
| ]; | |||
| </programlisting> | |||
| ''; | |||
| }; | |||
| config = mkIf cfg.enable | |||
| { | |||
| boot.kernelModules = ["tuxedo_keyboard"]; | |||
| boot.extraModulePackages = [ tuxedo-keyboard ]; | |||
| }; | |||
| } | |||
| @@ -21,6 +21,19 @@ with lib; | |||
| ###### implementation | |||
| config = mkIf config.hardware.usbWwan.enable { | |||
| # Attaches device specific handlers. | |||
| services.udev.packages = with pkgs; [ usb-modeswitch-data ]; | |||
| # Triggered by udev, usb-modeswitch creates systemd services via a | |||
| # template unit in the usb-modeswitch package. | |||
| systemd.packages = with pkgs; [ usb-modeswitch ]; | |||
| # The systemd service requires the usb-modeswitch-data. The | |||
| # usb-modeswitch package intends to discover this via the | |||
| # filesystem at /usr/share/usb_modeswitch, and merge it with user | |||
| # configuration in /etc/usb_modeswitch.d. Configuring the correct | |||
| # path in the package is difficult, as it would cause a cyclic | |||
| # dependency. | |||
| environment.etc."usb_modeswitch.d".source = "${pkgs.usb-modeswitch-data}/share/usb_modeswitch"; | |||
| }; | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| { pkgs, ... }: | |||
| { | |||
| imports = [ ./installation-cd-graphical-kde.nix ]; | |||
| imports = [ ./installation-cd-graphical-plasma5.nix ]; | |||
| boot.kernelPackages = pkgs.linuxPackages_latest; | |||
| } | |||
| @@ -569,14 +569,18 @@ in | |||
| }; | |||
| fileSystems."/nix/store" = | |||
| { fsType = "unionfs-fuse"; | |||
| device = "unionfs"; | |||
| options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; | |||
| { fsType = "overlay"; | |||
| device = "overlay"; | |||
| options = [ | |||
| "lowerdir=/nix/.ro-store" | |||
| "upperdir=/nix/.rw-store/store" | |||
| "workdir=/nix/.rw-store/work" | |||
| ]; | |||
| }; | |||
| boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" ]; | |||
| boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" "overlay" ]; | |||
| boot.initrd.kernelModules = [ "loop" ]; | |||
| boot.initrd.kernelModules = [ "loop" "overlay" ]; | |||
| # Closures to be copied to the Nix store on the CD, namely the init | |||
| # script and the top-level system configuration directory. | |||
| @@ -50,14 +50,18 @@ with lib; | |||
| }; | |||
| fileSystems."/nix/store" = | |||
| { fsType = "unionfs-fuse"; | |||
| device = "unionfs"; | |||
| options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; | |||
| { fsType = "overlay"; | |||
| device = "overlay"; | |||
| options = [ | |||
| "lowerdir=/nix/.ro-store" | |||
| "upperdir=/nix/.rw-store/store" | |||
| "workdir=/nix/.rw-store/work" | |||
| ]; | |||
| }; | |||
| boot.initrd.availableKernelModules = [ "squashfs" ]; | |||
| boot.initrd.availableKernelModules = [ "squashfs" "overlay" ]; | |||
| boot.initrd.kernelModules = [ "loop" ]; | |||
| boot.initrd.kernelModules = [ "loop" "overlay" ]; | |||
| # Closures to be copied to the Nix store, namely the init | |||
| # script and the top-level system configuration directory. | |||
| @@ -131,12 +131,12 @@ bool isOption(Context & ctx, const Value & v) | |||
| if (v.type != tAttrs) { | |||
| return false; | |||
| } | |||
| const auto & atualType = v.attrs->find(ctx.underscoreType); | |||
| if (atualType == v.attrs->end()) { | |||
| const auto & actualType = v.attrs->find(ctx.underscoreType); | |||
| if (actualType == v.attrs->end()) { | |||
| return false; | |||
| } | |||
| try { | |||
| Value evaluatedType = evaluateValue(ctx, *atualType->value); | |||
| Value evaluatedType = evaluateValue(ctx, *actualType->value); | |||
| if (evaluatedType.type != tString) { | |||
| return false; | |||
| } | |||
| @@ -197,9 +197,107 @@ void recurse(const std::function<bool(const std::string & path, std::variant<Val | |||
| } | |||
| } | |||
| // Calls f on all the option names | |||
| void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, Value root) | |||
| 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") || optionTypeIs(ctx, v, "loaOf"); | |||
| } | |||
| 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)); | |||
| @@ -208,7 +306,7 @@ void mapOptions(const std::function<void(const std::string & path)> & f, Context | |||
| } | |||
| return !isOpt; | |||
| }, | |||
| ctx, root, ""); | |||
| ctx, root.option, root.path); | |||
| } | |||
| // Calls f on all the config values inside one option. | |||
| @@ -294,9 +392,11 @@ 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; | |||
| attrsOut << name << " = "; | |||
| printValue(ctx, attrsOut, *a->value, appendPath(path, name)); | |||
| attrsOut << ";" << Out::sep; | |||
| if (!forbiddenRecursionName(name)) { | |||
| attrsOut << name << " = "; | |||
| printValue(ctx, attrsOut, *a->value, appendPath(path, name)); | |||
| attrsOut << ";" << Out::sep; | |||
| } | |||
| } | |||
| } | |||
| @@ -380,17 +480,26 @@ void printConfigValue(Context & ctx, Out & out, const std::string & path, std::v | |||
| out << ";\n"; | |||
| } | |||
| void printAll(Context & ctx, Out & out) | |||
| // 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](const std::string & optionPath) { | |||
| [&ctx, &out, &path](const std::string & optionPath) { | |||
| mapConfigValuesInOption( | |||
| [&ctx, &out](const std::string & configPath, std::variant<Value, std::exception_ptr> v) { | |||
| printConfigValue(ctx, out, configPath, v); | |||
| [&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, ctx.optionsRoot); | |||
| ctx, path); | |||
| } | |||
| void printAttr(Context & ctx, Out & out, const std::string & path, Value & root) | |||
| @@ -450,95 +559,17 @@ void printListing(Out & out, Value & v) | |||
| } | |||
| } | |||
| 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") || optionTypeIs(ctx, v, "loaOf"); | |||
| } | |||
| 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. | |||
| Value findAlongOptionPath(Context & ctx, const std::string & path) | |||
| { | |||
| Strings tokens = parseAttrPath(path); | |||
| Value v = ctx.optionsRoot; | |||
| 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) && !lastAttribute) { | |||
| v = getSubOptions(ctx, v); | |||
| // 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; | |||
| } | |||
| } catch (OptionPathError & e) { | |||
| throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg()); | |||
| } | |||
| } | |||
| return v; | |||
| } | |||
| void printOne(Context & ctx, Out & out, const std::string & path) | |||
| { | |||
| try { | |||
| Value option = findAlongOptionPath(ctx, path); | |||
| 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, path, option); | |||
| printOption(ctx, out, result.path, option); | |||
| } else { | |||
| printListing(out, option); | |||
| } | |||
| @@ -552,7 +583,7 @@ void printOne(Context & ctx, Out & out, const std::string & path) | |||
| int main(int argc, char ** argv) | |||
| { | |||
| bool all = false; | |||
| bool recursive = false; | |||
| std::string path = "."; | |||
| std::string optionsExpr = "(import <nixpkgs/nixos> {}).options"; | |||
| std::string configExpr = "(import <nixpkgs/nixos> {}).config"; | |||
| @@ -568,8 +599,8 @@ int main(int argc, char ** argv) | |||
| nix::showManPage("nixos-option"); | |||
| } else if (*arg == "--version") { | |||
| nix::printVersion("nixos-option"); | |||
| } else if (*arg == "--all") { | |||
| all = true; | |||
| } else if (*arg == "-r" || *arg == "--recursive") { | |||
| recursive = true; | |||
| } else if (*arg == "--path") { | |||
| path = nix::getArg(*arg, arg, end); | |||
| } else if (*arg == "--options_expr") { | |||
| @@ -598,18 +629,12 @@ int main(int argc, char ** argv) | |||
| Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot}; | |||
| Out out(std::cout); | |||
| if (all) { | |||
| if (!args.empty()) { | |||
| throw UsageError("--all cannot be used with arguments"); | |||
| } | |||
| printAll(ctx, out); | |||
| } else { | |||
| if (args.empty()) { | |||
| printOne(ctx, out, ""); | |||
| } | |||
| for (const auto & arg : args) { | |||
| printOne(ctx, out, arg); | |||
| } | |||
| auto print = recursive ? printRecursive : printOne; | |||
| if (args.empty()) { | |||
| print(ctx, out, ""); | |||
| } | |||
| for (const auto & arg : args) { | |||
| print(ctx, out, arg); | |||
| } | |||
| ctx.state.printStats(); | |||
| @@ -22,7 +22,7 @@ repair= | |||
| profile=/nix/var/nix/profiles/system | |||
| buildHost= | |||
| targetHost= | |||
| maybeSudo= | |||
| maybeSudo=() | |||
| while [ "$#" -gt 0 ]; do | |||
| i="$1"; shift 1 | |||
| @@ -91,9 +91,7 @@ while [ "$#" -gt 0 ]; do | |||
| shift 1 | |||
| ;; | |||
| --use-remote-sudo) | |||
| # note the trailing space | |||
| maybeSudo="sudo " | |||
| shift 1 | |||
| maybeSudo=(sudo --) | |||
| ;; | |||
| *) | |||
| echo "$0: unknown option \`$i'" | |||
| @@ -102,6 +100,10 @@ while [ "$#" -gt 0 ]; do | |||
| esac | |||
| done | |||
| if [ -n "$SUDO_USER" ]; then | |||
| maybeSudo=(sudo --) | |||
| fi | |||
| if [ -z "$buildHost" -a -n "$targetHost" ]; then | |||
| buildHost="$targetHost" | |||
| fi | |||
| @@ -116,17 +118,17 @@ buildHostCmd() { | |||
| if [ -z "$buildHost" ]; then | |||
| "$@" | |||
| elif [ -n "$remoteNix" ]; then | |||
| ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "$maybeSudo$@" | |||
| ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "${maybeSudo[@]}" "$@" | |||
| else | |||
| ssh $SSHOPTS "$buildHost" "$maybeSudo$@" | |||
| ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" "$@" | |||
| fi | |||
| } | |||
| targetHostCmd() { | |||
| if [ -z "$targetHost" ]; then | |||
| "$@" | |||
| "${maybeSudo[@]}" "$@" | |||
| else | |||
| ssh $SSHOPTS "$targetHost" "$maybeSudo$@" | |||
| ssh $SSHOPTS "$targetHost" "${maybeSudo[@]}" "$@" | |||
| fi | |||
| } | |||
| @@ -299,7 +299,7 @@ in | |||
| couchpotato = 267; | |||
| gogs = 268; | |||
| pdns-recursor = 269; | |||
| kresd = 270; | |||
| #kresd = 270; # switched to "knot-resolver" with dynamic ID | |||
| rpc = 271; | |||
| geoip = 272; | |||
| fcron = 273; | |||
| @@ -600,7 +600,7 @@ in | |||
| headphones = 266; | |||
| couchpotato = 267; | |||
| gogs = 268; | |||
| kresd = 270; | |||
| #kresd = 270; # switched to "knot-resolver" with dynamic ID | |||
| #rpc = 271; # unused | |||
| #geoip = 272; # unused | |||
| fcron = 273; | |||
| @@ -131,13 +131,6 @@ in { | |||
| ++ optional (isFindutils && cfg.pruneNames != []) "findutils locate does not support pruning by directory component" | |||
| ++ optional (isFindutils && cfg.pruneBindMounts) "findutils locate does not support skipping bind mounts"; | |||
| # directory creation needs to be separated from main service | |||
| # because ReadWritePaths fails when the directory doesn't already exist | |||
| systemd.tmpfiles.rules = | |||
| let dir = dirOf cfg.output; in | |||
| mkIf (dir != "/var/cache") | |||
| [ "d ${dir} 0755 root root -" ]; | |||
| systemd.services.update-locatedb = | |||
| { description = "Update Locate Database"; | |||
| path = mkIf (!isMLocate) [ pkgs.su ]; | |||
| @@ -6,6 +6,7 @@ let | |||
| cfg = config.system.nixos; | |||
| gitRepo = "${toString pkgs.path}/.git"; | |||
| gitRepoValid = lib.pathIsGitRepo gitRepo; | |||
| gitCommitId = lib.substring 0 7 (commitIdFromGitRepo gitRepo); | |||
| in | |||
| @@ -91,8 +92,8 @@ in | |||
| # These defaults are set here rather than up there so that | |||
| # changing them would not rebuild the manual | |||
| version = mkDefault (cfg.release + cfg.versionSuffix); | |||
| revision = mkIf (pathIsDirectory gitRepo) (mkDefault gitCommitId); | |||
| versionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId)); | |||
| revision = mkIf gitRepoValid (mkDefault gitCommitId); | |||
| versionSuffix = mkIf gitRepoValid (mkDefault (".git." + gitCommitId)); | |||
| }; | |||
| # Generate /etc/os-release. See | |||
| @@ -62,6 +62,7 @@ | |||
| ./hardware/printers.nix | |||
| ./hardware/raid/hpsa.nix | |||
| ./hardware/steam-hardware.nix | |||
| ./hardware/tuxedo-keyboard.nix | |||
| ./hardware/usb-wwan.nix | |||
| ./hardware/onlykey.nix | |||
| ./hardware/video/amdgpu.nix | |||
| @@ -97,6 +98,7 @@ | |||
| ./programs/autojump.nix | |||
| ./programs/bandwhich.nix | |||
| ./programs/bash/bash.nix | |||
| ./programs/bash-my-aws.nix | |||
| ./programs/bcc.nix | |||
| ./programs/browserpass.nix | |||
| ./programs/captive-browser.nix | |||
| @@ -116,6 +118,7 @@ | |||
| ./programs/fish.nix | |||
| ./programs/freetds.nix | |||
| ./programs/fuse.nix | |||
| ./programs/geary.nix | |||
| ./programs/gnome-disks.nix | |||
| ./programs/gnome-documents.nix | |||
| ./programs/gnome-terminal.nix | |||
| @@ -127,6 +130,7 @@ | |||
| ./programs/java.nix | |||
| ./programs/kbdlight.nix | |||
| ./programs/less.nix | |||
| ./programs/liboping.nix | |||
| ./programs/light.nix | |||
| ./programs/mosh.nix | |||
| ./programs/mininet.nix | |||
| @@ -152,13 +156,13 @@ | |||
| ./programs/system-config-printer.nix | |||
| ./programs/thefuck.nix | |||
| ./programs/tmux.nix | |||
| ./programs/traceroute.nix | |||
| ./programs/tsm-client.nix | |||
| ./programs/udevil.nix | |||
| ./programs/usbtop.nix | |||
| ./programs/venus.nix | |||
| ./programs/vim.nix | |||
| ./programs/wavemon.nix | |||
| ./programs/way-cooler.nix | |||
| ./programs/waybar.nix | |||
| ./programs/wireshark.nix | |||
| ./programs/x2goserver.nix | |||
| @@ -278,6 +282,7 @@ | |||
| ./services/databases/riak.nix | |||
| ./services/databases/riak-cs.nix | |||
| ./services/databases/stanchion.nix | |||
| ./services/databases/victoriametrics.nix | |||
| ./services/databases/virtuoso.nix | |||
| ./services/desktops/accountsservice.nix | |||
| ./services/desktops/bamf.nix | |||
| @@ -424,6 +429,7 @@ | |||
| ./services/misc/exhibitor.nix | |||
| ./services/misc/felix.nix | |||
| ./services/misc/folding-at-home.nix | |||
| ./services/misc/freeswitch.nix | |||
| ./services/misc/fstrim.nix | |||
| ./services/misc/gammu-smsd.nix | |||
| ./services/misc/geoip-updater.nix | |||
| @@ -524,6 +530,7 @@ | |||
| ./services/monitoring/prometheus/alertmanager.nix | |||
| ./services/monitoring/prometheus/exporters.nix | |||
| ./services/monitoring/prometheus/pushgateway.nix | |||
| ./services/monitoring/prometheus/xmpp-alerts.nix | |||
| ./services/monitoring/riemann.nix | |||
| ./services/monitoring/riemann-dash.nix | |||
| ./services/monitoring/riemann-tools.nix | |||
| @@ -577,6 +584,7 @@ | |||
| ./services/networking/connman.nix | |||
| ./services/networking/consul.nix | |||
| ./services/networking/coredns.nix | |||
| ./services/networking/corerad.nix | |||
| ./services/networking/coturn.nix | |||
| ./services/networking/dante.nix | |||
| ./services/networking/ddclient.nix | |||
| @@ -584,7 +592,7 @@ | |||
| ./services/networking/dhcpd.nix | |||
| ./services/networking/dnscache.nix | |||
| ./services/networking/dnschain.nix | |||
| ./services/networking/dnscrypt-proxy.nix | |||
| ./services/networking/dnscrypt-proxy2.nix | |||
| ./services/networking/dnscrypt-wrapper.nix | |||
| ./services/networking/dnsdist.nix | |||
| ./services/networking/dnsmasq.nix | |||
| @@ -735,6 +743,7 @@ | |||
| ./services/networking/wicd.nix | |||
| ./services/networking/wireguard.nix | |||
| ./services/networking/wpa_supplicant.nix | |||
| ./services/networking/xandikos.nix | |||
| ./services/networking/xinetd.nix | |||
| ./services/networking/xl2tpd.nix | |||
| ./services/networking/xrdp.nix | |||
| @@ -802,6 +811,7 @@ | |||
| ./services/web-apps/codimd.nix | |||
| ./services/web-apps/cryptpad.nix | |||
| ./services/web-apps/documize.nix | |||
| ./services/web-apps/dokuwiki.nix | |||
| ./services/web-apps/frab.nix | |||
| ./services/web-apps/gotify-server.nix | |||
| ./services/web-apps/icingaweb2/icingaweb2.nix | |||
| @@ -859,7 +869,6 @@ | |||
| ./services/x11/unclutter.nix | |||
| ./services/x11/unclutter-xfixes.nix | |||
| ./services/x11/desktop-managers/default.nix | |||
| ./services/x11/display-managers/auto.nix | |||
| ./services/x11/display-managers/default.nix | |||
| ./services/x11/display-managers/gdm.nix | |||
| ./services/x11/display-managers/lightdm.nix | |||
| @@ -869,7 +878,6 @@ | |||
| ./services/x11/display-managers/xpra.nix | |||
| ./services/x11/fractalart.nix | |||
| ./services/x11/hardware/libinput.nix | |||
| ./services/x11/hardware/multitouch.nix | |||
| ./services/x11/hardware/synaptics.nix | |||
| ./services/x11/hardware/wacom.nix | |||
| ./services/x11/hardware/digimend.nix | |||
| @@ -0,0 +1,25 @@ | |||
| { config, pkgs, lib, ... }: | |||
| with lib; | |||
| let | |||
| prg = config.programs; | |||
| cfg = prg.bash-my-aws; | |||
| initScript = '' | |||
| eval $(${pkgs.bash-my-aws}/bin/bma-init) | |||
| ''; | |||
| in | |||
| { | |||
| options = { | |||
| programs.bash-my-aws = { | |||
| enable = mkEnableOption "bash-my-aws"; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| environment.systemPackages = with pkgs; [ bash-my-aws ]; | |||
| programs.bash.interactiveShellInit = initScript; | |||
| }; | |||
| } | |||
| @@ -32,6 +32,10 @@ let | |||
| fi | |||
| ''; | |||
| lsColors = optionalString cfg.enableLsColors '' | |||
| eval "$(${pkgs.coreutils}/bin/dircolors -b)" | |||
| ''; | |||
| bashAliases = concatStringsSep "\n" ( | |||
| mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}") | |||
| (filterAttrs (k: v: v != null) cfg.shellAliases) | |||
| @@ -127,6 +131,14 @@ in | |||
| type = types.bool; | |||
| }; | |||
| enableLsColors = mkOption { | |||
| default = true; | |||
| description = '' | |||
| Enable extra colors in directory listings. | |||
| ''; | |||
| type = types.bool; | |||
| }; | |||
| }; | |||
| }; | |||
| @@ -156,6 +168,7 @@ in | |||
| ${cfg.promptInit} | |||
| ${bashCompletion} | |||
| ${lsColors} | |||
| ${bashAliases} | |||
| ${cfge.interactiveShellInit} | |||
| @@ -0,0 +1,20 @@ | |||
| { config, pkgs, lib, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.programs.geary; | |||
| in { | |||
| options = { | |||
| programs.geary.enable = mkEnableOption "Geary, a Mail client for GNOME 3"; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| environment.systemPackages = [ pkgs.gnome3.geary ]; | |||
| programs.dconf.enable = true; | |||
| services.gnome3.gnome-keyring.enable = true; | |||
| services.gnome3.gnome-online-accounts.enable = true; | |||
| }; | |||
| } | |||
| @@ -96,7 +96,7 @@ in | |||
| # This overrides the systemd user unit shipped with the gnupg package | |||
| systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) { | |||
| serviceConfig.ExecStart = [ "" '' | |||
| ${pkgs.gnupg}/bin/gpg-agent --supervised \ | |||
| ${cfg.package}/bin/gpg-agent --supervised \ | |||
| --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry | |||
| '' ]; | |||
| }; | |||
| @@ -0,0 +1,22 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.programs.liboping; | |||
| in { | |||
| options.programs.liboping = { | |||
| enable = mkEnableOption "liboping"; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| environment.systemPackages = with pkgs; [ liboping ]; | |||
| security.wrappers = mkMerge (map ( | |||
| exec: { | |||
| "${exec}" = { | |||
| source = "${pkgs.liboping}/bin/${exec}"; | |||
| capabilities = "cap_net_raw+p"; | |||
| }; | |||
| } | |||
| ) [ "oping" "noping" ]); | |||
| }; | |||
| } | |||
| @@ -87,7 +87,8 @@ in { | |||
| type = with types; listOf package; | |||
| default = with pkgs; [ | |||
| swaylock swayidle | |||
| xwayland rxvt_unicode dmenu | |||
| xwayland alacritty dmenu | |||
| rxvt_unicode # For backward compatibility (old default terminal) | |||
| ]; | |||
| defaultText = literalExample '' | |||
| with pkgs; [ swaylock swayidle xwayland rxvt_unicode dmenu ]; | |||
| @@ -52,7 +52,7 @@ let | |||
| set -s escape-time ${toString cfg.escapeTime} | |||
| set -g history-limit ${toString cfg.historyLimit} | |||
| ${cfg.extraTmuxConf} | |||
| ${cfg.extraConfig} | |||
| ''; | |||
| in { | |||
| @@ -102,7 +102,7 @@ in { | |||
| description = "Time in milliseconds for which tmux waits after an escape is input."; | |||
| }; | |||
| extraTmuxConf = mkOption { | |||
| extraConfig = mkOption { | |||
| default = ""; | |||
| description = '' | |||
| Additional contents of /etc/tmux.conf | |||
| @@ -181,4 +181,8 @@ in { | |||
| }; | |||
| }; | |||
| }; | |||
| imports = [ | |||
| (lib.mkRenamedOptionModule [ "programs" "tmux" "extraTmuxConf" ] [ "programs" "tmux" "extraConfig" ]) | |||
| ]; | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.programs.traceroute; | |||
| in { | |||
| options = { | |||
| programs.traceroute = { | |||
| enable = mkOption { | |||
| type = types.bool; | |||
| default = false; | |||
| description = '' | |||
| Whether to configure a setcap wrapper for traceroute. | |||
| ''; | |||
| }; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| security.wrappers.traceroute = { | |||
| source = "${pkgs.traceroute}/bin/traceroute"; | |||
| capabilities = "cap_net_raw+p"; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -1,78 +0,0 @@ | |||
| { config, pkgs, lib, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.programs.way-cooler; | |||
| way-cooler = pkgs.way-cooler; | |||
| wcWrapped = pkgs.writeShellScriptBin "way-cooler" '' | |||
| ${cfg.extraSessionCommands} | |||
| exec ${pkgs.dbus}/bin/dbus-run-session ${way-cooler}/bin/way-cooler | |||
| ''; | |||
| wcJoined = pkgs.symlinkJoin { | |||
| name = "way-cooler-wrapped"; | |||
| paths = [ wcWrapped way-cooler ]; | |||
| }; | |||
| configFile = readFile "${way-cooler}/etc/way-cooler/init.lua"; | |||
| spawnBar = '' | |||
| util.program.spawn_at_startup("lemonbar"); | |||
| ''; | |||
| in | |||
| { | |||
| options.programs.way-cooler = { | |||
| enable = mkEnableOption "way-cooler"; | |||
| extraSessionCommands = mkOption { | |||
| default = ""; | |||
| type = types.lines; | |||
| example = '' | |||
| export XKB_DEFAULT_LAYOUT=us,de | |||
| export XKB_DEFAULT_VARIANT=,nodeadkeys | |||
| export XKB_DEFAULT_OPTIONS=grp:caps_toggle, | |||
| ''; | |||
| description = '' | |||
| Shell commands executed just before way-cooler is started. | |||
| ''; | |||
| }; | |||
| extraPackages = mkOption { | |||
| type = with types; listOf package; | |||
| default = with pkgs; [ | |||
| westonLite xwayland dmenu | |||
| ]; | |||
| example = literalExample '' | |||
| with pkgs; [ | |||
| westonLite xwayland dmenu | |||
| ] | |||
| ''; | |||
| description = '' | |||
| Extra packages to be installed system wide. | |||
| ''; | |||
| }; | |||
| enableBar = mkOption { | |||
| type = types.bool; | |||
| default = true; | |||
| description = '' | |||
| Whether to enable an unofficial bar. | |||
| ''; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| environment.systemPackages = [ wcJoined ] ++ cfg.extraPackages; | |||
| security.pam.services.wc-lock = {}; | |||
| environment.etc."way-cooler/init.lua".text = '' | |||
| ${configFile} | |||
| ${optionalString cfg.enableBar spawnBar} | |||
| ''; | |||
| hardware.opengl.enable = mkDefault true; | |||
| fonts.enableDefaultFonts = mkDefault true; | |||
| programs.dconf.enable = mkDefault true; | |||
| }; | |||
| meta.maintainers = with maintainers; [ gnidorah ]; | |||
| } | |||
| @@ -15,6 +15,24 @@ let | |||
| (filterAttrs (k: v: v != null) cfg.shellAliases) | |||
| ); | |||
| zshStartupNotes = '' | |||
| # Note that generated /etc/zprofile and /etc/zshrc files do a lot of | |||
| # non-standard setup to make zsh usable with no configuration by default. | |||
| # | |||
| # Which means that unless you explicitly meticulously override everything | |||
| # generated, interactions between your ~/.zshrc and these files are likely | |||
| # to be rather surprising. | |||
| # | |||
| # Note however, that you can disable loading of the generated /etc/zprofile | |||
| # and /etc/zshrc (you can't disable loading of /etc/zshenv, but it is | |||
| # designed to not set anything surprising) by setting `no_global_rcs` option | |||
| # in ~/.zshenv: | |||
| # | |||
| # echo setopt no_global_rcs >> ~/.zshenv | |||
| # | |||
| # See "STARTUP/SHUTDOWN FILES" section of zsh(1) for more info. | |||
| ''; | |||
| in | |||
| { | |||
| @@ -69,6 +87,10 @@ in | |||
| promptInit = mkOption { | |||
| default = '' | |||
| # Note that to manually override this in ~/.zshrc you should run `prompt off` | |||
| # before setting your PS1 and etc. Otherwise this will likely to interact with | |||
| # your ~/.zshrc configuration in unexpected ways as the default prompt sets | |||
| # a lot of different prompt variables. | |||
| autoload -U promptinit && promptinit && prompt walters && setopt prompt_sp | |||
| ''; | |||
| description = '' | |||
| @@ -100,7 +122,8 @@ in | |||
| ]; | |||
| example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ]; | |||
| description = '' | |||
| Configure zsh options. | |||
| Configure zsh options. See | |||
| <citerefentry><refentrytitle>zshoptions</refentrytitle><manvolnum>1</manvolnum></citerefentry>. | |||
| ''; | |||
| }; | |||
| @@ -147,6 +170,14 @@ in | |||
| . ${config.system.build.setEnvironment} | |||
| fi | |||
| HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help" | |||
| # Tell zsh how to find installed completions. | |||
| for p in ''${(z)NIX_PROFILES}; do | |||
| fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions) | |||
| done | |||
| # Setup custom shell init stuff. | |||
| ${cfge.shellInit} | |||
| ${cfg.shellInit} | |||
| @@ -161,11 +192,14 @@ in | |||
| '' | |||
| # /etc/zprofile: DO NOT EDIT -- this file has been generated automatically. | |||
| # This file is read for login shells. | |||
| # | |||
| ${zshStartupNotes} | |||
| # Only execute this file once per shell. | |||
| if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi | |||
| __ETC_ZPROFILE_SOURCED=1 | |||
| # Setup custom login shell init stuff. | |||
| ${cfge.loginShellInit} | |||
| ${cfg.loginShellInit} | |||
| @@ -180,38 +214,44 @@ in | |||
| '' | |||
| # /etc/zshrc: DO NOT EDIT -- this file has been generated automatically. | |||
| # This file is read for interactive shells. | |||
| # | |||
| ${zshStartupNotes} | |||
| # Only execute this file once per shell. | |||
| if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi | |||
| __ETC_ZSHRC_SOURCED=1 | |||
| . /etc/zinputrc | |||
| ${optionalString (cfg.setOptions != []) '' | |||
| # Set zsh options. | |||
| setopt ${concatStringsSep " " cfg.setOptions} | |||
| ''} | |||
| # Don't export these, otherwise other shells (bash) will try to use same histfile | |||
| # Setup command line history. | |||
| # Don't export these, otherwise other shells (bash) will try to use same HISTFILE. | |||
| SAVEHIST=${toString cfg.histSize} | |||
| HISTSIZE=${toString cfg.histSize} | |||
| HISTFILE=${cfg.histFile} | |||
| HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help" | |||
| # Tell zsh how to find installed completions | |||
| for p in ''${(z)NIX_PROFILES}; do | |||
| fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions) | |||
| done | |||
| # Configure sane keyboard defaults. | |||
| . /etc/zinputrc | |||
| ${optionalString cfg.enableGlobalCompInit "autoload -U compinit && compinit"} | |||
| ${optionalString cfg.enableGlobalCompInit '' | |||
| # Enable autocompletion. | |||
| autoload -U compinit && compinit | |||
| ''} | |||
| # Setup custom interactive shell init stuff. | |||
| ${cfge.interactiveShellInit} | |||
| ${cfg.interactiveShellInit} | |||
| ${optionalString (cfg.setOptions != []) "setopt ${concatStringsSep " " cfg.setOptions}"} | |||
| # Setup aliases. | |||
| ${zshAliases} | |||
| # Setup prompt. | |||
| ${cfg.promptInit} | |||
| # Need to disable features to support TRAMP | |||
| # Disable some features to support TRAMP. | |||
| if [ "$TERM" = dumb ]; then | |||
| unsetopt zle prompt_cr prompt_subst | |||
| unset RPS1 RPROMPT | |||
| @@ -27,6 +27,21 @@ with lib; | |||
| (mkRemovedOptionModule [ "services.osquery" ] "The osquery module has been removed") | |||
| (mkRemovedOptionModule [ "services.fourStore" ] "The fourStore module has been removed") | |||
| (mkRemovedOptionModule [ "services.fourStoreEndpoint" ] "The fourStoreEndpoint module has been removed") | |||
| (mkRemovedOptionModule [ "programs" "way-cooler" ] ("way-cooler is abandoned by its author: " + | |||
| "https://way-cooler.org/blog/2020/01/09/way-cooler-post-mortem.html")) | |||
| (mkRemovedOptionModule [ "services" "xserver" "multitouch" ] '' | |||
| services.xserver.multitouch (which uses xf86_input_mtrack) has been removed | |||
| as the underlying package isn't being maintained. Working alternatives are | |||
| libinput and synaptics. | |||
| '') | |||
| (mkRemovedOptionModule [ "services" "xserver" "displayManager" "auto" ] '' | |||
| The services.xserver.displayManager.auto module has been removed | |||
| because it was only intended for use in internal NixOS tests, and gave the | |||
| false impression of it being a special display manager when it's actually | |||
| LightDM. Please use the services.xserver.displayManager.lightdm.autoLogin options | |||
| instead, or any other display manager in NixOS as they all support auto-login. | |||
| '') | |||
| (mkRemovedOptionModule [ "services" "dnscrypt-proxy" ] "Use services.dnscrypt-proxy2 instead") | |||
| # Do NOT add any option renames here, see top of the file | |||
| ]; | |||
| @@ -12,7 +12,7 @@ let | |||
| ikey=${cfg.ikey} | |||
| skey=${cfg.skey} | |||
| host=${cfg.host} | |||
| ${optionalString (cfg.group != "") ("group="+cfg.group)} | |||
| ${optionalString (cfg.groups != "") ("groups="+cfg.groups)} | |||
| failmode=${cfg.failmode} | |||
| pushinfo=${boolToStr cfg.pushinfo} | |||
| autopush=${boolToStr cfg.autopush} | |||
| @@ -42,6 +42,10 @@ let | |||
| }; | |||
| in | |||
| { | |||
| imports = [ | |||
| (mkRenamedOptionModule [ "security" "duosec" "group" ] [ "security" "duosec" "groups" ]) | |||
| ]; | |||
| options = { | |||
| security.duosec = { | |||
| ssh.enable = mkOption { | |||
| @@ -71,10 +75,16 @@ in | |||
| description = "Duo API hostname."; | |||
| }; | |||
| group = mkOption { | |||
| groups = mkOption { | |||
| type = types.str; | |||
| default = ""; | |||
| description = "Use Duo authentication for users only in this group."; | |||
| example = "users,!wheel,!*admin guests"; | |||
| description = '' | |||
| If specified, Duo authentication is required only for users | |||
| whose primary group or supplementary group list matches one | |||
| of the space-separated pattern lists. Refer to | |||
| <link xlink:href="https://duo.com/docs/duounix"/> for details. | |||
| ''; | |||
| }; | |||
| failmode = mkOption { | |||
| @@ -98,8 +98,8 @@ in { | |||
| will be merged into these options by RabbitMQ at runtime to | |||
| form the final configuration. | |||
| See http://www.rabbitmq.com/configure.html#config-items | |||
| For the distinct formats, see http://www.rabbitmq.com/configure.html#config-file-formats | |||
| See https://www.rabbitmq.com/configure.html#config-items | |||
| For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats | |||
| ''; | |||
| }; | |||
| @@ -116,8 +116,8 @@ in { | |||
| The contents of this option will be merged into the <literal>configItems</literal> | |||
| by RabbitMQ at runtime to form the final configuration. | |||
| See the second table on http://www.rabbitmq.com/configure.html#config-items | |||
| For the distinct formats, see http://www.rabbitmq.com/configure.html#config-file-formats | |||
| See the second table on https://www.rabbitmq.com/configure.html#config-items | |||
| For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats | |||
| ''; | |||
| }; | |||
| @@ -165,7 +165,10 @@ in { | |||
| after = [ "network.target" "epmd.socket" ]; | |||
| wants = [ "network.target" "epmd.socket" ]; | |||
| path = [ cfg.package pkgs.procps ]; | |||
| path = [ | |||
| cfg.package | |||
| pkgs.coreutils # mkdir/chown/chmod for preStart | |||
| ]; | |||
| environment = { | |||
| RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia"; | |||
| @@ -103,6 +103,34 @@ in | |||
| Create the repository if it doesn't exist. | |||
| ''; | |||
| }; | |||
| pruneOpts = mkOption { | |||
| type = types.listOf types.str; | |||
| default = []; | |||
| description = '' | |||
| A list of options (--keep-* et al.) for 'restic forget | |||
| --prune', to automatically prune old snapshots. The | |||
| 'forget' command is run *after* the 'backup' command, so | |||
| keep that in mind when constructing the --keep-* options. | |||
| ''; | |||
| example = [ | |||
| "--keep-daily 7" | |||
| "--keep-weekly 5" | |||
| "--keep-monthly 12" | |||
| "--keep-yearly 75" | |||
| ]; | |||
| }; | |||
| dynamicFilesFrom = mkOption { | |||
| type = with types; nullOr str; | |||
| default = null; | |||
| description = '' | |||
| A script that produces a list of files to back up. The | |||
| results of this command are given to the '--files-from' | |||
| option. | |||
| ''; | |||
| example = "find /home/matt/git -type d -name .git"; | |||
| }; | |||
| }; | |||
| })); | |||
| default = {}; | |||
| @@ -134,25 +162,41 @@ in | |||
| let | |||
| extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions; | |||
| resticCmd = "${pkgs.restic}/bin/restic${extraOptions}"; | |||
| filesFromTmpFile = "/run/restic-backups-${name}/includes"; | |||
| backupPaths = if (backup.dynamicFilesFrom == null) | |||
| then concatStringsSep " " backup.paths | |||
| else "--files-from ${filesFromTmpFile}"; | |||
| pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [ | |||
| ( resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts) ) | |||
| ( resticCmd + " check" ) | |||
| ]; | |||
| in nameValuePair "restic-backups-${name}" ({ | |||
| environment = { | |||
| RESTIC_PASSWORD_FILE = backup.passwordFile; | |||
| RESTIC_REPOSITORY = backup.repository; | |||
| }; | |||
| path = with pkgs; [ | |||
| openssh | |||
| ]; | |||
| path = [ pkgs.openssh ]; | |||
| restartIfChanged = false; | |||
| serviceConfig = { | |||
| Type = "oneshot"; | |||
| ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}"; | |||
| ExecStart = [ "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${backupPaths}" ] ++ pruneCmd; | |||
| User = backup.user; | |||
| RuntimeDirectory = "restic-backups-${name}"; | |||
| } // optionalAttrs (backup.s3CredentialsFile != null) { | |||
| EnvironmentFile = backup.s3CredentialsFile; | |||
| }; | |||
| } // optionalAttrs backup.initialize { | |||
| } // optionalAttrs (backup.initialize || backup.dynamicFilesFrom != null) { | |||
| preStart = '' | |||
| ${resticCmd} snapshots || ${resticCmd} init | |||
| ${optionalString (backup.initialize) '' | |||
| ${resticCmd} snapshots || ${resticCmd} init | |||
| ''} | |||
| ${optionalString (backup.dynamicFilesFrom != null) '' | |||
| ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} > ${filesFromTmpFile} | |||
| ''} | |||
| ''; | |||
| } // optionalAttrs (backup.dynamicFilesFrom != null) { | |||
| postStart = '' | |||
| rm ${filesFromTmpFile} | |||
| ''; | |||
| }) | |||
| ) config.services.restic.backups; | |||
| @@ -20,6 +20,7 @@ let | |||
| size = 2048; | |||
| }; | |||
| CN = top.masterAddress; | |||
| hosts = cfg.cfsslAPIExtraSANs; | |||
| }); | |||
| cfsslAPITokenBaseName = "apitoken.secret"; | |||
| @@ -66,6 +67,15 @@ in | |||
| type = bool; | |||
| }; | |||
| cfsslAPIExtraSANs = mkOption { | |||
| description = '' | |||
| Extra x509 Subject Alternative Names to be added to the cfssl API webserver TLS cert. | |||
| ''; | |||
| default = []; | |||
| example = [ "subdomain.example.com" ]; | |||
| type = listOf str; | |||
| }; | |||
| genCfsslAPIToken = mkOption { | |||
| description = '' | |||
| Whether to automatically generate cfssl API-token secret, | |||
| @@ -50,8 +50,8 @@ in | |||
| }; | |||
| runtimePackages = mkOption { | |||
| default = [ pkgs.bash pkgs.nix ]; | |||
| defaultText = "[ pkgs.bash pkgs.nix ]"; | |||
| default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]; | |||
| defaultText = "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]"; | |||
| description = "Add programs to the buildkite-agent environment"; | |||
| type = types.listOf types.package; | |||
| }; | |||
| @@ -74,13 +74,12 @@ in | |||
| ''; | |||
| }; | |||
| meta-data = mkOption { | |||
| type = types.str; | |||
| default = ""; | |||
| example = "queue=default,docker=true,ruby2=true"; | |||
| tags = mkOption { | |||
| type = types.attrsOf types.str; | |||
| default = {}; | |||
| example = { queue = "default"; docker = "true"; ruby2 ="true"; }; | |||
| description = '' | |||
| Meta data for the agent. This is a comma-separated list of | |||
| <code>key=value</code> pairs. | |||
| Tags for the agent. | |||
| ''; | |||
| }; | |||
| @@ -93,26 +92,20 @@ in | |||
| ''; | |||
| }; | |||
| openssh = | |||
| { privateKeyPath = mkOption { | |||
| type = types.path; | |||
| description = '' | |||
| Private agent key. | |||
| privateSshKeyPath = mkOption { | |||
| type = types.nullOr types.path; | |||
| default = null; | |||
| ## maximum care is taken so that secrets (ssh keys and the CI token) | |||
| ## don't end up in the Nix store. | |||
| apply = final: if final == null then null else toString final; | |||
| A run-time path to the key file, which is supposed to be provisioned | |||
| outside of Nix store. | |||
| ''; | |||
| }; | |||
| publicKeyPath = mkOption { | |||
| type = types.path; | |||
| description = '' | |||
| Public agent key. | |||
| A run-time path to the key file, which is supposed to be provisioned | |||
| outside of Nix store. | |||
| ''; | |||
| }; | |||
| }; | |||
| description = '' | |||
| OpenSSH private key | |||
| A run-time path to the key file, which is supposed to be provisioned | |||
| outside of Nix store. | |||
| ''; | |||
| }; | |||
| hooks = mkHookOptions [ | |||
| { name = "checkout"; | |||
| @@ -181,18 +174,26 @@ in | |||
| instead. | |||
| ''; | |||
| }; | |||
| shell = mkOption { | |||
| type = types.str; | |||
| default = "${pkgs.bash}/bin/bash -e -c"; | |||
| description = '' | |||
| Command that buildkite-agent 3 will execute when it spawns a shell. | |||
| ''; | |||
| }; | |||
| }; | |||
| }; | |||
| config = mkIf config.services.buildkite-agent.enable { | |||
| users.users.buildkite-agent = | |||
| { name = "buildkite-agent"; | |||
| home = cfg.dataDir; | |||
| createHome = true; | |||
| description = "Buildkite agent user"; | |||
| extraGroups = [ "keys" ]; | |||
| isSystemUser = true; | |||
| }; | |||
| users.users.buildkite-agent = { | |||
| name = "buildkite-agent"; | |||
| home = cfg.dataDir; | |||
| createHome = true; | |||
| description = "Buildkite agent user"; | |||
| extraGroups = [ "keys" ]; | |||
| isSystemUser = true; | |||
| }; | |||
| environment.systemPackages = [ cfg.package ]; | |||
| @@ -210,17 +211,18 @@ in | |||
| ## don't end up in the Nix store. | |||
| preStart = let | |||
| sshDir = "${cfg.dataDir}/.ssh"; | |||
| tagStr = lib.concatStringsSep "," (lib.mapAttrsToList (name: value: "${name}=${value}") cfg.tags); | |||
| in | |||
| '' | |||
| optionalString (cfg.privateSshKeyPath != null) '' | |||
| mkdir -m 0700 -p "${sshDir}" | |||
| cp -f "${toString cfg.openssh.privateKeyPath}" "${sshDir}/id_rsa" | |||
| cp -f "${toString cfg.openssh.publicKeyPath}" "${sshDir}/id_rsa.pub" | |||
| chmod 600 "${sshDir}"/id_rsa* | |||
| cp -f "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa" | |||
| chmod 600 "${sshDir}"/id_rsa | |||
| '' + '' | |||
| cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF | |||
| token="$(cat ${toString cfg.tokenPath})" | |||
| name="${cfg.name}" | |||
| meta-data="${cfg.meta-data}" | |||
| shell="${cfg.shell}" | |||
| tags="${tagStr}" | |||
| build-path="${cfg.dataDir}/builds" | |||
| hooks-path="${cfg.hooksPath}" | |||
| ${cfg.extraConfig} | |||
| @@ -228,11 +230,14 @@ in | |||
| ''; | |||
| serviceConfig = | |||
| { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config /var/lib/buildkite-agent/buildkite-agent.cfg"; | |||
| { ExecStart = "${cfg.package}/bin/buildkite-agent start --config /var/lib/buildkite-agent/buildkite-agent.cfg"; | |||
| User = "buildkite-agent"; | |||
| RestartSec = 5; | |||
| Restart = "on-failure"; | |||
| TimeoutSec = 10; | |||
| # set a long timeout to give buildkite-agent a chance to finish current builds | |||
| TimeoutStopSec = "2 min"; | |||
| KillMode = "mixed"; | |||
| }; | |||
| }; | |||
| @@ -246,8 +251,11 @@ in | |||
| ]; | |||
| }; | |||
| imports = [ | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "token" ] [ "services" "buildkite-agent" "tokenPath" ]) | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "privateKey" ] [ "services" "buildkite-agent" "openssh" "privateKeyPath" ]) | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "publicKey" ] [ "services" "buildkite-agent" "openssh" "publicKeyPath" ]) | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "token" ] [ "services" "buildkite-agent" "tokenPath" ]) | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "privateKey" ] [ "services" "buildkite-agent" "privateSshKeyPath" ]) | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "privateKeyPath" ] [ "services" "buildkite-agent" "privateSshKeyPath" ]) | |||
| (mkRemovedOptionModule [ "services" "buildkite-agent" "openssh" "publicKey" ] "SSH public keys aren't necessary to clone private repos.") | |||
| (mkRemovedOptionModule [ "services" "buildkite-agent" "openssh" "publicKeyPath" ] "SSH public keys aren't necessary to clone private repos.") | |||
| (mkRenamedOptionModule [ "services" "buildkite-agent" "meta-data"] [ "services" "buildkite-agent" "tags" ]) | |||
| ]; | |||
| } | |||
| @@ -167,7 +167,7 @@ in | |||
| buildMachinesFiles = mkOption { | |||
| type = types.listOf types.path; | |||
| default = [ "/etc/nix/machines" ]; | |||
| default = optional (config.nix.buildMachines != []) "/etc/nix/machines"; | |||
| example = [ "/etc/nix/machines" "/var/lib/hydra/provisioner/machines" ]; | |||
| description = "List of files containing build machines."; | |||
| }; | |||
| @@ -333,7 +333,7 @@ in | |||
| IN_SYSTEMD = "1"; # to get log severity levels | |||
| }; | |||
| serviceConfig = | |||
| { ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner -v --option build-use-substitutes ${boolToString cfg.useSubstitutes}"; | |||
| { ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner -v"; | |||
| ExecStopPost = "${cfg.package}/bin/hydra-queue-runner --unlock"; | |||
| User = "hydra-queue-runner"; | |||
| Restart = "always"; | |||
| @@ -0,0 +1,70 @@ | |||
| { config, pkgs, lib, ... }: | |||
| let cfg = config.services.victoriametrics; in | |||
| { | |||
| options.services.victoriametrics = with lib; { | |||
| enable = mkEnableOption "victoriametrics"; | |||
| package = mkOption { | |||
| type = types.package; | |||
| default = pkgs.victoriametrics; | |||
| defaultText = "pkgs.victoriametrics"; | |||
| description = '' | |||
| The VictoriaMetrics distribution to use. | |||
| ''; | |||
| }; | |||
| listenAddress = mkOption { | |||
| default = ":8428"; | |||
| type = types.str; | |||
| description = '' | |||
| The listen address for the http interface. | |||
| ''; | |||
| }; | |||
| retentionPeriod = mkOption { | |||
| type = types.int; | |||
| default = 1; | |||
| description = '' | |||
| Retention period in months. | |||
| ''; | |||
| }; | |||
| extraOptions = mkOption { | |||
| type = types.listOf types.str; | |||
| default = []; | |||
| description = '' | |||
| Extra options to pass to VictoriaMetrics. See the README: <link | |||
| xlink:href="https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md" /> | |||
| or <command>victoriametrics -help</command> for more | |||
| information. | |||
| ''; | |||
| }; | |||
| }; | |||
| config = lib.mkIf cfg.enable { | |||
| systemd.services.victoriametrics = { | |||
| description = "VictoriaMetrics time series database"; | |||
| after = [ "network.target" ]; | |||
| serviceConfig = { | |||
| Restart = "on-failure"; | |||
| RestartSec = 1; | |||
| StartLimitBurst = 5; | |||
| StateDirectory = "victoriametrics"; | |||
| DynamicUser = true; | |||
| ExecStart = '' | |||
| ${cfg.package}/bin/victoria-metrics \ | |||
| -storageDataPath=/var/lib/victoriametrics \ | |||
| -httpListenAddr ${cfg.listenAddress} | |||
| -retentionPeriod ${toString cfg.retentionPeriod} | |||
| ${lib.escapeShellArgs cfg.extraOptions} | |||
| ''; | |||
| }; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| postStart = | |||
| let | |||
| bindAddr = (lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress; | |||
| in | |||
| lib.mkBefore '' | |||
| until ${lib.getBin pkgs.curl}/bin/curl -s -o /dev/null http://${bindAddr}/ping; do | |||
| sleep 1; | |||
| done | |||
| ''; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -18,6 +18,9 @@ with lib; | |||
| description = '' | |||
| Whether to enable at-spi2-core, a service for the Assistive Technologies | |||
| available on the GNOME platform. | |||
| Enable this if you get the error or warning | |||
| <literal>The name org.a11y.Bus was not provided by any .service files</literal>. | |||
| ''; | |||
| }; | |||
| @@ -118,15 +118,15 @@ in { | |||
| in { | |||
| displayName = "Python 3 for machine learning"; | |||
| argv = [ | |||
| "$ {env.interpreter}" | |||
| "''${env.interpreter}" | |||
| "-m" | |||
| "ipykernel_launcher" | |||
| "-f" | |||
| "{connection_file}" | |||
| ]; | |||
| language = "python"; | |||
| logo32 = "$ {env.sitePackages}/ipykernel/resources/logo-32x32.png"; | |||
| logo64 = "$ {env.sitePackages}/ipykernel/resources/logo-64x64.png"; | |||
| logo32 = "''${env.sitePackages}/ipykernel/resources/logo-32x32.png"; | |||
| logo64 = "''${env.sitePackages}/ipykernel/resources/logo-64x64.png"; | |||
| }; | |||
| } | |||
| ''; | |||
| @@ -53,7 +53,7 @@ in { | |||
| blacklistPlugins = mkOption { | |||
| type = types.listOf types.str; | |||
| default = [ "test" ]; | |||
| default = []; | |||
| example = [ "udev" ]; | |||
| description = '' | |||
| Allow blacklisting specific plugins | |||
| @@ -91,6 +91,9 @@ in { | |||
| ###### implementation | |||
| config = mkIf cfg.enable { | |||
| # Disable test related plug-ins implicitly so that users do not have to care about them. | |||
| services.fwupd.blacklistPlugins = cfg.package.defaultBlacklistedPlugins; | |||
| environment.systemPackages = [ cfg.package ]; | |||
| environment.etc = { | |||
| @@ -13,18 +13,12 @@ in | |||
| config = mkIf cfg.enable { | |||
| systemd.services = { | |||
| irqbalance = { | |||
| description = "irqbalance daemon"; | |||
| path = [ pkgs.irqbalance ]; | |||
| serviceConfig = | |||
| { ExecStart = "${pkgs.irqbalance}/bin/irqbalance --foreground"; }; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| }; | |||
| }; | |||
| environment.systemPackages = [ pkgs.irqbalance ]; | |||
| systemd.services.irqbalance.wantedBy = ["multi-user.target"]; | |||
| systemd.packages = [ pkgs.irqbalance ]; | |||
| }; | |||
| } | |||
| @@ -6,37 +6,18 @@ let | |||
| cfg = config.services.mailman; | |||
| mailmanPyEnv = pkgs.python3.withPackages (ps: with ps; [mailman mailman-hyperkitty]); | |||
| mailmanExe = with pkgs; stdenv.mkDerivation { | |||
| name = "mailman-" + python3Packages.mailman.version; | |||
| buildInputs = [makeWrapper]; | |||
| unpackPhase = ":"; | |||
| installPhase = '' | |||
| mkdir -p $out/bin | |||
| makeWrapper ${mailmanPyEnv}/bin/mailman $out/bin/mailman \ | |||
| --set MAILMAN_CONFIG_FILE /etc/mailman.cfg | |||
| ''; | |||
| }; | |||
| mailmanWeb = pkgs.python3Packages.mailman-web.override { | |||
| serverEMail = cfg.siteOwner; | |||
| archiverKey = cfg.hyperkittyApiKey; | |||
| allowedHosts = cfg.webHosts; | |||
| }; | |||
| mailmanWebPyEnv = pkgs.python3.withPackages (x: with x; [mailman-web]); | |||
| mailmanWebExe = with pkgs; stdenv.mkDerivation { | |||
| inherit (mailmanWeb) name; | |||
| buildInputs = [makeWrapper]; | |||
| unpackPhase = ":"; | |||
| installPhase = '' | |||
| mkdir -p $out/bin | |||
| makeWrapper ${mailmanWebPyEnv}/bin/django-admin $out/bin/mailman-web \ | |||
| --set DJANGO_SETTINGS_MODULE settings | |||
| ''; | |||
| }; | |||
| # This deliberately doesn't use recursiveUpdate so users can | |||
| # override the defaults. | |||
| settings = { | |||
| DEFAULT_FROM_EMAIL = cfg.siteOwner; | |||
| SERVER_EMAIL = cfg.siteOwner; | |||
| ALLOWED_HOSTS = [ "localhost" "127.0.0.1" ] ++ cfg.webHosts; | |||
| COMPRESS_OFFLINE = true; | |||
| STATIC_ROOT = "/var/lib/mailman-web/static"; | |||
| MEDIA_ROOT = "/var/lib/mailman-web/media"; | |||
| } // cfg.webSettings; | |||
| settingsJSON = pkgs.writeText "settings.json" (builtins.toJSON settings); | |||
| mailmanCfg = '' | |||
| [mailman] | |||
| @@ -53,30 +34,42 @@ let | |||
| etc_dir: /etc | |||
| ext_dir: $etc_dir/mailman.d | |||
| pid_file: /run/mailman/master.pid | |||
| '' + optionalString (cfg.hyperkittyApiKey != null) '' | |||
| '' + optionalString cfg.hyperkitty.enable '' | |||
| [archiver.hyperkitty] | |||
| class: mailman_hyperkitty.Archiver | |||
| enable: yes | |||
| configuration: ${pkgs.writeText "mailman-hyperkitty.cfg" mailmanHyperkittyCfg} | |||
| configuration: /var/lib/mailman/mailman-hyperkitty.cfg | |||
| ''; | |||
| mailmanHyperkittyCfg = '' | |||
| mailmanHyperkittyCfg = pkgs.writeText "mailman-hyperkitty.cfg" '' | |||
| [general] | |||
| # This is your HyperKitty installation, preferably on the localhost. This | |||
| # address will be used by Mailman to forward incoming emails to HyperKitty | |||
| # for archiving. It does not need to be publicly available, in fact it's | |||
| # better if it is not. | |||
| base_url: ${cfg.hyperkittyBaseUrl} | |||
| base_url: ${cfg.hyperkitty.baseUrl} | |||
| # Shared API key, must be the identical to the value in HyperKitty's | |||
| # settings. | |||
| api_key: ${cfg.hyperkittyApiKey} | |||
| api_key: @API_KEY@ | |||
| ''; | |||
| in { | |||
| ###### interface | |||
| imports = [ | |||
| (mkRenamedOptionModule [ "services" "mailman" "hyperkittyBaseUrl" ] | |||
| [ "services" "mailman" "hyperkitty" "baseUrl" ]) | |||
| (mkRemovedOptionModule [ "services" "mailman" "hyperkittyApiKey" ] '' | |||
| The Hyperkitty API key is now generated on first run, and not | |||
| stored in the world-readable Nix store. To continue using | |||
| Hyperkitty, you must set services.mailman.hyperkitty.enable = true. | |||
| '') | |||
| ]; | |||
| options = { | |||
| services.mailman = { | |||
| @@ -87,9 +80,17 @@ in { | |||
| description = "Enable Mailman on this host. Requires an active Postfix installation."; | |||
| }; | |||
| package = mkOption { | |||
| type = types.package; | |||
| default = pkgs.mailman; | |||
| defaultText = "pkgs.mailman"; | |||
| example = "pkgs.mailman.override { archivers = []; }"; | |||
| description = "Mailman package to use"; | |||
| }; | |||
| siteOwner = mkOption { | |||
| type = types.str; | |||
| default = "postmaster@example.org"; | |||
| example = "postmaster@example.org"; | |||
| description = '' | |||
| Certain messages that must be delivered to a human, but which can't | |||
| be delivered to a list owner (e.g. a bounce from a list owner), will | |||
| @@ -99,12 +100,13 @@ in { | |||
| webRoot = mkOption { | |||
| type = types.path; | |||
| default = "${mailmanWeb}/${pkgs.python3.sitePackages}"; | |||
| defaultText = "pkgs.python3Packages.mailman-web"; | |||
| default = "${pkgs.mailman-web}/${pkgs.python3.sitePackages}"; | |||
| defaultText = "\${pkgs.mailman-web}/\${pkgs.python3.sitePackages}"; | |||
| description = '' | |||
| The web root for the Hyperkity + Postorius apps provided by Mailman. | |||
| This variable can be set, of course, but it mainly exists so that site | |||
| admins can refer to it in their own hand-written httpd configuration files. | |||
| admins can refer to it in their own hand-written web server | |||
| configuration files. | |||
| ''; | |||
| }; | |||
| @@ -120,26 +122,35 @@ in { | |||
| ''; | |||
| }; | |||
| hyperkittyBaseUrl = mkOption { | |||
| webUser = mkOption { | |||
| type = types.str; | |||
| default = "http://localhost/hyperkitty/"; | |||
| default = config.services.httpd.user; | |||
| description = '' | |||
| Where can Mailman connect to Hyperkitty's internal API, preferably on | |||
| localhost? | |||
| User to run mailman-web as | |||
| ''; | |||
| }; | |||
| hyperkittyApiKey = mkOption { | |||
| type = types.nullOr types.str; | |||
| default = null; | |||
| webSettings = mkOption { | |||
| type = types.attrs; | |||
| default = {}; | |||
| description = '' | |||
| The shared secret used to authenticate Mailman's internal | |||
| communication with Hyperkitty. Must be set to enable support for the | |||
| Hyperkitty archiver. Note that this secret is going to be visible to | |||
| all local users in the Nix store. | |||
| Overrides for the default mailman-web Django settings. | |||
| ''; | |||
| }; | |||
| hyperkitty = { | |||
| enable = mkEnableOption "the Hyperkitty archiver for Mailman"; | |||
| baseUrl = mkOption { | |||
| type = types.str; | |||
| default = "http://localhost/hyperkitty/"; | |||
| description = '' | |||
| Where can Mailman connect to Hyperkitty's internal API, preferably on | |||
| localhost? | |||
| ''; | |||
| }; | |||
| }; | |||
| }; | |||
| }; | |||
| @@ -147,25 +158,58 @@ in { | |||
| config = mkIf cfg.enable { | |||
| assertions = [ | |||
| { assertion = cfg.enable -> config.services.postfix.enable; | |||
| assertions = let | |||
| inherit (config.services) postfix; | |||
| requirePostfixHash = optionPath: dataFile: | |||
| with lib; | |||
| let | |||
| expected = "hash:/var/lib/mailman/data/${dataFile}"; | |||
| value = attrByPath optionPath [] postfix; | |||
| in | |||
| { assertion = postfix.enable -> isList value && elem expected value; | |||
| message = '' | |||
| services.postfix.${concatStringsSep "." optionPath} must contain | |||
| "${expected}". | |||
| See <https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html>. | |||
| ''; | |||
| }; | |||
| in [ | |||
| { assertion = postfix.enable; | |||
| message = "Mailman requires Postfix"; | |||
| } | |||
| (requirePostfixHash [ "relayDomains" ] "postfix_domains") | |||
| (requirePostfixHash [ "config" "transport_maps" ] "postfix_lmtp") | |||
| (requirePostfixHash [ "config" "local_recipient_maps" ] "postfix_lmtp") | |||
| ]; | |||
| users.users.mailman = { description = "GNU Mailman"; isSystemUser = true; }; | |||
| environment = { | |||
| systemPackages = [ mailmanExe mailmanWebExe pkgs.sassc ]; | |||
| etc."mailman.cfg".text = mailmanCfg; | |||
| }; | |||
| environment.etc."mailman.cfg".text = mailmanCfg; | |||
| environment.etc."mailman3/settings.py".text = '' | |||
| import os | |||
| # Required by mailman_web.settings, but will be overridden when | |||
| # settings_local.json is loaded. | |||
| os.environ["SECRET_KEY"] = "" | |||
| from mailman_web.settings import * | |||
| import json | |||
| with open('${settingsJSON}') as f: | |||
| globals().update(json.load(f)) | |||
| with open('/var/lib/mailman-web/settings_local.json') as f: | |||
| globals().update(json.load(f)) | |||
| ''; | |||
| environment.systemPackages = [ cfg.package ] ++ (with pkgs; [ mailman-web ]); | |||
| services.postfix = { | |||
| relayDomains = [ "hash:/var/lib/mailman/data/postfix_domains" ]; | |||
| recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP | |||
| config = { | |||
| transport_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ]; | |||
| local_recipient_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ]; | |||
| owner_request_special = "no"; # Mailman handles -owner addresses on its own | |||
| }; | |||
| }; | |||
| @@ -173,34 +217,71 @@ in { | |||
| systemd.services.mailman = { | |||
| description = "GNU Mailman Master Process"; | |||
| after = [ "network.target" ]; | |||
| restartTriggers = [ config.environment.etc."mailman.cfg".source ]; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanExe}/bin/mailman start"; | |||
| ExecStop = "${mailmanExe}/bin/mailman stop"; | |||
| ExecStart = "${cfg.package}/bin/mailman start"; | |||
| ExecStop = "${cfg.package}/bin/mailman stop"; | |||
| User = "mailman"; | |||
| Type = "forking"; | |||
| StateDirectory = "mailman"; | |||
| StateDirectoryMode = "0700"; | |||
| RuntimeDirectory = "mailman"; | |||
| PIDFile = "/run/mailman/master.pid"; | |||
| }; | |||
| }; | |||
| systemd.services.mailman-settings = { | |||
| description = "Generate settings files (including secrets) for Mailman"; | |||
| before = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ]; | |||
| requiredBy = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ]; | |||
| path = with pkgs; [ jq ]; | |||
| script = '' | |||
| mailmanDir=/var/lib/mailman | |||
| mailmanWebDir=/var/lib/mailman-web | |||
| mailmanCfg=$mailmanDir/mailman-hyperkitty.cfg | |||
| mailmanWebCfg=$mailmanWebDir/settings_local.json | |||
| install -m 0700 -o mailman -g nogroup -d $mailmanDir | |||
| install -m 0700 -o ${cfg.webUser} -g nogroup -d $mailmanWebDir | |||
| if [ ! -e $mailmanWebCfg ]; then | |||
| hyperkittyApiKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) | |||
| secretKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) | |||
| mailmanWebCfgTmp=$(mktemp) | |||
| jq -n '.MAILMAN_ARCHIVER_KEY=$archiver_key | .SECRET_KEY=$secret_key' \ | |||
| --arg archiver_key "$hyperkittyApiKey" \ | |||
| --arg secret_key "$secretKey" \ | |||
| >"$mailmanWebCfgTmp" | |||
| chown ${cfg.webUser} "$mailmanWebCfgTmp" | |||
| mv -n "$mailmanWebCfgTmp" $mailmanWebCfg | |||
| fi | |||
| hyperkittyApiKey="$(jq -r .MAILMAN_ARCHIVER_KEY $mailmanWebCfg)" | |||
| mailmanCfgTmp=$(mktemp) | |||
| sed "s/@API_KEY@/$hyperkittyApiKey/g" ${mailmanHyperkittyCfg} >"$mailmanCfgTmp" | |||
| chown mailman "$mailmanCfgTmp" | |||
| mv "$mailmanCfgTmp" $mailmanCfg | |||
| ''; | |||
| serviceConfig = { | |||
| Type = "oneshot"; | |||
| }; | |||
| }; | |||
| systemd.services.mailman-web = { | |||
| description = "Init Postorius DB"; | |||
| before = [ "httpd.service" ]; | |||
| requiredBy = [ "httpd.service" ]; | |||
| before = [ "httpd.service" "uwsgi.service" ]; | |||
| requiredBy = [ "httpd.service" "uwsgi.service" ]; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| script = '' | |||
| ${mailmanWebExe}/bin/mailman-web migrate | |||
| ${pkgs.mailman-web}/bin/mailman-web migrate | |||
| rm -rf static | |||
| ${mailmanWebExe}/bin/mailman-web collectstatic | |||
| ${mailmanWebExe}/bin/mailman-web compress | |||
| ${pkgs.mailman-web}/bin/mailman-web collectstatic | |||
| ${pkgs.mailman-web}/bin/mailman-web compress | |||
| ''; | |||
| serviceConfig = { | |||
| User = config.services.httpd.user; | |||
| User = cfg.webUser; | |||
| Type = "oneshot"; | |||
| StateDirectory = "mailman-web"; | |||
| StateDirectoryMode = "0700"; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| @@ -208,86 +289,94 @@ in { | |||
| systemd.services.mailman-daily = { | |||
| description = "Trigger daily Mailman events"; | |||
| startAt = "daily"; | |||
| restartTriggers = [ config.environment.etc."mailman.cfg".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanExe}/bin/mailman digests --send"; | |||
| ExecStart = "${cfg.package}/bin/mailman digests --send"; | |||
| User = "mailman"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "GNU Hyperkitty QCluster Process"; | |||
| after = [ "network.target" ]; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| wantedBy = [ "mailman.service" "multi-user.target" ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web qcluster"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web qcluster"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty-minutely = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "Trigger minutely Hyperkitty events"; | |||
| startAt = "minutely"; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs minutely"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs minutely"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty-quarter-hourly = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "Trigger quarter-hourly Hyperkitty events"; | |||
| startAt = "*:00/15"; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs quarter_hourly"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs quarter_hourly"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty-hourly = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "Trigger hourly Hyperkitty events"; | |||
| startAt = "hourly"; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs hourly"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs hourly"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty-daily = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "Trigger daily Hyperkitty events"; | |||
| startAt = "daily"; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs daily"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs daily"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty-weekly = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "Trigger weekly Hyperkitty events"; | |||
| startAt = "weekly"; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs weekly"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs weekly"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| systemd.services.hyperkitty-yearly = { | |||
| enable = cfg.hyperkittyApiKey != null; | |||
| inherit (cfg.hyperkitty) enable; | |||
| description = "Trigger yearly Hyperkitty events"; | |||
| startAt = "yearly"; | |||
| restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; | |||
| serviceConfig = { | |||
| ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs yearly"; | |||
| User = config.services.httpd.user; | |||
| ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs yearly"; | |||
| User = cfg.webUser; | |||
| WorkingDirectory = "/var/lib/mailman-web"; | |||
| }; | |||
| }; | |||
| @@ -5,6 +5,8 @@ with lib; | |||
| let | |||
| cfg = config.services.roundcube; | |||
| fpm = config.services.phpfpm.pools.roundcube; | |||
| localDB = cfg.database.host == "localhost"; | |||
| user = cfg.database.username; | |||
| in | |||
| { | |||
| options.services.roundcube = { | |||
| @@ -44,7 +46,10 @@ in | |||
| username = mkOption { | |||
| type = types.str; | |||
| default = "roundcube"; | |||
| description = "Username for the postgresql connection"; | |||
| description = '' | |||
| Username for the postgresql connection. | |||
| If <literal>database.host</literal> is set to <literal>localhost</literal>, a unix user and group of the same name will be created as well. | |||
| ''; | |||
| }; | |||
| host = mkOption { | |||
| type = types.str; | |||
| @@ -58,7 +63,12 @@ in | |||
| }; | |||
| password = mkOption { | |||
| type = types.str; | |||
| description = "Password for the postgresql connection"; | |||
| description = "Password for the postgresql connection. Do not use: the password will be stored world readable in the store; use <literal>passwordFile</literal> instead."; | |||
| default = ""; | |||
| }; | |||
| passwordFile = mkOption { | |||
| type = types.str; | |||
| description = "Password file for the postgresql connection. Must be readable by user <literal>nginx</literal>. Ignored if <literal>database.host</literal> is set to <literal>localhost</literal>, as peer authentication will be used."; | |||
| }; | |||
| dbname = mkOption { | |||
| type = types.str; | |||
| @@ -83,14 +93,22 @@ in | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| # backward compatibility: if password is set but not passwordFile, make one. | |||
| services.roundcube.database.passwordFile = mkIf (!localDB && cfg.database.password != "") (mkDefault ("${pkgs.writeText "roundcube-password" cfg.database.password}")); | |||
| warnings = lib.optional (!localDB && cfg.database.password != "") "services.roundcube.database.password is deprecated and insecure; use services.roundcube.database.passwordFile instead"; | |||
| environment.etc."roundcube/config.inc.php".text = '' | |||
| <?php | |||
| ${lib.optionalString (!localDB) "$password = file_get_contents('${cfg.database.passwordFile}');"} | |||
| $config = array(); | |||
| $config['db_dsnw'] = 'pgsql://${cfg.database.username}:${cfg.database.password}@${cfg.database.host}/${cfg.database.dbname}'; | |||
| $config['db_dsnw'] = 'pgsql://${cfg.database.username}${lib.optionalString (!localDB) ":' . $password . '"}@${if localDB then "unix(/run/postgresql)" else cfg.database.host}/${cfg.database.dbname}'; | |||
| $config['log_driver'] = 'syslog'; | |||
| $config['max_message_size'] = '25M'; | |||
| $config['plugins'] = [${concatMapStringsSep "," (p: "'${p}'") cfg.plugins}]; | |||
| $config['des_key'] = file_get_contents('/var/lib/roundcube/des_key'); | |||
| $config['mime_types'] = '${pkgs.nginx}/conf/mime.types'; | |||
| ${cfg.extraConfig} | |||
| ''; | |||
| @@ -116,12 +134,26 @@ in | |||
| }; | |||
| }; | |||
| services.postgresql = mkIf (cfg.database.host == "localhost") { | |||
| services.postgresql = mkIf localDB { | |||
| enable = true; | |||
| ensureDatabases = [ cfg.database.dbname ]; | |||
| ensureUsers = [ { | |||
| name = cfg.database.username; | |||
| ensurePermissions = { | |||
| "DATABASE ${cfg.database.username}" = "ALL PRIVILEGES"; | |||
| }; | |||
| } ]; | |||
| }; | |||
| users.users.${user} = mkIf localDB { | |||
| group = user; | |||
| isSystemUser = true; | |||
| createHome = false; | |||
| }; | |||
| users.groups.${user} = mkIf localDB {}; | |||
| services.phpfpm.pools.roundcube = { | |||
| user = "nginx"; | |||
| user = if localDB then user else "nginx"; | |||
| phpOptions = '' | |||
| error_log = 'stderr' | |||
| log_errors = on | |||
| @@ -143,9 +175,7 @@ in | |||
| }; | |||
| systemd.services.phpfpm-roundcube.after = [ "roundcube-setup.service" ]; | |||
| systemd.services.roundcube-setup = let | |||
| pgSuperUser = config.services.postgresql.superUser; | |||
| in mkMerge [ | |||
| systemd.services.roundcube-setup = mkMerge [ | |||
| (mkIf (cfg.database.host == "localhost") { | |||
| requires = [ "postgresql.service" ]; | |||
| after = [ "postgresql.service" ]; | |||
| @@ -153,22 +183,31 @@ in | |||
| }) | |||
| { | |||
| wantedBy = [ "multi-user.target" ]; | |||
| script = '' | |||
| mkdir -p /var/lib/roundcube | |||
| if [ ! -f /var/lib/roundcube/db-created ]; then | |||
| if [ "${cfg.database.host}" = "localhost" ]; then | |||
| ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "create role ${cfg.database.username} with login password '${cfg.database.password}'"; | |||
| ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "create database ${cfg.database.dbname} with owner ${cfg.database.username}"; | |||
| fi | |||
| PGPASSWORD="${cfg.database.password}" ${pkgs.postgresql}/bin/psql -U ${cfg.database.username} \ | |||
| -f ${cfg.package}/SQL/postgres.initial.sql \ | |||
| -h ${cfg.database.host} ${cfg.database.dbname} | |||
| touch /var/lib/roundcube/db-created | |||
| script = let | |||
| psql = "${lib.optionalString (!localDB) "PGPASSFILE=${cfg.database.passwordFile}"} ${pkgs.postgresql}/bin/psql ${lib.optionalString (!localDB) "-h ${cfg.database.host} -U ${cfg.database.username} "} ${cfg.database.dbname}"; | |||
| in | |||
| '' | |||
| version="$(${psql} -t <<< "select value from system where name = 'roundcube-version';" || true)" | |||
| if ! (grep -E '[a-zA-Z0-9]' <<< "$version"); then | |||
| ${psql} -f ${cfg.package}/SQL/postgres.initial.sql | |||
| fi | |||
| if [ ! -f /var/lib/roundcube/des_key ]; then | |||
| base64 /dev/urandom | head -c 24 > /var/lib/roundcube/des_key; | |||
| # we need to log out everyone in case change the des_key | |||
| # from the default when upgrading from nixos 19.09 | |||
| ${psql} <<< 'TRUNCATE TABLE session;' | |||
| fi | |||
| ${pkgs.php}/bin/php ${cfg.package}/bin/update.sh | |||
| ''; | |||
| serviceConfig.Type = "oneshot"; | |||
| serviceConfig = { | |||
| Type = "oneshot"; | |||
| StateDirectory = "roundcube"; | |||
| User = if localDB then user else "nginx"; | |||
| # so that the des_key is not world readable | |||
| StateDirectoryMode = "0700"; | |||
| }; | |||
| } | |||
| ]; | |||
| }; | |||
| @@ -6,15 +6,6 @@ let | |||
| cfg = config.services.spamassassin; | |||
| spamassassin-local-cf = pkgs.writeText "local.cf" cfg.config; | |||
| spamdEnv = pkgs.buildEnv { | |||
| name = "spamd-env"; | |||
| paths = []; | |||
| postBuild = '' | |||
| ln -sf ${spamassassin-init-pre} $out/init.pre | |||
| ln -sf ${spamassassin-local-cf} $out/local.cf | |||
| ''; | |||
| }; | |||
| in | |||
| { | |||
| @@ -120,13 +111,11 @@ in | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| environment.etc."mail/spamassassin/init.pre".source = cfg.initPreConf; | |||
| environment.etc."mail/spamassassin/local.cf".source = spamassassin-local-cf; | |||
| # Allow users to run 'spamc'. | |||
| environment = { | |||
| etc.spamassassin.source = spamdEnv; | |||
| systemPackages = [ pkgs.spamassassin ]; | |||
| }; | |||
| environment.systemPackages = [ pkgs.spamassassin ]; | |||
| users.users.spamd = { | |||
| description = "Spam Assassin Daemon"; | |||
| @@ -141,7 +130,7 @@ in | |||
| systemd.services.sa-update = { | |||
| script = '' | |||
| set +e | |||
| ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/ --siteconfigpath=${spamdEnv}/" spamd | |||
| ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd | |||
| v=$? | |||
| set -e | |||
| @@ -172,7 +161,7 @@ in | |||
| after = [ "network.target" ]; | |||
| serviceConfig = { | |||
| ExecStart = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --siteconfigpath=${spamdEnv} --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; | |||
| ExecStart = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; | |||
| ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; | |||
| }; | |||
| @@ -183,7 +172,7 @@ in | |||
| mkdir -p /var/lib/spamassassin | |||
| chown spamd:spamd /var/lib/spamassassin -R | |||
| set +e | |||
| ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/ --siteconfigpath=${spamdEnv}/" spamd | |||
| ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd | |||
| v=$? | |||
| set -e | |||
| if [ $v -gt 1 ]; then | |||
| @@ -0,0 +1,103 @@ | |||
| { config, lib, pkgs, ...}: | |||
| with lib; | |||
| let | |||
| cfg = config.services.freeswitch; | |||
| pkg = cfg.package; | |||
| configDirectory = pkgs.runCommand "freeswitch-config-d" { } '' | |||
| mkdir -p $out | |||
| cp -rT ${cfg.configTemplate} $out | |||
| chmod -R +w $out | |||
| ${concatStringsSep "\n" (mapAttrsToList (fileName: filePath: '' | |||
| mkdir -p $out/$(dirname ${fileName}) | |||
| cp ${filePath} $out/${fileName} | |||
| '') cfg.configDir)} | |||
| ''; | |||
| configPath = if cfg.enableReload | |||
| then "/etc/freeswitch" | |||
| else configDirectory; | |||
| in { | |||
| options = { | |||
| services.freeswitch = { | |||
| enable = mkEnableOption "FreeSWITCH"; | |||
| enableReload = mkOption { | |||
| default = false; | |||
| type = types.bool; | |||
| description = '' | |||
| Issue the <literal>reloadxml</literal> command to FreeSWITCH when configuration directory changes (instead of restart). | |||
| See <link xlink:href="https://freeswitch.org/confluence/display/FREESWITCH/Reloading">FreeSWITCH documentation</link> for more info. | |||
| The configuration directory is exposed at <filename>/etc/freeswitch</filename>. | |||
| See also <literal>systemd.services.*.restartIfChanged</literal>. | |||
| ''; | |||
| }; | |||
| configTemplate = mkOption { | |||
| type = types.path; | |||
| default = "${config.services.freeswitch.package}/share/freeswitch/conf/vanilla"; | |||
| defaultText = literalExample "\${config.services.freeswitch.package}/share/freeswitch/conf/vanilla"; | |||
| example = literalExample "\${config.services.freeswitch.package}/share/freeswitch/conf/minimal"; | |||
| description = '' | |||
| Configuration template to use. | |||
| See available templates in <link xlink:href="https://github.com/signalwire/freeswitch/tree/master/conf">FreeSWITCH repository</link>. | |||
| You can also set your own configuration directory. | |||
| ''; | |||
| }; | |||
| configDir = mkOption { | |||
| type = with types; attrsOf path; | |||
| default = { }; | |||
| example = literalExample '' | |||
| { | |||
| "freeswitch.xml" = ./freeswitch.xml; | |||
| "dialplan/default.xml" = pkgs.writeText "dialplan-default.xml" ''' | |||
| [xml lines] | |||
| '''; | |||
| } | |||
| ''; | |||
| description = '' | |||
| Override file in FreeSWITCH config template directory. | |||
| Each top-level attribute denotes a file path in the configuration directory, its value is the file path. | |||
| See <link xlink:href="https://freeswitch.org/confluence/display/FREESWITCH/Default+Configuration">FreeSWITCH documentation</link> for more info. | |||
| Also check available templates in <link xlink:href="https://github.com/signalwire/freeswitch/tree/master/conf">FreeSWITCH repository</link>. | |||
| ''; | |||
| }; | |||
| package = mkOption { | |||
| type = types.package; | |||
| default = pkgs.freeswitch; | |||
| defaultText = literalExample "pkgs.freeswitch"; | |||
| example = literalExample "pkgs.freeswitch"; | |||
| description = '' | |||
| FreeSWITCH package. | |||
| ''; | |||
| }; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| environment.etc.freeswitch = mkIf cfg.enableReload { | |||
| source = configDirectory; | |||
| }; | |||
| systemd.services.freeswitch-config-reload = mkIf cfg.enableReload { | |||
| before = [ "freeswitch.service" ]; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| restartTriggers = [ configDirectory ]; | |||
| serviceConfig = { | |||
| ExecStart = "${pkgs.systemd}/bin/systemctl try-reload-or-restart freeswitch.service"; | |||
| RemainAfterExit = true; | |||
| Type = "oneshot"; | |||
| }; | |||
| }; | |||
| systemd.services.freeswitch = { | |||
| description = "Free and open-source application server for real-time communication"; | |||
| after = [ "network.target" ]; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| serviceConfig = { | |||
| DynamicUser = true; | |||
| StateDirectory = "freeswitch"; | |||
| ExecStart = "${pkg}/bin/freeswitch -nf \\ | |||
| -mod ${pkg}/lib/freeswitch/mod \\ | |||
| -conf ${configPath} \\ | |||
| -base /var/lib/freeswitch"; | |||
| ExecReload = "${pkg}/bin/fs_cli -x reloadxml"; | |||
| Restart = "always"; | |||
| RestartSec = "5s"; | |||
| }; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -364,7 +364,7 @@ in | |||
| ''} | |||
| sed -e "s,#secretkey#,$KEY,g" \ | |||
| -e "s,#dbpass#,$DBPASS,g" \ | |||
| -e "s,#jwtsecet#,$JWTSECET,g" \ | |||
| -e "s,#jwtsecret#,$JWTSECRET,g" \ | |||
| -e "s,#mailerpass#,$MAILERPASSWORD,g" \ | |||
| -i ${runConfig} | |||
| chmod 640 ${runConfig} ${secretKey} ${jwtSecret} | |||
| @@ -251,6 +251,7 @@ in { | |||
| home = cfg.configDir; | |||
| createHome = true; | |||
| group = "hass"; | |||
| extraGroups = [ "dialout" ]; | |||
| uid = config.ids.uids.hass; | |||
| }; | |||
| @@ -123,9 +123,9 @@ in | |||
| config = mkIf cfg.enable { | |||
| systemd.tmpfiles.rules = [ | |||
| "d '${cfg.dataDir}' - ${cfg.user} ${cfg.user} - -" | |||
| "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -" | |||
| ] ++ (optional cfg.consumptionDirIsPublic | |||
| "d '${cfg.consumptionDir}' 777 ${cfg.user} ${cfg.user} - -" | |||
| "d '${cfg.consumptionDir}' 777 - - - -" | |||
| # If the consumption dir is not created here, it's automatically created by | |||
| # 'manage' with the default permissions. | |||
| ); | |||
| @@ -169,17 +169,15 @@ in | |||
| }; | |||
| users = optionalAttrs (cfg.user == defaultUser) { | |||
| users = [{ | |||
| name = defaultUser; | |||
| users.${defaultUser} = { | |||
| group = defaultUser; | |||
| uid = config.ids.uids.paperless; | |||
| home = cfg.dataDir; | |||
| }]; | |||
| }; | |||
| groups = [{ | |||
| name = defaultUser; | |||
| groups.${defaultUser} = { | |||
| gid = config.ids.gids.paperless; | |||
| }]; | |||
| }; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -154,7 +154,7 @@ in | |||
| }; | |||
| virtualHost = mkOption { | |||
| type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix); | |||
| type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix); | |||
| example = literalExample '' | |||
| { hostName = "example.org"; | |||
| adminAddr = "webmaster@example.org"; | |||
| @@ -18,7 +18,7 @@ let | |||
| in checkedConfig yml; | |||
| cmdlineArgs = cfg.extraFlags ++ [ | |||
| "--config.file ${alertmanagerYml}" | |||
| "--config.file /tmp/alert-manager-substituted.yaml" | |||
| "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}" | |||
| "--log.level ${cfg.logLevel}" | |||
| ] ++ (optional (cfg.webExternalUrl != null) | |||
| @@ -127,6 +127,18 @@ in { | |||
| Extra commandline options when launching the Alertmanager. | |||
| ''; | |||
| }; | |||
| environmentFile = mkOption { | |||
| type = types.nullOr types.path; | |||
| default = null; | |||
| example = "/root/alertmanager.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> | |||
| ''; | |||
| }; | |||
| }; | |||
| }; | |||
| @@ -144,9 +156,14 @@ in { | |||
| systemd.services.alertmanager = { | |||
| wantedBy = [ "multi-user.target" ]; | |||
| after = [ "network.target" ]; | |||
| preStart = '' | |||
| ${lib.getBin pkgs.envsubst}/bin/envsubst -o "/tmp/alert-manager-substituted.yaml" \ | |||
| -i "${alertmanagerYml}" | |||
| ''; | |||
| serviceConfig = { | |||
| Restart = "always"; | |||
| DynamicUser = true; | |||
| DynamicUser = true; # implies PrivateTmp | |||
| EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; | |||
| WorkingDirectory = "/tmp"; | |||
| ExecStart = "${cfg.package}/bin/alertmanager" + | |||
| optionalString (length cmdlineArgs != 0) (" \\\n " + | |||
| @@ -74,7 +74,7 @@ in | |||
| then "--systemd.slice ${cfg.systemd.slice}" | |||
| else "--systemd.unit ${cfg.systemd.unit}") | |||
| ++ optional (cfg.systemd.enable && (cfg.systemd.journalPath != null)) | |||
| "--systemd.jounal_path ${cfg.systemd.journalPath}" | |||
| "--systemd.journal_path ${cfg.systemd.journalPath}" | |||
| ++ optional (!cfg.systemd.enable) "--postfix.logfile_path ${cfg.logfilePath}")} | |||
| ''; | |||
| }; | |||
| @@ -0,0 +1,47 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.services.prometheus.xmpp-alerts; | |||
| configFile = pkgs.writeText "prometheus-xmpp-alerts.yml" (builtins.toJSON cfg.configuration); | |||
| in | |||
| { | |||
| options.services.prometheus.xmpp-alerts = { | |||
| enable = mkEnableOption "XMPP Web hook service for Alertmanager"; | |||
| configuration = mkOption { | |||
| type = types.attrs; | |||
| description = "Configuration as attribute set which will be converted to YAML"; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| systemd.services.prometheus-xmpp-alerts = { | |||
| wantedBy = [ "multi-user.target" ]; | |||
| after = [ "network-online.target" ]; | |||
| wants = [ "network-online.target" ]; | |||
| serviceConfig = { | |||
| ExecStart = "${pkgs.prometheus-xmpp-alerts}/bin/prometheus-xmpp-alerts --config ${configFile}"; | |||
| Restart = "on-failure"; | |||
| DynamicUser = true; | |||
| PrivateTmp = true; | |||
| PrivateDevices = true; | |||
| ProtectHome = true; | |||
| ProtectSystem = "strict"; | |||
| ProtectKernelTunables = true; | |||
| ProtectKernelModules = true; | |||
| ProtectControlGroups = true; | |||
| NoNewPrivileges = true; | |||
| SystemCallArchitectures = "native"; | |||
| RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; | |||
| SystemCallFilter = [ "@system-service" ]; | |||
| }; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| inherit (config.security) wrapperDir; | |||
| cfg = config.services.kbfs; | |||
| in { | |||
| @@ -17,6 +18,16 @@ in { | |||
| description = "Whether to mount the Keybase filesystem."; | |||
| }; | |||
| enableRedirector = mkOption { | |||
| type = types.bool; | |||
| default = false; | |||
| description = '' | |||
| Whether to enable the Keybase root redirector service, allowing | |||
| any user to access KBFS files via <literal>/keybase</literal>, | |||
| which will show different contents depending on the requester. | |||
| ''; | |||
| }; | |||
| mountPoint = mkOption { | |||
| type = types.str; | |||
| default = "%h/keybase"; | |||
| @@ -41,26 +52,67 @@ in { | |||
| ###### implementation | |||
| config = mkIf cfg.enable { | |||
| systemd.user.services.kbfs = { | |||
| description = "Keybase File System"; | |||
| requires = [ "keybase.service" ]; | |||
| after = [ "keybase.service" ]; | |||
| path = [ "/run/wrappers" ]; | |||
| unitConfig.ConditionUser = "!@system"; | |||
| serviceConfig = { | |||
| ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${cfg.mountPoint}"; | |||
| ExecStart = "${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}"; | |||
| ExecStopPost = "/run/wrappers/bin/fusermount -u ${cfg.mountPoint}"; | |||
| Restart = "on-failure"; | |||
| PrivateTmp = true; | |||
| config = mkIf cfg.enable (mkMerge [ | |||
| { | |||
| # Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/kbfs.service | |||
| systemd.user.services.kbfs = { | |||
| description = "Keybase File System"; | |||
| # Note that the "Requires" directive will cause a unit to be restarted whenever its dependency is restarted. | |||
| # Do not issue a hard dependency on keybase, because kbfs can reconnect to a restarted service. | |||
| # Do not issue a hard dependency on keybase-redirector, because it's ok if it fails (e.g., if it is disabled). | |||
| wants = [ "keybase.service" ] ++ optional cfg.enableRedirector "keybase-redirector.service"; | |||
| path = [ "/run/wrappers" ]; | |||
| unitConfig.ConditionUser = "!@system"; | |||
| serviceConfig = { | |||
| Type = "notify"; | |||
| # Keybase notifies from a forked process | |||
| EnvironmentFile = [ | |||
| "-%E/keybase/keybase.autogen.env" | |||
| "-%E/keybase/keybase.env" | |||
| ]; | |||
| ExecStartPre = [ | |||
| "${pkgs.coreutils}/bin/mkdir -p \"${cfg.mountPoint}\"" | |||
| "-${wrapperDir}/fusermount -uz \"${cfg.mountPoint}\"" | |||
| ]; | |||
| ExecStart = "${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} \"${cfg.mountPoint}\""; | |||
| ExecStop = "${wrapperDir}/fusermount -uz \"${cfg.mountPoint}\""; | |||
| Restart = "on-failure"; | |||
| PrivateTmp = true; | |||
| }; | |||
| wantedBy = [ "default.target" ]; | |||
| }; | |||
| wantedBy = [ "default.target" ]; | |||
| }; | |||
| services.keybase.enable = true; | |||
| services.keybase.enable = true; | |||
| environment.systemPackages = [ pkgs.kbfs ]; | |||
| }; | |||
| environment.systemPackages = [ pkgs.kbfs ]; | |||
| } | |||
| (mkIf cfg.enableRedirector { | |||
| security.wrappers."keybase-redirector".source = "${pkgs.kbfs}/bin/redirector"; | |||
| systemd.tmpfiles.rules = [ "d /keybase 0755 root root 0" ]; | |||
| # Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/keybase-redirector.service | |||
| systemd.user.services.keybase-redirector = { | |||
| description = "Keybase Root Redirector for KBFS"; | |||
| wants = [ "keybase.service" ]; | |||
| unitConfig.ConditionUser = "!@system"; | |||
| serviceConfig = { | |||
| EnvironmentFile = [ | |||
| "-%E/keybase/keybase.autogen.env" | |||
| "-%E/keybase/keybase.env" | |||
| ]; | |||
| # Note: The /keybase mount point is not currently configurable upstream. | |||
| ExecStart = "${wrapperDir}/keybase-redirector /keybase"; | |||
| Restart = "on-failure"; | |||
| PrivateTmp = true; | |||
| }; | |||
| wantedBy = [ "default.target" ]; | |||
| }; | |||
| }) | |||
| ]); | |||
| } | |||
| @@ -168,8 +168,7 @@ in | |||
| createHome = true; | |||
| }; | |||
| users.groups = singleton { | |||
| name = "bitlbee"; | |||
| users.groups.bitlbee = { | |||
| gid = config.ids.gids.bitlbee; | |||
| }; | |||
| @@ -0,0 +1,46 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.services.corerad; | |||
| in { | |||
| meta = { | |||
| maintainers = with maintainers; [ mdlayher ]; | |||
| }; | |||
| options.services.corerad = { | |||
| enable = mkEnableOption "CoreRAD IPv6 NDP RA daemon"; | |||
| configFile = mkOption { | |||
| type = types.path; | |||
| example = literalExample "\"\${pkgs.corerad}/etc/corerad/corerad.toml\""; | |||
| description = "Path to CoreRAD TOML configuration file."; | |||
| }; | |||
| package = mkOption { | |||
| default = pkgs.corerad; | |||
| defaultText = literalExample "pkgs.corerad"; | |||
| type = types.package; | |||
| description = "CoreRAD package to use."; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| systemd.services.corerad = { | |||
| description = "CoreRAD IPv6 NDP RA daemon"; | |||
| after = [ "network.target" ]; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| serviceConfig = { | |||
| LimitNPROC = 512; | |||
| LimitNOFILE = 1048576; | |||
| CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW"; | |||
| AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW"; | |||
| NoNewPrivileges = true; | |||
| DynamicUser = true; | |||
| ExecStart = "${getBin cfg.package}/bin/corerad -c=${cfg.configFile}"; | |||
| Restart = "on-failure"; | |||
| }; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -59,6 +59,16 @@ let | |||
| # Use the list of allowed interfaces if specified | |||
| ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} | |||
| # Immediately fork to background if specified, otherwise wait for IP address to be assigned | |||
| ${{ | |||
| background = "background"; | |||
| any = "waitip"; | |||
| ipv4 = "waitip 4"; | |||
| ipv6 = "waitip 6"; | |||
| both = "waitip 4\nwaitip 6"; | |||
| if-carrier-up = ""; | |||
| }.${cfg.wait}} | |||
| ${cfg.extraConfig} | |||
| ''; | |||
| @@ -146,6 +156,21 @@ in | |||
| ''; | |||
| }; | |||
| networking.dhcpcd.wait = mkOption { | |||
| type = types.enum [ "background" "any" "ipv4" "ipv6" "both" "if-carrier-up" ]; | |||
| default = "any"; | |||
| description = '' | |||
| This option specifies when the dhcpcd service will fork to background. | |||
| If set to "background", dhcpcd will fork to background immediately. | |||
| If set to "ipv4" or "ipv6", dhcpcd will wait for the corresponding IP | |||
| address to be assigned. If set to "any", dhcpcd will wait for any type | |||
| (IPv4 or IPv6) to be assigned. If set to "both", dhcpcd will wait for | |||
| both an IPv4 and an IPv6 address before forking. | |||
| The option "if-carrier-up" is equivalent to "any" if either ethernet | |||
| is plugged nor WiFi is powered, and to "background" otherwise. | |||
| ''; | |||
| }; | |||
| }; | |||
| @@ -165,6 +190,8 @@ in | |||
| before = [ "network-online.target" ]; | |||
| after = [ "systemd-udev-settle.service" ]; | |||
| restartTriggers = [ exitHook ]; | |||
| # Stopping dhcpcd during a reconfiguration is undesirable | |||
| # because it brings down the network interfaces configured by | |||
| # dhcpcd. So do a "systemctl restart" instead. | |||
| @@ -177,7 +204,7 @@ in | |||
| serviceConfig = | |||
| { Type = "forking"; | |||
| PIDFile = "/run/dhcpcd.pid"; | |||
| ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd -w --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; | |||
| ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; | |||
| ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; | |||
| Restart = "always"; | |||
| }; | |||
| @@ -1,328 +0,0 @@ | |||
| { config, lib, pkgs, ... }: | |||
| with lib; | |||
| let | |||
| cfg = config.services.dnscrypt-proxy; | |||
| stateDirectory = "/var/lib/dnscrypt-proxy"; | |||
| # The minisign public key used to sign the upstream resolver list. | |||
| # This is somewhat more flexible than preloading the key as an | |||
| # embedded string. | |||
| upstreamResolverListPubKey = pkgs.fetchurl { | |||
| url = https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/minisign.pub; | |||
| sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh"; | |||
| }; | |||
| # Internal flag indicating whether the upstream resolver list is used. | |||
| useUpstreamResolverList = cfg.customResolver == null; | |||
| # The final local address. | |||
| localAddress = "${cfg.localAddress}:${toString cfg.localPort}"; | |||
| # The final resolvers list path. | |||
| resolverList = "${stateDirectory}/dnscrypt-resolvers.csv"; | |||
| # Build daemon command line | |||
| resolverArgs = | |||
| if (cfg.customResolver == null) | |||
| then | |||
| [ "-L ${resolverList}" | |||
| "-R ${cfg.resolverName}" | |||
| ] | |||
| else with cfg.customResolver; | |||
| [ "-N ${name}" | |||
| "-k ${key}" | |||
| "-r ${address}:${toString port}" | |||
| ]; | |||
| daemonArgs = | |||
| [ "-a ${localAddress}" ] | |||
| ++ resolverArgs | |||
| ++ cfg.extraArgs; | |||
| in | |||
| { | |||
| meta = { | |||
| maintainers = with maintainers; [ joachifm ]; | |||
| doc = ./dnscrypt-proxy.xml; | |||
| }; | |||
| options = { | |||
| # Before adding another option, consider whether it could | |||
| # equally well be passed via extraArgs. | |||
| services.dnscrypt-proxy = { | |||
| enable = mkOption { | |||
| default = false; | |||
| type = types.bool; | |||
| description = "Whether to enable the DNSCrypt client proxy"; | |||
| }; | |||
| localAddress = mkOption { | |||
| default = "127.0.0.1"; | |||
| type = types.str; | |||
| description = '' | |||
| Listen for DNS queries to relay on this address. The only reason to | |||
| change this from its default value is to proxy queries on behalf | |||
| of other machines (typically on the local network). | |||
| ''; | |||
| }; | |||
| localPort = mkOption { | |||
| default = 53; | |||
| type = types.int; | |||
| description = '' | |||
| Listen for DNS queries to relay on this port. The default value | |||
| assumes that the DNSCrypt proxy should relay DNS queries directly. | |||
| When running as a forwarder for another DNS client, set this option | |||
| to a different value; otherwise leave the default. | |||
| ''; | |||
| }; | |||
| resolverName = mkOption { | |||
| default = "random"; | |||
| example = "dnscrypt.eu-nl"; | |||
| type = types.nullOr types.str; | |||
| description = '' | |||
| The name of the DNSCrypt resolver to use, taken from | |||
| <filename>${resolverList}</filename>. The default is to | |||
| pick a random non-logging resolver that supports DNSSEC. | |||
| ''; | |||
| }; | |||
| customResolver = mkOption { | |||
| default = null; | |||
| description = '' | |||
| Use an unlisted resolver (e.g., a private DNSCrypt provider). For | |||
| advanced users only. If specified, this option takes precedence. | |||
| ''; | |||
| type = types.nullOr (types.submodule ({ ... }: { options = { | |||
| address = mkOption { | |||
| type = types.str; | |||
| description = "IP address"; | |||
| example = "208.67.220.220"; | |||
| }; | |||
| port = mkOption { | |||
| type = types.int; | |||
| description = "Port"; | |||
| default = 443; | |||
| }; | |||
| name = mkOption { | |||
| type = types.str; | |||
| description = "Fully qualified domain name"; | |||
| example = "2.dnscrypt-cert.example.com"; | |||
| }; | |||
| key = mkOption { | |||
| type = types.str; | |||
| description = "Public key"; | |||
| example = "B735:1140:206F:225D:3E2B:D822:D7FD:691E:A1C3:3CC8:D666:8D0C:BE04:BFAB:CA43:FB79"; | |||
| }; | |||
| }; })); | |||
| }; | |||
| extraArgs = mkOption { | |||
| default = []; | |||
| type = types.listOf types.str; | |||
| description = '' | |||
| Additional command-line arguments passed verbatim to the daemon. | |||
| See <citerefentry><refentrytitle>dnscrypt-proxy</refentrytitle> | |||
| <manvolnum>8</manvolnum></citerefentry> for details. | |||
| ''; | |||
| example = [ "-X libdcplugin_example_cache.so,--min-ttl=60" ]; | |||
| }; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable (mkMerge [{ | |||
| assertions = [ | |||
| { assertion = (cfg.customResolver != null) || (cfg.resolverName != null); | |||
| message = "please configure upstream DNSCrypt resolver"; | |||
| } | |||
| ]; | |||
| # make man 8 dnscrypt-proxy work | |||
| environment.systemPackages = [ pkgs.dnscrypt-proxy ]; | |||
| users.users.dnscrypt-proxy = { | |||
| description = "dnscrypt-proxy daemon user"; | |||
| isSystemUser = true; | |||
| group = "dnscrypt-proxy"; | |||
| }; | |||
| users.groups.dnscrypt-proxy = {}; | |||
| systemd.sockets.dnscrypt-proxy = { | |||
| description = "dnscrypt-proxy listening socket"; | |||
| documentation = [ "man:dnscrypt-proxy(8)" ]; | |||
| wantedBy = [ "sockets.target" ]; | |||
| socketConfig = { | |||
| ListenStream = localAddress; | |||
| ListenDatagram = localAddress; | |||
| }; | |||
| }; | |||
| systemd.services.dnscrypt-proxy = { | |||
| description = "dnscrypt-proxy daemon"; | |||
| documentation = [ "man:dnscrypt-proxy(8)" ]; | |||
| before = [ "nss-lookup.target" ]; | |||
| after = [ "network.target" ]; | |||
| requires = [ "dnscrypt-proxy.socket "]; | |||
| serviceConfig = { | |||
| NonBlocking = "true"; | |||
| ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}"; | |||
| ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; | |||
| User = "dnscrypt-proxy"; | |||
| PrivateTmp = true; | |||
| PrivateDevices = true; | |||
| ProtectHome = true; | |||
| }; | |||
| }; | |||
| } | |||
| (mkIf config.security.apparmor.enable { | |||
| systemd.services.dnscrypt-proxy.after = [ "apparmor.service" ]; | |||
| security.apparmor.profiles = singleton (pkgs.writeText "apparmor-dnscrypt-proxy" '' | |||
| ${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy { | |||
| /dev/null rw, | |||
| /dev/random r, | |||
| /dev/urandom r, | |||
| /etc/passwd r, | |||
| /etc/group r, | |||
| ${config.environment.etc."nsswitch.conf".source} r, | |||
| ${getLib pkgs.glibc}/lib/*.so mr, | |||
| ${pkgs.tzdata}/share/zoneinfo/** r, | |||
| network inet stream, | |||
| network inet6 stream, | |||
| network inet dgram, | |||
| network inet6 dgram, | |||
| ${getLib pkgs.dnscrypt-proxy}/lib/dnscrypt-proxy/libdcplugin*.so mr, | |||
| ${getLib pkgs.gcc.cc}/lib/libssp.so.* mr, | |||
| ${getLib pkgs.libsodium}/lib/libsodium.so.* mr, | |||
| ${getLib pkgs.systemd}/lib/libsystemd.so.* mr, | |||
| ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so.* mr, | |||
| ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so.* mr, | |||
| ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so.* mr, | |||
| ${getLib pkgs.xz}/lib/liblzma.so.* mr, | |||
| ${getLib pkgs.libgcrypt}/lib/libgcrypt.so.* mr, | |||
| ${getLib pkgs.libgpgerror}/lib/libgpg-error.so.* mr, | |||
| ${getLib pkgs.libcap}/lib/libcap.so.* mr, | |||
| ${getLib pkgs.lz4}/lib/liblz4.so.* mr, | |||
| ${getLib pkgs.attr}/lib/libattr.so.* mr, # */ | |||
| ${resolverList} r, | |||
| /run/systemd/notify rw, | |||
| } | |||
| ''); | |||
| }) | |||
| (mkIf useUpstreamResolverList { | |||
| systemd.services.init-dnscrypt-proxy-statedir = { | |||
| description = "Initialize dnscrypt-proxy state directory"; | |||
| wantedBy = [ "dnscrypt-proxy.service" ]; | |||
| before = [ "dnscrypt-proxy.service" ]; | |||
| script = '' | |||
| mkdir -pv ${stateDirectory} | |||
| chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory} | |||
| cp -uv \ | |||
| ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \ | |||
| ${stateDirectory} | |||
| ''; | |||
| serviceConfig = { | |||
| Type = "oneshot"; | |||
| RemainAfterExit = true; | |||
| }; | |||
| }; | |||
| systemd.services.update-dnscrypt-resolvers = { | |||
| description = "Update list of DNSCrypt resolvers"; | |||
| requires = [ "init-dnscrypt-proxy-statedir.service" ]; | |||
| after = [ "init-dnscrypt-proxy-statedir.service" ]; | |||
| path = with pkgs; [ curl diffutils dnscrypt-proxy minisign ]; | |||
| script = '' | |||
| cd ${stateDirectory} | |||
| domain=raw.githubusercontent.com | |||
| get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)" | |||
| $get -o dnscrypt-resolvers.csv.tmp \ | |||
| https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv | |||
| $get -o dnscrypt-resolvers.csv.minisig.tmp \ | |||
| https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig | |||
| mv dnscrypt-resolvers.csv.minisig{.tmp,} | |||
| if ! minisign -q -V -p ${upstreamResolverListPubKey} \ | |||
| -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then | |||
| echo "failed to verify resolver list!" >&2 | |||
| exit 1 | |||
| fi | |||
| [[ -f dnscrypt-resolvers.csv ]] && mv dnscrypt-resolvers.csv{,.old} | |||
| mv dnscrypt-resolvers.csv{.tmp,} | |||
| if cmp dnscrypt-resolvers.csv{,.old} ; then | |||
| echo "no change" | |||
| else | |||
| echo "resolver list updated" | |||
| fi | |||
| ''; | |||
| serviceConfig = { | |||
| PrivateTmp = true; | |||
| PrivateDevices = true; | |||
| ProtectHome = true; | |||
| ProtectSystem = "strict"; | |||
| ReadWritePaths = "${dirOf stateDirectory} ${stateDirectory}"; | |||
| SystemCallFilter = "~@mount"; | |||
| }; | |||
| }; | |||
| systemd.timers.update-dnscrypt-resolvers = { | |||
| wantedBy = [ "timers.target" ]; | |||
| timerConfig = { | |||
| OnBootSec = "5min"; | |||
| OnUnitActiveSec = "6h"; | |||
| }; | |||
| }; | |||
| }) | |||
| ]); | |||
| imports = [ | |||
| (mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ]) | |||
| (mkChangedOptionModule | |||
| [ "services" "dnscrypt-proxy" "tcpOnly" ] | |||
| [ "services" "dnscrypt-proxy" "extraArgs" ] | |||
| (config: | |||
| let val = getAttrFromPath [ "services" "dnscrypt-proxy" "tcpOnly" ] config; in | |||
| optional val "-T")) | |||
| (mkChangedOptionModule | |||
| [ "services" "dnscrypt-proxy" "ephemeralKeys" ] | |||
| [ "services" "dnscrypt-proxy" "extraArgs" ] | |||
| (config: | |||
| let val = getAttrFromPath [ "services" "dnscrypt-proxy" "ephemeralKeys" ] config; in | |||
| optional val "-E")) | |||
| (mkRemovedOptionModule [ "services" "dnscrypt-proxy" "resolverList" ] '' | |||
| The current resolver listing from upstream is always used | |||
| unless a custom resolver is specified. | |||
| '') | |||
| ]; | |||
| } | |||
| @@ -1,66 +0,0 @@ | |||
| <chapter xmlns="http://docbook.org/ns/docbook" | |||
| xmlns:xlink="http://www.w3.org/1999/xlink" | |||
| xmlns:xi="http://www.w3.org/2001/XInclude" | |||
| version="5.0" | |||
| xml:id="sec-dnscrypt-proxy"> | |||
| <title>DNSCrypt client proxy</title> | |||
| <para> | |||
| The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled upstream | |||
| resolver. The traffic between the client and the upstream resolver is | |||
| encrypted and authenticated, mitigating the risk of MITM attacks, DNS | |||
| poisoning attacks, and third-party snooping (assuming the upstream is | |||
| trustworthy). | |||
| </para> | |||
| <sect1 xml:id="sec-dnscrypt-proxy-configuration"> | |||
| <title>Basic configuration</title> | |||
| <para> | |||
| To enable the client proxy, set | |||
| <programlisting> | |||
| <xref linkend="opt-services.dnscrypt-proxy.enable"/> = true; | |||
| </programlisting> | |||
| </para> | |||
| <para> | |||
| Enabling the client proxy does not alter the system nameserver; to relay | |||
| local queries, prepend <literal>127.0.0.1</literal> to | |||
| <option>networking.nameservers</option>. | |||
| </para> | |||
| </sect1> | |||
| <sect1 xml:id="sec-dnscrypt-proxy-forwarder"> | |||
| <title>As a forwarder for another DNS client</title> | |||
| <para> | |||
| To run the DNSCrypt proxy client as a forwarder for another DNS client, | |||
| change the default proxy listening port to a non-standard value and point | |||
| the other client to it: | |||
| <programlisting> | |||
| <xref linkend="opt-services.dnscrypt-proxy.localPort"/> = 43; | |||
| </programlisting> | |||
| </para> | |||
| <sect2 xml:id="sec-dnscrypt-proxy-forwarder-dsnmasq"> | |||
| <title>dnsmasq</title> | |||
| <para> | |||
| <programlisting> | |||
| { | |||
| <xref linkend="opt-services.dnsmasq.enable"/> = true; | |||
| <xref linkend="opt-services.dnsmasq.servers"/> = [ "127.0.0.1#43" ]; | |||
| } | |||
| </programlisting> | |||
| </para> | |||
| </sect2> | |||
| <sect2 xml:id="sec-dnscrypt-proxy-forwarder-unbound"> | |||
| <title>unbound</title> | |||
| <para> | |||
| <programlisting> | |||
| { | |||
| <xref linkend="opt-services.unbound.enable"/> = true; | |||
| <xref linkend="opt-services.unbound.forwardAddresses"/> = [ "127.0.0.1@43" ]; | |||
| } | |||
| </programlisting> | |||
| </para> | |||
| </sect2> | |||
| </sect1> | |||
| </chapter> | |||
| @@ -0,0 +1,61 @@ | |||
| { config, lib, pkgs, ... }: with lib; | |||
| let | |||
| cfg = config.services.dnscrypt-proxy2; | |||
| in | |||
| { | |||
| options.services.dnscrypt-proxy2 = { | |||
| enable = mkEnableOption "dnscrypt-proxy2"; | |||
| settings = mkOption { | |||
| description = '' | |||
| Attrset that is converted and passed as TOML config file. | |||
| For available params, see: <link xlink:href="https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/dnscrypt-proxy/example-dnscrypt-proxy.toml"/> | |||
| ''; | |||
| example = literalExample '' | |||
| { | |||
| sources.public-resolvers = { | |||
| urls = [ "https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md" ]; | |||
| cache_file = "public-resolvers.md"; | |||
| minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"; | |||
| refresh_delay = 72; | |||
| }; | |||
| } | |||
| ''; | |||
| type = types.attrs; | |||
| default = {}; | |||
| }; | |||
| configFile = mkOption { | |||
| description = '' | |||
| Path to TOML config file. See: <link xlink:href="https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/dnscrypt-proxy/example-dnscrypt-proxy.toml"/> | |||
| If this option is set, it will override any configuration done in options.services.dnscrypt-proxy2.settings. | |||
| ''; | |||
| example = "/etc/dnscrypt-proxy/dnscrypt-proxy.toml"; | |||
| type = types.path; | |||
| default = pkgs.runCommand "dnscrypt-proxy.toml" { | |||
| json = builtins.toJSON cfg.settings; | |||
| passAsFile = [ "json" ]; | |||
| } '' | |||
| ${pkgs.remarshal}/bin/json2toml < $jsonPath > $out | |||
| ''; | |||
| defaultText = literalExample "TOML file generated from services.dnscrypt-proxy2.settings"; | |||
| }; | |||
| }; | |||
| config = mkIf cfg.enable { | |||
| networking.nameservers = lib.mkDefault [ "127.0.0.1" ]; | |||
| systemd.services.dnscrypt-proxy2 = { | |||
| after = [ "network.target" ]; | |||
| wantedBy = [ "multi-user.target" ]; | |||
| serviceConfig = { | |||
| AmbientCapabilities = "CAP_NET_BIND_SERVICE"; | |||
| DynamicUser = true; | |||
| ExecStart = "${pkgs.dnscrypt-proxy2}/bin/dnscrypt-proxy -config ${cfg.configFile}"; | |||
| }; | |||
| }; | |||
| }; | |||
| } | |||
| @@ -24,13 +24,18 @@ in { | |||
| config = mkIf cfg.enable { | |||
| # Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/keybase.service | |||
| systemd.user.services.keybase = { | |||
| description = "Keybase service"; | |||
| unitConfig.ConditionUser = "!@system"; | |||
| environment.KEYBASE_SERVICE_TYPE = "systemd"; | |||
| serviceConfig = { | |||
| ExecStart = '' | |||
| ${pkgs.keybase}/bin/keybase service --auto-forked | |||
| ''; | |||
| Type = "notify"; | |||
| EnvironmentFile = [ | |||
| "-%E/keybase/keybase.autogen.env" | |||
| "-%E/keybase/keybase.env" | |||
| ]; | |||
| ExecStart = "${pkgs.keybase}/bin/keybase service"; | |||
| Restart = "on-failure"; | |||
| PrivateTmp = true; | |||
| }; | |||
| @@ -56,6 +56,7 @@ in { | |||
| package = mkOption { | |||
| type = types.package; | |||
| default = pkgs.knot-dns; | |||
| defaultText = "pkgs.knot-dns"; | |||
| description = '' | |||
| Which Knot DNS package to use | |||
| ''; | |||
| @@ -92,4 +93,3 @@ in { | |||
| environment.systemPackages = [ knot-cli-wrappers ]; | |||
| }; | |||
| } | |||
| @@ -3,14 +3,39 @@ | |||
| with lib; | |||
| let | |||
| cfg = config.services.kresd; | |||
| package = pkgs.knot-resolver; | |||
| configFile = pkgs.writeText "kresd.conf" cfg.extraConfig; | |||
| in | |||
| # Convert systemd-style address specification to kresd config line(s). | |||
| # On Nix level we don't attempt to precisely validate the address specifications. | |||
| mkListen = kind: addr: let | |||
| al_v4 = builtins.match "([0-9.]\+):([0-9]\+)" addr; | |||
| al_v6 = builtins.match "\\[(.\+)]:([0-9]\+)" addr; | |||
| al_portOnly = builtins.match "()([0-9]\+)" addr; | |||
| al = findFirst (a: a != null) | |||
| (throw "services.kresd.*: incorrect address specification '${addr}'") | |||
| [ al_v4 al_v6 al_portOnly ]; | |||
| port = last al; | |||
| addrSpec = if al_portOnly == null then "'${head al}'" else "{'::', '127.0.0.1'}"; | |||
| in # freebind is set for compatibility with earlier kresd services; | |||
| # it could be configurable, for example. | |||
| '' | |||
| net.listen(${addrSpec}, ${port}, { kind = '${kind}', freebind = true }) | |||
| ''; | |||
| { | |||
| configFile = pkgs.writeText "kresd.conf" ( | |||
| optionalString (cfg.listenDoH != []) '' | |||
| modules.load('http') | |||
| '' | |||
| + concatMapStrings (mkListen "dns") cfg.listenPlain | |||
| + concatMapStrings (mkListen "tls") cfg.listenTLS | |||
| + concatMapStrings (mkListen "doh") cfg.listenDoH | |||
| + cfg.extraConfig | |||
| ); | |||
| package = pkgs.knot-resolver.override { | |||
| extraFeatures = cfg.listenDoH != []; | |||
| }; | |||
| in { | |||
| meta.maintainers = [ maintainers.vcunat /* upstream developer */ ]; | |||
| imports = [ | |||
| @@ -22,6 +47,7 @@ in | |||
| value | |||
| ) | |||
| ) | |||
| (mkRemovedOptionModule [ "services" "kresd" "cacheDir" ] "Please use (bind-)mounting instead.") | |||
| ]; | |||
| ###### interface | |||
| @@ -32,8 +58,8 @@ in | |||
| description = '' | |||
| Whether to enable knot-resolver domain name server. | |||
| DNSSEC validation is turned on by default. | |||
| You can run <literal>sudo nc -U /run/kresd/control</literal> | |||
| and give commands interactively to kresd. | |||
| You can run <literal>sudo nc -U /run/knot-resolver/control/1</literal> | |||
| and give commands interactively to kresd@1.service. | |||
| ''; | |||
| }; | |||
| extraConfig = mkOption { | |||
| @@ -43,16 +69,10 @@ in | |||
| Extra lines to be added verbatim to the generated configuration file. | |||
| ''; | |||
| }; | |||
| cacheDir = mkOption { | |||
| type = types.path; | |||
| default = "/var/cache/kresd"; | |||
| description = '' | |||
| Directory for caches. They are intended to survive reboots. | |||
| ''; | |||
| }; | |||
| listenPlain = mkOption { | |||
| type = with types; listOf str; | |||
| default = [ "[::1]:53" "127.0.0.1:53" ]; | |||
| example = [ "53" ]; | |||
| description = '' | |||
| What addresses and ports the server should listen on. | |||
| For detailed syntax see ListenStream in man systemd.socket. | |||
| @@ -67,75 +87,59 @@ in | |||
| For detailed syntax see ListenStream in man systemd.socket. | |||
| ''; | |||
| }; | |||
| listenDoH = mkOption { | |||
| type = with types; listOf str; | |||
| default = []; | |||
| example = [ "198.51.100.1:443" "[2001:db8::1]:443" "443" ]; | |||
| description = '' | |||
| Addresses and ports on which kresd should provide DNS over HTTPS (see RFC 8484). | |||
| For detailed syntax see ListenStream in man systemd.socket. | |||
| ''; | |||
| }; | |||
| instances = mkOption { | |||
| type = types.ints.unsigned; | |||
| default = 1; | |||
| description = '' | |||
| The number of instances to start. They will be called kresd@{1,2,...}.service. | |||
| Knot Resolver uses no threads, so this is the way to scale. | |||
| You can dynamically start/stop them at will, so this is just system default. | |||
| ''; | |||
| }; | |||
| # TODO: perhaps options for more common stuff like cache size or forwarding | |||
| }; | |||
| ###### implementation | |||
| config = mkIf cfg.enable { | |||
| environment.etc."kresd.conf".source = configFile; # not required | |||
| environment.etc."knot-resolver/kresd.conf".source = configFile; # not required | |||
| users.users.kresd = | |||
| { uid = config.ids.uids.kresd; | |||
| group = "kresd"; | |||
| users.users.knot-resolver = | |||
| { isSystemUser = true; | |||
| group = "knot-resolver"; | |||
| description = "Knot-resolver daemon user"; | |||
| }; | |||
| users.groups.kresd.gid = config.ids.gids.kresd; | |||
| users.groups.knot-resolver.gid = null; | |||
| systemd.sockets.kresd = rec { | |||
| wantedBy = [ "sockets.target" ]; | |||
| before = wantedBy; | |||
| listenStreams = cfg.listenPlain; | |||
| socketConfig = { | |||
| ListenDatagram = listenStreams; | |||
| FreeBind = true; | |||
| FileDescriptorName = "dns"; | |||
| }; | |||
| }; | |||
| systemd.packages = [ package ]; # the units are patched inside the package a bit | |||
| systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec { | |||
| wantedBy = [ "sockets.target" ]; | |||
| before = wantedBy; | |||
| partOf = [ "kresd.socket" ]; | |||
| listenStreams = cfg.listenTLS; | |||
| socketConfig = { | |||
| FileDescriptorName = "tls"; | |||
| FreeBind = true; | |||
| Service = "kresd.service"; | |||
| }; | |||
| systemd.targets.kresd = { # configure units started by default | |||
| wantedBy = [ "multi-user.target" ]; | |||
| wants = [ "kres-cache-gc.service" ] | |||
| ++ map (i: "kresd@${toString i}.service") (range 1 cfg.instances); | |||
| }; | |||
| systemd.sockets.kresd-control = rec { | |||
| wantedBy = [ "sockets.target" ]; | |||
| before = wantedBy; | |||
| partOf = [ "kresd.socket" ]; | |||
| listenStreams = [ "/run/kresd/control" ]; | |||
| socketConfig = { | |||
| FileDescriptorName = "control"; | |||
| Service = "kresd.service"; | |||
| SocketMode = "0660"; # only root user/group may connect and control kresd | |||
| }; | |||
| systemd.services."kresd@".serviceConfig = { | |||
| ExecStart = "${package}/bin/kresd --noninteractive " | |||
| + "-c ${package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}"; | |||
| # Ensure correct ownership in case UID or GID changes. | |||
| CacheDirectory = "knot-resolver"; | |||
| CacheDirectoryMode = "0750"; | |||
| }; | |||
| systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ]; | |||
| environment.etc."tmpfiles.d/knot-resolver.conf".source = | |||
| "${package}/lib/tmpfiles.d/knot-resolver.conf"; | |||
| systemd.services.kresd = { | |||
| description = "Knot-resolver daemon"; | |||
| serviceConfig = { | |||
| User = "kresd"; | |||
| Type = "notify"; | |||
| WorkingDirectory = cfg.cacheDir; | |||
| Restart = "on-failure"; | |||
| Sockets = [ "kresd.socket" "kresd-control.socket" ] | |||
| ++ optional (cfg.listenTLS != []) "kresd-tls.socket"; | |||
| }; | |||
| # Trust anchor goes from dns-root-data by default. | |||
| script = '' | |||
| exec '${package}/bin/kresd' --config '${configFile}' --forks=1 | |||
| ''; | |||
| requires = [ "kresd.socket" ]; | |||
| }; | |||
| # Try cleaning up the previously default location of cache file. | |||
| # Note that /var/cache/* should always be safe to remove. | |||
| # TODO: remove later, probably between 20.09 and 21.03 | |||
| systemd.tmpfiles.rules = [ "R /var/cache/kresd" ]; | |||
| }; | |||
| } | |||
| @@ -111,7 +111,7 @@ in | |||
| serviceConfig = { | |||
| User = cfg.user; | |||
| Group = cfg.group; | |||
| ExecStart = "${pkgs.matterbridge.bin}/bin/matterbridge -conf ${matterbridgeConfToml}"; | |||
| ExecStart = "${pkgs.matterbridge}/bin/matterbridge -conf ${matterbridgeConfToml}"; | |||
| Restart = "always"; | |||
| RestartSec = "10"; | |||
| }; | |||