nixos/acme: Update release note, remove redundant requires

Merge remote-tracking branch 'remotes/upstream/master'
This commit is contained in:
Lucas Savva 2020-02-09 16:31:07 +00:00
commit 75fa8027eb
No known key found for this signature in database
GPG Key ID: F9CE6D3DCDC78F2D
2114 changed files with 45244 additions and 37631 deletions

4
.github/CODEOWNERS vendored
View File

@ -14,7 +14,9 @@
/lib @edolstra @nbp @infinisil /lib @edolstra @nbp @infinisil
/lib/systems @nbp @ericson2314 @matthewbauer /lib/systems @nbp @ericson2314 @matthewbauer
/lib/generators.nix @edolstra @nbp @Profpatsch /lib/generators.nix @edolstra @nbp @Profpatsch
/lib/cli.nix @edolstra @nbp @Profpatsch
/lib/debug.nix @edolstra @nbp @Profpatsch /lib/debug.nix @edolstra @nbp @Profpatsch
/lib/asserts.nix @edolstra @nbp @Profpatsch
# Nixpkgs Internals # Nixpkgs Internals
/default.nix @nbp /default.nix @nbp
@ -162,7 +164,7 @@
/pkgs/top-level/emacs-packages.nix @adisbladis /pkgs/top-level/emacs-packages.nix @adisbladis
# VimPlugins # VimPlugins
/pkgs/misc/vim-plugins @jonringer /pkgs/misc/vim-plugins @jonringer @softinio
# VsCode Extensions # VsCode Extensions
/pkgs/misc/vscode-extensions @jonringer /pkgs/misc/vscode-extensions @jonringer

View File

@ -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. 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 ## Reviewing contributions
See the nixpkgs manual for more details on how to [Review contributions](https://nixos.org/nixpkgs/manual/#chap-reviewing-contributions). See the nixpkgs manual for more details on how to [Review contributions](https://nixos.org/nixpkgs/manual/#chap-reviewing-contributions).

View File

@ -80,7 +80,7 @@ appimageTools.wrapType2 { # or wrapType1
<varname>src</varname> specifies the AppImage file to extract. <varname>src</varname> specifies the AppImage file to extract.
</para> </para>
</callout> </callout>
<callout arearefs='ex-appimageTools-wrapping-2'> <callout arearefs='ex-appimageTools-wrapping-3'>
<para> <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: <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> <itemizedlist>

View File

@ -496,8 +496,8 @@ and in this case the `python35` interpreter is automatically used.
### Interpreters ### Interpreters
Versions 2.7, 3.5, 3.6 and 3.7 of the CPython interpreter are available as Versions 2.7, 3.5, 3.6, 3.7 and 3.8 of the CPython interpreter are available as
respectively `python27`, `python35`, `python36` and `python37`. The aliases respectively `python27`, `python35`, `python36`, `python37` and `python38`. The aliases
`python2` and `python3` correspond to respectively `python27` and `python2` and `python3` correspond to respectively `python27` and
`python37`. The default interpreter, `python`, maps to `python2`. The PyPy `python37`. The default interpreter, `python`, maps to `python2`. The PyPy
interpreters compatible with Python 2.7 and 3 are available as `pypy27` and 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. - `pythonRemoveBinBytecode` to remove bytecode from the `/bin` folder.
- `setuptoolsBuildHook` to build a wheel using `setuptools`. - `setuptoolsBuildHook` to build a wheel using `setuptools`.
- `setuptoolsCheckHook` to run tests with `python setup.py test`. - `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`. - `wheelUnpackHook` to move a wheel to the correct folder so it can be installed with the `pipInstallHook`.
### Development mode ### 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` method is to use `nix-shell`, either with or without the `python.buildEnv`
function. 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. and install python modules through `pip` the traditional way.
Create this `default.nix` file, together with a `requirements.txt` and simply execute `nix-shell`. Create this `default.nix` file, together with a `requirements.txt` and simply execute `nix-shell`.
```nix ```nix
with import <nixpkgs> {}; with import <nixpkgs> { };
let let
pythonPackages = python27Packages; pythonPackages = python3Packages;
in in pkgs.mkShell rec {
stdenv.mkDerivation {
name = "impurePythonEnv"; name = "impurePythonEnv";
venvDir = "./.venv";
src = null;
buildInputs = [ buildInputs = [
# these packages are required for virtualenv and pip to work: # A python interpreter including the 'venv' module is required to bootstrap
# # the environment.
pythonPackages.virtualenv pythonPackages.python
pythonPackages.pip
# the following packages are related to the dependencies of your python # This execute some shell code to initialize a venv in $venvDir before
# project. # dropping into the shell
# In this particular example the python modules listed in the pythonPackages.venvShellHook
# requirements.txt require the following packages to be installed locally
# in order to compile any binary extensions they may require. # 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 taglib
openssl openssl
git git
@ -1067,11 +1073,54 @@ stdenv.mkDerivation {
zlib 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 = '' shellHook = ''
# set SOURCE_DATE_EPOCH so that we can use python wheels
SOURCE_DATE_EPOCH=$(date +%s) 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 pip install -r requirements.txt
''; '';
} }

View File

@ -16,12 +16,6 @@ cargo
into the `environment.systemPackages` or bring them into into the `environment.systemPackages` or bring them into
scope with `nix-shell -p rustc cargo`. 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 For daily builds (beta and nightly) use either rustup from
nixpkgs or use the [Rust nightlies nixpkgs or use the [Rust nightlies
overlay](#using-the-rust-nightlies-overlay). overlay](#using-the-rust-nightlies-overlay).

View File

@ -1,9 +1,22 @@
.docbook .xref img[src^=images\/callouts\/], .docbook .xref img[src^=images\/callouts\/],
.screen img, .screen img,
.programlisting img { .programlisting img,
.literallayout img,
.synopsis img {
width: 1em; width: 1em;
} }
.calloutlist img { .calloutlist img {
width: 1.5em; 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;
}

View File

@ -60,7 +60,7 @@ rec {
[ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ]; [ { 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. path it will throw.
Example: Example:

83
lib/cli.nix Normal file
View File

@ -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 its 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);
}

View File

@ -37,10 +37,13 @@ let
licenses = callLibs ./licenses.nix; licenses = callLibs ./licenses.nix;
systems = callLibs ./systems; systems = callLibs ./systems;
# serialization
cli = callLibs ./cli.nix;
generators = callLibs ./generators.nix;
# misc # misc
asserts = callLibs ./asserts.nix; asserts = callLibs ./asserts.nix;
debug = callLibs ./debug.nix; debug = callLibs ./debug.nix;
generators = callLibs ./generators.nix;
misc = callLibs ./deprecated.nix; misc = callLibs ./deprecated.nix;
# domain-specific # domain-specific
@ -100,7 +103,7 @@ let
inherit (sources) pathType pathIsDirectory cleanSourceFilter inherit (sources) pathType pathIsDirectory cleanSourceFilter
cleanSource sourceByRegex sourceFilesBySuffices cleanSource sourceByRegex sourceFilesBySuffices
commitIdFromGitRepo cleanSourceWith pathHasContext commitIdFromGitRepo cleanSourceWith pathHasContext
canCleanSource; canCleanSource pathIsRegularFile pathIsGitRepo;
inherit (modules) evalModules unifyModuleSyntax inherit (modules) evalModules unifyModuleSyntax
applyIfFunction mergeModules applyIfFunction mergeModules
mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions

View File

@ -46,7 +46,10 @@ rec {
else if isList v then err "lists" v else if isList v then err "lists" v
# same as for lists, might want to replace # same as for lists, might want to replace
else if isAttrs v then err "attrsets" v else if isAttrs v then err "attrsets" v
# functions cant be printed of course
else if isFunction v then err "functions" v else if isFunction v then err "functions" v
# lets 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); else err "this value is" (toString v);

View File

@ -536,11 +536,6 @@ lib.mapAttrs (n: v: v // { shortName = n; }) {
fullName = "University of Illinois/NCSA Open Source License"; 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 { nposl3 = spdx {
spdxId = "NPOSL-3.0"; spdxId = "NPOSL-3.0";
fullName = "Non-Profit Open Software License 3.0"; fullName = "Non-Profit Open Software License 3.0";

View File

@ -764,12 +764,15 @@ rec {
fromOpt = getAttrFromPath from options; fromOpt = getAttrFromPath from options;
toOf = attrByPath to toOf = attrByPath to
(abort "Renaming error: option `${showOption to}' does not exist."); (abort "Renaming error: option `${showOption to}' does not exist.");
toType = let opt = attrByPath to {} options; in opt.type or null;
in in
{ {
options = setAttrByPath from (mkOption { options = setAttrByPath from (mkOption {
inherit visible; inherit visible;
description = "Alias of <option>${showOption to}</option>."; description = "Alias of <option>${showOption to}</option>.";
apply = x: use (toOf config); apply = x: use (toOf config);
} // optionalAttrs (toType != null) {
type = toType;
}); });
config = mkMerge [ config = mkMerge [
{ {

View File

@ -9,6 +9,9 @@ rec {
# Returns true if the path exists and is a directory, false otherwise # Returns true if the path exists and is a directory, false otherwise
pathIsDirectory = p: if builtins.pathExists p then (pathType p) == "directory" else false; 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 # Bring in a path as a source, filtering out all Subversion and CVS
# directories, as well as backup files (*~). # directories, as well as backup files (*~).
cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! ( 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 type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts;
in cleanSourceWith { inherit filter; src = path; }; in cleanSourceWith { inherit filter; src = path; };
pathIsGitRepo = path: (builtins.tryEval (commitIdFromGitRepo path)).success;
# Get the commit id of a git repo # Get the commit id of a git repo
# Example: commitIdFromGitRepo <nixpkgs/.git> # Example: commitIdFromGitRepo <nixpkgs/.git>
@ -110,24 +114,45 @@ rec {
with builtins; with builtins;
let fileName = toString path + "/" + file; let fileName = toString path + "/" + file;
packedRefsName = toString path + "/packed-refs"; 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 then
let fileContent = lib.fileContents fileName; 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 git stores the commitId directly in the file but
# sometimes it stores something like: «ref: refs/heads/branch-name» # sometimes it stores something like: «ref: refs/heads/branch-name»
then
let fileContent = lib.fileContents fileName;
matchRef = match "^ref: (.*)$" fileContent; matchRef = match "^ref: (.*)$" fileContent;
in if matchRef == null in if matchRef == null
then fileContent then fileContent
else readCommitFromFile (lib.head matchRef) path 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 # 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: # packed-refs file, so we have to grep through it:
else if lib.pathExists packedRefsName
then then
let fileContent = readFile packedRefsName; let fileContent = readFile packedRefsName;
matchRef = match (".*\n([^\n ]*) " + file + "\n.*") fileContent; matchRef = match (".*\n([^\n ]*) " + file + "\n.*") fileContent;
in if matchRef == null in if matchRef == null
then throw ("Could not find " + file + " in " + packedRefsName) then throw ("Could not find " + file + " in " + packedRefsName)
else lib.head matchRef else lib.head matchRef
else throw ("Not a .git directory: " + path); else throw ("Not a .git directory: " + path);
in readCommitFromFile "HEAD"; in readCommitFromFile "HEAD";

View File

@ -244,7 +244,7 @@ rec {
Also note that Nix treats strings as a list of bytes and thus doesn't Also note that Nix treats strings as a list of bytes and thus doesn't
handle unicode. handle unicode.
Type: stringtoCharacters :: string -> [string] Type: stringToCharacters :: string -> [string]
Example: Example:
stringToCharacters "" stringToCharacters ""

View File

@ -84,7 +84,7 @@ rec {
else final.parsed.cpu.name; else final.parsed.cpu.name;
qemuArch = qemuArch =
if final.isArm then "arm" if final.isAarch32 then "arm"
else if final.isx86_64 then "x86_64" else if final.isx86_64 then "x86_64"
else if final.isx86 then "i386" else if final.isx86 then "i386"
else { else {

View File

@ -170,8 +170,8 @@ rec {
iphone64 = { iphone64 = {
config = "aarch64-apple-ios"; config = "aarch64-apple-ios";
# config = "aarch64-apple-darwin14"; # config = "aarch64-apple-darwin14";
sdkVer = "10.2"; sdkVer = "12.4";
xcodeVer = "8.2"; xcodeVer = "10.3";
xcodePlatform = "iPhoneOS"; xcodePlatform = "iPhoneOS";
useiOSPrebuilt = true; useiOSPrebuilt = true;
platform = {}; platform = {};
@ -180,8 +180,8 @@ rec {
iphone32 = { iphone32 = {
config = "armv7a-apple-ios"; config = "armv7a-apple-ios";
# config = "arm-apple-darwin10"; # config = "arm-apple-darwin10";
sdkVer = "10.2"; sdkVer = "12.4";
xcodeVer = "8.2"; xcodeVer = "10.3";
xcodePlatform = "iPhoneOS"; xcodePlatform = "iPhoneOS";
useiOSPrebuilt = true; useiOSPrebuilt = true;
platform = {}; platform = {};
@ -190,8 +190,8 @@ rec {
iphone64-simulator = { iphone64-simulator = {
config = "x86_64-apple-ios"; config = "x86_64-apple-ios";
# config = "x86_64-apple-darwin14"; # config = "x86_64-apple-darwin14";
sdkVer = "10.2"; sdkVer = "12.4";
xcodeVer = "8.2"; xcodeVer = "10.3";
xcodePlatform = "iPhoneSimulator"; xcodePlatform = "iPhoneSimulator";
useiOSPrebuilt = true; useiOSPrebuilt = true;
platform = {}; platform = {};
@ -200,8 +200,8 @@ rec {
iphone32-simulator = { iphone32-simulator = {
config = "i686-apple-ios"; config = "i686-apple-ios";
# config = "i386-apple-darwin11"; # config = "i386-apple-darwin11";
sdkVer = "10.2"; sdkVer = "12.4";
xcodeVer = "8.2"; xcodeVer = "10.3";
xcodePlatform = "iPhoneSimulator"; xcodePlatform = "iPhoneSimulator";
useiOSPrebuilt = true; useiOSPrebuilt = true;
platform = {}; platform = {};

View File

@ -55,9 +55,6 @@ rec {
isEfi = map (family: { cpu.family = family; }) isEfi = map (family: { cpu.family = family; })
[ "x86" "arm" "aarch64" ]; [ "x86" "arm" "aarch64" ];
# Deprecated after 18.03
isArm = isAarch32;
}; };
matchAnyAttrs = patterns: matchAnyAttrs = patterns:

View File

@ -441,4 +441,41 @@ runTests {
expected = "«foo»"; 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'";
};
} }

View File

@ -191,7 +191,7 @@ rec {
let let
revisionFile = "${toString ./..}/.git-revision"; revisionFile = "${toString ./..}/.git-revision";
gitRepo = "${toString ./..}/.git"; gitRepo = "${toString ./..}/.git";
in if lib.pathIsDirectory gitRepo in if lib.pathIsGitRepo gitRepo
then lib.commitIdFromGitRepo gitRepo then lib.commitIdFromGitRepo gitRepo
else if lib.pathExists revisionFile then lib.fileContents revisionFile else if lib.pathExists revisionFile then lib.fileContents revisionFile
else default; else default;

View File

@ -367,18 +367,6 @@ rec {
{ path = [ "services" "geoclue2" "appConfig" ]; { path = [ "services" "geoclue2" "appConfig" ];
name = "desktopID"; 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 matched = let
equals = a: b: b == anyString || a == b; equals = a: b: b == anyString || a == b;
@ -418,7 +406,7 @@ rec {
In file ${def.file} In file ${def.file}
a list is being assigned to the option config.${option}. a list is being assigned to the option config.${option}.
This will soon be an error as type loaOf is deprecated. 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 Do
${option} = ${option} =
{ ${set}${more}} { ${set}${more}}
@ -602,7 +590,7 @@ rec {
tail' = tail ts; tail' = tail ts;
in foldl' either head' tail'; 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`. # converted to `finalType` using `coerceFunc`.
coercedTo = coercedType: coerceFunc: finalType: coercedTo = coercedType: coerceFunc: finalType:
assert lib.assertMsg (coercedType.getSubModules == null) assert lib.assertMsg (coercedType.getSubModules == null)
@ -611,12 +599,12 @@ rec {
mkOptionType rec { mkOptionType rec {
name = "coercedTo"; name = "coercedTo";
description = "${finalType.description} or ${coercedType.description} convertible to it"; 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: merge = loc: defs:
let let
coerceVal = val: coerceVal = val:
if finalType.check val then val if coercedType.check val then coerceFunc val
else coerceFunc val; else val;
in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
emptyValue = finalType.emptyValue; emptyValue = finalType.emptyValue;
getSubOptions = finalType.getSubOptions; getSubOptions = finalType.getSubOptions;

View File

@ -40,12 +40,6 @@
See `./scripts/check-maintainer-github-handles.sh` for an example on how to work with this data. 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" = { "0x4A6F" = {
email = "0x4A6F@shackspace.de"; email = "0x4A6F@shackspace.de";
name = "Joachim Ernst"; name = "Joachim Ernst";
@ -517,6 +511,12 @@
githubId = 5327697; githubId = 5327697;
name = "Anatolii Prylutskyi"; name = "Anatolii Prylutskyi";
}; };
antoinerg = {
email = "roygobeil.antoine@gmail.com";
github = "antoinerg";
githubId = 301546;
name = "Antoine Roy-Gobeil";
};
anton-dessiatov = { anton-dessiatov = {
email = "anton.dessiatov@gmail.com"; email = "anton.dessiatov@gmail.com";
github = "anton-dessiatov"; github = "anton-dessiatov";
@ -594,6 +594,12 @@
githubId = 1296771; githubId = 1296771;
name = "Anders Riutta"; name = "Anders Riutta";
}; };
arnoldfarkas = {
email = "arnold.farkas@gmail.com";
github = "arnoldfarkas";
githubId = 59696216;
name = "Arnold Farkas";
};
arobyn = { arobyn = {
email = "shados@shados.net"; email = "shados@shados.net";
github = "shados"; github = "shados";
@ -676,6 +682,12 @@
githubId = 192147; githubId = 192147;
name = "aszlig"; name = "aszlig";
}; };
atemu = {
name = "Atemu";
email = "atemu.main+nixpkgs@gmail.com";
github = "Atemu";
githubId = 18599032;
};
athas = { athas = {
email = "athas@sigkill.dk"; email = "athas@sigkill.dk";
github = "athas"; github = "athas";
@ -951,6 +963,12 @@
githubId = 5718007; githubId = 5718007;
name = "Bastian Köcher"; name = "Bastian Köcher";
}; };
blanky0230 = {
email = "blanky0230@gmail.com";
github = "blanky0230";
githubId = 5700358;
name = "Thomas Blank";
};
blitz = { blitz = {
email = "js@alien8.de"; email = "js@alien8.de";
github = "blitz"; github = "blitz";
@ -1893,6 +1911,12 @@
email = "burkett.andrew@gmail.com"; email = "burkett.andrew@gmail.com";
name = "Andrew Burkett"; name = "Andrew Burkett";
}; };
drewrisinger = {
email = "drisinger+nixpkgs@gmail.com";
github = "drewrisinger";
gitHubId = 10198051;
name = "Drew Risinger";
};
dsferruzza = { dsferruzza = {
email = "david.sferruzza@gmail.com"; email = "david.sferruzza@gmail.com";
github = "dsferruzza"; github = "dsferruzza";
@ -1919,6 +1943,12 @@
fingerprint = "5DD7 C6F6 0630 F08E DAE7 4711 1525 585D 1B43 C62A"; fingerprint = "5DD7 C6F6 0630 F08E DAE7 4711 1525 585D 1B43 C62A";
}]; }];
}; };
dwarfmaster = {
email = "nixpkgs@dwarfmaster.net";
github = "dwarfmaster";
githubId = 2025623;
name = "Luc Chabassier";
};
dxf = { dxf = {
email = "dingxiangfei2009@gmail.com"; email = "dingxiangfei2009@gmail.com";
github = "dingxiangfei2009"; github = "dingxiangfei2009";
@ -2393,6 +2423,12 @@
githubId = 415760; githubId = 415760;
name = "Jonas Höglund"; name = "Jonas Höglund";
}; };
fishi0x01 = {
email = "fishi0x01@gmail.com";
github = "fishi0x01";
githubId = 10799507;
name = "Karl Fischer";
};
Flakebi = { Flakebi = {
email = "flakebi@t-online.de"; email = "flakebi@t-online.de";
github = "Flakebi"; github = "Flakebi";
@ -2439,6 +2475,12 @@
githubId = 844574; githubId = 844574;
name = "Daniel Austin"; name = "Daniel Austin";
}; };
flyfloh = {
email = "nix@halbmastwurf.de";
github = "flyfloh";
githubId = 74379;
name = "Florian Pester";
};
fmthoma = { fmthoma = {
email = "f.m.thoma@googlemail.com"; email = "f.m.thoma@googlemail.com";
github = "fmthoma"; github = "fmthoma";
@ -2520,6 +2562,12 @@
githubId = 1943632; githubId = 1943632;
name = "fro_ozen"; name = "fro_ozen";
}; };
Frostman = {
email = "me@slukjanov.name";
github = "Frostman";
githubId = 134872;
name = "Sergei Lukianov";
};
frontsideair = { frontsideair = {
email = "photonia@gmail.com"; email = "photonia@gmail.com";
github = "frontsideair"; github = "frontsideair";
@ -2992,6 +3040,12 @@
githubId = 4401220; githubId = 4401220;
name = "Michael Eden"; name = "Michael Eden";
}; };
ilya-fedin = {
email = "fedin-ilja2010@ya.ru";
github = "ilya-fedin";
githubId = 17829319;
name = "Ilya Fedin";
};
ilya-kolpakov = { ilya-kolpakov = {
email = "ilya.kolpakov@gmail.com"; email = "ilya.kolpakov@gmail.com";
github = "ilya-kolpakov"; github = "ilya-kolpakov";
@ -3474,6 +3528,12 @@
github = "jorsn"; github = "jorsn";
githubId = 4646725; githubId = 4646725;
}; };
jpas = {
name = "Jarrod Pas";
email = "jarrod@jarrodpas.com";
github = "jpas";
githubId = 5689724;
};
jpdoyle = { jpdoyle = {
email = "joethedoyle@gmail.com"; email = "joethedoyle@gmail.com";
github = "jpdoyle"; github = "jpdoyle";
@ -3504,6 +3564,16 @@
githubId = 4611077; githubId = 4611077;
name = "Raymond Gauthier"; 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 = { jtobin = {
email = "jared@jtobin.io"; email = "jared@jtobin.io";
github = "jtobin"; github = "jtobin";
@ -3790,6 +3860,12 @@
githubId = 787421; githubId = 787421;
name = "Kevin Quick"; name = "Kevin Quick";
}; };
kraem = {
email = "me@kraem.xyz";
github = "kraem";
githubId = 26622971;
name = "Ronnie Ebrin";
};
kragniz = { kragniz = {
email = "louis@kragniz.eu"; email = "louis@kragniz.eu";
github = "kragniz"; github = "kragniz";
@ -3808,6 +3884,12 @@
githubId = 17659803; githubId = 17659803;
name = "Matthias Axel Kröll"; name = "Matthias Axel Kröll";
}; };
kristian-brucaj = {
email = "kbrucaj@gmail.com";
github = "kristian-brucaj";
githubID = "8893110";
name = "Kristian Brucaj";
};
kristoff3r = { kristoff3r = {
email = "k.soeholm@gmail.com"; email = "k.soeholm@gmail.com";
github = "kristoff3r"; github = "kristoff3r";
@ -3838,6 +3920,12 @@
githubId = 449813; githubId = 449813;
name = "Roman Kuznetsov"; name = "Roman Kuznetsov";
}; };
kwohlfahrt = {
email = "kai.wohlfahrt@gmail.com";
github = "kwohlfahrt";
githubId = 2422454;
name = "Kai Wohlfahrt";
};
kylesferrazza = { kylesferrazza = {
name = "Kyle Sferrazza"; name = "Kyle Sferrazza";
email = "kyle.sferrazza@gmail.com"; email = "kyle.sferrazza@gmail.com";
@ -3903,6 +3991,12 @@
githubId = 32152; githubId = 32152;
name = "Luka Blaskovic"; name = "Luka Blaskovic";
}; };
ldelelis = {
email = "ldelelis@est.frba.utn.edu.ar";
github = "ldelelis";
githubId = 20250323;
name = "Lucio Delelis";
};
ldesgoui = { ldesgoui = {
email = "ldesgoui@gmail.com"; email = "ldesgoui@gmail.com";
github = "ldesgoui"; github = "ldesgoui";
@ -4147,12 +4241,6 @@
github = "ltavard"; github = "ltavard";
name = "Laure Tavard"; name = "Laure Tavard";
}; };
lucas8 = {
email = "luc.linux@mailoo.org";
github = "lucas8";
githubId = 2025623;
name = "Luc Chabassier";
};
lucus16 = { lucus16 = {
email = "lars.jellema@gmail.com"; email = "lars.jellema@gmail.com";
github = "Lucus16"; github = "Lucus16";
@ -5088,6 +5176,12 @@
githubId = 7588406; githubId = 7588406;
name = "Andrew R. M."; name = "Andrew R. M.";
}; };
nloomans = {
email = "noah@nixos.noahloomans.com";
github = "nloomans";
githubId = 7829481;
name = "Noah Loomans";
};
nmattia = { nmattia = {
email = "nicolas@nmattia.com"; email = "nicolas@nmattia.com";
github = "nmattia"; github = "nmattia";
@ -5403,6 +5497,12 @@
githubId = 3250809; githubId = 3250809;
name = "Milan Pässler"; name = "Milan Pässler";
}; };
petercommand = {
email = "petercommand@gmail.com";
github = "petercommand";
githubId = 1260660;
name = "petercommand";
};
peterhoeg = { peterhoeg = {
email = "peter@hoeg.com"; email = "peter@hoeg.com";
github = "peterhoeg"; github = "peterhoeg";
@ -6147,6 +6247,16 @@
githubId = 6022042; githubId = 6022042;
name = "Sam Parkinson"; 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 = { samrose = {
email = "samuel.rose@gmail.com"; email = "samuel.rose@gmail.com";
github = "samrose"; github = "samrose";
@ -6254,6 +6364,12 @@
github = "scubed2"; github = "scubed2";
name = "Sterling Stein"; name = "Sterling Stein";
}; };
sdier = {
email = "scott@dier.name";
github = "sdier";
githubId = 11613056;
name = "Scott Dier";
};
sdll = { sdll = {
email = "sasha.delly@gmail.com"; email = "sasha.delly@gmail.com";
github = "sdll"; github = "sdll";
@ -6841,6 +6957,12 @@
githubId = 870673; githubId = 870673;
name = "Takuo Yonezawa"; name = "Takuo Yonezawa";
}; };
talkara = {
email = "taito.horiuchi@relexsolutions.com";
github = "talkara";
githubId = 51232929;
name = "Taito Horiuchi";
};
talyz = { talyz = {
email = "kim.lindberger@gmail.com"; email = "kim.lindberger@gmail.com";
github = "talyz"; github = "talyz";
@ -7032,6 +7154,11 @@
github = "timbertson"; github = "timbertson";
name = "Tim Cuthbertson"; name = "Tim Cuthbertson";
}; };
timma = {
email = "kunduru.it.iitb@gmail.com";
github = "ktrsoft";
name = "Timma";
};
timokau = { timokau = {
email = "timokau@zoho.com"; email = "timokau@zoho.com";
github = "timokau"; github = "timokau";
@ -7823,6 +7950,12 @@
githubId = 1069303; githubId = 1069303;
name = "Kim Simmons"; name = "Kim Simmons";
}; };
zowoq = {
email = "59103226+zowoq@users.noreply.github.com";
github = "zowoq";
githubId = 59103226;
name = "zowoq";
};
zraexy = { zraexy = {
email = "zraexy@gmail.com"; email = "zraexy@gmail.com";
github = "zraexy"; github = "zraexy";
@ -7895,4 +8028,16 @@
githubId = 8686360; githubId = 8686360;
name = "Illia Shestakov"; 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";
};
} }

View File

@ -28,7 +28,8 @@ find . -type f | while read src; do
done done
cat >"$SRCS" <<EOF 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 }: { fetchurl, mirror }:
{ {

View File

@ -11,13 +11,14 @@ compat53,,,,,vcunat
coxpcall,,,1.17.0-1,, coxpcall,,,1.17.0-1,,
cqueues,,,,,vcunat cqueues,,,,,vcunat
cyrussasl,,,,,vcunat cyrussasl,,,,,vcunat
digestif,,http://luarocks.org/dev,,lua5_3, digestif,,,,lua5_3,
dkjson,,,,, dkjson,,,,,
fifo,,,,, fifo,,,,,
http,,,,,vcunat http,,,,,vcunat
inspect,,,,, inspect,,,,,
ldoc,,,,, ldoc,,,,,
lgi,,,,, lgi,,,,,
linenoise,,,,,
ljsyscall,,,,lua5_1,lblasc ljsyscall,,,,lua5_1,lblasc
lpeg,,,,,vyp lpeg,,,,,vyp
lpeg_patterns,,,,, lpeg_patterns,,,,,
@ -43,6 +44,7 @@ luadbi-mysql,,,,,
luadbi-postgresql,,,,, luadbi-postgresql,,,,,
luadbi-sqlite3,,,,, luadbi-sqlite3,,,,,
luadoc,,,,, luadoc,,,,,
luaepnf,,,,,
luaevent,,,,, luaevent,,,,,
luaexpat,,,1.3.0-1,,arobyn flosse luaexpat,,,1.3.0-1,,arobyn flosse
luaffi,,http://luarocks.org/dev,,, luaffi,,http://luarocks.org/dev,,,
@ -50,6 +52,7 @@ luafilesystem,,,1.7.0-2,,flosse vcunat
lualogging,,,,, lualogging,,,,,
luaossl,,,,lua5_1,vcunat luaossl,,,,lua5_1,vcunat
luaposix,,,,,vyp lblasc luaposix,,,,,vyp lblasc
luarepl,,,,,
luasec,,,,,flosse luasec,,,,,flosse
luasocket,,,,, luasocket,,,,,
luasql-sqlite3,,,,,vyp luasql-sqlite3,,,,,vyp
@ -72,3 +75,4 @@ std__debug,std._debug,,,,
std_normalize,std.normalize,,,, std_normalize,std.normalize,,,,
stdlib,,,,,vyp stdlib,,,,,vyp
pulseaudio,,,,,doronbehar pulseaudio,,,,,doronbehar
vstruct,,,,,

1 # nix name luarocks name server version luaversion maintainers
11 coxpcall 1.17.0-1
12 cqueues vcunat
13 cyrussasl vcunat
14 digestif http://luarocks.org/dev lua5_3
15 dkjson
16 fifo
17 http vcunat
18 inspect
19 ldoc
20 lgi
21 linenoise
22 ljsyscall lua5_1 lblasc
23 lpeg vyp
24 lpeg_patterns
44 luadbi-postgresql
45 luadbi-sqlite3
46 luadoc
47 luaepnf
48 luaevent
49 luaexpat 1.3.0-1 arobyn flosse
50 luaffi http://luarocks.org/dev
52 lualogging
53 luaossl lua5_1 vcunat
54 luaposix vyp lblasc
55 luarepl
56 luasec flosse
57 luasocket
58 luasql-sqlite3 vyp
75 std_normalize std.normalize
76 stdlib vyp
77 pulseaudio doronbehar
78 vstruct

View File

@ -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 # 10 is a pretty arbitrary number of simultaneous jobs, but it is generally
# impolite to hit a webserver with *too* many simultaneous connections :) # impolite to hit a webserver with *too* many simultaneous connections :)
PARALLEL_JOBS=10 PARALLEL_JOBS=1
exit_trap() { exit_trap() {
local lc="$BASH_COMMAND" rc=$? local lc="$BASH_COMMAND" rc=$?

View File

@ -19,6 +19,12 @@
<command>nixos-rebuild switch</command>. <command>nixos-rebuild switch</command>.
</para> </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> <para>
You can get a list of the available packages as follows: You can get a list of the available packages as follows:
<screen> <screen>

View File

@ -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: on an encrypted partition, it is necessary to add the following grub option:
<programlisting><xref linkend="opt-boot.loader.grub.enableCryptodisk"/> = true;</programlisting> <programlisting><xref linkend="opt-boot.loader.grub.enableCryptodisk"/> = true;</programlisting>
</para> </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> </section>

View File

@ -28,17 +28,21 @@
<command>nmtui</command> (curses-based terminal user interface). See their <command>nmtui</command> (curses-based terminal user interface). See their
manual pages for details on their usage. Some desktop environments (GNOME, manual pages for details on their usage. Some desktop environments (GNOME,
KDE) have their own configuration tools for NetworkManager. On XFCE, there is KDE) have their own configuration tools for NetworkManager. On XFCE, there is
no configuration tool for NetworkManager by default: by adding no configuration tool for NetworkManager by default: by enabling <xref linkend="opt-programs.nm-applet.enable"/>, the
<code>networkmanagerapplet</code> to the list of system packages, the graphical applet will be installed and will launch automatically when the graphical session is started.
graphical applet will be installed and will launch automatically when XFCE is
starting (and will show in the status tray).
</para> </para>
<note> <note>
<para> <para>
<code>networking.networkmanager</code> and <code>networking.wireless</code> <code>networking.networkmanager</code> and <code>networking.wireless</code>
(WPA Supplicant) cannot be enabled at the same time: you can still connect (WPA Supplicant) can be used together if desired. To do this you need to instruct
to the wireless networks using NetworkManager. 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> </para>
</note> </note>
</section> </section>

View File

@ -85,11 +85,14 @@
<programlisting> <programlisting>
<xref linkend="opt-services.xserver.displayManager.defaultSession"/> = "none+i3"; <xref linkend="opt-services.xserver.displayManager.defaultSession"/> = "none+i3";
</programlisting> </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> <programlisting>
<xref linkend="opt-services.xserver.displayManager.auto.enable"/> = true; <xref linkend="opt-services.xserver.displayManager.lightdm.enable"/> = true;
<xref linkend="opt-services.xserver.displayManager.auto.user"/> = "johndoe"; <xref linkend="opt-services.xserver.displayManager.lightdm.autoLogin.enable"/> = true;
<xref linkend="opt-services.xserver.displayManager.lightdm.autoLogin.user"/> = "alice";
</programlisting> </programlisting>
The options are named identically for all other display managers.
</para> </para>
</simplesect> </simplesect>
<simplesect xml:id="sec-x11-graphics-cards-nvidia"> <simplesect xml:id="sec-x11-graphics-cards-nvidia">

View File

@ -28,25 +28,14 @@
<para> <para>
Some Xfce programs are not installed automatically. To install them manually Some Xfce programs are not installed automatically. To install them manually
(system wide), put them into your (system wide), put them into your
<xref linkend="opt-environment.systemPackages"/>. <xref linkend="opt-environment.systemPackages"/> from <literal>pkgs.xfce</literal>.
</para> </para>
<simplesect xml:id="sec-xfce-thunar-volumes"> <simplesect xml:id="sec-xfce-thunar-plugins">
<title>Thunar Volume Support</title> <title>Thunar Plugins</title>
<para> <para>
To enable <emphasis>Thunar</emphasis> volume support, put If you'd like to add extra plugins to Thunar, add them to
<programlisting> <xref linkend="opt-services.xserver.desktopManager.xfce.thunarPlugins"/>.
<xref linkend="opt-services.xserver.desktopManager.xfce.enable"/> = true; You shouldn't just add them to <xref linkend="opt-environment.systemPackages"/>.
</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.
</para> </para>
</simplesect> </simplesect>
<simplesect xml:id="sec-xfce-troubleshooting"> <simplesect xml:id="sec-xfce-troubleshooting">

View File

@ -236,6 +236,10 @@
introduced to their role, making it easier to pass on knowledge and introduced to their role, making it easier to pass on knowledge and
experience. experience.
</para> </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> <para>
A release manager's role and responsibilities are: A release manager's role and responsibilities are:
</para> </para>

View File

@ -120,12 +120,17 @@ nixos https://nixos.org/channels/nixos-unstable
to <filename>configuration.nix</filename>: to <filename>configuration.nix</filename>:
<programlisting> <programlisting>
<xref linkend="opt-system.autoUpgrade.enable"/> = true; <xref linkend="opt-system.autoUpgrade.enable"/> = true;
<xref linkend="opt-system.autoUpgrade.allowReboot"/> = true;
</programlisting> </programlisting>
This enables a periodically executed systemd service named This enables a periodically executed systemd service named
<literal>nixos-upgrade.service</literal>. It runs <command>nixos-rebuild <literal>nixos-upgrade.service</literal>. If the <literal>allowReboot</literal>
switch --upgrade</command> to upgrade NixOS to the latest version in the option is <literal>false</literal>, it runs <command>nixos-rebuild switch
current channel. (To see when the service runs, see <command>systemctl --upgrade</command> to upgrade NixOS to the latest version in the current
list-timers</command>.) You can also specify a channel explicitly, e.g. 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> <programlisting>
<xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-19.09; <xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-19.09;
</programlisting> </programlisting>

View File

@ -210,7 +210,7 @@
The closure must be an appropriately configured NixOS system, with boot The closure must be an appropriately configured NixOS system, with boot
loader and partition configuration that fits the target host. Such a loader and partition configuration that fits the target host. Such a
closure is typically obtained with a command such as <command>nix-build closure is typically obtained with a command such as <command>nix-build
-I nixos-config=./configuration.nix '&lt;nixos&gt;' -A system -I nixos-config=./configuration.nix '&lt;nixpkgs/nixos&gt;' -A system
--no-out-link</command> --no-out-link</command>
</para> </para>
</listitem> </listitem>

View File

@ -14,12 +14,16 @@
<refsynopsisdiv> <refsynopsisdiv>
<cmdsynopsis> <cmdsynopsis>
<command>nixos-option</command> <command>nixos-option</command>
<arg> <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>
<arg> <arg>
<option>--all</option> <option>-I</option> <replaceable>path</replaceable>
</arg> </arg>
<arg> <arg>
@ -45,6 +49,15 @@
This command accepts the following options: This command accepts the following options:
</para> </para>
<variablelist> <variablelist>
<varlistentry>
<term><option>-r</option></term>
<term><option>--recursive</option></term>
<listitem>
<para>
Print all the values at or below the specified path recursively.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<option>-I</option> <replaceable>path</replaceable> <option>-I</option> <replaceable>path</replaceable>
@ -56,16 +69,6 @@
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<option>--all</option>
</term>
<listitem>
<para>
Print the values of all options.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsection> </refsection>
<refsection> <refsection>

View File

@ -6,7 +6,7 @@
<author><personname><firstname>Eelco</firstname><surname>Dolstra</surname></personname> <author><personname><firstname>Eelco</firstname><surname>Dolstra</surname></personname>
<contrib>Author</contrib> <contrib>Author</contrib>
</author> </author>
<copyright><year>2007-2019</year><holder>Eelco Dolstra</holder> <copyright><year>2007-2020</year><holder>Eelco Dolstra</holder>
</copyright> </copyright>
</info> </info>
<xi:include href="man-configuration.xml" /> <xi:include href="man-configuration.xml" />

View File

@ -23,6 +23,13 @@
Support is planned until the end of October 2020, handing over to 20.09. Support is planned until the end of October 2020, handing over to 20.09.
</para> </para>
</listitem> </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> <listitem>
<para> <para>
Postgresql for NixOS service now defaults to v11. Postgresql for NixOS service now defaults to v11.
@ -52,7 +59,7 @@
<listitem> <listitem>
<para> <para>
<command>nixos-option</command> has been rewritten in C++, speeding it up, improving correctness, <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> </para>
</listitem> </listitem>
<listitem> <listitem>
@ -96,6 +103,13 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
via <option>services.upower</option>. via <option>services.upower</option>.
</para> </para>
</listitem> </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> </itemizedlist>
</section> </section>
@ -126,7 +140,7 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
<listitem> <listitem>
<para> <para>
The <literal>dynamicHosts</literal> option has been removed from the 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 module. Allowing (multiple) regular users to override host entries
affecting the whole system opens up a huge attack vector. affecting the whole system opens up a huge attack vector.
There seem to be very rare cases where this might be useful. 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. SDDM, GDM, or using the startx module which uses Xinitrc.
</para> </para>
</listitem> </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> <listitem>
<para> <para>
The BEAM package set has been deleted. You will only find there the different interpreters. 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. upstream issue</link> for more information.
</para> </para>
</listitem> </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> <listitem>
<para> <para>
The packages <literal>openobex</literal> and <literal>obexftp</literal> The packages <literal>openobex</literal> and <literal>obexftp</literal>
@ -401,6 +438,183 @@ users.users.me =
the type to <literal>either path (submodule ...)</literal>. the type to <literal>either path (submodule ...)</literal>.
</para> </para>
</listitem> </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> </itemizedlist>
</section> </section>
@ -452,9 +666,14 @@ users.users.me =
As well as this, the options <literal>security.acme.acceptTerms</literal> and either As well as this, the options <literal>security.acme.acceptTerms</literal> and either
<literal>security.acme.email</literal> or <literal>security.acme.certs.&lt;name&gt;.email</literal> <literal>security.acme.email</literal> or <literal>security.acme.certs.&lt;name&gt;.email</literal>
must be set in order to use the ACME module. 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 preserved and thus it is possible to roll back to previous versions without breaking certificate
generation. 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> </para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>

View File

@ -17,9 +17,9 @@ in
else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'"; else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'";
qemuBinary = qemuPkg: { 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"; 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"; 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"; }.${pkgs.stdenv.hostPlatform.system} or "${qemuPkg}/bin/qemu-kvm";
} }

View File

@ -1,13 +1,17 @@
#! /somewhere/python3 #! /somewhere/python3
from contextlib import contextmanager, _GeneratorContextManager 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 from xml.sax.saxutils import XMLGenerator
import _thread import _thread
import atexit import atexit
import base64
import os import os
import pathlib
import ptpython.repl import ptpython.repl
import pty import pty
from queue import Queue, Empty
import re import re
import shlex
import shutil import shutil
import socket import socket
import subprocess import subprocess
@ -15,9 +19,6 @@ import sys
import tempfile import tempfile
import time import time
import unicodedata import unicodedata
from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List
import shlex
import pathlib
CHAR_TO_KEY = { CHAR_TO_KEY = {
"A": "shift-a", "A": "shift-a",
@ -84,7 +85,7 @@ CHAR_TO_KEY = {
# Forward references # Forward references
nr_tests: int nr_tests: int
nr_succeeded: int failed_tests: list
log: "Logger" log: "Logger"
machines: "List[Machine]" machines: "List[Machine]"
@ -221,7 +222,7 @@ class Machine:
return path return path
self.state_dir = create_dir("vm-state-{}".format(self.name)) 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.booted = False
self.connected = False self.connected = False
@ -395,7 +396,7 @@ class Machine:
status_code_pattern = re.compile(r"(.*)\|\!EOF\s+(\d+)") status_code_pattern = re.compile(r"(.*)\|\!EOF\s+(\d+)")
while True: while True:
chunk = self.shell.recv(4096).decode() chunk = self.shell.recv(4096).decode(errors="ignore")
match = status_code_pattern.match(chunk) match = status_code_pattern.match(chunk)
if match: if match:
output += match[1] output += match[1]
@ -566,6 +567,41 @@ class Machine:
if ret.returncode != 0: if ret.returncode != 0:
raise Exception("Cannot convert screenshot") 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: 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 """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 relative to `$out`. The file is copied via the `shared_dir` shared among
@ -576,7 +612,7 @@ class Machine:
vm_src = pathlib.Path(source) vm_src = pathlib.Path(source)
with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td: with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td:
shared_temp = pathlib.Path(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 vm_intermediate = vm_shared_temp / vm_src.name
intermediate = shared_temp / vm_src.name intermediate = shared_temp / vm_src.name
# Copy the file to the shared directory inside VM # Copy the file to the shared directory inside VM
@ -704,7 +740,8 @@ class Machine:
def process_serial_output() -> None: def process_serial_output() -> None:
for _line in self.process.stdout: 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)) eprint("{} # {}".format(self.name, line))
self.logger.enqueue({"msg": line, "machine": self.name}) self.logger.enqueue({"msg": line, "machine": self.name})
@ -841,23 +878,31 @@ def run_tests() -> None:
machine.execute("sync") machine.execute("sync")
if nr_tests != 0: if nr_tests != 0:
nr_succeeded = nr_tests - len(failed_tests)
eprint("{} out of {} tests succeeded".format(nr_succeeded, nr_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) sys.exit(1)
@contextmanager @contextmanager
def subtest(name: str) -> Iterator[None]: def subtest(name: str) -> Iterator[None]:
global nr_tests global nr_tests
global nr_succeeded global failed_tests
with log.nested(name): with log.nested(name):
nr_tests += 1 nr_tests += 1
try: try:
yield yield
nr_succeeded += 1
return True return True
except Exception as e: except Exception as e:
failed_tests.append(
'Test "{}" failed with error: "{}"'.format(name, str(e))
)
log.log("error: {}".format(str(e))) log.log("error: {}".format(str(e)))
return False return False
@ -879,7 +924,7 @@ if __name__ == "__main__":
exec("\n".join(machine_eval)) exec("\n".join(machine_eval))
nr_tests = 0 nr_tests = 0
nr_succeeded = 0 failed_tests = []
@atexit.register @atexit.register
def clean_up() -> None: def clean_up() -> None:

View File

@ -155,7 +155,7 @@ in rec {
--add-flags "''${vms[*]}" \ --add-flags "''${vms[*]}" \
${lib.optionalString enableOCR ${lib.optionalString enableOCR
"--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \ "--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}' --set VLANS '${toString vlans}'
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
wrapProgram $out/bin/nixos-run-vms \ wrapProgram $out/bin/nixos-run-vms \

View File

@ -4,7 +4,7 @@ stdenv.mkDerivation rec {
name = "jquery-ui-1.11.4"; name = "jquery-ui-1.11.4";
src = fetchurl { src = fetchurl {
url = "http://jqueryui.com/resources/download/${name}.zip"; url = "https://jqueryui.com/resources/download/${name}.zip";
sha256 = "0ciyaj1acg08g8hpzqx6whayq206fvf4whksz2pjgxlv207lqgjh"; sha256 = "0ciyaj1acg08g8hpzqx6whayq206fvf4whksz2pjgxlv207lqgjh";
}; };
@ -17,7 +17,7 @@ stdenv.mkDerivation rec {
''; '';
meta = { meta = {
homepage = http://jqueryui.com/; homepage = https://jqueryui.com/;
description = "A library of JavaScript widgets and effects"; description = "A library of JavaScript widgets and effects";
platforms = stdenv.lib.platforms.all; platforms = stdenv.lib.platforms.all;
}; };

View File

@ -28,8 +28,6 @@ let
}; };
nslcdConfig = writeText "nslcd.conf" '' nslcdConfig = writeText "nslcd.conf" ''
uid nslcd
gid nslcd
uri ${cfg.server} uri ${cfg.server}
base ${cfg.base} base ${cfg.base}
timelimit ${toString cfg.timeLimit} timelimit ${toString cfg.timeLimit}
@ -282,6 +280,7 @@ in
Group = "nslcd"; Group = "nslcd";
RuntimeDirectory = [ "nslcd" ]; RuntimeDirectory = [ "nslcd" ];
PIDFile = "/run/nslcd/nslcd.pid"; PIDFile = "/run/nslcd/nslcd.pid";
AmbientCapabilities = "CAP_SYS_RESOURCE";
}; };
}; };

View File

@ -248,6 +248,9 @@ in {
security.rtkit.enable = true; security.rtkit.enable = true;
systemd.packages = [ overriddenPackage ]; systemd.packages = [ overriddenPackage ];
# PulseAudio is packaged with udev rules to handle various audio device quirks
services.udev.packages = [ overriddenPackage ];
}) })
(mkIf (cfg.extraModules != []) { (mkIf (cfg.extraModules != []) {

View File

@ -38,6 +38,7 @@ in
(mkRenamedOptionModule [ "networking" "dnsExtensionMechanism" ] [ "networking" "resolvconf" "dnsExtensionMechanism" ]) (mkRenamedOptionModule [ "networking" "dnsExtensionMechanism" ] [ "networking" "resolvconf" "dnsExtensionMechanism" ])
(mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ]) (mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ])
(mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ]) (mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ])
(mkRemovedOptionModule [ "networking" "resolvconf" "useHostResolvConf" ] "This option was never used for anything anyways")
]; ];
options = { 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 { dnsSingleRequest = lib.mkOption {
type = types.bool; type = types.bool;
default = false; default = false;

View File

@ -43,11 +43,11 @@ in
description = '' description = ''
Whether to enable OpenGL drivers. This is needed to enable Whether to enable OpenGL drivers. This is needed to enable
OpenGL support in X11 systems, as well as for Wayland compositors 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 by the corresponding modules, so you do not usually have to
set it yourself, only if there is no module for your wayland set it yourself, only if there is no module for your wayland
compositor of choice. See services.xserver.enable, compositor of choice. See services.xserver.enable and
programs.sway.enable, and programs.way-cooler.enable. programs.sway.enable.
''; '';
type = types.bool; type = types.bool;
default = false; default = false;

View File

@ -49,7 +49,7 @@ in
{ {
options = { options = {
hardware.openrazer = { hardware.openrazer = {
enable = mkEnableOption "OpenRazer drivers and userspace daemon."; enable = mkEnableOption "OpenRazer drivers and userspace daemon";
verboseLogging = mkOption { verboseLogging = mkOption {
type = types.bool; type = types.bool;

View File

@ -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 ];
};
}

View File

@ -21,6 +21,19 @@ with lib;
###### implementation ###### implementation
config = mkIf config.hardware.usbWwan.enable { config = mkIf config.hardware.usbWwan.enable {
# Attaches device specific handlers.
services.udev.packages = with pkgs; [ usb-modeswitch-data ]; 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";
}; };
} }

View File

@ -1,7 +1,7 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
imports = [ ./installation-cd-graphical-kde.nix ]; imports = [ ./installation-cd-graphical-plasma5.nix ];
boot.kernelPackages = pkgs.linuxPackages_latest; boot.kernelPackages = pkgs.linuxPackages_latest;
} }

View File

@ -569,14 +569,18 @@ in
}; };
fileSystems."/nix/store" = fileSystems."/nix/store" =
{ fsType = "unionfs-fuse"; { fsType = "overlay";
device = "unionfs"; device = "overlay";
options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; 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 # Closures to be copied to the Nix store on the CD, namely the init
# script and the top-level system configuration directory. # script and the top-level system configuration directory.

View File

@ -50,14 +50,18 @@ with lib;
}; };
fileSystems."/nix/store" = fileSystems."/nix/store" =
{ fsType = "unionfs-fuse"; { fsType = "overlay";
device = "unionfs"; device = "overlay";
options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; 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 # Closures to be copied to the Nix store, namely the init
# script and the top-level system configuration directory. # script and the top-level system configuration directory.

View File

@ -131,12 +131,12 @@ bool isOption(Context & ctx, const Value & v)
if (v.type != tAttrs) { if (v.type != tAttrs) {
return false; return false;
} }
const auto & atualType = v.attrs->find(ctx.underscoreType); const auto & actualType = v.attrs->find(ctx.underscoreType);
if (atualType == v.attrs->end()) { if (actualType == v.attrs->end()) {
return false; return false;
} }
try { try {
Value evaluatedType = evaluateValue(ctx, *atualType->value); Value evaluatedType = evaluateValue(ctx, *actualType->value);
if (evaluatedType.type != tString) { if (evaluatedType.type != tString) {
return false; 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 bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, Value root)
{ {
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( recurse(
[f, &ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) { [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)); 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; return !isOpt;
}, },
ctx, root, ""); ctx, root.option, root.path);
} }
// Calls f on all the config values inside one option. // Calls f on all the config values inside one option.
@ -294,10 +392,12 @@ void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
Out attrsOut(out, "{", "}", v.attrs->size()); Out attrsOut(out, "{", "}", v.attrs->size());
for (const auto & a : v.attrs->lexicographicOrder()) { for (const auto & a : v.attrs->lexicographicOrder()) {
std::string name = a->name; std::string name = a->name;
if (!forbiddenRecursionName(name)) {
attrsOut << name << " = "; attrsOut << name << " = ";
printValue(ctx, attrsOut, *a->value, appendPath(path, name)); printValue(ctx, attrsOut, *a->value, appendPath(path, name));
attrsOut << ";" << Out::sep; attrsOut << ";" << Out::sep;
} }
}
} }
void multiLineStringEscape(Out & out, const std::string & s) void multiLineStringEscape(Out & out, const std::string & s)
@ -380,17 +480,26 @@ void printConfigValue(Context & ctx, Out & out, const std::string & path, std::v
out << ";\n"; 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( mapOptions(
[&ctx, &out](const std::string & optionPath) { [&ctx, &out, &path](const std::string & optionPath) {
mapConfigValuesInOption( mapConfigValuesInOption(
[&ctx, &out](const std::string & configPath, std::variant<Value, std::exception_ptr> 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); printConfigValue(ctx, out, configPath, v);
}
}, },
optionPath, ctx); optionPath, ctx);
}, },
ctx, ctx.optionsRoot); ctx, path);
} }
void printAttr(Context & ctx, Out & out, const std::string & path, Value & root) 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) void printOne(Context & ctx, Out & out, const std::string & path)
{ {
try { try {
Value option = findAlongOptionPath(ctx, path); auto result = findAlongOptionPath(ctx, path);
Value & option = result.option;
option = evaluateValue(ctx, option); option = evaluateValue(ctx, option);
if (path != result.path) {
out << "Note: showing " << result.path << " instead of " << path << "\n";
}
if (isOption(ctx, option)) { if (isOption(ctx, option)) {
printOption(ctx, out, path, option); printOption(ctx, out, result.path, option);
} else { } else {
printListing(out, option); printListing(out, option);
} }
@ -552,7 +583,7 @@ void printOne(Context & ctx, Out & out, const std::string & path)
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
bool all = false; bool recursive = false;
std::string path = "."; std::string path = ".";
std::string optionsExpr = "(import <nixpkgs/nixos> {}).options"; std::string optionsExpr = "(import <nixpkgs/nixos> {}).options";
std::string configExpr = "(import <nixpkgs/nixos> {}).config"; std::string configExpr = "(import <nixpkgs/nixos> {}).config";
@ -568,8 +599,8 @@ int main(int argc, char ** argv)
nix::showManPage("nixos-option"); nix::showManPage("nixos-option");
} else if (*arg == "--version") { } else if (*arg == "--version") {
nix::printVersion("nixos-option"); nix::printVersion("nixos-option");
} else if (*arg == "--all") { } else if (*arg == "-r" || *arg == "--recursive") {
all = true; recursive = true;
} else if (*arg == "--path") { } else if (*arg == "--path") {
path = nix::getArg(*arg, arg, end); path = nix::getArg(*arg, arg, end);
} else if (*arg == "--options_expr") { } else if (*arg == "--options_expr") {
@ -598,18 +629,12 @@ int main(int argc, char ** argv)
Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot}; Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot};
Out out(std::cout); Out out(std::cout);
if (all) { auto print = recursive ? printRecursive : printOne;
if (!args.empty()) {
throw UsageError("--all cannot be used with arguments");
}
printAll(ctx, out);
} else {
if (args.empty()) { if (args.empty()) {
printOne(ctx, out, ""); print(ctx, out, "");
} }
for (const auto & arg : args) { for (const auto & arg : args) {
printOne(ctx, out, arg); print(ctx, out, arg);
}
} }
ctx.state.printStats(); ctx.state.printStats();

View File

@ -22,7 +22,7 @@ repair=
profile=/nix/var/nix/profiles/system profile=/nix/var/nix/profiles/system
buildHost= buildHost=
targetHost= targetHost=
maybeSudo= maybeSudo=()
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
i="$1"; shift 1 i="$1"; shift 1
@ -91,9 +91,7 @@ while [ "$#" -gt 0 ]; do
shift 1 shift 1
;; ;;
--use-remote-sudo) --use-remote-sudo)
# note the trailing space maybeSudo=(sudo --)
maybeSudo="sudo "
shift 1
;; ;;
*) *)
echo "$0: unknown option \`$i'" echo "$0: unknown option \`$i'"
@ -102,6 +100,10 @@ while [ "$#" -gt 0 ]; do
esac esac
done done
if [ -n "$SUDO_USER" ]; then
maybeSudo=(sudo --)
fi
if [ -z "$buildHost" -a -n "$targetHost" ]; then if [ -z "$buildHost" -a -n "$targetHost" ]; then
buildHost="$targetHost" buildHost="$targetHost"
fi fi
@ -116,17 +118,17 @@ buildHostCmd() {
if [ -z "$buildHost" ]; then if [ -z "$buildHost" ]; then
"$@" "$@"
elif [ -n "$remoteNix" ]; then elif [ -n "$remoteNix" ]; then
ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "$maybeSudo$@" ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "${maybeSudo[@]}" "$@"
else else
ssh $SSHOPTS "$buildHost" "$maybeSudo$@" ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" "$@"
fi fi
} }
targetHostCmd() { targetHostCmd() {
if [ -z "$targetHost" ]; then if [ -z "$targetHost" ]; then
"$@" "${maybeSudo[@]}" "$@"
else else
ssh $SSHOPTS "$targetHost" "$maybeSudo$@" ssh $SSHOPTS "$targetHost" "${maybeSudo[@]}" "$@"
fi fi
} }

View File

@ -299,7 +299,7 @@ in
couchpotato = 267; couchpotato = 267;
gogs = 268; gogs = 268;
pdns-recursor = 269; pdns-recursor = 269;
kresd = 270; #kresd = 270; # switched to "knot-resolver" with dynamic ID
rpc = 271; rpc = 271;
geoip = 272; geoip = 272;
fcron = 273; fcron = 273;
@ -600,7 +600,7 @@ in
headphones = 266; headphones = 266;
couchpotato = 267; couchpotato = 267;
gogs = 268; gogs = 268;
kresd = 270; #kresd = 270; # switched to "knot-resolver" with dynamic ID
#rpc = 271; # unused #rpc = 271; # unused
#geoip = 272; # unused #geoip = 272; # unused
fcron = 273; fcron = 273;

View File

@ -131,13 +131,6 @@ in {
++ optional (isFindutils && cfg.pruneNames != []) "findutils locate does not support pruning by directory component" ++ 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"; ++ 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 = systemd.services.update-locatedb =
{ description = "Update Locate Database"; { description = "Update Locate Database";
path = mkIf (!isMLocate) [ pkgs.su ]; path = mkIf (!isMLocate) [ pkgs.su ];

View File

@ -6,6 +6,7 @@ let
cfg = config.system.nixos; cfg = config.system.nixos;
gitRepo = "${toString pkgs.path}/.git"; gitRepo = "${toString pkgs.path}/.git";
gitRepoValid = lib.pathIsGitRepo gitRepo;
gitCommitId = lib.substring 0 7 (commitIdFromGitRepo gitRepo); gitCommitId = lib.substring 0 7 (commitIdFromGitRepo gitRepo);
in in
@ -91,8 +92,8 @@ in
# These defaults are set here rather than up there so that # These defaults are set here rather than up there so that
# changing them would not rebuild the manual # changing them would not rebuild the manual
version = mkDefault (cfg.release + cfg.versionSuffix); version = mkDefault (cfg.release + cfg.versionSuffix);
revision = mkIf (pathIsDirectory gitRepo) (mkDefault gitCommitId); revision = mkIf gitRepoValid (mkDefault gitCommitId);
versionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId)); versionSuffix = mkIf gitRepoValid (mkDefault (".git." + gitCommitId));
}; };
# Generate /etc/os-release. See # Generate /etc/os-release. See

View File

@ -62,6 +62,7 @@
./hardware/printers.nix ./hardware/printers.nix
./hardware/raid/hpsa.nix ./hardware/raid/hpsa.nix
./hardware/steam-hardware.nix ./hardware/steam-hardware.nix
./hardware/tuxedo-keyboard.nix
./hardware/usb-wwan.nix ./hardware/usb-wwan.nix
./hardware/onlykey.nix ./hardware/onlykey.nix
./hardware/video/amdgpu.nix ./hardware/video/amdgpu.nix
@ -97,6 +98,7 @@
./programs/autojump.nix ./programs/autojump.nix
./programs/bandwhich.nix ./programs/bandwhich.nix
./programs/bash/bash.nix ./programs/bash/bash.nix
./programs/bash-my-aws.nix
./programs/bcc.nix ./programs/bcc.nix
./programs/browserpass.nix ./programs/browserpass.nix
./programs/captive-browser.nix ./programs/captive-browser.nix
@ -116,6 +118,7 @@
./programs/fish.nix ./programs/fish.nix
./programs/freetds.nix ./programs/freetds.nix
./programs/fuse.nix ./programs/fuse.nix
./programs/geary.nix
./programs/gnome-disks.nix ./programs/gnome-disks.nix
./programs/gnome-documents.nix ./programs/gnome-documents.nix
./programs/gnome-terminal.nix ./programs/gnome-terminal.nix
@ -127,6 +130,7 @@
./programs/java.nix ./programs/java.nix
./programs/kbdlight.nix ./programs/kbdlight.nix
./programs/less.nix ./programs/less.nix
./programs/liboping.nix
./programs/light.nix ./programs/light.nix
./programs/mosh.nix ./programs/mosh.nix
./programs/mininet.nix ./programs/mininet.nix
@ -152,13 +156,13 @@
./programs/system-config-printer.nix ./programs/system-config-printer.nix
./programs/thefuck.nix ./programs/thefuck.nix
./programs/tmux.nix ./programs/tmux.nix
./programs/traceroute.nix
./programs/tsm-client.nix ./programs/tsm-client.nix
./programs/udevil.nix ./programs/udevil.nix
./programs/usbtop.nix ./programs/usbtop.nix
./programs/venus.nix ./programs/venus.nix
./programs/vim.nix ./programs/vim.nix
./programs/wavemon.nix ./programs/wavemon.nix
./programs/way-cooler.nix
./programs/waybar.nix ./programs/waybar.nix
./programs/wireshark.nix ./programs/wireshark.nix
./programs/x2goserver.nix ./programs/x2goserver.nix
@ -278,6 +282,7 @@
./services/databases/riak.nix ./services/databases/riak.nix
./services/databases/riak-cs.nix ./services/databases/riak-cs.nix
./services/databases/stanchion.nix ./services/databases/stanchion.nix
./services/databases/victoriametrics.nix
./services/databases/virtuoso.nix ./services/databases/virtuoso.nix
./services/desktops/accountsservice.nix ./services/desktops/accountsservice.nix
./services/desktops/bamf.nix ./services/desktops/bamf.nix
@ -424,6 +429,7 @@
./services/misc/exhibitor.nix ./services/misc/exhibitor.nix
./services/misc/felix.nix ./services/misc/felix.nix
./services/misc/folding-at-home.nix ./services/misc/folding-at-home.nix
./services/misc/freeswitch.nix
./services/misc/fstrim.nix ./services/misc/fstrim.nix
./services/misc/gammu-smsd.nix ./services/misc/gammu-smsd.nix
./services/misc/geoip-updater.nix ./services/misc/geoip-updater.nix
@ -524,6 +530,7 @@
./services/monitoring/prometheus/alertmanager.nix ./services/monitoring/prometheus/alertmanager.nix
./services/monitoring/prometheus/exporters.nix ./services/monitoring/prometheus/exporters.nix
./services/monitoring/prometheus/pushgateway.nix ./services/monitoring/prometheus/pushgateway.nix
./services/monitoring/prometheus/xmpp-alerts.nix
./services/monitoring/riemann.nix ./services/monitoring/riemann.nix
./services/monitoring/riemann-dash.nix ./services/monitoring/riemann-dash.nix
./services/monitoring/riemann-tools.nix ./services/monitoring/riemann-tools.nix
@ -577,6 +584,7 @@
./services/networking/connman.nix ./services/networking/connman.nix
./services/networking/consul.nix ./services/networking/consul.nix
./services/networking/coredns.nix ./services/networking/coredns.nix
./services/networking/corerad.nix
./services/networking/coturn.nix ./services/networking/coturn.nix
./services/networking/dante.nix ./services/networking/dante.nix
./services/networking/ddclient.nix ./services/networking/ddclient.nix
@ -584,7 +592,7 @@
./services/networking/dhcpd.nix ./services/networking/dhcpd.nix
./services/networking/dnscache.nix ./services/networking/dnscache.nix
./services/networking/dnschain.nix ./services/networking/dnschain.nix
./services/networking/dnscrypt-proxy.nix ./services/networking/dnscrypt-proxy2.nix
./services/networking/dnscrypt-wrapper.nix ./services/networking/dnscrypt-wrapper.nix
./services/networking/dnsdist.nix ./services/networking/dnsdist.nix
./services/networking/dnsmasq.nix ./services/networking/dnsmasq.nix
@ -735,6 +743,7 @@
./services/networking/wicd.nix ./services/networking/wicd.nix
./services/networking/wireguard.nix ./services/networking/wireguard.nix
./services/networking/wpa_supplicant.nix ./services/networking/wpa_supplicant.nix
./services/networking/xandikos.nix
./services/networking/xinetd.nix ./services/networking/xinetd.nix
./services/networking/xl2tpd.nix ./services/networking/xl2tpd.nix
./services/networking/xrdp.nix ./services/networking/xrdp.nix
@ -802,6 +811,7 @@
./services/web-apps/codimd.nix ./services/web-apps/codimd.nix
./services/web-apps/cryptpad.nix ./services/web-apps/cryptpad.nix
./services/web-apps/documize.nix ./services/web-apps/documize.nix
./services/web-apps/dokuwiki.nix
./services/web-apps/frab.nix ./services/web-apps/frab.nix
./services/web-apps/gotify-server.nix ./services/web-apps/gotify-server.nix
./services/web-apps/icingaweb2/icingaweb2.nix ./services/web-apps/icingaweb2/icingaweb2.nix
@ -859,7 +869,6 @@
./services/x11/unclutter.nix ./services/x11/unclutter.nix
./services/x11/unclutter-xfixes.nix ./services/x11/unclutter-xfixes.nix
./services/x11/desktop-managers/default.nix ./services/x11/desktop-managers/default.nix
./services/x11/display-managers/auto.nix
./services/x11/display-managers/default.nix ./services/x11/display-managers/default.nix
./services/x11/display-managers/gdm.nix ./services/x11/display-managers/gdm.nix
./services/x11/display-managers/lightdm.nix ./services/x11/display-managers/lightdm.nix
@ -869,7 +878,6 @@
./services/x11/display-managers/xpra.nix ./services/x11/display-managers/xpra.nix
./services/x11/fractalart.nix ./services/x11/fractalart.nix
./services/x11/hardware/libinput.nix ./services/x11/hardware/libinput.nix
./services/x11/hardware/multitouch.nix
./services/x11/hardware/synaptics.nix ./services/x11/hardware/synaptics.nix
./services/x11/hardware/wacom.nix ./services/x11/hardware/wacom.nix
./services/x11/hardware/digimend.nix ./services/x11/hardware/digimend.nix

View File

@ -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;
};
}

View File

@ -32,6 +32,10 @@ let
fi fi
''; '';
lsColors = optionalString cfg.enableLsColors ''
eval "$(${pkgs.coreutils}/bin/dircolors -b)"
'';
bashAliases = concatStringsSep "\n" ( bashAliases = concatStringsSep "\n" (
mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}") mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}")
(filterAttrs (k: v: v != null) cfg.shellAliases) (filterAttrs (k: v: v != null) cfg.shellAliases)
@ -127,6 +131,14 @@ in
type = types.bool; type = types.bool;
}; };
enableLsColors = mkOption {
default = true;
description = ''
Enable extra colors in directory listings.
'';
type = types.bool;
};
}; };
}; };
@ -156,6 +168,7 @@ in
${cfg.promptInit} ${cfg.promptInit}
${bashCompletion} ${bashCompletion}
${lsColors}
${bashAliases} ${bashAliases}
${cfge.interactiveShellInit} ${cfge.interactiveShellInit}

View File

@ -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;
};
}

View File

@ -96,7 +96,7 @@ in
# This overrides the systemd user unit shipped with the gnupg package # This overrides the systemd user unit shipped with the gnupg package
systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) { systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) {
serviceConfig.ExecStart = [ "" '' serviceConfig.ExecStart = [ "" ''
${pkgs.gnupg}/bin/gpg-agent --supervised \ ${cfg.package}/bin/gpg-agent --supervised \
--pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
'' ]; '' ];
}; };

View File

@ -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" ]);
};
}

View File

@ -87,7 +87,8 @@ in {
type = with types; listOf package; type = with types; listOf package;
default = with pkgs; [ default = with pkgs; [
swaylock swayidle swaylock swayidle
xwayland rxvt_unicode dmenu xwayland alacritty dmenu
rxvt_unicode # For backward compatibility (old default terminal)
]; ];
defaultText = literalExample '' defaultText = literalExample ''
with pkgs; [ swaylock swayidle xwayland rxvt_unicode dmenu ]; with pkgs; [ swaylock swayidle xwayland rxvt_unicode dmenu ];

View File

@ -52,7 +52,7 @@ let
set -s escape-time ${toString cfg.escapeTime} set -s escape-time ${toString cfg.escapeTime}
set -g history-limit ${toString cfg.historyLimit} set -g history-limit ${toString cfg.historyLimit}
${cfg.extraTmuxConf} ${cfg.extraConfig}
''; '';
in { in {
@ -102,7 +102,7 @@ in {
description = "Time in milliseconds for which tmux waits after an escape is input."; description = "Time in milliseconds for which tmux waits after an escape is input.";
}; };
extraTmuxConf = mkOption { extraConfig = mkOption {
default = ""; default = "";
description = '' description = ''
Additional contents of /etc/tmux.conf Additional contents of /etc/tmux.conf
@ -181,4 +181,8 @@ in {
}; };
}; };
}; };
imports = [
(lib.mkRenamedOptionModule [ "programs" "tmux" "extraTmuxConf" ] [ "programs" "tmux" "extraConfig" ])
];
} }

View File

@ -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";
};
};
}

View File

@ -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 ];
}

View File

@ -15,6 +15,24 @@ let
(filterAttrs (k: v: v != null) cfg.shellAliases) (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 in
{ {
@ -69,6 +87,10 @@ in
promptInit = mkOption { promptInit = mkOption {
default = '' 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 autoload -U promptinit && promptinit && prompt walters && setopt prompt_sp
''; '';
description = '' description = ''
@ -100,7 +122,8 @@ in
]; ];
example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ]; example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
description = '' 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} . ${config.system.build.setEnvironment}
fi 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} ${cfge.shellInit}
${cfg.shellInit} ${cfg.shellInit}
@ -161,11 +192,14 @@ in
'' ''
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically. # /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
# This file is read for login shells. # This file is read for login shells.
#
${zshStartupNotes}
# Only execute this file once per shell. # Only execute this file once per shell.
if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi
__ETC_ZPROFILE_SOURCED=1 __ETC_ZPROFILE_SOURCED=1
# Setup custom login shell init stuff.
${cfge.loginShellInit} ${cfge.loginShellInit}
${cfg.loginShellInit} ${cfg.loginShellInit}
@ -180,38 +214,44 @@ in
'' ''
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically. # /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
# This file is read for interactive shells. # This file is read for interactive shells.
#
${zshStartupNotes}
# Only execute this file once per shell. # Only execute this file once per shell.
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
__ETC_ZSHRC_SOURCED=1 __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} SAVEHIST=${toString cfg.histSize}
HISTSIZE=${toString cfg.histSize} HISTSIZE=${toString cfg.histSize}
HISTFILE=${cfg.histFile} HISTFILE=${cfg.histFile}
HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help" # Configure sane keyboard defaults.
. /etc/zinputrc
# Tell zsh how to find installed completions ${optionalString cfg.enableGlobalCompInit ''
for p in ''${(z)NIX_PROFILES}; do # Enable autocompletion.
fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions) autoload -U compinit && compinit
done ''}
${optionalString cfg.enableGlobalCompInit "autoload -U compinit && compinit"}
# Setup custom interactive shell init stuff.
${cfge.interactiveShellInit} ${cfge.interactiveShellInit}
${cfg.interactiveShellInit} ${cfg.interactiveShellInit}
${optionalString (cfg.setOptions != []) "setopt ${concatStringsSep " " cfg.setOptions}"} # Setup aliases.
${zshAliases} ${zshAliases}
# Setup prompt.
${cfg.promptInit} ${cfg.promptInit}
# Need to disable features to support TRAMP # Disable some features to support TRAMP.
if [ "$TERM" = dumb ]; then if [ "$TERM" = dumb ]; then
unsetopt zle prompt_cr prompt_subst unsetopt zle prompt_cr prompt_subst
unset RPS1 RPROMPT unset RPS1 RPROMPT

View File

@ -27,6 +27,21 @@ with lib;
(mkRemovedOptionModule [ "services.osquery" ] "The osquery module has been removed") (mkRemovedOptionModule [ "services.osquery" ] "The osquery module has been removed")
(mkRemovedOptionModule [ "services.fourStore" ] "The fourStore module has been removed") (mkRemovedOptionModule [ "services.fourStore" ] "The fourStore module has been removed")
(mkRemovedOptionModule [ "services.fourStoreEndpoint" ] "The fourStoreEndpoint 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 # Do NOT add any option renames here, see top of the file
]; ];

View File

@ -12,7 +12,7 @@ let
ikey=${cfg.ikey} ikey=${cfg.ikey}
skey=${cfg.skey} skey=${cfg.skey}
host=${cfg.host} host=${cfg.host}
${optionalString (cfg.group != "") ("group="+cfg.group)} ${optionalString (cfg.groups != "") ("groups="+cfg.groups)}
failmode=${cfg.failmode} failmode=${cfg.failmode}
pushinfo=${boolToStr cfg.pushinfo} pushinfo=${boolToStr cfg.pushinfo}
autopush=${boolToStr cfg.autopush} autopush=${boolToStr cfg.autopush}
@ -42,6 +42,10 @@ let
}; };
in in
{ {
imports = [
(mkRenamedOptionModule [ "security" "duosec" "group" ] [ "security" "duosec" "groups" ])
];
options = { options = {
security.duosec = { security.duosec = {
ssh.enable = mkOption { ssh.enable = mkOption {
@ -71,10 +75,16 @@ in
description = "Duo API hostname."; description = "Duo API hostname.";
}; };
group = mkOption { groups = mkOption {
type = types.str; type = types.str;
default = ""; 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 { failmode = mkOption {

View File

@ -98,8 +98,8 @@ in {
will be merged into these options by RabbitMQ at runtime to will be merged into these options by RabbitMQ at runtime to
form the final configuration. form the final configuration.
See http://www.rabbitmq.com/configure.html#config-items See https://www.rabbitmq.com/configure.html#config-items
For the distinct formats, see http://www.rabbitmq.com/configure.html#config-file-formats 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> The contents of this option will be merged into the <literal>configItems</literal>
by RabbitMQ at runtime to form the final configuration. by RabbitMQ at runtime to form the final configuration.
See the second table on http://www.rabbitmq.com/configure.html#config-items See the second table on https://www.rabbitmq.com/configure.html#config-items
For the distinct formats, see http://www.rabbitmq.com/configure.html#config-file-formats For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats
''; '';
}; };
@ -165,7 +165,10 @@ in {
after = [ "network.target" "epmd.socket" ]; after = [ "network.target" "epmd.socket" ];
wants = [ "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 = { environment = {
RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia"; RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia";

View File

@ -103,6 +103,34 @@ in
Create the repository if it doesn't exist. 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 = {}; default = {};
@ -134,25 +162,41 @@ in
let let
extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions; extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
resticCmd = "${pkgs.restic}/bin/restic${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}" ({ in nameValuePair "restic-backups-${name}" ({
environment = { environment = {
RESTIC_PASSWORD_FILE = backup.passwordFile; RESTIC_PASSWORD_FILE = backup.passwordFile;
RESTIC_REPOSITORY = backup.repository; RESTIC_REPOSITORY = backup.repository;
}; };
path = with pkgs; [ path = [ pkgs.openssh ];
openssh
];
restartIfChanged = false; restartIfChanged = false;
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}"; ExecStart = [ "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${backupPaths}" ] ++ pruneCmd;
User = backup.user; User = backup.user;
RuntimeDirectory = "restic-backups-${name}";
} // optionalAttrs (backup.s3CredentialsFile != null) { } // optionalAttrs (backup.s3CredentialsFile != null) {
EnvironmentFile = backup.s3CredentialsFile; EnvironmentFile = backup.s3CredentialsFile;
}; };
} // optionalAttrs backup.initialize { } // optionalAttrs (backup.initialize || backup.dynamicFilesFrom != null) {
preStart = '' preStart = ''
${optionalString (backup.initialize) ''
${resticCmd} snapshots || ${resticCmd} init ${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; ) config.services.restic.backups;

View File

@ -20,6 +20,7 @@ let
size = 2048; size = 2048;
}; };
CN = top.masterAddress; CN = top.masterAddress;
hosts = cfg.cfsslAPIExtraSANs;
}); });
cfsslAPITokenBaseName = "apitoken.secret"; cfsslAPITokenBaseName = "apitoken.secret";
@ -66,6 +67,15 @@ in
type = bool; 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 { genCfsslAPIToken = mkOption {
description = '' description = ''
Whether to automatically generate cfssl API-token secret, Whether to automatically generate cfssl API-token secret,

View File

@ -50,8 +50,8 @@ in
}; };
runtimePackages = mkOption { runtimePackages = mkOption {
default = [ pkgs.bash pkgs.nix ]; default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ];
defaultText = "[ pkgs.bash pkgs.nix ]"; defaultText = "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
description = "Add programs to the buildkite-agent environment"; description = "Add programs to the buildkite-agent environment";
type = types.listOf types.package; type = types.listOf types.package;
}; };
@ -74,13 +74,12 @@ in
''; '';
}; };
meta-data = mkOption { tags = mkOption {
type = types.str; type = types.attrsOf types.str;
default = ""; default = {};
example = "queue=default,docker=true,ruby2=true"; example = { queue = "default"; docker = "true"; ruby2 ="true"; };
description = '' description = ''
Meta data for the agent. This is a comma-separated list of Tags for the agent.
<code>key=value</code> pairs.
''; '';
}; };
@ -93,26 +92,20 @@ in
''; '';
}; };
openssh = privateSshKeyPath = mkOption {
{ privateKeyPath = mkOption { type = types.nullOr types.path;
type = 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;
description = '' description = ''
Private agent key. OpenSSH private key
A run-time path to the key file, which is supposed to be provisioned A run-time path to the key file, which is supposed to be provisioned
outside of Nix store. 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.
'';
};
};
hooks = mkHookOptions [ hooks = mkHookOptions [
{ name = "checkout"; { name = "checkout";
@ -181,12 +174,20 @@ in
instead. 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 { config = mkIf config.services.buildkite-agent.enable {
users.users.buildkite-agent = users.users.buildkite-agent = {
{ name = "buildkite-agent"; name = "buildkite-agent";
home = cfg.dataDir; home = cfg.dataDir;
createHome = true; createHome = true;
description = "Buildkite agent user"; description = "Buildkite agent user";
@ -210,17 +211,18 @@ in
## don't end up in the Nix store. ## don't end up in the Nix store.
preStart = let preStart = let
sshDir = "${cfg.dataDir}/.ssh"; sshDir = "${cfg.dataDir}/.ssh";
tagStr = lib.concatStringsSep "," (lib.mapAttrsToList (name: value: "${name}=${value}") cfg.tags);
in in
'' optionalString (cfg.privateSshKeyPath != null) ''
mkdir -m 0700 -p "${sshDir}" mkdir -m 0700 -p "${sshDir}"
cp -f "${toString cfg.openssh.privateKeyPath}" "${sshDir}/id_rsa" cp -f "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
cp -f "${toString cfg.openssh.publicKeyPath}" "${sshDir}/id_rsa.pub" chmod 600 "${sshDir}"/id_rsa
chmod 600 "${sshDir}"/id_rsa* '' + ''
cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
token="$(cat ${toString cfg.tokenPath})" token="$(cat ${toString cfg.tokenPath})"
name="${cfg.name}" name="${cfg.name}"
meta-data="${cfg.meta-data}" shell="${cfg.shell}"
tags="${tagStr}"
build-path="${cfg.dataDir}/builds" build-path="${cfg.dataDir}/builds"
hooks-path="${cfg.hooksPath}" hooks-path="${cfg.hooksPath}"
${cfg.extraConfig} ${cfg.extraConfig}
@ -228,11 +230,14 @@ in
''; '';
serviceConfig = 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"; User = "buildkite-agent";
RestartSec = 5; RestartSec = 5;
Restart = "on-failure"; Restart = "on-failure";
TimeoutSec = 10; TimeoutSec = 10;
# set a long timeout to give buildkite-agent a chance to finish current builds
TimeoutStopSec = "2 min";
KillMode = "mixed";
}; };
}; };
@ -247,7 +252,10 @@ in
}; };
imports = [ imports = [
(mkRenamedOptionModule [ "services" "buildkite-agent" "token" ] [ "services" "buildkite-agent" "tokenPath" ]) (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" "privateKey" ] [ "services" "buildkite-agent" "privateSshKeyPath" ])
(mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "publicKey" ] [ "services" "buildkite-agent" "openssh" "publicKeyPath" ]) (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" ])
]; ];
} }

View File

@ -167,7 +167,7 @@ in
buildMachinesFiles = mkOption { buildMachinesFiles = mkOption {
type = types.listOf types.path; 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" ]; example = [ "/etc/nix/machines" "/var/lib/hydra/provisioner/machines" ];
description = "List of files containing build machines."; description = "List of files containing build machines.";
}; };
@ -333,7 +333,7 @@ in
IN_SYSTEMD = "1"; # to get log severity levels IN_SYSTEMD = "1"; # to get log severity levels
}; };
serviceConfig = 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"; ExecStopPost = "${cfg.package}/bin/hydra-queue-runner --unlock";
User = "hydra-queue-runner"; User = "hydra-queue-runner";
Restart = "always"; Restart = "always";

View File

@ -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
'';
};
};
}

View File

@ -18,6 +18,9 @@ with lib;
description = '' description = ''
Whether to enable at-spi2-core, a service for the Assistive Technologies Whether to enable at-spi2-core, a service for the Assistive Technologies
available on the GNOME platform. 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>.
''; '';
}; };

View File

@ -118,15 +118,15 @@ in {
in { in {
displayName = "Python 3 for machine learning"; displayName = "Python 3 for machine learning";
argv = [ argv = [
"$ {env.interpreter}" "''${env.interpreter}"
"-m" "-m"
"ipykernel_launcher" "ipykernel_launcher"
"-f" "-f"
"{connection_file}" "{connection_file}"
]; ];
language = "python"; language = "python";
logo32 = "$ {env.sitePackages}/ipykernel/resources/logo-32x32.png"; logo32 = "''${env.sitePackages}/ipykernel/resources/logo-32x32.png";
logo64 = "$ {env.sitePackages}/ipykernel/resources/logo-64x64.png"; logo64 = "''${env.sitePackages}/ipykernel/resources/logo-64x64.png";
}; };
} }
''; '';

View File

@ -53,7 +53,7 @@ in {
blacklistPlugins = mkOption { blacklistPlugins = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ "test" ]; default = [];
example = [ "udev" ]; example = [ "udev" ];
description = '' description = ''
Allow blacklisting specific plugins Allow blacklisting specific plugins
@ -91,6 +91,9 @@ in {
###### implementation ###### implementation
config = mkIf cfg.enable { 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.systemPackages = [ cfg.package ];
environment.etc = { environment.etc = {

View File

@ -13,18 +13,12 @@ in
config = mkIf cfg.enable { 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 ]; environment.systemPackages = [ pkgs.irqbalance ];
systemd.services.irqbalance.wantedBy = ["multi-user.target"];
systemd.packages = [ pkgs.irqbalance ];
}; };
} }

View File

@ -6,37 +6,18 @@ let
cfg = config.services.mailman; cfg = config.services.mailman;
mailmanPyEnv = pkgs.python3.withPackages (ps: with ps; [mailman mailman-hyperkitty]); # 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;
mailmanExe = with pkgs; stdenv.mkDerivation { settingsJSON = pkgs.writeText "settings.json" (builtins.toJSON settings);
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
'';
};
mailmanCfg = '' mailmanCfg = ''
[mailman] [mailman]
@ -53,30 +34,42 @@ let
etc_dir: /etc etc_dir: /etc
ext_dir: $etc_dir/mailman.d ext_dir: $etc_dir/mailman.d
pid_file: /run/mailman/master.pid pid_file: /run/mailman/master.pid
'' + optionalString (cfg.hyperkittyApiKey != null) '' '' + optionalString cfg.hyperkitty.enable ''
[archiver.hyperkitty] [archiver.hyperkitty]
class: mailman_hyperkitty.Archiver class: mailman_hyperkitty.Archiver
enable: yes enable: yes
configuration: ${pkgs.writeText "mailman-hyperkitty.cfg" mailmanHyperkittyCfg} configuration: /var/lib/mailman/mailman-hyperkitty.cfg
''; '';
mailmanHyperkittyCfg = '' mailmanHyperkittyCfg = pkgs.writeText "mailman-hyperkitty.cfg" ''
[general] [general]
# This is your HyperKitty installation, preferably on the localhost. This # This is your HyperKitty installation, preferably on the localhost. This
# address will be used by Mailman to forward incoming emails to HyperKitty # 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 # for archiving. It does not need to be publicly available, in fact it's
# better if it is not. # 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 # Shared API key, must be the identical to the value in HyperKitty's
# settings. # settings.
api_key: ${cfg.hyperkittyApiKey} api_key: @API_KEY@
''; '';
in { in {
###### interface ###### 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 = { options = {
services.mailman = { services.mailman = {
@ -87,9 +80,17 @@ in {
description = "Enable Mailman on this host. Requires an active Postfix installation."; 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 { siteOwner = mkOption {
type = types.str; type = types.str;
default = "postmaster@example.org"; example = "postmaster@example.org";
description = '' description = ''
Certain messages that must be delivered to a human, but which can't 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 be delivered to a list owner (e.g. a bounce from a list owner), will
@ -99,12 +100,13 @@ in {
webRoot = mkOption { webRoot = mkOption {
type = types.path; type = types.path;
default = "${mailmanWeb}/${pkgs.python3.sitePackages}"; default = "${pkgs.mailman-web}/${pkgs.python3.sitePackages}";
defaultText = "pkgs.python3Packages.mailman-web"; defaultText = "\${pkgs.mailman-web}/\${pkgs.python3.sitePackages}";
description = '' description = ''
The web root for the Hyperkity + Postorius apps provided by Mailman. 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 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,7 +122,26 @@ in {
''; '';
}; };
hyperkittyBaseUrl = mkOption { webUser = mkOption {
type = types.str;
default = config.services.httpd.user;
description = ''
User to run mailman-web as
'';
};
webSettings = mkOption {
type = types.attrs;
default = {};
description = ''
Overrides for the default mailman-web Django settings.
'';
};
hyperkitty = {
enable = mkEnableOption "the Hyperkitty archiver for Mailman";
baseUrl = mkOption {
type = types.str; type = types.str;
default = "http://localhost/hyperkitty/"; default = "http://localhost/hyperkitty/";
description = '' description = ''
@ -128,16 +149,6 @@ in {
localhost? localhost?
''; '';
}; };
hyperkittyApiKey = mkOption {
type = types.nullOr types.str;
default = null;
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.
'';
}; };
}; };
@ -147,25 +158,58 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = [ assertions = let
{ assertion = cfg.enable -> config.services.postfix.enable; 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"; 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; }; users.users.mailman = { description = "GNU Mailman"; isSystemUser = true; };
environment = { environment.etc."mailman.cfg".text = mailmanCfg;
systemPackages = [ mailmanExe mailmanWebExe pkgs.sassc ];
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 = { services.postfix = {
relayDomains = [ "hash:/var/lib/mailman/data/postfix_domains" ];
recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP
config = { 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 owner_request_special = "no"; # Mailman handles -owner addresses on its own
}; };
}; };
@ -173,34 +217,71 @@ in {
systemd.services.mailman = { systemd.services.mailman = {
description = "GNU Mailman Master Process"; description = "GNU Mailman Master Process";
after = [ "network.target" ]; after = [ "network.target" ];
restartTriggers = [ config.environment.etc."mailman.cfg".source ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanExe}/bin/mailman start"; ExecStart = "${cfg.package}/bin/mailman start";
ExecStop = "${mailmanExe}/bin/mailman stop"; ExecStop = "${cfg.package}/bin/mailman stop";
User = "mailman"; User = "mailman";
Type = "forking"; Type = "forking";
StateDirectory = "mailman";
StateDirectoryMode = "0700";
RuntimeDirectory = "mailman"; RuntimeDirectory = "mailman";
PIDFile = "/run/mailman/master.pid"; PIDFile = "/run/mailman/master.pid";
}; };
}; };
systemd.services.mailman-web = { systemd.services.mailman-settings = {
description = "Init Postorius DB"; description = "Generate settings files (including secrets) for Mailman";
before = [ "httpd.service" ]; before = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ];
requiredBy = [ "httpd.service" ]; requiredBy = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ];
path = with pkgs; [ jq ];
script = '' script = ''
${mailmanWebExe}/bin/mailman-web migrate mailmanDir=/var/lib/mailman
rm -rf static mailmanWebDir=/var/lib/mailman-web
${mailmanWebExe}/bin/mailman-web collectstatic
${mailmanWebExe}/bin/mailman-web compress 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 = { serviceConfig = {
User = config.services.httpd.user;
Type = "oneshot"; Type = "oneshot";
StateDirectory = "mailman-web"; };
StateDirectoryMode = "0700"; };
systemd.services.mailman-web = {
description = "Init Postorius DB";
before = [ "httpd.service" "uwsgi.service" ];
requiredBy = [ "httpd.service" "uwsgi.service" ];
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
script = ''
${pkgs.mailman-web}/bin/mailman-web migrate
rm -rf static
${pkgs.mailman-web}/bin/mailman-web collectstatic
${pkgs.mailman-web}/bin/mailman-web compress
'';
serviceConfig = {
User = cfg.webUser;
Type = "oneshot";
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
@ -208,86 +289,94 @@ in {
systemd.services.mailman-daily = { systemd.services.mailman-daily = {
description = "Trigger daily Mailman events"; description = "Trigger daily Mailman events";
startAt = "daily"; startAt = "daily";
restartTriggers = [ config.environment.etc."mailman.cfg".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanExe}/bin/mailman digests --send"; ExecStart = "${cfg.package}/bin/mailman digests --send";
User = "mailman"; User = "mailman";
}; };
}; };
systemd.services.hyperkitty = { systemd.services.hyperkitty = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "GNU Hyperkitty QCluster Process"; description = "GNU Hyperkitty QCluster Process";
after = [ "network.target" ]; after = [ "network.target" ];
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
wantedBy = [ "mailman.service" "multi-user.target" ]; wantedBy = [ "mailman.service" "multi-user.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web qcluster"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web qcluster";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
systemd.services.hyperkitty-minutely = { systemd.services.hyperkitty-minutely = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "Trigger minutely Hyperkitty events"; description = "Trigger minutely Hyperkitty events";
startAt = "minutely"; startAt = "minutely";
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs minutely"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs minutely";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
systemd.services.hyperkitty-quarter-hourly = { systemd.services.hyperkitty-quarter-hourly = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "Trigger quarter-hourly Hyperkitty events"; description = "Trigger quarter-hourly Hyperkitty events";
startAt = "*:00/15"; startAt = "*:00/15";
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs quarter_hourly"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs quarter_hourly";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
systemd.services.hyperkitty-hourly = { systemd.services.hyperkitty-hourly = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "Trigger hourly Hyperkitty events"; description = "Trigger hourly Hyperkitty events";
startAt = "hourly"; startAt = "hourly";
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs hourly"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs hourly";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
systemd.services.hyperkitty-daily = { systemd.services.hyperkitty-daily = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "Trigger daily Hyperkitty events"; description = "Trigger daily Hyperkitty events";
startAt = "daily"; startAt = "daily";
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs daily"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs daily";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
systemd.services.hyperkitty-weekly = { systemd.services.hyperkitty-weekly = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "Trigger weekly Hyperkitty events"; description = "Trigger weekly Hyperkitty events";
startAt = "weekly"; startAt = "weekly";
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs weekly"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs weekly";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };
systemd.services.hyperkitty-yearly = { systemd.services.hyperkitty-yearly = {
enable = cfg.hyperkittyApiKey != null; inherit (cfg.hyperkitty) enable;
description = "Trigger yearly Hyperkitty events"; description = "Trigger yearly Hyperkitty events";
startAt = "yearly"; startAt = "yearly";
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
serviceConfig = { serviceConfig = {
ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs yearly"; ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs yearly";
User = config.services.httpd.user; User = cfg.webUser;
WorkingDirectory = "/var/lib/mailman-web"; WorkingDirectory = "/var/lib/mailman-web";
}; };
}; };

View File

@ -5,6 +5,8 @@ with lib;
let let
cfg = config.services.roundcube; cfg = config.services.roundcube;
fpm = config.services.phpfpm.pools.roundcube; fpm = config.services.phpfpm.pools.roundcube;
localDB = cfg.database.host == "localhost";
user = cfg.database.username;
in in
{ {
options.services.roundcube = { options.services.roundcube = {
@ -44,7 +46,10 @@ in
username = mkOption { username = mkOption {
type = types.str; type = types.str;
default = "roundcube"; 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 { host = mkOption {
type = types.str; type = types.str;
@ -58,7 +63,12 @@ in
}; };
password = mkOption { password = mkOption {
type = types.str; 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 { dbname = mkOption {
type = types.str; type = types.str;
@ -83,14 +93,22 @@ in
}; };
config = mkIf cfg.enable { 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 = '' environment.etc."roundcube/config.inc.php".text = ''
<?php <?php
${lib.optionalString (!localDB) "$password = file_get_contents('${cfg.database.passwordFile}');"}
$config = array(); $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['log_driver'] = 'syslog';
$config['max_message_size'] = '25M'; $config['max_message_size'] = '25M';
$config['plugins'] = [${concatMapStringsSep "," (p: "'${p}'") cfg.plugins}]; $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} ${cfg.extraConfig}
''; '';
@ -116,12 +134,26 @@ in
}; };
}; };
services.postgresql = mkIf (cfg.database.host == "localhost") { services.postgresql = mkIf localDB {
enable = true; 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 = { services.phpfpm.pools.roundcube = {
user = "nginx"; user = if localDB then user else "nginx";
phpOptions = '' phpOptions = ''
error_log = 'stderr' error_log = 'stderr'
log_errors = on log_errors = on
@ -143,9 +175,7 @@ in
}; };
systemd.services.phpfpm-roundcube.after = [ "roundcube-setup.service" ]; systemd.services.phpfpm-roundcube.after = [ "roundcube-setup.service" ];
systemd.services.roundcube-setup = let systemd.services.roundcube-setup = mkMerge [
pgSuperUser = config.services.postgresql.superUser;
in mkMerge [
(mkIf (cfg.database.host == "localhost") { (mkIf (cfg.database.host == "localhost") {
requires = [ "postgresql.service" ]; requires = [ "postgresql.service" ];
after = [ "postgresql.service" ]; after = [ "postgresql.service" ];
@ -153,22 +183,31 @@ in
}) })
{ {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
script = '' script = let
mkdir -p /var/lib/roundcube 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}";
if [ ! -f /var/lib/roundcube/db-created ]; then in
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}'"; version="$(${psql} -t <<< "select value from system where name = 'roundcube-version';" || true)"
${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "create database ${cfg.database.dbname} with owner ${cfg.database.username}"; if ! (grep -E '[a-zA-Z0-9]' <<< "$version"); then
${psql} -f ${cfg.package}/SQL/postgres.initial.sql
fi fi
PGPASSWORD="${cfg.database.password}" ${pkgs.postgresql}/bin/psql -U ${cfg.database.username} \
-f ${cfg.package}/SQL/postgres.initial.sql \ if [ ! -f /var/lib/roundcube/des_key ]; then
-h ${cfg.database.host} ${cfg.database.dbname} base64 /dev/urandom | head -c 24 > /var/lib/roundcube/des_key;
touch /var/lib/roundcube/db-created # 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 fi
${pkgs.php}/bin/php ${cfg.package}/bin/update.sh ${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";
};
} }
]; ];
}; };

View File

@ -6,15 +6,6 @@ let
cfg = config.services.spamassassin; cfg = config.services.spamassassin;
spamassassin-local-cf = pkgs.writeText "local.cf" cfg.config; 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 in
{ {
@ -120,13 +111,11 @@ in
}; };
config = mkIf cfg.enable { 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'. # Allow users to run 'spamc'.
environment.systemPackages = [ pkgs.spamassassin ];
environment = {
etc.spamassassin.source = spamdEnv;
systemPackages = [ pkgs.spamassassin ];
};
users.users.spamd = { users.users.spamd = {
description = "Spam Assassin Daemon"; description = "Spam Assassin Daemon";
@ -141,7 +130,7 @@ in
systemd.services.sa-update = { systemd.services.sa-update = {
script = '' script = ''
set +e 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=$? v=$?
set -e set -e
@ -172,7 +161,7 @@ in
after = [ "network.target" ]; after = [ "network.target" ];
serviceConfig = { 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"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
}; };
@ -183,7 +172,7 @@ in
mkdir -p /var/lib/spamassassin mkdir -p /var/lib/spamassassin
chown spamd:spamd /var/lib/spamassassin -R chown spamd:spamd /var/lib/spamassassin -R
set +e 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=$? v=$?
set -e set -e
if [ $v -gt 1 ]; then if [ $v -gt 1 ]; then

View File

@ -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";
};
};
};
}

View File

@ -364,7 +364,7 @@ in
''} ''}
sed -e "s,#secretkey#,$KEY,g" \ sed -e "s,#secretkey#,$KEY,g" \
-e "s,#dbpass#,$DBPASS,g" \ -e "s,#dbpass#,$DBPASS,g" \
-e "s,#jwtsecet#,$JWTSECET,g" \ -e "s,#jwtsecret#,$JWTSECRET,g" \
-e "s,#mailerpass#,$MAILERPASSWORD,g" \ -e "s,#mailerpass#,$MAILERPASSWORD,g" \
-i ${runConfig} -i ${runConfig}
chmod 640 ${runConfig} ${secretKey} ${jwtSecret} chmod 640 ${runConfig} ${secretKey} ${jwtSecret}

View File

@ -251,6 +251,7 @@ in {
home = cfg.configDir; home = cfg.configDir;
createHome = true; createHome = true;
group = "hass"; group = "hass";
extraGroups = [ "dialout" ];
uid = config.ids.uids.hass; uid = config.ids.uids.hass;
}; };

View File

@ -123,9 +123,9 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' - ${cfg.user} ${cfg.user} - -" "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
] ++ (optional cfg.consumptionDirIsPublic ] ++ (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 # If the consumption dir is not created here, it's automatically created by
# 'manage' with the default permissions. # 'manage' with the default permissions.
); );
@ -169,17 +169,15 @@ in
}; };
users = optionalAttrs (cfg.user == defaultUser) { users = optionalAttrs (cfg.user == defaultUser) {
users = [{ users.${defaultUser} = {
name = defaultUser;
group = defaultUser; group = defaultUser;
uid = config.ids.uids.paperless; uid = config.ids.uids.paperless;
home = cfg.dataDir; home = cfg.dataDir;
}]; };
groups = [{ groups.${defaultUser} = {
name = defaultUser;
gid = config.ids.gids.paperless; gid = config.ids.gids.paperless;
}]; };
}; };
}; };
} }

View File

@ -154,7 +154,7 @@ in
}; };
virtualHost = mkOption { 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 '' example = literalExample ''
{ hostName = "example.org"; { hostName = "example.org";
adminAddr = "webmaster@example.org"; adminAddr = "webmaster@example.org";

View File

@ -18,7 +18,7 @@ let
in checkedConfig yml; in checkedConfig yml;
cmdlineArgs = cfg.extraFlags ++ [ cmdlineArgs = cfg.extraFlags ++ [
"--config.file ${alertmanagerYml}" "--config.file /tmp/alert-manager-substituted.yaml"
"--web.listen-address ${cfg.listenAddress}:${toString cfg.port}" "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}"
"--log.level ${cfg.logLevel}" "--log.level ${cfg.logLevel}"
] ++ (optional (cfg.webExternalUrl != null) ] ++ (optional (cfg.webExternalUrl != null)
@ -127,6 +127,18 @@ in {
Extra commandline options when launching the Alertmanager. 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 = { systemd.services.alertmanager = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network.target" ]; after = [ "network.target" ];
preStart = ''
${lib.getBin pkgs.envsubst}/bin/envsubst -o "/tmp/alert-manager-substituted.yaml" \
-i "${alertmanagerYml}"
'';
serviceConfig = { serviceConfig = {
Restart = "always"; Restart = "always";
DynamicUser = true; DynamicUser = true; # implies PrivateTmp
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
WorkingDirectory = "/tmp"; WorkingDirectory = "/tmp";
ExecStart = "${cfg.package}/bin/alertmanager" + ExecStart = "${cfg.package}/bin/alertmanager" +
optionalString (length cmdlineArgs != 0) (" \\\n " + optionalString (length cmdlineArgs != 0) (" \\\n " +

View File

@ -74,7 +74,7 @@ in
then "--systemd.slice ${cfg.systemd.slice}" then "--systemd.slice ${cfg.systemd.slice}"
else "--systemd.unit ${cfg.systemd.unit}") else "--systemd.unit ${cfg.systemd.unit}")
++ optional (cfg.systemd.enable && (cfg.systemd.journalPath != null)) ++ 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}")} ++ optional (!cfg.systemd.enable) "--postfix.logfile_path ${cfg.logfilePath}")}
''; '';
}; };

View File

@ -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" ];
};
};
};
}

View File

@ -1,6 +1,7 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
let let
inherit (config.security) wrapperDir;
cfg = config.services.kbfs; cfg = config.services.kbfs;
in { in {
@ -17,6 +18,16 @@ in {
description = "Whether to mount the Keybase filesystem."; 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 { mountPoint = mkOption {
type = types.str; type = types.str;
default = "%h/keybase"; default = "%h/keybase";
@ -41,18 +52,32 @@ in {
###### implementation ###### implementation
config = mkIf cfg.enable { config = mkIf cfg.enable (mkMerge [
{
# Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/kbfs.service
systemd.user.services.kbfs = { systemd.user.services.kbfs = {
description = "Keybase File System"; description = "Keybase File System";
requires = [ "keybase.service" ];
after = [ "keybase.service" ]; # 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" ]; path = [ "/run/wrappers" ];
unitConfig.ConditionUser = "!@system"; unitConfig.ConditionUser = "!@system";
serviceConfig = { serviceConfig = {
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${cfg.mountPoint}"; Type = "notify";
ExecStart = "${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}"; # Keybase notifies from a forked process
ExecStopPost = "/run/wrappers/bin/fusermount -u ${cfg.mountPoint}"; 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"; Restart = "on-failure";
PrivateTmp = true; PrivateTmp = true;
}; };
@ -62,5 +87,32 @@ in {
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" ];
};
})
]);
} }

View File

@ -168,8 +168,7 @@ in
createHome = true; createHome = true;
}; };
users.groups = singleton { users.groups.bitlbee = {
name = "bitlbee";
gid = config.ids.gids.bitlbee; gid = config.ids.gids.bitlbee;
}; };

View File

@ -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";
};
};
};
}

View File

@ -59,6 +59,16 @@ let
# Use the list of allowed interfaces if specified # Use the list of allowed interfaces if specified
${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} ${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} ${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" ]; before = [ "network-online.target" ];
after = [ "systemd-udev-settle.service" ]; after = [ "systemd-udev-settle.service" ];
restartTriggers = [ exitHook ];
# Stopping dhcpcd during a reconfiguration is undesirable # Stopping dhcpcd during a reconfiguration is undesirable
# because it brings down the network interfaces configured by # because it brings down the network interfaces configured by
# dhcpcd. So do a "systemctl restart" instead. # dhcpcd. So do a "systemctl restart" instead.
@ -177,7 +204,7 @@ in
serviceConfig = serviceConfig =
{ Type = "forking"; { Type = "forking";
PIDFile = "/run/dhcpcd.pid"; 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"; ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
Restart = "always"; Restart = "always";
}; };

View File

@ -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.
'')
];
}

View File

@ -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>

View File

@ -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}";
};
};
};
}

View File

@ -24,13 +24,18 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
# Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/keybase.service
systemd.user.services.keybase = { systemd.user.services.keybase = {
description = "Keybase service"; description = "Keybase service";
unitConfig.ConditionUser = "!@system"; unitConfig.ConditionUser = "!@system";
environment.KEYBASE_SERVICE_TYPE = "systemd";
serviceConfig = { serviceConfig = {
ExecStart = '' Type = "notify";
${pkgs.keybase}/bin/keybase service --auto-forked EnvironmentFile = [
''; "-%E/keybase/keybase.autogen.env"
"-%E/keybase/keybase.env"
];
ExecStart = "${pkgs.keybase}/bin/keybase service";
Restart = "on-failure"; Restart = "on-failure";
PrivateTmp = true; PrivateTmp = true;
}; };

View File

@ -56,6 +56,7 @@ in {
package = mkOption { package = mkOption {
type = types.package; type = types.package;
default = pkgs.knot-dns; default = pkgs.knot-dns;
defaultText = "pkgs.knot-dns";
description = '' description = ''
Which Knot DNS package to use Which Knot DNS package to use
''; '';
@ -92,4 +93,3 @@ in {
environment.systemPackages = [ knot-cli-wrappers ]; environment.systemPackages = [ knot-cli-wrappers ];
}; };
} }

View File

@ -3,14 +3,39 @@
with lib; with lib;
let let
cfg = config.services.kresd; cfg = config.services.kresd;
package = pkgs.knot-resolver;
configFile = pkgs.writeText "kresd.conf" cfg.extraConfig; # Convert systemd-style address specification to kresd config line(s).
in # 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 */ ]; meta.maintainers = [ maintainers.vcunat /* upstream developer */ ];
imports = [ imports = [
@ -22,6 +47,7 @@ in
value value
) )
) )
(mkRemovedOptionModule [ "services" "kresd" "cacheDir" ] "Please use (bind-)mounting instead.")
]; ];
###### interface ###### interface
@ -32,8 +58,8 @@ in
description = '' description = ''
Whether to enable knot-resolver domain name server. Whether to enable knot-resolver domain name server.
DNSSEC validation is turned on by default. DNSSEC validation is turned on by default.
You can run <literal>sudo nc -U /run/kresd/control</literal> You can run <literal>sudo nc -U /run/knot-resolver/control/1</literal>
and give commands interactively to kresd. and give commands interactively to kresd@1.service.
''; '';
}; };
extraConfig = mkOption { extraConfig = mkOption {
@ -43,16 +69,10 @@ in
Extra lines to be added verbatim to the generated configuration file. 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 { listenPlain = mkOption {
type = with types; listOf str; type = with types; listOf str;
default = [ "[::1]:53" "127.0.0.1:53" ]; default = [ "[::1]:53" "127.0.0.1:53" ];
example = [ "53" ];
description = '' description = ''
What addresses and ports the server should listen on. What addresses and ports the server should listen on.
For detailed syntax see ListenStream in man systemd.socket. For detailed syntax see ListenStream in man systemd.socket.
@ -67,75 +87,59 @@ in
For detailed syntax see ListenStream in man systemd.socket. 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 # TODO: perhaps options for more common stuff like cache size or forwarding
}; };
###### implementation ###### implementation
config = mkIf cfg.enable { 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 = users.users.knot-resolver =
{ uid = config.ids.uids.kresd; { isSystemUser = true;
group = "kresd"; group = "knot-resolver";
description = "Knot-resolver daemon user"; description = "Knot-resolver daemon user";
}; };
users.groups.kresd.gid = config.ids.gids.kresd; users.groups.knot-resolver.gid = null;
systemd.sockets.kresd = rec { systemd.packages = [ package ]; # the units are patched inside the package a bit
wantedBy = [ "sockets.target" ];
before = wantedBy; systemd.targets.kresd = { # configure units started by default
listenStreams = cfg.listenPlain; wantedBy = [ "multi-user.target" ];
socketConfig = { wants = [ "kres-cache-gc.service" ]
ListenDatagram = listenStreams; ++ map (i: "kresd@${toString i}.service") (range 1 cfg.instances);
FreeBind = true;
FileDescriptorName = "dns";
}; };
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.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec { environment.etc."tmpfiles.d/knot-resolver.conf".source =
wantedBy = [ "sockets.target" ]; "${package}/lib/tmpfiles.d/knot-resolver.conf";
before = wantedBy;
partOf = [ "kresd.socket" ];
listenStreams = cfg.listenTLS;
socketConfig = {
FileDescriptorName = "tls";
FreeBind = true;
Service = "kresd.service";
};
};
systemd.sockets.kresd-control = rec { # Try cleaning up the previously default location of cache file.
wantedBy = [ "sockets.target" ]; # Note that /var/cache/* should always be safe to remove.
before = wantedBy; # TODO: remove later, probably between 20.09 and 21.03
partOf = [ "kresd.socket" ]; systemd.tmpfiles.rules = [ "R /var/cache/kresd" ];
listenStreams = [ "/run/kresd/control" ];
socketConfig = {
FileDescriptorName = "control";
Service = "kresd.service";
SocketMode = "0660"; # only root user/group may connect and control kresd
};
};
systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ];
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" ];
};
}; };
} }

View File

@ -111,7 +111,7 @@ in
serviceConfig = { serviceConfig = {
User = cfg.user; User = cfg.user;
Group = cfg.group; Group = cfg.group;
ExecStart = "${pkgs.matterbridge.bin}/bin/matterbridge -conf ${matterbridgeConfToml}"; ExecStart = "${pkgs.matterbridge}/bin/matterbridge -conf ${matterbridgeConfToml}";
Restart = "always"; Restart = "always";
RestartSec = "10"; RestartSec = "10";
}; };

Some files were not shown because too many files have changed in this diff Show More