Merge branch 'master' into flip-map-foreach

This commit is contained in:
Danylo Hlynskyi 2019-08-05 14:09:28 +03:00 committed by GitHub
commit 7585496eff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1893 changed files with 176031 additions and 219951 deletions

4
.github/CODEOWNERS vendored
View file

@ -107,8 +107,8 @@
# Eclipse
/pkgs/applications/editors/eclipse @rycee
# https://github.com/NixOS/nixpkgs/issues/31401
/lib/licenses.nix @ghost
# Licenses
/lib/licenses.nix @alyssais
# Qt / KDE
/pkgs/applications/kde @ttuegel

3
.github/FUNDING.yml vendored
View file

@ -1,3 +0,0 @@
# These are supported funding model platforms
custom: https://nixos.org/nixos/foundation.html

View file

@ -8,5 +8,4 @@
## Technical details
Please run `nix-shell -p nix-info --run "nix-info -m"` and paste the
results.
Please run `nix run nixpkgs.nix-info -c nix-info -m` and paste the result.

37
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,37 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: '0.kind: bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. ...
2. ...
3. ...
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
**Metadata**
Please run `nix run nixpkgs.nix-info -c nix-info -m` and paste the result.
Maintainer information:
```yaml
# a list of nixpkgs attributes affected by the problem
attribute:
# a list of nixos modules affected by the problem
module:
```

View file

@ -0,0 +1,18 @@
---
name: Packaging requests
about: For packages that are missing
title: ''
labels: '0.kind: packaging request'
assignees: ''
---
**Project description**
_describe the project a little_
**Metadata**
* homepage URL:
* source URL:
* license: mit, bsd, gpl2+ , ...
* platforms: unix, linux, darwin, ...

View file

@ -18,4 +18,6 @@
- [ ] Ensured that relevant documentation is up to date
- [ ] Fits [CONTRIBUTING.md](https://github.com/NixOS/nixpkgs/blob/master/.github/CONTRIBUTING.md).
---
###### Notify maintainers
cc @

View file

@ -1,6 +1,7 @@
[<img src="https://nixos.org/logo/nixos-hires.png" width="500px" alt="logo" />](https://nixos.org/nixos)
[![Code Triagers Badge](https://www.codetriage.com/nixos/nixpkgs/badges/users.svg)](https://www.codetriage.com/nixos/nixpkgs)
[![Open Collective supporters](https://opencollective.com/nixos/tiers/supporter/badge.svg?label=Supporter&color=brightgreen)](https://opencollective.com/nixos)
Nixpkgs is a collection of packages for the [Nix](https://nixos.org/nix/) package
manager. It is periodically built and tested by the [Hydra](https://hydra.nixos.org/)

View file

@ -10,7 +10,7 @@ stdenv.mkDerivation {
name = "...";
src = fetchurl { ... };
buildInputs = [ jdk ant ];
nativeBuildInputs = [ jdk ant ];
buildPhase = "ant";
}
@ -30,7 +30,8 @@ stdenv.mkDerivation {
<filename>foo.jar</filename> in its <filename>share/java</filename>
directory, and another package declares the attribute
<programlisting>
buildInputs = [ jdk libfoo ];
buildInputs = [ libfoo ];
nativeBuildInputs = [ jdk ];
</programlisting>
then <envar>CLASSPATH</envar> will be set to
<filename>/nix/store/...-libfoo/share/java/foo.jar</filename>.
@ -46,7 +47,7 @@ buildInputs = [ jdk libfoo ];
script to run it using the OpenJRE. You can use
<literal>makeWrapper</literal> for this:
<programlisting>
buildInputs = [ makeWrapper ];
nativeBuildInputs = [ makeWrapper ];
installPhase =
''
@ -76,7 +77,7 @@ installPhase =
It is possible to use a different Java compiler than <command>javac</command>
from the OpenJDK. For instance, to use the GNU Java Compiler:
<programlisting>
buildInputs = [ gcj ant ];
nativeBuildInputs = [ gcj ant ];
</programlisting>
Here, Ant will automatically use <command>gij</command> (the GNU Java
Runtime) instead of the OpenJRE.

View file

@ -603,6 +603,7 @@ All parameters from `stdenv.mkDerivation` function are still supported. The foll
* `preShellHook`: Hook to execute commands before `shellHook`.
* `postShellHook`: Hook to execute commands after `shellHook`.
* `removeBinByteCode ? true`: Remove bytecode from `/bin`. Bytecode is only created when the filenames end with `.py`.
* `setupPyGlobalFlags ? []`: List of flags passed to `setup.py` command.
* `setupPyBuildFlags ? []`: List of flags passed to `setup.py build_ext` command.
The `stdenv.mkDerivation` function accepts various parameters for describing build inputs (see "Specifying dependencies"). The following are of special

View file

@ -4,71 +4,173 @@
<title>Qt</title>
<para>
Qt is a comprehensive desktop and mobile application development toolkit for
C++. Legacy support is available for Qt 3 and Qt 4, but all current
development uses Qt 5. The Qt 5 packages in Nixpkgs are updated frequently to
take advantage of new features, but older versions are typically retained
until their support window ends. The most important consideration in
packaging Qt-based software is ensuring that each package and all its
dependencies use the same version of Qt 5; this consideration motivates most
of the tools described below.
This section describes the differences between Nix expressions for Qt
libraries and applications and Nix expressions for other C++ software. Some
knowledge of the latter is assumed. There are primarily two problems which
the Qt infrastructure is designed to address: ensuring consistent versioning
of all dependencies and finding dependencies at runtime.
</para>
<section xml:id="ssec-qt-libraries">
<title>Packaging Libraries for Nixpkgs</title>
<example xml:id='qt-default-nix'>
<title>Nix expression for a Qt package (<filename>default.nix</filename>)</title>
<programlisting>
{ mkDerivation, lib, qtbase }: <co xml:id='qt-default-nix-co-1' />
<para>
Whenever possible, libraries that use Qt 5 should be built with each
available version. Packages providing libraries should be added to the
top-level function <varname>mkLibsForQt5</varname>, which is used to build a
set of libraries for every Qt 5 version. A special
<varname>callPackage</varname> function is used in this scope to ensure that
the entire dependency tree uses the same Qt 5 version. Import dependencies
unqualified, i.e., <literal>qtbase</literal> not
<literal>qt5.qtbase</literal>. <emphasis>Do not</emphasis> import a package
set such as <literal>qt5</literal> or <literal>libsForQt5</literal>.
</para>
mkDerivation { <co xml:id='qt-default-nix-co-2' />
pname = "myapp";
version = "1.0";
<para>
If a library does not support a particular version of Qt 5, it is best to
mark it as broken by setting its <literal>meta.broken</literal> attribute. A
package may be marked broken for certain versions by testing the
<literal>qtbase.version</literal> attribute, which will always give the
current Qt 5 version.
</para>
</section>
buildInputs = [ qtbase ]; <co xml:id='qt-default-nix-co-3' />
}
</programlisting>
</example>
<section xml:id="ssec-qt-applications">
<title>Packaging Applications for Nixpkgs</title>
<calloutlist>
<callout arearefs='qt-default-nix-co-1'>
<para>
Import <literal>mkDerivation</literal> and Qt (such as
<literal>qtbase</literal> modules directly. <emphasis>Do not</emphasis>
import Qt package sets; the Qt versions of dependencies may not be
coherent, causing build and runtime failures.
</para>
</callout>
<callout arearefs='qt-default-nix-co-2'>
<para>
Use <literal>mkDerivation</literal> instead of
<literal>stdenv.mkDerivation</literal>. <literal>mkDerivation</literal>
is a wrapper around <literal>stdenv.mkDerivation</literal> which
applies some Qt-specific settings.
This deriver accepts the same arguments as
<literal>stdenv.mkDerivation</literal>; refer to
<xref linkend='chap-stdenv' /> for details.
</para>
<para>
To use another deriver instead of
<literal>stdenv.mkDerivation</literal>, use
<literal>mkDerivationWith</literal>:
<programlisting>
mkDerivationWith myDeriver {
# ...
}
</programlisting>
If you cannot use <literal>mkDerivationWith</literal>, please refer to
<xref linkend='qt-runtime-dependencies' />.
</para>
</callout>
<callout arearefs='qt-default-nix-co-3'>
<para>
<literal>mkDerivation</literal> accepts the same arguments as
<literal>stdenv.mkDerivation</literal>, such as
<literal>buildInputs</literal>.
</para>
</callout>
</calloutlist>
<para>
Call your application expression using
<literal>libsForQt5.callPackage</literal> instead of
<literal>callPackage</literal>. Import dependencies unqualified, i.e.,
<literal>qtbase</literal> not <literal>qt5.qtbase</literal>. <emphasis>Do
not</emphasis> import a package set such as <literal>qt5</literal> or
<literal>libsForQt5</literal>.
</para>
<formalpara xml:id='qt-runtime-dependencies'>
<title>Locating runtime dependencies</title>
<para>
Qt applications need to be wrapped to find runtime dependencies. If you
cannot use <literal>mkDerivation</literal> or
<literal>mkDerivationWith</literal> above, include
<literal>wrapQtAppsHook</literal> in <literal>nativeBuildInputs</literal>:
<programlisting>
stdenv.mkDerivation {
# ...
<para>
Qt 5 maintains strict backward compatibility, so it is generally best to
build an application package against the latest version using the
<varname>libsForQt5</varname> library set. In case a package does not build
with the latest Qt version, it is possible to pick a set pinned to a
particular version, e.g. <varname>libsForQt55</varname> for Qt 5.5, if that
is the latest version the package supports. If a package must be pinned to
an older Qt version, be sure to file a bug upstream; because Qt is strictly
backwards-compatible, any incompatibility is by definition a bug in the
application.
</para>
nativeBuildInputs = [ wrapQtAppsHook ];
}
</programlisting>
</para>
</formalpara>
<para>
Entries added to <literal>qtWrapperArgs</literal> are used to modify the
wrappers created by <literal>wrapQtAppsHook</literal>. The entries are
passed as arguments to <xref linkend='fun-wrapProgram' />.
<programlisting>
mkDerivation {
# ...
qtWrapperArgs = [ ''--prefix PATH : /path/to/bin'' ];
}
</programlisting>
</para>
<para>
Set <literal>dontWrapQtApps</literal> to stop applications from being
wrapped automatically. It is required to wrap applications manually with
<literal>wrapQtApp</literal>, using the syntax of
<xref linkend='fun-wrapProgram' />:
<programlisting>
mkDerivation {
# ...
dontWrapQtApps = true;
preFixup = ''
wrapQtApp "$out/bin/myapp" --prefix PATH : /path/to/bin
'';
}
</programlisting>
</para>
<para>
Libraries are built with every available version of Qt. Use the <literal>meta.broken</literal>
attribute to disable the package for unsupported Qt versions:
<programlisting>
mkDerivation {
# ...
# Disable this library with Qt &lt; 5.9.0
meta.broken = builtins.compareVersions qtbase.version "5.9.0" &lt; 0;
}
</programlisting>
</para>
<formalpara>
<title>Adding a library to Nixpkgs</title>
<para>
Add a Qt library to <filename>all-packages.nix</filename> by adding it to the
collection inside <literal>mkLibsForQt5</literal>. This ensures that the
library is built with every available version of Qt as needed.
<example xml:id='qt-library-all-packages-nix'>
<title>Adding a Qt library to <filename>all-packages.nix</filename></title>
<programlisting>
{
# ...
mkLibsForQt5 = self: with self; {
# ...
mylib = callPackage ../path/to/mylib {};
};
# ...
}
</programlisting>
</example>
</para>
</formalpara>
<formalpara>
<title>Adding an application to Nixpkgs</title>
<para>
Add a Qt application to <filename>all-packages.nix</filename> using
<literal>libsForQt5.callPackage</literal> instead of the usual
<literal>callPackage</literal>. The former ensures that all dependencies
are built with the same version of Qt.
<example xml:id='qt-application-all-packages-nix'>
<title>Adding a Qt application to <filename>all-packages.nix</filename></title>
<programlisting>
{
# ...
myapp = libsForQt5.callPackage ../path/to/myapp/ {};
# ...
}
</programlisting>
</example>
</para>
</formalpara>
<para>
When testing applications in Nixpkgs, it is a common practice to build the
package with <literal>nix-build</literal> and run it using the created
symbolic link. This will not work with Qt applications, however, because
they have many hard runtime requirements that can only be guaranteed if the
package is actually installed. To test a Qt application, install it with
<literal>nix-env</literal> or run it inside <literal>nix-shell</literal>.
</para>
</section>
</section>

View file

@ -21,7 +21,7 @@ At the moment we support three different methods for managing plugins:
Adding custom .vimrc lines can be done using the following code:
```
```nix
vim_configurable.customize {
# `name` specifies the name of the executable and package
name = "vim-with-plugins";
@ -32,11 +32,11 @@ vim_configurable.customize {
}
```
This configuration is used when vim is invoked with the command specified as name, in this case `vim-with-plugins`.
This configuration is used when Vim is invoked with the command specified as name, in this case `vim-with-plugins`.
For Neovim the `configure` argument can be overridden to achieve the same:
```
```nix
neovim.override {
configure = {
customRC = ''
@ -46,10 +46,10 @@ neovim.override {
}
```
If you want to use `neovim-qt` as a graphical editor, you can configure it by overriding neovim in an overlay
or passing it an overridden neovimn:
If you want to use `neovim-qt` as a graphical editor, you can configure it by overriding Neovim in an overlay
or passing it an overridden Neovimn:
```
```nix
neovim-qt.override {
neovim = neovim.override {
configure = {
@ -63,16 +63,16 @@ neovim-qt.override {
## Managing plugins with Vim packages
To store you plugins in Vim packages (the native vim plugin manager, see `:help packages`) the following example can be used:
To store you plugins in Vim packages (the native Vim plugin manager, see `:help packages`) the following example can be used:
```
```nix
vim_configurable.customize {
vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
start = [ youcompleteme fugitive ];
# manually loadable by calling `:packadd $plugin-name`
# however, if a vim plugin has a dependency that is not explicitly listed in
# opt that dependency will always be added to start to avoid confusion.
# however, if a Vim plugin has a dependency that is not explicitly listed in
# opt that dependency will always be added to start to avoid confusion.
opt = [ phpCompletion elm-vim ];
# To automatically load a plugin when opening a filetype, add vimrc lines like:
# autocmd FileType php :packadd phpCompletion
@ -83,7 +83,7 @@ vim_configurable.customize {
`myVimPackage` is an arbitrary name for the generated package. You can choose any name you like.
For Neovim the syntax is:
```
```nix
neovim.override {
configure = {
customRC = ''
@ -92,7 +92,7 @@ neovim.override {
packages.myVimPackage = with pkgs.vimPlugins; {
# see examples below how to use custom packages
start = [ ];
# If a vim plugin has a dependency that is not explicitly listed in
# If a Vim plugin has a dependency that is not explicitly listed in
# opt that dependency will always be added to start to avoid confusion.
opt = [ ];
};
@ -102,7 +102,7 @@ neovim.override {
The resulting package can be added to `packageOverrides` in `~/.nixpkgs/config.nix` to make it installable:
```
```nix
{
packageOverrides = pkgs: with pkgs; {
myVim = vim_configurable.customize {
@ -126,7 +126,7 @@ After that you can install your special grafted `myVim` or `myNeovim` packages.
To use [vim-plug](https://github.com/junegunn/vim-plug) to manage your Vim
plugins the following example can be used:
```
```nix
vim_configurable.customize {
vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
@ -137,7 +137,7 @@ vim_configurable.customize {
For Neovim the syntax is:
```
```nix
neovim.override {
configure = {
customRC = ''
@ -161,89 +161,112 @@ assuming that "using latest version" is ok most of the time.
First create a vim-scripts file having one plugin name per line. Example:
"tlib"
{'name': 'vim-addon-sql'}
{'filetype_regex': '\%(vim)$', 'names': ['reload', 'vim-dev-plugin']}
```
"tlib"
{'name': 'vim-addon-sql'}
{'filetype_regex': '\%(vim)$', 'names': ['reload', 'vim-dev-plugin']}
```
Such vim-scripts file can be read by VAM as well like this:
call vam#Scripts(expand('~/.vim-scripts'), {})
```vim
call vam#Scripts(expand('~/.vim-scripts'), {})
```
Create a default.nix file:
{ nixpkgs ? import <nixpkgs> {}, compiler ? "ghc7102" }:
nixpkgs.vim_configurable.customize { name = "vim"; vimrcConfig.vam.pluginDictionaries = [ "vim-addon-vim2nix" ]; }
```nix
{ nixpkgs ? import <nixpkgs> {}, compiler ? "ghc7102" }:
nixpkgs.vim_configurable.customize { name = "vim"; vimrcConfig.vam.pluginDictionaries = [ "vim-addon-vim2nix" ]; }
```
Create a generate.vim file:
ActivateAddons vim-addon-vim2nix
let vim_scripts = "vim-scripts"
call nix#ExportPluginsForNix({
\ 'path_to_nixpkgs': eval('{"'.substitute(substitute(substitute($NIX_PATH, ':', ',', 'g'), '=',':', 'g'), '\([:,]\)', '"\1"',"g").'"}')["nixpkgs"],
\ 'cache_file': '/tmp/vim2nix-cache',
\ 'try_catch': 0,
\ 'plugin_dictionaries': ["vim-addon-manager"]+map(readfile(vim_scripts), 'eval(v:val)')
\ })
```vim
ActivateAddons vim-addon-vim2nix
let vim_scripts = "vim-scripts"
call nix#ExportPluginsForNix({
\ 'path_to_nixpkgs': eval('{"'.substitute(substitute(substitute($NIX_PATH, ':', ',', 'g'), '=',':', 'g'), '\([:,]\)', '"\1"',"g").'"}')["nixpkgs"],
\ 'cache_file': '/tmp/vim2nix-cache',
\ 'try_catch': 0,
\ 'plugin_dictionaries': ["vim-addon-manager"]+map(readfile(vim_scripts), 'eval(v:val)')
\ })
```
Then run
nix-shell -p vimUtils.vim_with_vim2nix --command "vim -c 'source generate.vim'"
```bash
nix-shell -p vimUtils.vim_with_vim2nix --command "vim -c 'source generate.vim'"
```
You should get a Vim buffer with the nix derivations (output1) and vam.pluginDictionaries (output2).
You can add your vim to your system's configuration file like this and start it by "vim-my":
You can add your Vim to your system's configuration file like this and start it by "vim-my":
my-vim =
let plugins = let inherit (vimUtils) buildVimPluginFrom2Nix; in {
copy paste output1 here
}; in vim_configurable.customize {
name = "vim-my";
```
my-vim =
let plugins = let inherit (vimUtils) buildVimPluginFrom2Nix; in {
copy paste output1 here
}; in vim_configurable.customize {
name = "vim-my";
vimrcConfig.vam.knownPlugins = plugins; # optional
vimrcConfig.vam.pluginDictionaries = [
copy paste output2 here
];
# Pathogen would be
# vimrcConfig.pathogen.knownPlugins = plugins; # plugins
# vimrcConfig.pathogen.pluginNames = ["tlib"];
};
vimrcConfig.vam.knownPlugins = plugins; # optional
vimrcConfig.vam.pluginDictionaries = [
copy paste output2 here
];
# Pathogen would be
# vimrcConfig.pathogen.knownPlugins = plugins; # plugins
# vimrcConfig.pathogen.pluginNames = ["tlib"];
};
```
Sample output1:
"reload" = buildVimPluginFrom2Nix { # created by nix#NixDerivation
name = "reload";
src = fetchgit {
url = "git://github.com/xolox/vim-reload";
rev = "0a601a668727f5b675cb1ddc19f6861f3f7ab9e1";
sha256 = "0vb832l9yxj919f5hfg6qj6bn9ni57gnjd3bj7zpq7d4iv2s4wdh";
};
dependencies = ["nim-misc"];
```
"reload" = buildVimPluginFrom2Nix { # created by nix#NixDerivation
name = "reload";
src = fetchgit {
url = "git://github.com/xolox/vim-reload";
rev = "0a601a668727f5b675cb1ddc19f6861f3f7ab9e1";
sha256 = "0vb832l9yxj919f5hfg6qj6bn9ni57gnjd3bj7zpq7d4iv2s4wdh";
};
dependencies = ["nim-misc"];
};
[...]
};
[...]
```
Sample output2:
[
''vim-addon-manager''
''tlib''
{ "name" = ''vim-addon-sql''; }
{ "filetype_regex" = ''\%(vim)$$''; "names" = [ ''reload'' ''vim-dev-plugin'' ]; }
]
```nix
[
''vim-addon-manager''
''tlib''
{ "name" = ''vim-addon-sql''; }
{ "filetype_regex" = ''\%(vim)$$''; "names" = [ ''reload'' ''vim-dev-plugin'' ]; }
]
```
## Adding new plugins to nixpkgs
In `pkgs/misc/vim-plugins/vim-plugin-names` we store the plugin names
for all vim plugins we automatically generate plugins for.
The format of this file `github username/github repository`:
For example https://github.com/scrooloose/nerdtree becomes `scrooloose/nerdtree`.
After adding your plugin to this file run the `./update.py` in the same folder.
This will updated a file called `generated.nix` and make your plugin accessible in the
`vimPlugins` attribute set (`vimPlugins.nerdtree` in our example).
If additional steps to the build process of the plugin are required, add an
override to the `pkgs/misc/vim-plugins/default.nix` in the same directory.
Nix expressions for Vim plugins are stored in [pkgs/misc/vim-plugins](/pkgs/misc/vim-plugins). For the vast majority of plugins, Nix expressions are automatically generated by running [`./update.py`](/pkgs/misc/vim-plugins/update.py). This creates a [generated.nix](/pkgs/misc/vim-plugins/generated.nix) file based on the plugins listed in [vim-plugin-names](/pkgs/misc/vim-plugins/vim-plugin-names). Plugins are listed in alphabetical order in `vim-plugin-names` using the format `[github username]/[repository]`. For example https://github.com/scrooloose/nerdtree becomes `scrooloose/nerdtree`.
Some plugins require overrides in order to function properly. Overrides are placed in [overrides.nix](/pkgs/misc/vim-plugins/overrides.nix). Overrides are most often required when a plugin requires some dependencies, or extra steps are required during the build process. For example `deoplete-fish` requires both `deoplete-nvim` and `vim-fish`, and so the following override was added:
```
deoplete-fish = super.deoplete-fish.overrideAttrs(old: {
dependencies = with super; [ deoplete-nvim vim-fish ];
});
```
Sometimes plugins require an override that must be changed when the plugin is updated. This can cause issues when Vim plugins are auto-updated but the associated override isn't updated. For these plugins, the override should be written so that it specifies all information required to install the plugin, and running `./update.py` doesn't change the derivation for the plugin. Manually updating the override is required to update these types of plugins. An example of such a plugin is `LanguageClient-neovim`.
To add a new plugin:
1. run `./update.py` and create a commit named "vimPlugins: Update",
2. add the new plugin to [vim-plugin-names](/pkgs/misc/vim-plugins/vim-plugin-names) and add overrides if required to [overrides.nix](/pkgs/misc/vim-plugins/overrides.nix),
3. run `./update.py` again and create a commit named "vimPlugins.[name]: init at [version]" (where `name` and `version` can be found in [generated.nix](/pkgs/misc/vim-plugins/generated.nix)), and
4. create a pull request.
## Important repositories
@ -252,4 +275,3 @@ override to the `pkgs/misc/vim-plugins/default.nix` in the same directory.
- [vim2nix](https://github.com/MarcWeber/vim-addon-vim2nix) which generates the
.nix code

View file

@ -409,11 +409,19 @@ overrides = self: super: rec {
</section>
<section xml:id="sec-citrix">
<title>Citrix Receiver</title>
<title>Citrix Receiver &amp; Citrix Workspace App</title>
<para>
The <link xlink:href="https://www.citrix.com/products/receiver/">Citrix
Receiver</link> is a remote desktop viewer which provides access to
<note>
<para>
Please note that the <literal>citrix_receiver</literal> package has been deprecated since its
development was <link xlink:href="https://docs.citrix.com/en-us/citrix-workspace-app.html">discontinued by upstream</link>
and will be replaced by <link xlink:href="https://www.citrix.com/products/workspace-app/">the citrix workspace app</link>.
</para>
</note>
<link xlink:href="https://www.citrix.com/products/receiver/">Citrix Receiver</link> and
<link xlink:href="https://www.citrix.com/products/workspace-app/">Citrix Workspace App</link>
are a remote desktop viewers which provide access to
<link xlink:href="https://www.citrix.com/products/xenapp-xendesktop/">XenDesktop</link>
installations.
</para>
@ -423,30 +431,35 @@ overrides = self: super: rec {
<para>
The tarball archive needs to be downloaded manually as the license
agreements of the vendor need to be accepted first. This is available at
the
<link xlink:href="https://www.citrix.com/downloads/citrix-receiver/">download
page at citrix.com</link>. Then run <literal>nix-prefetch-url
file://$PWD/linuxx64-$version.tar.gz</literal>. With the archive available
agreements of the vendor for
<link xlink:href="https://www.citrix.com/downloads/citrix-receiver/">Citrix Receiver</link>
or <link xlink:href="https://www.citrix.de/downloads/workspace-app/linux/workspace-app-for-linux-latest.html">Citrix Workspace</link>
need to be accepted first.
Then run <command>nix-prefetch-url file://$PWD/linuxx64-$version.tar.gz</command>.
With the archive available
in the store the package can be built and installed with Nix.
</para>
<para>
<emphasis>Note: it's recommended to install <literal>Citrix
Receiver</literal> using <literal>nix-env -i</literal> or globally to
ensure that the <literal>.desktop</literal> files are installed properly
into <literal>$XDG_CONFIG_DIRS</literal>. Otherwise it won't be possible to
open <literal>.ica</literal> files automatically from the browser to start
a Citrix connection.</emphasis>
</para>
<warning>
<title>Caution with <command>nix-shell</command> installs</title>
<para>
It's recommended to install <literal>Citrix Receiver</literal>
and/or <literal>Citrix Workspace</literal> using
<literal>nix-env -i</literal> or globally to
ensure that the <literal>.desktop</literal> files are installed properly
into <literal>$XDG_CONFIG_DIRS</literal>. Otherwise it won't be possible to
open <literal>.ica</literal> files automatically from the browser to start
a Citrix connection.
</para>
</warning>
</section>
<section xml:id="sec-citrix-custom-certs">
<title>Custom certificates</title>
<para>
The <literal>Citrix Receiver</literal> in <literal>nixpkgs</literal> trusts
several certificates
The <literal>Citrix Receiver</literal> and <literal>Citrix Workspace App</literal>
in <literal>nixpkgs</literal> trust several certificates
<link xlink:href="https://curl.haxx.se/docs/caextract.html">from the
Mozilla database</link> by default. However several companies using Citrix
might require their own corporate certificate. On distros with imperative
@ -459,7 +472,7 @@ overrides = self: super: rec {
<programlisting>
<![CDATA[with import <nixpkgs> { config.allowUnfree = true; };
let extraCerts = [ ./custom-cert-1.pem ./custom-cert-2.pem /* ... */ ]; in
citrix_receiver.override {
citrix_workspace.override { # the same applies for `citrix_receiver` if used.
inherit extraCerts;
}]]>
</programlisting>

View file

@ -786,7 +786,7 @@ passthru.updateScript = [ ../../update.sh pname "--requested-release=unstable" ]
set, the default value is used, which is <literal>$prePhases
unpackPhase patchPhase $preConfigurePhases configurePhase
$preBuildPhases buildPhase checkPhase $preInstallPhases installPhase
fixupPhase $preDistPhases distPhase $postPhases</literal>.
fixupPhase installCheckPhase $preDistPhases distPhase $postPhases</literal>.
</para>
<para>
Usually, if you just want to add a few phases, its more convenient

View file

@ -53,12 +53,16 @@ rec {
# Filter sources by a list of regular expressions.
#
# E.g. `src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]`
sourceByRegex = src: regexes: cleanSourceWith {
filter = (path: type:
let relPath = lib.removePrefix (toString src + "/") (toString path);
in lib.any (re: builtins.match re relPath != null) regexes);
inherit src;
};
sourceByRegex = src: regexes:
let
isFiltered = src ? _isLibCleanSourceWith;
origSrc = if isFiltered then src.origSrc else src;
in lib.cleanSourceWith {
filter = (path: type:
let relPath = lib.removePrefix (toString origSrc + "/") (toString path);
in lib.any (re: builtins.match re relPath != null) regexes);
inherit src;
};
# Get all files ending with the specified suffices from the given
# directory or its descendants. E.g. `sourceFilesBySuffices ./dir

View file

@ -26,7 +26,7 @@ let
"riscv32-linux" "riscv64-linux"
"aarch64-none" "avr-none" "arm-none" "i686-none" "x86_64-none" "powerpc-none" "msp430-none"
"aarch64-none" "avr-none" "arm-none" "i686-none" "x86_64-none" "powerpc-none" "msp430-none" "riscv64-none" "riscv32-none"
];
allParsed = map parse.mkSystemFromString all;

View file

@ -97,6 +97,18 @@ rec {
riscv64 = riscv "64";
riscv32 = riscv "32";
riscv64-embedded = {
config = "riscv64-none-elf";
libc = "newlib";
platform = platforms.riscv-multiplatform "64";
};
riscv32-embedded = {
config = "riscv32-none-elf";
libc = "newlib";
platform = platforms.riscv-multiplatform "32";
};
msp430 = {
config = "msp430-elf";
libc = "newlib";

View file

@ -111,7 +111,7 @@ rec {
name = "int";
description = "signed integer";
check = isInt;
merge = mergeOneOption;
merge = mergeEqualOption;
};
# Specialized subdomains of int
@ -176,14 +176,14 @@ rec {
name = "float";
description = "floating point number";
check = isFloat;
merge = mergeOneOption;
merge = mergeEqualOption;
};
str = mkOptionType {
name = "str";
description = "string";
check = isString;
merge = mergeOneOption;
merge = mergeEqualOption;
};
strMatching = pattern: mkOptionType {
@ -243,7 +243,7 @@ rec {
name = "path";
# Hacky: there is no isPath primop.
check = x: builtins.substring 0 1 (toString x) == "/";
merge = mergeOneOption;
merge = mergeEqualOption;
};
# drop this in the future:
@ -415,7 +415,7 @@ rec {
name = "enum";
description = "one of ${concatMapStringsSep ", " show values}";
check = flip elem values;
merge = mergeOneOption;
merge = mergeEqualOption;
functor = (defaultFunctor name) // { payload = values; binOp = a: b: unique (a ++ b); };
};

View file

@ -250,6 +250,11 @@
github = "akru";
name = "Alexander Krupenkin ";
};
alexarice = {
email = "alexrice999@hotmail.co.uk";
github = "alexarice";
name = "Alex Rice";
};
alexchapman = {
email = "alex@farfromthere.net";
github = "AJChapman";
@ -328,6 +333,11 @@
github = "andersk";
name = "Anders Kaseorg";
};
anderslundstedt = {
email = "git@anderslundstedt.se";
github = "anderslundstedt";
name = "Anders Lundstedt";
};
AndersonTorres = {
email = "torres.anderson.85@protonmail.com";
github = "AndersonTorres";
@ -383,6 +393,11 @@
github = "aneeshusa";
name = "Aneesh Agrawal";
};
angristan = {
email = "angristan@pm.me";
github = "angristan";
name = "Stanislas Lange";
};
ankhers = {
email = "justin.k.wood@gmail.com";
github = "ankhers";
@ -537,6 +552,15 @@
github = "averelld";
name = "averelld";
};
avitex = {
email = "theavitex@gmail.com";
github = "avitex";
name = "avitex";
keys = [{
longkeyid = "rsa4096/0x8B366C443CABE942";
fingerprint = "271E 136C 178E 06FA EA4E B854 8B36 6C44 3CAB E942";
}];
};
avnik = {
email = "avn@avnik.info";
github = "avnik";
@ -1134,6 +1158,19 @@
github = "cwoac";
name = "Oliver Matthews";
};
cypherpunk2140 = {
email = "stefan.mihaila@pm.me";
github = "cypherpunk2140";
name = "Ștefan D. Mihăilă";
keys = [
{ longkeyid = "rsa4096/6E68A39BF16A3ECB";
fingerprint = "CBC9 C7CC 51F0 4A61 3901 C723 6E68 A39B F16A 3ECB";
}
{ longkeyid = "rsa4096/6220AD7846220A52";
fingerprint = "7EAB 1447 5BBA 7DDE 7092 7276 6220 AD78 4622 0A52";
}
];
};
dalance = {
email = "dalance@gmail.com";
github = "dalance";
@ -1223,6 +1260,11 @@
github = "deepfire";
name = "Kosyrev Serge";
};
delan = {
name = "Delan Azabani";
email = "delan@azabani.com";
github = "delan";
};
delroth = {
email = "delroth@gmail.com";
github = "delroth";
@ -2100,6 +2142,11 @@
github = "henrytill";
name = "Henry Till";
};
herberteuler = {
email = "herberteuler@gmail.com";
github = "herberteuler";
name = "Guanpeng Xu";
};
hhm = {
email = "heehooman+nixpkgs@gmail.com";
github = "hhm0";
@ -2114,6 +2161,11 @@
github = "hlolli";
name = "Hlodver Sigurdsson";
};
hugoreeves = {
email = "hugolreeves@gmail.com";
github = "hugoreeves";
name = "Hugo Reeves";
};
hodapp = {
email = "hodapp87@gmail.com";
github = "Hodapp87";
@ -2228,6 +2280,15 @@
email = "tkatchev@gmail.com";
name = "Ivan Tkatchev";
};
ivanbrennan = {
email = "ivan.brennan@gmail.com";
github = "ivanbrennan";
name = "Ivan Brennan";
keys = [{
longkeyid = "rsa4096/0x79C3C47DC652EA54";
fingerprint = "7311 2700 AB4F 4CDF C68C F6A5 79C3 C47D C652 EA54";
}];
};
ivegotasthma = {
email = "ivegotasthma@protonmail.com";
github = "ivegotasthma";
@ -2257,6 +2318,11 @@
github = "j-keck";
name = "Jürgen Keck";
};
j03 = {
email = "github@johannesloetzsch.de";
github = "johannesloetzsch";
name = "Johannes Lötzsch";
};
jagajaga = {
email = "ars.seroka@gmail.com";
github = "jagajaga";
@ -2445,6 +2511,11 @@
email = "me@joelt.io";
name = "Joel Taylor";
};
joepie91 = {
email = "admin@cryto.net";
name = "Sven Slootweg";
github = "joepie91";
};
johanot = {
email = "write@ownrisk.dk";
github = "johanot";
@ -2540,6 +2611,11 @@
github = "jraygauthier";
name = "Raymond Gauthier";
};
jtobin = {
email = "jared@jtobin.io";
github = "jtobin";
name = "Jared Tobin";
};
jtojnar = {
email = "jtojnar@gmail.com";
github = "jtojnar";
@ -2908,6 +2984,15 @@
github = "listx";
name = "Linus Arver";
};
livnev = {
email = "lev@liv.nev.org.uk";
github = "livnev";
name = "Lev Livnev";
keys = [{
longkeyid = "rsa2048/0x68FF81E6A7850F49";
fingerprint = "74F5 E5CC 19D3 B5CB 608F 6124 68FF 81E6 A785 0F49";
}];
};
luis = {
email = "luis.nixos@gmail.com";
github = "Luis-Hebendanz";
@ -3253,6 +3338,11 @@
email = "softs@metabarcoding.org";
name = "Celine Mercier";
};
metadark = {
email = "kira.bruneau@gmail.com";
name = "Kira Bruneau";
github = "metadark";
};
mfossen = {
email = "msfossen@gmail.com";
github = "mfossen";
@ -4123,10 +4213,19 @@
github = "proglodyte";
name = "Proglodyte";
};
protoben = {
email = "protob3n@gmail.com";
github = "protoben";
name = "Ben Hamlin";
};
prusnak = {
email = "stick@gk2.sk";
email = "pavol@rusnak.io";
github = "prusnak";
name = "Pavol Rusnak";
keys = [{
longkeyid = "rsa4096/0x91F3B339B9A02A3D";
fingerprint = "86E6 792F C27B FD47 8860 C110 91F3 B339 B9A0 2A3D";
}];
};
pshendry = {
email = "paul@pshendry.com";
@ -4528,11 +4627,6 @@
github = "rzetterberg";
name = "Richard Zetterberg";
};
s1lvester = {
email = "s1lvester@bockhacker.me";
github = "s1lvester";
name = "Markus Silvester";
};
samdroid-apps = {
email = "sam@sam.today";
github = "samdroid-apps";
@ -4626,6 +4720,11 @@
github = "scriptkiddi";
name = "Fritz Otlinghaus";
};
scubed2 = {
email = "scubed2@gmail.com";
github = "scubed2";
name = "Sterling Stein";
};
sdll = {
email = "sasha.delly@gmail.com";
github = "sdll";
@ -4916,6 +5015,15 @@
github = "sternenseemann";
name = "Lukas Epple";
};
steshaw = {
name = "Steven Shaw";
email = "steven@steshaw.org";
github = "steshaw";
keys = [{
longkeyid = "rsa4096/0x1D9A17DFD23DCB91";
fingerprint = "0AFE 77F7 474D 1596 EE55 7A29 1D9A 17DF D23D CB91";
}];
};
stesie = {
email = "stesie@brokenpipe.de";
github = "stesie";
@ -5348,6 +5456,15 @@
github = "twey";
name = "James Twey Kay";
};
twhitehead = {
name = "Tyson Whitehead";
email = "twhitehead@gmail.com";
github = "twhitehead";
keys = [{
longkeyid = "rsa2048/0x594258F0389D2802";
fingerprint = "E631 8869 586F 99B4 F6E6 D785 5942 58F0 389D 2802";
}];
};
typetetris = {
email = "ericwolf42@mail.com";
github = "typetetris";
@ -5536,6 +5653,11 @@
github = "vskilet";
name = "Victor SENE";
};
vyorkin = {
email = "vasiliy.yorkin@gmail.com";
github = "vyorkin";
name = "Vasiliy Yorkin";
};
vyp = {
email = "elisp.vim@gmail.com";
github = "vyp";
@ -5749,9 +5871,9 @@
name = "Christian Zagrodnick";
};
zalakain = {
email = "contact@unaizalakain.info";
email = "ping@umazalakain.info";
github = "umazalakain";
name = "Unai Zalakain";
name = "Uma Zalakain";
};
zaninime = {
email = "francesco@zanini.me";
@ -5822,4 +5944,14 @@
github = "turboMaCk";
name = "Marek Fajkus";
};
melling = {
email = "mattmelling@fastmail.com";
github = "mattmelling";
name = "Matt Melling";
};
wd15 = {
email = "daniel.wheeler2@gmail.com";
github = "wd15";
name = "Daniel Wheeler";
};
}

View file

@ -95,7 +95,7 @@ in {
# forward all Matrix API calls to the synapse Matrix homeserver
locations."/_matrix" = {
proxyPass = "http://[::1]:8008/_matrix";
proxyPass = "http://[::1]:8008"; # without a trailing /
};
};
};

View file

@ -125,10 +125,12 @@
You will need to reboot after enabling this driver to prevent a clash with
other kernel modules.
</para>
<note>
<para>
Note: for recent AMD GPUs you most likely want to keep either the defaults
For recent AMD GPUs you most likely want to keep either the defaults
or <literal>"amdgpu"</literal> (both free).
</para>
</note>
</simplesect>
<simplesect xml:id="sec-x11-touchpads">
<title>Touchpads</title>
@ -157,4 +159,134 @@
versions.
</para>
</simplesect>
<simplesect xml:id="custom-xkb-layouts">
<title>Custom XKB layouts</title>
<para>
It is possible to install custom
<link xlink:href="https://en.wikipedia.org/wiki/X_keyboard_extension">
XKB
</link>
keyboard layouts using the option
<option>
<link linkend="opt-services.xserver.extraLayouts">
services.xserver.extraLayouts
</link>
</option>.
As a first example, we are going to create a layout based on the basic US
layout, with an additional layer to type some greek symbols by pressing the
right-alt key.
</para>
<para>
To do this we are going to create a <literal>us-greek</literal> file
with a <literal>xkb_symbols</literal> section.
</para>
<programlisting>
xkb_symbols &quot;us-greek&quot;
{
include &quot;us(basic)&quot; // includes the base US keys
include &quot;level3(ralt_switch)&quot; // configures right alt as a third level switch
key &lt;LatA&gt; { [ a, A, Greek_alpha ] };
key &lt;LatB&gt; { [ b, B, Greek_beta ] };
key &lt;LatG&gt; { [ g, G, Greek_gamma ] };
key &lt;LatD&gt; { [ d, D, Greek_delta ] };
key &lt;LatZ&gt; { [ z, Z, Greek_zeta ] };
};
</programlisting>
<para>
To install the layout, the filepath, a description and the list of
languages must be given:
</para>
<programlisting>
<xref linkend="opt-services.xserver.extraLayouts"/>.us-greek = {
description = "US layout with alt-gr greek";
languages = [ "eng" ];
symbolsFile = /path/to/us-greek;
}
</programlisting>
<note>
<para>
The name should match the one given to the
<literal>xkb_symbols</literal> block.
</para>
</note>
<para>
The layout should now be installed and ready to use: try it by
running <literal>setxkbmap us-greek</literal> and type
<literal>&lt;alt&gt;+a</literal>. To change the default the usual
<option>
<link linkend="opt-services.xserver.layout">
services.xserver.layout
</link>
</option>
option can still be used.
</para>
<para>
A layout can have several other components besides
<literal>xkb_symbols</literal>, for example we will define new
keycodes for some multimedia key and bind these to some symbol.
</para>
<para>
Use the <emphasis>xev</emphasis> utility from
<literal>pkgs.xorg.xev</literal> to find the codes of the keys of
interest, then create a <literal>media-key</literal> file to hold
the keycodes definitions
</para>
<programlisting>
xkb_keycodes &quot;media&quot;
{
&lt;volUp&gt; = 123;
&lt;volDown&gt; = 456;
}
</programlisting>
<para>
Now use the newly define keycodes in <literal>media-sym</literal>:
</para>
<programlisting>
xkb_symbols &quot;media&quot;
{
key.type = &quot;ONE_LEVEL&quot;;
key &lt;volUp&gt; { [ XF86AudioLowerVolume ] };
key &lt;volDown&gt; { [ XF86AudioRaiseVolume ] };
}
</programlisting>
<para>
As before, to install the layout do
</para>
<programlisting>
<xref linkend="opt-services.xserver.extraLayouts"/>.media = {
description = "Multimedia keys remapping";
languages = [ "eng" ];
symbolsFile = /path/to/media-key;
keycodesFile = /path/to/media-sym;
};
</programlisting>
<note>
<para>
The function <literal>pkgs.writeText &lt;filename&gt; &lt;content&gt;
</literal> can be useful if you prefer to keep the layout definitions
inside the NixOS configuration.
</para>
</note>
<para>
Unfortunately, the Xorg server does not (currently) support setting a
keymap directly but relies instead on XKB rules to select the matching
components (keycodes, types, ...) of a layout. This means that components
other than symbols won't be loaded by default. As a workaround, you
can set the keymap using <literal>setxkbmap</literal> at the start of the
session with:
</para>
<programlisting>
<xref linkend="opt-services.xserver.displayManager.sessionCommands"/> = "setxkbmap -keycodes media";
</programlisting>
<para>
To learn how to write layouts take a look at the XKB
<link xlink:href="https://www.x.org/releases/current/doc/xorg-docs/input/XKB-Enhancing.html#Defining_New_Layouts">
documentation
</link>. More example layouts can also be found
<link xlink:href="https://wiki.archlinux.org/index.php/X_KeyBoard_extension#Basic_examples">
here
</link>.
</para>
</simplesect>
</chapter>

View file

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

View file

@ -122,6 +122,19 @@
</link>
</para>
</listitem>
<listitem>
<para>
IPv6 Privacy Extensions are now enabled by default for undeclared
interfaces. The previous behaviour was quite misleading — even though
the default value for
<option>networking.interfaces.*.preferTempAddress</option> was
<literal>true</literal>, undeclared interfaces would not prefer temporary
addresses. Now, interfaces not mentioned in the config will prefer
temporary addresses. EUI64 addresses can still be set as preferred by
explicitly setting the option to <literal>false</literal> for the
interface in question.
</para>
</listitem>
<listitem>
<para>
Since Bittorrent Sync was superseded by Resilio Sync in 2016, the
@ -136,10 +149,22 @@
</listitem>
<listitem>
<para>
Several of the apache subservices have been replaced with full NixOS
modules including LimeSurvey, WordPress, and Zabbix.
These modules can be enabled using the <option>services.limesurvey.enable</option>,
<option>services.wordpress.enable</option>, and <option>services.zabbixWeb.enable</option> options.
The httpd service no longer attempts to start the postgresql service. If you have come to depend
on this behaviour then you can preserve the behavior with the following configuration:
<literal>systemd.services.httpd.after = [ "postgresql.service" ];</literal>
</para>
<para>
The option <option>services.httpd.extraSubservices</option> has been
marked as deprecated. You may still use this feature, but it will be
removed in a future release of NixOS. You are encouraged to convert any
httpd subservices you may have written to a full NixOS module.
</para>
<para>
Most of the httpd subservices packaged with NixOS have been replaced with
full NixOS modules including LimeSurvey, WordPress, and Zabbix. These
modules can be enabled using the <option>services.limesurvey.enable</option>,
<option>services.mediawiki.enable</option>, <option>services.wordpress.enable</option>,
and <option>services.zabbixWeb.enable</option> options.
</para>
</listitem>
<listitem>
@ -182,6 +207,25 @@
<xref linkend="opt-programs.zsh.loginShellInit" /> and <xref linkend="opt-programs.zsh.promptInit" /> may break if it relies on those options being set.
</para>
</listitem>
<listitem>
<para>
The <literal>prometheus-nginx-exporter</literal> package now uses the offical exporter provided by NGINX Inc.
Its metrics are differently structured and are incompatible to the old ones. For information about the metrics,
have a look at the <link xlink:href="https://github.com/nginxinc/nginx-prometheus-exporter">official repo</link>.
</para>
</listitem>
<listitem>
<para>
Nodejs 8 is scheduled EOL under the lifetime of 19.09 and has been dropped.
</para>
</listitem>
<listitem>
<para>
By default, prometheus exporters are now run with <literal>DynamicUser</literal> enabled.
Exporters that need a real user, now run under a seperate user and group which follow the pattern <literal>&lt;exporter-name&gt;-exporter</literal>, instead of the previous default <literal>nobody</literal> and <literal>nogroup</literal>.
Only some exporters are affected by the latter, namely the exporters <literal>dovecot</literal>, <literal>node</literal>, <literal>postfix</literal> and <literal>varnish</literal>.
</para>
</listitem>
</itemizedlist>
</section>
@ -329,6 +373,20 @@
from nixpkgs due to lack of maintainer.
</para>
</listitem>
<listitem>
<para>
The <literal>tomcat-connector</literal> <literal>httpd.extraSubservice</literal> has been removed from nixpkgs.
</para>
</listitem>
<listitem>
<para>
It's now possible to change configuration in
<link linkend="opt-services.nextcloud.enable">services.nextcloud</link> after the initial deploy
since all config parameters are persisted in an additional config file generated by the module.
Previously core configuration like database parameters were set using their imperative
installer after creating <literal>/var/lib/nextcloud</literal>.
</para>
</listitem>
</itemizedlist>
</section>
</section>

View file

@ -31,9 +31,17 @@ sub new {
if (!$startCommand) {
# !!! merge with qemu-vm.nix.
my $netBackend = "-netdev user,id=net0";
my $netFrontend = "-device virtio-net-pci,netdev=net0";
$netBackend .= "," . $args->{netBackendArgs}
if defined $args->{netBackendArgs};
$netFrontend .= "," . $args->{netFrontendArgs}
if defined $args->{netFrontendArgs};
$startCommand =
"qemu-kvm -m 384 " .
"-net nic,model=virtio \$QEMU_OPTS ";
"qemu-kvm -m 384 $netBackend $netFrontend \$QEMU_OPTS ";
if (defined $args->{hda}) {
if ($args->{hdaInterface} eq "scsi") {

View file

@ -7,16 +7,6 @@ with lib;
let
cfg = config.networking;
dnsmasqResolve = config.services.dnsmasq.enable &&
config.services.dnsmasq.resolveLocalQueries;
hasLocalResolver = config.services.bind.enable ||
config.services.unbound.enable ||
dnsmasqResolve;
resolvconfOptions = cfg.resolvconfOptions
++ optional cfg.dnsSingleRequest "single-request"
++ optional cfg.dnsExtensionMechanism "edns0";
localhostMapped4 = cfg.hosts ? "127.0.0.1" && elem "localhost" cfg.hosts."127.0.0.1";
localhostMapped6 = cfg.hosts ? "::1" && elem "localhost" cfg.hosts."::1";
@ -64,48 +54,6 @@ in
'';
};
networking.dnsSingleRequest = lib.mkOption {
type = types.bool;
default = false;
description = ''
Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
address queries at the same time, from the same port. Sometimes upstream
routers will systemically drop the ipv4 queries. The symptom of this problem is
that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
workaround for this is to specify the option 'single-request' in
/etc/resolv.conf. This option enables that.
'';
};
networking.dnsExtensionMechanism = lib.mkOption {
type = types.bool;
default = true;
description = ''
Enable the <code>edns0</code> option in <filename>resolv.conf</filename>. With
that option set, <code>glibc</code> supports use of the extension mechanisms for
DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC,
which does not work without it.
'';
};
networking.extraResolvconfConf = lib.mkOption {
type = types.lines;
default = "";
example = "libc=NO";
description = ''
Extra configuration to append to <filename>resolvconf.conf</filename>.
'';
};
networking.resolvconfOptions = lib.mkOption {
type = types.listOf types.str;
default = [];
example = [ "ndots:1" "rotate" ];
description = ''
Set the options in <filename>/etc/resolv.conf</filename>.
'';
};
networking.timeServers = mkOption {
default = [
"0.nixos.pool.ntp.org"
@ -240,35 +188,6 @@ in
# /etc/host.conf: resolver configuration file
"host.conf".text = cfg.hostConf;
# /etc/resolvconf.conf: Configuration for openresolv.
"resolvconf.conf".text =
''
# This is the default, but we must set it here to prevent
# a collision with an apparently unrelated environment
# variable with the same name exported by dhcpcd.
interface_order='lo lo[0-9]*'
'' + optionalString config.services.nscd.enable ''
# Invalidate the nscd cache whenever resolv.conf is
# regenerated.
libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service 2> /dev/null'
'' + optionalString (length resolvconfOptions > 0) ''
# Options as described in resolv.conf(5)
resolv_conf_options='${concatStringsSep " " resolvconfOptions}'
'' + optionalString hasLocalResolver ''
# This hosts runs a full-blown DNS resolver.
name_servers='127.0.0.1'
'' + optionalString dnsmasqResolve ''
dnsmasq_conf=/etc/dnsmasq-conf.conf
dnsmasq_resolv=/etc/dnsmasq-resolv.conf
'' + cfg.extraResolvconfConf + ''
'';
} // optionalAttrs config.services.resolved.enable {
# symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
# https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
"resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
} // optionalAttrs (config.services.resolved.enable && dnsmasqResolve) {
"dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
} // optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
# /etc/rpc: RPC program numbers.
"rpc".source = pkgs.glibc.out + "/etc/rpc";
@ -295,29 +214,6 @@ in
# Install the proxy environment variables
environment.sessionVariables = cfg.proxy.envVars;
# This is needed when /etc/resolv.conf is being overriden by networkd
# and other configurations. If the file is destroyed by an environment
# activation then it must be rebuilt so that applications which interface
# with /etc/resolv.conf directly don't break.
system.activationScripts.resolvconf = stringAfter [ "etc" "specialfs" "var" ]
''
# Systemd resolved controls its own resolv.conf
rm -f /run/resolvconf/interfaces/systemd
${optionalString config.services.resolved.enable ''
rm -rf /run/resolvconf/interfaces
mkdir -p /run/resolvconf/interfaces
ln -s /run/systemd/resolve/resolv.conf /run/resolvconf/interfaces/systemd
''}
# Make sure resolv.conf is up to date if not managed manually, by systemd or
# by NetworkManager
${optionalString (!config.environment.etc?"resolv.conf" &&
(cfg.networkmanager.enable ->
cfg.networkmanager.rc-manager == "resolvconf")) ''
${pkgs.openresolv}/bin/resolvconf -u
''}
'';
};
}

View file

@ -34,7 +34,7 @@ with lib;
networkmanager-openvpn = super.networkmanager-openvpn.override { withGnome = false; };
networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; };
networkmanager-iodine = super.networkmanager-iodine.override { withGnome = false; };
pinentry = super.pinentry.override { gtk2 = null; gcr = null; qt = null; };
pinentry = super.pinentry.override { gtk2 = null; gcr = null; qt4 = null; qt5 = null; };
gobject-introspection = super.gobject-introspection.override { x11Support = false; };
}));
};

View file

@ -0,0 +1,149 @@
# /etc files related to networking, such as /etc/services.
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.networking.resolvconf;
resolvconfOptions = cfg.extraOptions
++ optional cfg.dnsSingleRequest "single-request"
++ optional cfg.dnsExtensionMechanism "edns0";
configText =
''
# This is the default, but we must set it here to prevent
# a collision with an apparently unrelated environment
# variable with the same name exported by dhcpcd.
interface_order='lo lo[0-9]*'
'' + optionalString config.services.nscd.enable ''
# Invalidate the nscd cache whenever resolv.conf is
# regenerated.
libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service 2> /dev/null'
'' + optionalString (length resolvconfOptions > 0) ''
# Options as described in resolv.conf(5)
resolv_conf_options='${concatStringsSep " " resolvconfOptions}'
'' + optionalString cfg.useLocalResolver ''
# This hosts runs a full-blown DNS resolver.
name_servers='127.0.0.1'
'' + cfg.extraConfig;
in
{
options = {
networking.resolvconf = {
enable = mkOption {
type = types.bool;
default = false;
internal = true;
description = ''
DNS configuration is managed by resolvconf.
'';
};
useHostResolvConf = mkOption {
type = types.bool;
default = false;
description = ''
In containers, whether to use the
<filename>resolv.conf</filename> supplied by the host.
'';
};
dnsSingleRequest = lib.mkOption {
type = types.bool;
default = false;
description = ''
Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
address queries at the same time, from the same port. Sometimes upstream
routers will systemically drop the ipv4 queries. The symptom of this problem is
that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
workaround for this is to specify the option 'single-request' in
/etc/resolv.conf. This option enables that.
'';
};
dnsExtensionMechanism = mkOption {
type = types.bool;
default = true;
description = ''
Enable the <code>edns0</code> option in <filename>resolv.conf</filename>. With
that option set, <code>glibc</code> supports use of the extension mechanisms for
DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC,
which does not work without it.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
example = "libc=NO";
description = ''
Extra configuration to append to <filename>resolvconf.conf</filename>.
'';
};
extraOptions = mkOption {
type = types.listOf types.str;
default = [];
example = [ "ndots:1" "rotate" ];
description = ''
Set the options in <filename>/etc/resolv.conf</filename>.
'';
};
useLocalResolver = mkOption {
type = types.bool;
default = false;
description = ''
Use local DNS server for resolving.
'';
};
};
};
config = mkMerge [
{
networking.resolvconf.enable = !(config.environment.etc ? "resolv.conf");
environment.etc."resolvconf.conf".text =
if !cfg.enable then
# Force-stop any attempts to use resolvconf
''
echo "resolvconf is disabled on this system but was used anyway:" >&2
echo "$0 $*" >&2
exit 1
''
else configText;
}
(mkIf cfg.enable {
environment.systemPackages = [ pkgs.openresolv ];
systemd.services.resolvconf = {
description = "resolvconf update";
before = [ "network-pre.target" ];
wants = [ "network-pre.target" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ config.environment.etc."resolvconf.conf".source ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.openresolv}/bin/resolvconf -u";
RemainAfterExit = true;
};
};
})
];
}

View file

@ -0,0 +1,58 @@
{ config, pkgs ,lib ,... }:
with lib;
{
options.xdg.portal = {
enable =
mkEnableOption "<link xlink:href='https://github.com/flatpak/xdg-desktop-portal'>xdg desktop integration</link>"//{
default = false;
};
extraPortals = mkOption {
type = types.listOf types.package;
default = [];
description = ''
List of additional portals to add to path. Portals allow interaction
with system, like choosing files or taking screenshots. At minimum,
a desktop portal implementation should be listed. GNOME and KDE already
adds <package>xdg-desktop-portal-gtk</package>; and
<package>xdg-desktop-portal-kde</package> respectively. On other desktop
environments you probably want to add them yourself.
'';
};
gtkUsePortal = mkOption {
type = types.bool;
default = false;
description = ''
Sets environment variable <literal>GTK_USE_PORTAL</literal> to <literal>1</literal>.
This is needed for packages ran outside Flatpak to respect and use XDG Desktop Portals.
For example, you'd need to set this for non-flatpak Firefox to use native filechoosers.
Defaults to <literal>false</literal> to respect its opt-in nature.
'';
};
};
config =
let
cfg = config.xdg.portal;
packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
in mkIf cfg.enable {
assertions = [
{ assertion = (cfg.gtkUsePortal -> cfg.extraPortals != []);
message = "Setting xdg.portal.gtkUsePortal to true requires a portal implementation in xdg.portal.extraPortals such as xdg-desktop-portal-gtk or xdg-desktop-portal-kde.";
}
];
services.dbus.packages = packages;
systemd.packages = packages;
environment.variables = {
GTK_USE_PORTAL = mkIf cfg.gtkUsePortal "1";
XDG_DESKTOP_PORTAL_PATH = map (p: "${p}/share/xdg-desktop-portal/portals") cfg.extraPortals;
};
};
}

View file

@ -84,7 +84,7 @@ with lib;
system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
#!ipxe
kernel ${pkgs.stdenv.hostPlatform.platform.kernelTarget} init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
kernel ${pkgs.stdenv.hostPlatform.platform.kernelTarget} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams}
initrd initrd
boot
'';

View file

@ -11,6 +11,7 @@
./config/xdg/icons.nix
./config/xdg/menus.nix
./config/xdg/mime.nix
./config/xdg/portal.nix
./config/appstream.nix
./config/xdg/sounds.nix
./config/gtk/gtk-icon-cache.nix
@ -25,6 +26,7 @@
./config/nsswitch.nix
./config/power-management.nix
./config/pulseaudio.nix
./config/resolvconf.nix
./config/shells-environment.nix
./config/swap.nix
./config/sysctl.nix
@ -138,6 +140,7 @@
./programs/sway.nix
./programs/thefuck.nix
./programs/tmux.nix
./programs/tsm-client.nix
./programs/udevil.nix
./programs/venus.nix
./programs/vim.nix
@ -195,6 +198,7 @@
./services/audio/slimserver.nix
./services/audio/snapserver.nix
./services/audio/squeezelite.nix
./services/audio/spotifyd.nix
./services/audio/ympd.nix
./services/backup/automysqlbackup.nix
./services/backup/bacula.nix
@ -209,6 +213,7 @@
./services/backup/restic-rest-server.nix
./services/backup/rsnapshot.nix
./services/backup/tarsnap.nix
./services/backup/tsm.nix
./services/backup/znapzend.nix
./services/cluster/hadoop/default.nix
./services/cluster/kubernetes/addons/dns.nix
@ -418,6 +423,7 @@
./services/misc/gollum.nix
./services/misc/gpsd.nix
./services/misc/headphones.nix
./services/misc/greenclip.nix
./services/misc/home-assistant.nix
./services/misc/ihaskell.nix
./services/misc/irkerd.nix
@ -469,6 +475,7 @@
./services/misc/synergy.nix
./services/misc/sysprof.nix
./services/misc/taskserver
./services/misc/tiddlywiki.nix
./services/misc/tzupdate.nix
./services/misc/uhub.nix
./services/misc/weechat.nix
@ -512,6 +519,7 @@
./services/monitoring/systemhealth.nix
./services/monitoring/teamviewer.nix
./services/monitoring/telegraf.nix
./services/monitoring/thanos.nix
./services/monitoring/ups.nix
./services/monitoring/uptime.nix
./services/monitoring/vnstat.nix
@ -686,6 +694,7 @@
./services/networking/tcpcrypt.nix
./services/networking/teamspeak3.nix
./services/networking/tedicross.nix
./services/networking/thelounge.nix
./services/networking/tinc.nix
./services/networking/tinydns.nix
./services/networking/tftpd.nix
@ -772,6 +781,7 @@
./services/web-apps/icingaweb2/module-monitoring.nix
./services/web-apps/limesurvey.nix
./services/web-apps/mattermost.nix
./services/web-apps/mediawiki.nix
./services/web-apps/miniflux.nix
./services/web-apps/nextcloud.nix
./services/web-apps/nexus.nix
@ -807,6 +817,7 @@
./services/web-servers/uwsgi.nix
./services/web-servers/varnish/default.nix
./services/web-servers/zope2.nix
./services/x11/extra-layouts.nix
./services/x11/colord.nix
./services/x11/compton.nix
./services/x11/unclutter.nix

View file

@ -26,7 +26,9 @@ with lib;
security.allowSimultaneousMultithreading = mkDefault false;
security.virtualization.flushL1DataCache = mkDefault "always";
security.forcePageTableIsolation = mkDefault true;
security.virtualisation.flushL1DataCache = mkDefault "always";
security.apparmor.enable = mkDefault true;
@ -42,9 +44,6 @@ with lib;
# Disable legacy virtual syscalls
"vsyscall=none"
# Enable PTI even if CPU claims to be safe from meltdown
"pti=on"
];
boot.blacklistedKernelModules = [

View file

@ -12,7 +12,7 @@ with lib;
''
# Set up the per-user profile.
mkdir -m 0755 -p "$NIX_USER_PROFILE_DIR"
if [ "$(stat --printf '%u' "$NIX_USER_PROFILE_DIR")" != "$(id -u)" ]; then
if [ "$(stat -c '%u' "$NIX_USER_PROFILE_DIR")" != "$(id -u)" ]; then
echo "WARNING: the per-user profile dir $NIX_USER_PROFILE_DIR should belong to user id $(id -u)" >&2
fi
@ -34,7 +34,7 @@ with lib;
# Create the per-user garbage collector roots directory.
NIX_USER_GCROOTS_DIR="/nix/var/nix/gcroots/per-user/$USER"
mkdir -m 0755 -p "$NIX_USER_GCROOTS_DIR"
if [ "$(stat --printf '%u' "$NIX_USER_GCROOTS_DIR")" != "$(id -u)" ]; then
if [ "$(stat -c '%u' "$NIX_USER_GCROOTS_DIR")" != "$(id -u)" ]; then
echo "WARNING: the per-user gcroots dir $NIX_USER_GCROOTS_DIR should belong to user id $(id -u)" >&2
fi

View file

@ -21,7 +21,7 @@ let
knownHostsText = (flip (concatMapStringsSep "\n") knownHosts
(h: assert h.hostNames != [];
concatStringsSep "," h.hostNames + " "
optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " "
+ (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile)
)) + "\n";
@ -128,6 +128,14 @@ in
default = {};
type = types.loaOf (types.submodule ({ name, ... }: {
options = {
certAuthority = mkOption {
type = types.bool;
default = false;
description = ''
This public key is an SSH certificate authority, rather than an
individual host's key.
'';
};
hostNames = mkOption {
type = types.listOf types.str;
default = [];
@ -227,6 +235,7 @@ in
systemd.user.services.ssh-agent = mkIf cfg.startAgent
{ description = "SSH Agent";
wantedBy = [ "default.target" ];
unitConfig.ConditionUser = "!@system";
serviceConfig =
{ ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
ExecStart =

View file

@ -0,0 +1,287 @@
{ config, lib, pkgs, ... }:
let
inherit (builtins) length map;
inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs mapAttrsToList optionalAttrs;
inherit (lib.modules) mkDefault mkIf;
inherit (lib.options) literalExample mkEnableOption mkOption;
inherit (lib.strings) concatStringsSep optionalString toLower;
inherit (lib.types) addCheck attrsOf lines loaOf nullOr package path port str strMatching submodule;
# Checks if given list of strings contains unique
# elements when compared without considering case.
# Type: checkIUnique :: [string] -> bool
# Example: checkIUnique ["foo" "Foo"] => false
checkIUnique = lst:
let
lenUniq = l: length (lib.lists.unique l);
in
lenUniq lst == lenUniq (map toLower lst);
# TSM rejects servername strings longer than 64 chars.
servernameType = strMatching ".{1,64}";
serverOptions = { name, config, ... }: {
options.name = mkOption {
type = servernameType;
example = "mainTsmServer";
description = ''
Local name of the IBM TSM server,
must be uncapitalized and no longer than 64 chars.
The value will be used for the
<literal>server</literal>
directive in <filename>dsm.sys</filename>.
'';
};
options.server = mkOption {
type = strMatching ".+";
example = "tsmserver.company.com";
description = ''
Host/domain name or IP address of the IBM TSM server.
The value will be used for the
<literal>tcpserveraddress</literal>
directive in <filename>dsm.sys</filename>.
'';
};
options.port = mkOption {
type = addCheck port (p: p<=32767);
default = 1500; # official default
description = ''
TCP port of the IBM TSM server.
The value will be used for the
<literal>tcpport</literal>
directive in <filename>dsm.sys</filename>.
TSM does not support ports above 32767.
'';
};
options.node = mkOption {
type = strMatching ".+";
example = "MY-TSM-NODE";
description = ''
Target node name on the IBM TSM server.
The value will be used for the
<literal>nodename</literal>
directive in <filename>dsm.sys</filename>.
'';
};
options.genPasswd = mkEnableOption ''
automatic client password generation.
This option influences the
<literal>passwordaccess</literal>
directive in <filename>dsm.sys</filename>.
The password will be stored in the directory
given by the option <option>passwdDir</option>.
<emphasis>Caution</emphasis>:
If this option is enabled and the server forces
to renew the password (e.g. on first connection),
a random password will be generated and stored
'';
options.passwdDir = mkOption {
type = path;
example = "/home/alice/tsm-password";
description = ''
Directory that holds the TSM
node's password information.
The value will be used for the
<literal>passworddir</literal>
directive in <filename>dsm.sys</filename>.
'';
};
options.includeExclude = mkOption {
type = lines;
default = "";
example = ''
exclude.dir /nix/store
include.encrypt /home/.../*
'';
description = ''
<literal>include.*</literal> and
<literal>exclude.*</literal> directives to be
used when sending files to the IBM TSM server.
The lines will be written into a file that the
<literal>inclexcl</literal>
directive in <filename>dsm.sys</filename> points to.
'';
};
options.extraConfig = mkOption {
# TSM option keys are case insensitive;
# we have to ensure there are no keys that
# differ only by upper and lower case.
type = addCheck
(attrsOf (nullOr str))
(attrs: checkIUnique (attrNames attrs));
default = {};
example.compression = "yes";
example.passwordaccess = null;
description = ''
Additional key-value pairs for the server stanza.
Values must be strings, or <literal>null</literal>
for the key not to be used in the stanza
(e.g. to overrule values generated by other options).
'';
};
options.text = mkOption {
type = lines;
example = literalExample
''lib.modules.mkAfter "compression no"'';
description = ''
Additional text lines for the server stanza.
This option can be used if certion configuration keys
must be used multiple times or ordered in a certain way
as the <option>extraConfig</option> option can't
control the order of lines in the resulting stanza.
Note that the <literal>server</literal>
line at the beginning of the stanza is
not part of this option's value.
'';
};
options.stanza = mkOption {
type = str;
internal = true;
visible = false;
description = "Server stanza text generated from the options.";
};
config.name = mkDefault name;
# Client system-options file directives are explained here:
# https://www.ibm.com/support/knowledgecenter/SSEQVQ_8.1.8/client/c_opt_usingopts.html
config.extraConfig =
mapAttrs (lib.trivial.const mkDefault) (
{
commmethod = "v6tcpip"; # uses v4 or v6, based on dns lookup result
tcpserveraddress = config.server;
tcpport = builtins.toString config.port;
nodename = config.node;
passwordaccess = if config.genPasswd then "generate" else "prompt";
passworddir = ''"${config.passwdDir}"'';
} // optionalAttrs (config.includeExclude!="") {
inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"'';
}
);
config.text =
let
attrset = filterAttrs (k: v: v!=null) config.extraConfig;
mkLine = k: v: k + optionalString (v!="") " ${v}";
lines = mapAttrsToList mkLine attrset;
in
concatStringsSep "\n" lines;
config.stanza = ''
server ${config.name}
${config.text}
'';
};
options.programs.tsmClient = {
enable = mkEnableOption ''
IBM Spectrum Protect (Tivoli Storage Manager, TSM)
client command line applications with a
client system-options file "dsm.sys"
'';
servers = mkOption {
type = loaOf (submodule [ serverOptions ]);
default = {};
example.mainTsmServer = {
server = "tsmserver.company.com";
node = "MY-TSM-NODE";
extraConfig.compression = "yes";
};
description = ''
Server definitions ("stanzas")
for the client system-options file.
'';
};
defaultServername = mkOption {
type = nullOr servernameType;
default = null;
example = "mainTsmServer";
description = ''
If multiple server stanzas are declared with
<option>programs.tsmClient.servers</option>,
this option may be used to name a default
server stanza that IBM TSM uses in the absence of
a user-defined <filename>dsm.opt</filename> file.
This option translates to a
<literal>defaultserver</literal> configuration line.
'';
};
dsmSysText = mkOption {
type = lines;
readOnly = true;
description = ''
This configuration key contains the effective text
of the client system-options file "dsm.sys".
It should not be changed, but may be
used to feed the configuration into other
TSM-depending packages used on the system.
'';
};
package = mkOption {
type = package;
default = pkgs.tsm-client;
defaultText = "pkgs.tsm-client";
example = literalExample "pkgs.tsm-client-withGui";
description = ''
The TSM client derivation to be
added to the system environment.
It will called with <literal>.override</literal>
to add paths to the client system-options file.
'';
};
wrappedPackage = mkOption {
type = package;
readOnly = true;
description = ''
The TSM client derivation, wrapped with the path
to the client system-options file "dsm.sys".
This option is to provide the effective derivation
for other modules that want to call TSM executables.
'';
};
};
cfg = config.programs.tsmClient;
assertions = [
{
assertion = checkIUnique (mapAttrsToList (k: v: v.name) cfg.servers);
message = ''
TSM servernames contain duplicate name
(note that case doesn't matter!)
'';
}
{
assertion = (cfg.defaultServername!=null)->(hasAttr cfg.defaultServername cfg.servers);
message = "TSM defaultServername not found in list of servers";
}
];
dsmSysText = ''
**** IBM Spectrum Protect (Tivoli Storage Manager)
**** client system-options file "dsm.sys".
**** Do not edit!
**** This file is generated by NixOS configuration.
${optionalString (cfg.defaultServername!=null) "defaultserver ${cfg.defaultServername}"}
${concatStringsSep "\n" (mapAttrsToList (k: v: v.stanza) cfg.servers)}
'';
in
{
inherit options;
config = mkIf cfg.enable {
inherit assertions;
programs.tsmClient.dsmSysText = dsmSysText;
programs.tsmClient.wrappedPackage = cfg.package.override rec {
dsmSysCli = pkgs.writeText "dsm.sys" cfg.dsmSysText;
dsmSysApi = dsmSysCli;
};
environment.systemPackages = [ cfg.wrappedPackage ];
};
meta.maintainers = [ lib.maintainers.yarny ];
}

View file

@ -26,6 +26,7 @@ in
package = mkOption {
type = types.package;
default = pkgs.xonsh;
example = literalExample "pkgs.xonsh.override { configFile = \"/path/to/xonshrc\"; }";
description = ''
xonsh package to use.
@ -46,11 +47,11 @@ in
environment.etc."xonshrc".text = cfg.config;
environment.systemPackages = [ pkgs.xonsh ];
environment.systemPackages = [ cfg.package ];
environment.shells =
[ "/run/current-system/sw/bin/xonsh"
"${pkgs.xonsh}/bin/xonsh"
"${cfg.package}/bin/xonsh"
];
};

View file

@ -19,6 +19,7 @@ with lib;
let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
in if value != "" then [ value ] else []))
(mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
(mkRenamedOptionModule [ "services" "flatpak" "extraPortals" ] [ "xdg" "portal" "extraPortals" ])
(mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ])
(mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "admissionControl" ] [ "services" "kubernetes" "apiserver" "enableAdmissionPlugins" ])
(mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "address" ] ["services" "kubernetes" "apiserver" "bindAddress"])
@ -62,6 +63,8 @@ with lib;
(mkRemovedOptionModule [ "security" "setuidOwners" ] "Use security.wrappers instead")
(mkRemovedOptionModule [ "security" "setuidPrograms" ] "Use security.wrappers instead")
(mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
# PAM
(mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
@ -248,6 +251,12 @@ with lib;
# KSM
(mkRenamedOptionModule [ "hardware" "enableKSM" ] [ "hardware" "ksm" "enable" ])
# resolvconf
(mkRenamedOptionModule [ "networking" "dnsSingleRequest" ] [ "networking" "resolvconf" "dnsSingleRequest" ])
(mkRenamedOptionModule [ "networking" "dnsExtensionMechanism" ] [ "networking" "resolvconf" "dnsExtensionMechanism" ])
(mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ])
(mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ])
] ++ (forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
"jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
"snmpExporter" "unifiExporter" "varnishExporter" ]

View file

@ -48,13 +48,25 @@ with lib;
e.g., shared caches). This attack vector is unproven.
Disabling SMT is a supplement to the L1 data cache flushing mitigation
(see <xref linkend="opt-security.virtualization.flushL1DataCache"/>)
(see <xref linkend="opt-security.virtualisation.flushL1DataCache"/>)
versus malicious VM guests (SMT could "bring back" previously flushed
data).
'';
};
security.virtualization.flushL1DataCache = mkOption {
security.forcePageTableIsolation = mkOption {
type = types.bool;
default = false;
description = ''
Whether to force-enable the Page Table Isolation (PTI) Linux kernel
feature even on CPU models that claim to be safe from Meltdown.
This hardening feature is most beneficial to systems that run untrusted
workloads that rely on address space isolation for security.
'';
};
security.virtualisation.flushL1DataCache = mkOption {
type = types.nullOr (types.enum [ "never" "cond" "always" ]);
default = null;
description = ''
@ -114,8 +126,12 @@ with lib;
boot.kernelParams = [ "nosmt" ];
})
(mkIf (config.security.virtualization.flushL1DataCache != null) {
boot.kernelParams = [ "kvm-intel.vmentry_l1d_flush=${config.security.virtualization.flushL1DataCache}" ];
(mkIf config.security.forcePageTableIsolation {
boot.kernelParams = [ "pti=on" ];
})
(mkIf (config.security.virtualisation.flushL1DataCache != null) {
boot.kernelParams = [ "kvm-intel.vmentry_l1d_flush=${config.security.virtualisation.flushL1DataCache}" ];
})
];
}

View file

@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.spotifyd;
spotifydConf = pkgs.writeText "spotifyd.conf" cfg.config;
in
{
options = {
services.spotifyd = {
enable = mkEnableOption "spotifyd, a Spotify playing daemon";
config = mkOption {
default = "";
type = types.lines;
description = ''
Configuration for Spotifyd. For syntax and directives, see
https://github.com/Spotifyd/spotifyd#Configuration.
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.spotifyd = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" "sound.target" ];
description = "spotifyd, a Spotify playing daemon";
serviceConfig = {
ExecStart = "${pkgs.spotifyd}/bin/spotifyd --no-daemon --cache_path /var/cache/spotifyd --config ${spotifydConf}";
Restart = "always";
RestartSec = 12;
DynamicUser = true;
CacheDirectory = "spotifyd";
SupplementaryGroups = ["audio"];
};
};
};
meta.maintainers = [ maintainers.anderslundstedt ];
}

View file

@ -0,0 +1,106 @@
{ config, lib, ... }:
let
inherit (lib.attrsets) hasAttr;
inherit (lib.modules) mkDefault mkIf;
inherit (lib.options) mkEnableOption mkOption;
inherit (lib.types) nullOr strMatching;
options.services.tsmBackup = {
enable = mkEnableOption ''
automatic backups with the
IBM Spectrum Protect (Tivoli Storage Manager, TSM) client.
This also enables
<option>programs.tsmClient.enable</option>
'';
command = mkOption {
type = strMatching ".+";
default = "backup";
example = "incr";
description = ''
The actual command passed to the
<literal>dsmc</literal> executable to start the backup.
'';
};
servername = mkOption {
type = strMatching ".+";
example = "mainTsmServer";
description = ''
Create a systemd system service
<literal>tsm-backup.service</literal> that starts
a backup based on the given servername's stanza.
Note that this server's
<option>passwdDir</option> will default to
<filename>/var/lib/tsm-backup/password</filename>
(but may be overridden);
also, the service will use
<filename>/var/lib/tsm-backup</filename> as
<literal>HOME</literal> when calling
<literal>dsmc</literal>.
'';
};
autoTime = mkOption {
type = nullOr (strMatching ".+");
default = null;
example = "12:00";
description = ''
The backup service will be invoked
automatically at the given date/time,
which must be in the format described in
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
The default <literal>null</literal>
disables automatic backups.
'';
};
};
cfg = config.services.tsmBackup;
cfgPrg = config.programs.tsmClient;
assertions = [
{
assertion = hasAttr cfg.servername cfgPrg.servers;
message = "TSM service servername not found in list of servers";
}
{
assertion = cfgPrg.servers.${cfg.servername}.genPasswd;
message = "TSM service requires automatic password generation";
}
];
in
{
inherit options;
config = mkIf cfg.enable {
inherit assertions;
programs.tsmClient.enable = true;
programs.tsmClient.servers."${cfg.servername}".passwdDir =
mkDefault "/var/lib/tsm-backup/password";
systemd.services.tsm-backup = {
description = "IBM Spectrum Protect (Tivoli Storage Manager) Backup";
# DSM_LOG needs a trailing slash to have it treated as a directory.
# `/var/log` would be littered with TSM log files otherwise.
environment.DSM_LOG = "/var/log/tsm-backup/";
# TSM needs a HOME dir to store certificates.
environment.HOME = "/var/lib/tsm-backup";
# for exit status description see
# https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.8/client/c_sched_rtncode.html
serviceConfig.SuccessExitStatus = "4 8";
# The `-se` option must come after the command.
# The `-optfile` option suppresses a `dsm.opt`-not-found warning.
serviceConfig.ExecStart =
"${cfgPrg.wrappedPackage}/bin/dsmc ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null";
serviceConfig.LogsDirectory = "tsm-backup";
serviceConfig.StateDirectory = "tsm-backup";
serviceConfig.StateDirectoryMode = "0750";
startAt = mkIf (cfg.autoTime!=null) cfg.autoTime;
};
};
meta.maintainers = [ lib.maintainers.yarny ];
}

View file

@ -0,0 +1,90 @@
{ lib, pkgs, config, ... }:
with lib;
let
cfg = config.services.zfs.autoReplication;
recursive = optionalString cfg.recursive " --recursive";
followDelete = optionalString cfg.followDelete " --follow-delete";
in {
options = {
services.zfs.autoReplication = {
enable = mkEnableOption "ZFS snapshot replication.";
followDelete = mkOption {
description = "Remove remote snapshots that don't have a local correspondant.";
default = true;
type = types.bool;
};
host = mkOption {
description = "Remote host where snapshots should be sent.";
example = "example.com";
type = types.str;
};
identityFilePath = mkOption {
description = "Path to SSH key used to login to host.";
example = "/home/username/.ssh/id_rsa";
type = types.path;
};
localFilesystem = mkOption {
description = "Local ZFS fileystem from which snapshots should be sent. Defaults to the attribute name.";
example = "pool/file/path";
type = types.str;
};
remoteFilesystem = mkOption {
description = "Remote ZFS filesystem where snapshots should be sent.";
example = "pool/file/path";
type = types.str;
};
recursive = mkOption {
description = "Recursively discover snapshots to send.";
default = true;
type = types.bool;
};
username = mkOption {
description = "Username used by SSH to login to remote host.";
example = "username";
type = types.str;
};
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [
pkgs.lz4
];
systemd.services."zfs-replication" = {
after = [
"zfs-snapshot-daily.service"
"zfs-snapshot-frequent.service"
"zfs-snapshot-hourly.service"
"zfs-snapshot-monthly.service"
"zfs-snapshot-weekly.service"
];
description = "ZFS Snapshot Replication";
documentation = [
"https://github.com/alunduil/zfs-replicate"
];
restartIfChanged = false;
serviceConfig.ExecStart = "${pkgs.zfs-replicate}/bin/zfs-replicate${recursive} -l ${escapeShellArg cfg.username} -i ${escapeShellArg cfg.identityFilePath}${followDelete} ${escapeShellArg cfg.host} ${escapeShellArg cfg.remoteFilesystem} ${escapeShellArg cfg.localFilesystem}";
wantedBy = [
"zfs-snapshot-daily.service"
"zfs-snapshot-frequent.service"
"zfs-snapshot-hourly.service"
"zfs-snapshot-monthly.service"
"zfs-snapshot-weekly.service"
];
};
};
meta = {
maintainers = with lib.maintainers; [ alunduil ];
};
}

View file

@ -237,8 +237,8 @@ in
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.rootpwFile != null || cfg.rootpw != null;
message = "Either services.openldap.rootpw or services.openldap.rootpwFile must be set";
assertion = cfg.configDir != null || cfg.rootpwFile != null || cfg.rootpw != null;
message = "services.openldap: Unless configDir is set, either rootpw or rootpwFile must be set";
}
];

View file

@ -6,23 +6,10 @@ let
cfg = config.services.postgresql;
# see description of extraPlugins
postgresqlAndPlugins = pg:
if cfg.extraPlugins == [] then pg
else pkgs.buildEnv {
name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}";
paths = [ pg pg.lib ] ++ cfg.extraPlugins;
buildInputs = [ pkgs.makeWrapper ];
postBuild =
''
mkdir -p $out/bin
rm $out/bin/{pg_config,postgres,pg_ctl}
cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
'';
};
postgresql = postgresqlAndPlugins cfg.package;
postgresql =
if cfg.extraPlugins == []
then cfg.package
else cfg.package.withPackages (_: cfg.extraPlugins);
# The main PostgreSQL configuration file.
configFile = pkgs.writeText "postgresql.conf"
@ -55,7 +42,7 @@ in
package = mkOption {
type = types.package;
example = literalExample "pkgs.postgresql_9_6";
example = literalExample "pkgs.postgresql_11";
description = ''
PostgreSQL package to use.
'';
@ -71,7 +58,7 @@ in
dataDir = mkOption {
type = types.path;
example = "/var/lib/postgresql/9.6";
example = "/var/lib/postgresql/11";
description = ''
Data directory for PostgreSQL.
'';
@ -192,17 +179,11 @@ in
extraPlugins = mkOption {
type = types.listOf types.path;
default = [];
example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql_9_4; }) ]";
example = literalExample "with pkgs.postgresql_11.pkgs; [ postgis pg_repack ]";
description = ''
When this list contains elements a new store path is created.
PostgreSQL and the elements are symlinked into it. Then pg_config,
postgres and pg_ctl are copied to make them use the new
$out/lib directory as pkglibdir. This makes it possible to use postgis
without patching the .sql files which reference $libdir/postgis-1.5.
List of PostgreSQL plugins. PostgreSQL version for each plugin should
match version for <literal>services.postgresql.package</literal> value.
'';
# Note: the duplication of executables is about 4MB size.
# So a nicer solution was patching postgresql to allow setting the
# libdir explicitely.
};
extraConfig = mkOption {

View file

@ -27,10 +27,10 @@
<filename>configuration.nix</filename>:
<programlisting>
<xref linkend="opt-services.postgresql.enable"/> = true;
<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_9_4;
<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_11;
</programlisting>
Note that you are required to specify the desired version of PostgreSQL
(e.g. <literal>pkgs.postgresql_9_4</literal>). Since upgrading your
(e.g. <literal>pkgs.postgresql_11</literal>). Since upgrading your
PostgreSQL version requires a database dump and reload (see below), NixOS
cannot provide a default value for
<xref linkend="opt-services.postgresql.package"/> such as the most recent
@ -52,7 +52,7 @@ Type "help" for help.
<para>
By default, PostgreSQL stores its databases in
<filename>/var/db/postgresql</filename>. You can override this using
<filename>/var/lib/postgresql/$psqlSchema</filename>. You can override this using
<xref linkend="opt-services.postgresql.dataDir"/>, e.g.
<programlisting>
<xref linkend="opt-services.postgresql.dataDir"/> = "/data/postgresql";
@ -74,4 +74,70 @@ Type "help" for help.
<link linkend="opt-services.postgresql.enable">here</link>.
</para>
</section>
<section xml:id="module-services-postgres-plugins">
<title>Plugins</title>
<para>
Plugins collection for each PostgreSQL version can be accessed with
<literal>.pkgs</literal>. For example, for
<literal>pkgs.postgresql_11</literal> package, its plugin collection is
accessed by <literal>pkgs.postgresql_11.pkgs</literal>:
<screen>
<prompt>$ </prompt>nix repl '&lt;nixpkgs&gt;'
Loading '&lt;nixpkgs&gt;'...
Added 10574 variables.
<prompt>nix-repl&gt; </prompt>postgresql_11.pkgs.&lt;TAB&gt;&lt;TAB&gt;
postgresql_11.pkgs.cstore_fdw postgresql_11.pkgs.pg_repack
postgresql_11.pkgs.pg_auto_failover postgresql_11.pkgs.pg_safeupdate
postgresql_11.pkgs.pg_bigm postgresql_11.pkgs.pg_similarity
postgresql_11.pkgs.pg_cron postgresql_11.pkgs.pg_topn
postgresql_11.pkgs.pg_hll postgresql_11.pkgs.pgjwt
postgresql_11.pkgs.pg_partman postgresql_11.pkgs.pgroonga
...
</screen>
</para>
<para>
To add plugins via NixOS configuration, set <literal>services.postgresql.extraPlugins</literal>:
<programlisting>
<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_11;
<xref linkend="opt-services.postgresql.extraPlugins"/> = with pkgs.postgresql_11.pkgs; [
pg_repack
postgis
];
</programlisting>
</para>
<para>
You can build custom PostgreSQL-with-plugins (to be used outside of NixOS) using
function <literal>.withPackages</literal>. For example, creating a custom
PostgreSQL package in an overlay can look like:
<programlisting>
self: super: {
postgresql_custom = self.postgresql_11.withPackages (ps: [
ps.pg_repack
ps.postgis
]);
}
</programlisting>
</para>
<para>
Here's a recipe on how to override a particular plugin through an overlay:
<programlisting>
self: super: {
postgresql_11 = super.postgresql_11.override { this = self.postgresql_11; } // {
pkgs = super.postgresql_11.pkgs // {
pg_repack = super.postgresql_11.pkgs.pg_repack.overrideAttrs (_: {
name = "pg_repack-v20181024";
src = self.fetchzip {
url = "https://github.com/reorg/pg_repack/archive/923fa2f3c709a506e111cc963034bf2fd127aa00.tar.gz";
sha256 = "17k6hq9xaax87yz79j773qyigm4fwk8z4zh5cyp6z0sxnwfqxxw5";
};
});
};
};
}
</programlisting>
</para>
</section>
</chapter>

View file

@ -15,38 +15,28 @@ in {
options = {
services.flatpak = {
enable = mkEnableOption "flatpak";
extraPortals = mkOption {
type = types.listOf types.package;
default = [];
description = ''
List of additional portals to add to path. Portals allow interaction
with system, like choosing files or taking screenshots. At minimum,
a desktop portal implementation should be listed. GNOME already
adds <package>xdg-desktop-portal-gtk</package>; for KDE, there
is <package>xdg-desktop-portal-kde</package>. Other desktop
environments will probably want to do the same.
'';
};
};
};
###### implementation
config = mkIf cfg.enable {
assertions = [
{ assertion = (config.xdg.portal.enable == true);
message = "To use Flatpak you must enable XDG Desktop Portals with xdg.portal.enable.";
}
];
environment.systemPackages = [ pkgs.flatpak ];
services.dbus.packages = [ pkgs.flatpak pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
services.dbus.packages = [ pkgs.flatpak ];
systemd.packages = [ pkgs.flatpak pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
systemd.packages = [ pkgs.flatpak ];
environment.profiles = [
"$HOME/.local/share/flatpak/exports"
"/var/lib/flatpak/exports"
];
environment.variables = {
XDG_DESKTOP_PORTAL_PATH = map (p: "${p}/share/xdg-desktop-portal/portals") cfg.extraPortals;
};
};
}

View file

@ -29,7 +29,7 @@
in other cases, you will need to add something like the following to your
<filename>configuration.nix</filename>:
<programlisting>
<xref linkend="opt-services.flatpak.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ];
<xref linkend="opt-xdg.portal.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ];
</programlisting>
</para>
<para>

View file

@ -9,6 +9,20 @@ let
in {
options.services.bloop = {
extraOptions = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"-J-Xmx2G"
"-J-XX:MaxInlineLevel=20"
"-J-XX:+UseParallelGC"
];
description = ''
Specifies additional command line argument to pass to bloop
java process.
'';
};
install = mkOption {
type = types.bool;
default = false;
@ -25,10 +39,13 @@ in {
systemd.user.services.bloop = {
description = "Bloop Scala build server";
environment = {
PATH = mkForce "${makeBinPath [ config.programs.java.package ]}";
};
serviceConfig = {
Type = "simple";
ExecStart = ''${pkgs.bloop}/bin/blp-server'';
Restart = "always";
Type = "simple";
ExecStart = ''${pkgs.bloop}/bin/bloop server'';
Restart = "always";
};
};

View file

@ -150,6 +150,9 @@ in
rm -rf /var/lib/graylog/plugins || true
mkdir -p /var/lib/graylog/plugins -m 755
mkdir -p "$(dirname ${cfg.nodeIdFile})"
chown -R ${cfg.user} "$(dirname ${cfg.nodeIdFile})"
for declarativeplugin in `ls ${glPlugins}/bin/`; do
ln -sf ${glPlugins}/bin/$declarativeplugin /var/lib/graylog/plugins/$declarativeplugin
done

View file

@ -13,6 +13,7 @@ let
|| cfg.extraAliases != "";
haveTransport = cfg.transport != "";
haveVirtual = cfg.virtual != "";
haveLocalRecipients = cfg.localRecipients != null;
clientAccess =
optional (cfg.dnsBlacklistOverrides != "")
@ -244,6 +245,7 @@ let
aliasesFile = pkgs.writeText "postfix-aliases" aliases;
virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
localRecipientMapFile = pkgs.writeText "postfix-local-recipient-map" (concatMapStrings (x: x + " ACCEPT\n") cfg.localRecipients);
checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides;
mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
masterCfFile = pkgs.writeText "postfix-master.cf" masterCfContent;
@ -506,6 +508,19 @@ in
'';
};
localRecipients = mkOption {
type = with types; nullOr (listOf string);
default = null;
description = ''
List of accepted local users. Specify a bare username, an
<literal>"@domain.tld"</literal> wild-card, or a complete
<literal>"user@domain.tld"</literal> address. If set, these names end
up in the local recipient map -- see the local(8) man-page -- and
effectively replace the system user database lookup that's otherwise
used by default.
'';
};
transport = mkOption {
default = "";
description = "
@ -742,6 +757,7 @@ in
// optionalAttrs haveAliases { alias_maps = [ "${cfg.aliasMapType}:/etc/postfix/aliases" ]; }
// optionalAttrs haveTransport { transport_maps = [ "hash:/etc/postfix/transport" ]; }
// optionalAttrs haveVirtual { virtual_alias_maps = [ "${cfg.virtualMapType}:/etc/postfix/virtual" ]; }
// optionalAttrs haveLocalRecipients { local_recipient_maps = [ "hash:/etc/postfix/local_recipients" ] ++ optional haveAliases "$alias_maps"; }
// optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
// optionalAttrs cfg.useSrs {
sender_canonical_maps = [ "tcp:127.0.0.1:10001" ];
@ -869,6 +885,9 @@ in
(mkIf haveVirtual {
services.postfix.mapFiles."virtual" = virtualFile;
})
(mkIf haveLocalRecipients {
services.postfix.mapFiles."local_recipients" = localRecipientMapFile;
})
(mkIf cfg.enableHeaderChecks {
services.postfix.mapFiles."header_checks" = headerChecksFile;
})

View file

@ -55,6 +55,11 @@ let
[service]
DISABLE_REGISTRATION = ${boolToString cfg.disableRegistration}
${optionalString (cfg.mailerPasswordFile != null) ''
[mailer]
PASSWD = #mailerpass#
''}
${cfg.extraConfig}
'';
in
@ -255,6 +260,13 @@ in
description = "Upper level of template and static files path.";
};
mailerPasswordFile = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/secrets/gitea/mailpw";
description = "Path to a file containing the SMTP password.";
};
disableRegistration = mkEnableOption "the registration lock" // {
description = ''
By default any user can create an account on this <literal>gitea</literal> instance.
@ -344,9 +356,15 @@ in
KEY="$(head -n1 ${secretKey})"
DBPASS="$(head -n1 ${cfg.database.passwordFile})"
JWTSECRET="$(head -n1 ${jwtSecret})"
${if (cfg.mailerPasswordFile == null) then ''
MAILERPASSWORD="#mailerpass#"
'' else ''
MAILERPASSWORD="$(head -n1 ${cfg.mailerPasswordFile} || :)"
''}
sed -e "s,#secretkey#,$KEY,g" \
-e "s,#dbpass#,$DBPASS,g" \
-e "s,#jwtsecet#,$JWTSECET,g" \
-e "s,#mailerpass#,$MAILERPASSWORD,g" \
-i ${runConfig}
chmod 640 ${runConfig} ${secretKey} ${jwtSecret}
''}

View file

@ -52,7 +52,7 @@ let
gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}";
http_settings.self_signed_cert = false;
repos_path = "${cfg.statePath}/repositories";
secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
secret_file = "${cfg.statePath}/gitlab_shell_secret";
log_file = "${cfg.statePath}/log/gitlab-shell.log";
custom_hooks_dir = "${cfg.statePath}/custom_hooks";
redis = {
@ -109,7 +109,7 @@ let
gitlab_shell = {
path = "${cfg.packages.gitlab-shell}";
hooks_path = "${cfg.statePath}/shell/hooks";
secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
secret_file = "${cfg.statePath}/gitlab_shell_secret";
upload_pack = true;
receive_pack = true;
};
@ -132,14 +132,9 @@ let
HOME = "${cfg.statePath}/home";
UNICORN_PATH = "${cfg.statePath}/";
GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
GITLAB_STATE_PATH = cfg.statePath;
GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
SCHEMA = "${cfg.statePath}/db/schema.rb";
GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
GITLAB_LOG_PATH = "${cfg.statePath}/log";
GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret";
GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks";
GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "redis.yml" (builtins.toJSON redisConfig);
prometheus_multiproc_dir = "/run/gitlab";
RAILS_ENV = "production";
@ -502,23 +497,44 @@ in {
systemd.tmpfiles.rules = [
"d /run/gitlab 0755 ${cfg.user} ${cfg.group} -"
"d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -"
"z ${gitlabEnv.HOME}/.ssh/authorized_keys 0600 ${cfg.user} ${cfg.group} -"
"d ${cfg.backupPath} 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/config/initializers 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/shell 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/tmp 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/tmp/pids 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/tmp/sockets 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/uploads 0700 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/custom_hooks 0700 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/custom_hooks/pre-receive.d 0700 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path} 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -"
];
"L+ ${cfg.statePath}/lib - - - - ${cfg.packages.gitlab}/share/gitlab/lib"
"L+ /run/gitlab/config - - - - ${cfg.statePath}/config"
"L+ /run/gitlab/log - - - - ${cfg.statePath}/log"
"L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp"
"L+ /run/gitlab/uploads - - - - ${cfg.statePath}/uploads"
"L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}"
"L+ ${cfg.statePath}/config/gitlab.yml - - - - ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)}"
"L+ ${cfg.statePath}/config/database.yml - - - - ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)}"
"L+ ${cfg.statePath}/config/secrets.yml - - - - ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)}"
"L+ ${cfg.statePath}/config/unicorn.rb - - - - ${./defaultUnicornConfig.rb}"
"L+ ${cfg.statePath}/config/initializers/extra-gitlab.rb - - - - ${extraGitlabRb}"
] ++ optional cfg.smtp.enable
"L+ ${cfg.statePath}/config/initializers/smtp_settings.rb - - - - ${smtpSettings}" ;
systemd.services.gitlab-sidekiq = {
after = [ "network.target" "redis.service" "gitlab.service" ];
@ -570,6 +586,7 @@ in {
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
exiftool
gitAndTools.git
gnutar
gzip
@ -609,40 +626,14 @@ in {
gnupg
];
preStart = ''
cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
rm -rf ${cfg.statePath}/config
mkdir ${cfg.statePath}/config
if [ -e ${cfg.statePath}/lib ]; then
rm ${cfg.statePath}/lib
fi
${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
${pkgs.sudo}/bin/sudo -u ${cfg.user} rm -rf ${cfg.statePath}/db/*
${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
ln -sf ${cfg.packages.gitlab}/share/gitlab/lib ${cfg.statePath}/lib
[ -L /run/gitlab/config ] || ln -sf ${cfg.statePath}/config /run/gitlab/config
[ -L /run/gitlab/log ] || ln -sf ${cfg.statePath}/log /run/gitlab/log
[ -L /run/gitlab/tmp ] || ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
[ -L /run/gitlab/uploads ] || ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb
${optionalString cfg.smtp.enable ''
ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
''}
${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/config/gitlab_shell_secret
${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret
# JSON is a subset of YAML
ln -sf ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml
ln -sf ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} ${cfg.statePath}/config/database.yml
ln -sf ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)} ${cfg.statePath}/config/secrets.yml
ln -sf ${./defaultUnicornConfig.rb} ${cfg.statePath}/config/unicorn.rb
# Install the shell required to push repositories
ln -sf ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)} /run/gitlab/shell-config.yml
[ -L ${cfg.statePath}/shell/hooks ] || ln -sf ${cfg.packages.gitlab-shell}/hooks ${cfg.statePath}/shell/hooks
${cfg.packages.gitlab-shell}/bin/install
chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/
chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/
chown -R ${cfg.user}:${cfg.group} /run/gitlab
${pkgs.sudo}/bin/sudo -u ${cfg.user} ${cfg.packages.gitlab-shell}/bin/install
if ! test -e "${cfg.statePath}/db-created"; then
if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
@ -655,7 +646,7 @@ in {
${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:schema:load
touch "${cfg.statePath}/db-created"
${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-created"
fi
# Always do the db migrations just to be sure the database is up-to-date
@ -664,22 +655,13 @@ in {
if ! test -e "${cfg.statePath}/db-seeded"; then
${pkgs.sudo}/bin/sudo -u ${cfg.user} ${gitlab-rake}/bin/gitlab-rake db:seed_fu \
GITLAB_ROOT_PASSWORD='${cfg.initialRootPassword}' GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}'
touch "${cfg.statePath}/db-seeded"
${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-seeded"
fi
# The gitlab:shell:create_hooks task seems broken for fixing links
# so we instead delete all the hooks and create them anew
# We remove potentially broken links to old gitlab-shell versions
rm -f ${cfg.statePath}/repositories/**/*.git/hooks
${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake gitlab:shell:create_hooks
${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${pkgs.git}/bin/git config --global core.autocrlf "input"
# Change permissions in the last step because some of the
# intermediary scripts like to create directories as root.
chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}
chmod -R ug+rwX,o-rwx ${cfg.statePath}/repositories
chmod -R ug-s ${cfg.statePath}/repositories
find ${cfg.statePath}/repositories -type d -print0 | xargs -0 chmod g+s
'';
serviceConfig = {

View file

@ -143,21 +143,37 @@ in
users.users.${cfg.user} = {
description = "Gitolite user";
home = cfg.dataDir;
createHome = true;
uid = config.ids.uids.gitolite;
group = cfg.group;
useDefaultShell = true;
};
users.groups."${cfg.group}".gid = config.ids.gids.gitolite;
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.dataDir}'/.gitolite - ${cfg.user} ${cfg.group} - -"
"d '${cfg.dataDir}'/.gitolite/logs - ${cfg.user} ${cfg.group} - -"
"Z ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
];
systemd.services."gitolite-init" = {
description = "Gitolite initialization";
wantedBy = [ "multi-user.target" ];
unitConfig.RequiresMountsFor = cfg.dataDir;
serviceConfig.User = "${cfg.user}";
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
environment = {
GITOLITE_RC = ".gitolite.rc";
GITOLITE_RC_DEFAULT = "${rcDir}/gitolite.rc.default";
};
serviceConfig = {
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = "~";
RemainAfterExit = true;
};
path = [ pkgs.gitolite pkgs.git pkgs.perl pkgs.bash pkgs.diffutils config.programs.ssh.package ];
script =
@ -187,11 +203,6 @@ in
'';
in
''
cd ${cfg.dataDir}
mkdir -p .gitolite/logs
GITOLITE_RC=.gitolite.rc
GITOLITE_RC_DEFAULT=${rcDir}/gitolite.rc.default
if ( [[ ! -e "$GITOLITE_RC" ]] && [[ ! -L "$GITOLITE_RC" ]] ) ||
( [[ -f "$GITOLITE_RC" ]] && diff -q "$GITOLITE_RC" "$GITOLITE_RC_DEFAULT" >/dev/null ) ||
( [[ -L "$GITOLITE_RC" ]] && [[ "$(readlink "$GITOLITE_RC")" =~ ^/nix/store/ ]] )

View file

@ -0,0 +1,31 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.greenclip;
in {
options.services.greenclip = {
enable = mkEnableOption "Greenclip daemon";
package = mkOption {
type = types.package;
default = pkgs.haskellPackages.greenclip;
defaultText = "pkgs.haskellPackages.greenclip";
description = "greenclip derivation to use.";
};
};
config = mkIf cfg.enable {
systemd.user.services.greenclip = {
enable = true;
description = "greenclip daemon";
wantedBy = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
serviceConfig.ExecStart = "${cfg.package}/bin/greenclip daemon";
};
environment.systemPackages = [ cfg.package ];
};
}

View file

@ -467,7 +467,7 @@ in
fi
'';
nix.nrBuildUsers = mkDefault (lib.max 32 cfg.maxJobs);
nix.nrBuildUsers = mkDefault (lib.max 32 (if cfg.maxJobs == "auto" then 0 else cfg.maxJobs));
users.users = nixbldUsers;

View file

@ -1,8 +1,10 @@
{ config, lib, pkgs, ... }:
with lib;
let
inherit (lib) mkDefault mkEnableOption mkIf mkOption types;
inherit (lib) concatStringsSep literalExample mapAttrsToList;
inherit (lib) optional optionalAttrs optionalString singleton versionAtLeast;
cfg = config.services.redmine;
bundle = "${cfg.package}/share/redmine/bin/bundle";
@ -11,11 +13,11 @@ let
production:
adapter: ${cfg.database.type}
database: ${cfg.database.name}
host: ${cfg.database.host}
host: ${if (cfg.database.type == "postgresql" && cfg.database.socket != null) then cfg.database.socket else cfg.database.host}
port: ${toString cfg.database.port}
username: ${cfg.database.user}
password: #dbpass#
${optionalString (cfg.database.socket != null) "socket: ${cfg.database.socket}"}
${optionalString (cfg.database.type == "mysql2" && cfg.database.socket != null) "socket: ${cfg.database.socket}"}
'';
configurationYml = pkgs.writeText "configuration.yml" ''
@ -50,16 +52,15 @@ let
'';
});
mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql2";
pgsqlLocal = cfg.database.createLocally && cfg.database.type == "postgresql";
in
{
options = {
services.redmine = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable the Redmine service.";
};
enable = mkEnableOption "Redmine";
# default to the 4.x series not forcing major version upgrade of those on the 3.x series
package = mkOption {
@ -107,7 +108,8 @@ in
description = ''
Extra configuration in configuration.yml.
See https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration
See <link xlink:href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration"/>
for details.
'';
example = literalExample ''
email_delivery:
@ -124,7 +126,8 @@ in
description = ''
Extra configuration in additional_environment.rb.
See https://svn.redmine.org/redmine/trunk/config/additional_environment.rb.example
See <link xlink:href="https://svn.redmine.org/redmine/trunk/config/additional_environment.rb.example"/>
for details.
'';
example = literalExample ''
config.logger.level = Logger::DEBUG
@ -169,13 +172,14 @@ in
host = mkOption {
type = types.str;
default = (if cfg.database.socket != null then "localhost" else "127.0.0.1");
default = "localhost";
description = "Database host address.";
};
port = mkOption {
type = types.int;
default = 3306;
default = if cfg.database.type == "postgresql" then 5432 else 3306;
defaultText = "3306";
description = "Database host port.";
};
@ -213,10 +217,20 @@ in
socket = mkOption {
type = types.nullOr types.path;
default = null;
default =
if mysqlLocal then "/run/mysqld/mysqld.sock"
else if pgsqlLocal then "/run/postgresql"
else null;
defaultText = "/run/mysqld/mysqld.sock";
example = "/run/mysqld/mysqld.sock";
description = "Path to the unix socket file to use for authentication.";
};
createLocally = mkOption {
type = types.bool;
default = true;
description = "Create the database and database user locally.";
};
};
};
};
@ -227,12 +241,37 @@ in
{ assertion = cfg.database.passwordFile != null || cfg.database.password != "" || cfg.database.socket != null;
message = "one of services.redmine.database.socket, services.redmine.database.passwordFile, or services.redmine.database.password must be set";
}
{ assertion = cfg.database.socket != null -> (cfg.database.type == "mysql2");
message = "Socket authentication is only available for the mysql2 database type";
{ assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
message = "services.redmine.database.socket must be set if services.redmine.database.createLocally is set to true";
}
{ assertion = cfg.database.createLocally -> cfg.database.host == "localhost";
message = "services.redmine.database.host must be set to localhost if services.redmine.database.createLocally is set to true";
}
];
environment.systemPackages = [ cfg.package ];
services.mysql = mkIf mysqlLocal {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
services.postgresql = mkIf pgsqlLocal {
enable = true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
# create symlinks for the basic directory layout the redmine package expects
systemd.tmpfiles.rules = [
@ -259,7 +298,7 @@ in
];
systemd.services.redmine = {
after = [ "network.target" (if cfg.database.type == "mysql2" then "mysql.service" else "postgresql.service") ];
after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
wantedBy = [ "multi-user.target" ];
environment.RAILS_ENV = "production";
environment.RAILS_CACHE = "${cfg.stateDir}/cache";

View file

@ -411,7 +411,7 @@ in {
} else {
cert = "${cfg.pki.manual.server.cert}";
key = "${cfg.pki.manual.server.key}";
crl = "${cfg.pki.manual.server.crl}";
${mapNullable (_: "crl") cfg.pki.manual.server.crl} = "${cfg.pki.manual.server.crl}";
});
ca.cert = if needToCreateCA then "${cfg.dataDir}/keys/ca.cert"

View file

@ -0,0 +1,52 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.tiddlywiki;
listenParams = concatStrings (mapAttrsToList (n: v: " '${n}=${toString v}' ") cfg.listenOptions);
exe = "${pkgs.nodePackages.tiddlywiki}/lib/node_modules/.bin/tiddlywiki";
name = "tiddlywiki";
dataDir = "/var/lib/" + name;
in {
options.services.tiddlywiki = {
enable = mkEnableOption "TiddlyWiki nodejs server";
listenOptions = mkOption {
type = types.attrs;
default = {};
example = {
credentials = "../credentials.csv";
readers="(authenticated)";
port = 3456;
};
description = ''
Parameters passed to <literal>--listen</literal> command.
Refer to <link xlink:href="https://tiddlywiki.com/#WebServer"/>
for details on supported values.
'';
};
};
config = mkIf cfg.enable {
systemd = {
services.tiddlywiki = {
description = "TiddlyWiki nodejs server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
Restart = "on-failure";
DynamicUser = true;
StateDirectory = name;
ExecStartPre = "-${exe} ${dataDir} --init server";
ExecStart = "${exe} ${dataDir} --listen ${listenParams}";
};
};
};
};
}

View file

@ -552,6 +552,8 @@ in {
description = "Grafana user";
home = cfg.dataDir;
createHome = true;
group = "grafana";
};
users.groups.grafana = {};
};
}

View file

@ -1,8 +1,10 @@
{ config, pkgs, lib, ... }:
with lib;
{ config, pkgs, lib, options, ... }:
let
inherit (lib) concatStrings foldl foldl' genAttrs literalExample maintainers
mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption
optional types;
cfg = config.services.prometheus.exporters;
# each attribute in `exporterOpts` is expected to have specified:
@ -17,25 +19,30 @@ let
# Note that `extraOpts` is optional, but a script for the exporter's
# systemd service must be provided by specifying either
# `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
exporterOpts = {
blackbox = import ./exporters/blackbox.nix { inherit config lib pkgs; };
collectd = import ./exporters/collectd.nix { inherit config lib pkgs; };
dnsmasq = import ./exporters/dnsmasq.nix { inherit config lib pkgs; };
dovecot = import ./exporters/dovecot.nix { inherit config lib pkgs; };
fritzbox = import ./exporters/fritzbox.nix { inherit config lib pkgs; };
json = import ./exporters/json.nix { inherit config lib pkgs; };
minio = import ./exporters/minio.nix { inherit config lib pkgs; };
nginx = import ./exporters/nginx.nix { inherit config lib pkgs; };
node = import ./exporters/node.nix { inherit config lib pkgs; };
postfix = import ./exporters/postfix.nix { inherit config lib pkgs; };
snmp = import ./exporters/snmp.nix { inherit config lib pkgs; };
surfboard = import ./exporters/surfboard.nix { inherit config lib pkgs; };
tor = import ./exporters/tor.nix { inherit config lib pkgs; };
unifi = import ./exporters/unifi.nix { inherit config lib pkgs; };
varnish = import ./exporters/varnish.nix { inherit config lib pkgs; };
bind = import ./exporters/bind.nix { inherit config lib pkgs; };
wireguard = import ./exporters/wireguard.nix { inherit config lib pkgs; };
};
exporterOpts = genAttrs [
"bind"
"blackbox"
"collectd"
"dnsmasq"
"dovecot"
"fritzbox"
"json"
"mail"
"minio"
"nginx"
"node"
"postfix"
"postgres"
"snmp"
"surfboard"
"tor"
"unifi"
"varnish"
"wireguard"
] (name:
import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; }
);
mkExporterOpts = ({ name, port }: {
enable = mkEnableOption "the prometheus ${name} exporter";
@ -81,7 +88,7 @@ let
};
user = mkOption {
type = types.str;
default = "nobody";
default = "${name}-exporter";
description = ''
User name under which the ${name} exporter shall be run.
Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
@ -89,7 +96,7 @@ let
};
group = mkOption {
type = types.str;
default = "nobody";
default = "${name}-exporter";
description = ''
Group under which the ${name} exporter shall be run.
Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
@ -97,9 +104,10 @@ let
};
});
mkSubModule = { name, port, extraOpts, ... }: {
mkSubModule = { name, port, extraOpts, imports }: {
${name} = mkOption {
type = types.submodule {
inherit imports;
options = (mkExporterOpts {
inherit name port;
} // extraOpts);
@ -112,13 +120,30 @@ let
mkSubModules = (foldl' (a: b: a//b) {}
(mapAttrsToList (name: opts: mkSubModule {
inherit name;
inherit (opts) port serviceOpts;
inherit (opts) port;
extraOpts = opts.extraOpts or {};
imports = opts.imports or [];
}) exporterOpts)
);
mkExporterConf = { name, conf, serviceOpts }:
let
enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true;
in
mkIf conf.enable {
warnings = conf.warnings or [];
users.users = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) {
"${name}-exporter" = {
description = ''
Prometheus ${name} exporter service user
'';
isSystemUser = true;
inherit (conf) group;
};
});
users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) {
"${name}-exporter" = {};
});
networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [
"ip46tables -A nixos-fw ${conf.firewallFilter} "
"-m comment --comment ${name}-exporter -j nixos-fw-accept"
@ -129,7 +154,8 @@ let
serviceConfig.Restart = mkDefault "always";
serviceConfig.PrivateTmp = mkDefault true;
serviceConfig.WorkingDirectory = mkDefault /tmp;
} serviceOpts ] ++ optional (!(serviceOpts.serviceConfig.DynamicUser or false)) {
serviceConfig.DynamicUser = mkDefault enableDynamicUser;
} serviceOpts ] ++ optional (!enableDynamicUser) {
serviceConfig.User = conf.user;
serviceConfig.Group = conf.group;
});
@ -154,13 +180,19 @@ in
};
config = mkMerge ([{
assertions = [{
assertions = [ {
assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null);
message = ''
Please ensure you have either `services.prometheus.exporters.snmp.configuration'
or `services.prometheus.exporters.snmp.configurationPath' set!
'';
}];
} {
assertion = (cfg.mail.configFile == null) != (cfg.mail.configuration == {});
message = ''
Please specify either 'services.prometheus.exporters.mail.configuration'
or 'services.prometheus.exporters.mail.configFile'.
'';
} ];
}] ++ [(mkIf config.services.minio.enable {
services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000";
services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;

View file

@ -17,7 +17,7 @@
exporter</link>, it provides hardware and OS metrics from the host it's
running on. The exporter could be configured as follows:
<programlisting>
services.promtheus.exporters.node = {
services.prometheus.exporters.node = {
enable = true;
enabledCollectors = [
"logind"
@ -113,7 +113,7 @@
specific options and configuration:
<programlisting>
# nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -159,8 +159,10 @@ in
# `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart`
# has to be specified here. This will be merged with the default
# service confiuration.
# Note that by default 'DynamicUser' is 'true'.
serviceOpts = {
serviceConfig = {
DynamicUser = false;
ExecStart = ''
${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
@ -184,4 +186,42 @@ in
</listitem>
</itemizedlist>
</section>
<section xml:id="module-services-prometheus-exporters-update-exporter-module">
<title>Updating an exporter module</title>
<para>
Should an exporter option change at some point, it is possible to add
information about the change to the exporter definition similar to
<literal>nixpkgs/nixos/modules/rename.nix</literal>:
<programlisting>
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.nginx;
in
{
port = 9113;
extraOpts = {
# additional module options
# ...
};
serviceOpts = {
# service configuration
# ...
};
imports = [
# 'services.prometheus.exporters.nginx.telemetryEndpoint' -> 'services.prometheus.exporters.nginx.telemetryPath'
(mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ])
# removed option 'services.prometheus.exporters.nginx.insecure'
(mkRemovedOptionModule [ "insecure" ] ''
This option was replaced by 'prometheus.exporters.nginx.sslVerify' which defaults to true.
'')
({ options.warnings = options.warnings; })
];
}
</programlisting>
</para>
</section>
</chapter>

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -39,7 +39,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-bind-exporter}/bin/bind_exporter \
-web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -18,7 +18,6 @@ in
serviceOpts = {
serviceConfig = {
AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -64,7 +64,6 @@ in
'' else "";
in {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
-log.format ${cfg.logFormat} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -26,7 +26,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-dnsmasq-exporter}/bin/dnsmasq_exporter \
--listen ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -39,8 +39,8 @@ in
mail_plugins = $mail_plugins old_stats
service old-stats {
unix_listener old-stats {
user = nobody
group = nobody
user = dovecot-exporter
group = dovecot-exporter
}
}
''';
@ -59,6 +59,7 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = false;
ExecStart = ''
${pkgs.prometheus-dovecot-exporter}/bin/dovecot_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -26,7 +26,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-fritzbox-exporter}/bin/exporter \
-listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -24,7 +24,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
--port ${toString cfg.port} \

View file

@ -0,0 +1,157 @@
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.mail;
configurationFile = pkgs.writeText "prometheus-mail-exporter.conf" (builtins.toJSON (
# removes the _module attribute, null values and converts attrNames to lowercase
mapAttrs' (name: value:
if name == "servers"
then nameValuePair (toLower name)
((map (srv: (mapAttrs' (n: v: nameValuePair (toLower n) v)
(filterAttrs (n: v: !(n == "_module" || v == null)) srv)
))) value)
else nameValuePair (toLower name) value
) (filterAttrs (n: _: !(n == "_module")) cfg.configuration)
));
serverOptions.options = {
name = mkOption {
type = types.str;
description = ''
Value for label 'configname' which will be added to all metrics.
'';
};
server = mkOption {
type = types.str;
description = ''
Hostname of the server that should be probed.
'';
};
port = mkOption {
type = types.int;
example = 587;
description = ''
Port to use for SMTP.
'';
};
from = mkOption {
type = types.str;
example = "exporteruser@domain.tld";
description = ''
Content of 'From' Header for probing mails.
'';
};
to = mkOption {
type = types.str;
example = "exporteruser@domain.tld";
description = ''
Content of 'To' Header for probing mails.
'';
};
detectionDir = mkOption {
type = types.path;
example = "/var/spool/mail/exporteruser/new";
description = ''
Directory in which new mails for the exporter user are placed.
Note that this needs to exist when the exporter starts.
'';
};
login = mkOption {
type = types.nullOr types.str;
default = null;
example = "exporteruser@domain.tld";
description = ''
Username to use for SMTP authentication.
'';
};
passphrase = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Password to use for SMTP authentication.
'';
};
};
exporterOptions.options = {
monitoringInterval = mkOption {
type = types.str;
example = "10s";
description = ''
Time interval between two probe attempts.
'';
};
mailCheckTimeout = mkOption {
type = types.str;
description = ''
Timeout until mails are considered "didn't make it".
'';
};
disableFileDelition = mkOption {
type = types.bool;
default = false;
description = ''
Disables the exporter's function to delete probing mails.
'';
};
servers = mkOption {
type = types.listOf (types.submodule serverOptions);
default = [];
example = literalExample ''
[ {
name = "testserver";
server = "smtp.domain.tld";
port = 587;
from = "exporteruser@domain.tld";
to = "exporteruser@domain.tld";
detectionDir = "/path/to/Maildir/new";
} ]
'';
description = ''
List of servers that should be probed.
'';
};
};
in
{
port = 9225;
extraOpts = {
configFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Specify the mailexporter configuration file to use.
'';
};
configuration = mkOption {
type = types.submodule exporterOptions;
default = {};
description = ''
Specify the mailexporter configuration file to use.
'';
};
telemetryPath = mkOption {
type = types.str;
default = "/metrics";
description = ''
Path under which to expose metrics.
'';
};
};
serviceOpts = {
serviceConfig = {
DynamicUser = false;
ExecStart = ''
${pkgs.prometheus-mail-exporter}/bin/mailexporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
--config.file ${
if cfg.configuration != {} then configurationFile else cfg.configFile
} \
${concatStringsSep " \\\n " cfg.extraFlags}
'';
};
};
}

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -50,7 +50,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
-web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -16,32 +16,39 @@ in
Can be enabled with services.nginx.statusPage = true.
'';
};
telemetryEndpoint = mkOption {
telemetryPath = mkOption {
type = types.str;
default = "/metrics";
description = ''
Path under which to expose metrics.
'';
};
insecure = mkOption {
sslVerify = mkOption {
type = types.bool;
default = true;
description = ''
Ignore server certificate if using https.
Whether to perform certificate verification for https.
'';
};
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
--nginx.scrape_uri '${cfg.scrapeUri}' \
--telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
--telemetry.endpoint ${cfg.telemetryEndpoint} \
--insecure ${toString cfg.insecure} \
${pkgs.prometheus-nginx-exporter}/bin/nginx-prometheus-exporter \
--nginx.scrape-uri '${cfg.scrapeUri}' \
--nginx.ssl-verify ${toString cfg.sslVerify} \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
--web.telemetry-path ${cfg.telemetryPath} \
${concatStringsSep " \\\n " cfg.extraFlags}
'';
};
};
imports = [
(mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ])
(mkRemovedOptionModule [ "insecure" ] ''
This option was replaced by 'prometheus.exporters.nginx.sslVerify'.
'')
({ options.warnings = options.warnings; })
];
}

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -27,6 +27,7 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = false;
RuntimeDirectory = "prometheus-node-exporter";
ExecStart = ''
${pkgs.prometheus-node-exporter}/bin/node_exporter \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -62,6 +62,7 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = false;
ExecStart = ''
${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -0,0 +1,47 @@
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.postgres;
in
{
port = 9187;
extraOpts = {
telemetryPath = mkOption {
type = types.str;
default = "/metrics";
description = ''
Path under which to expose metrics.
'';
};
dataSourceName = mkOption {
type = types.str;
default = "user=postgres database=postgres host=/run/postgresql sslmode=disable";
example = "postgresql://username:password@localhost:5432/postgres?sslmode=disable";
description = ''
Accepts PostgreSQL URI form and key=value form arguments.
'';
};
runAsLocalSuperUser = mkOption {
type = types.bool;
default = false;
description = ''
Whether to run the exporter as the local 'postgres' super user.
'';
};
};
serviceOpts = {
environment.DATA_SOURCE_NAME = cfg.dataSourceName;
serviceConfig = {
DynamicUser = false;
User = mkIf cfg.runAsLocalSuperUser (mkForce "postgres");
ExecStart = ''
${pkgs.prometheus-postgres-exporter}/bin/postgres_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
--web.telemetry-path ${cfg.telemetryPath} \
${concatStringsSep " \\\n " cfg.extraFlags}
'';
};
};
}

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -57,7 +57,6 @@ in
else "${pkgs.writeText "snmp-eporter-conf.yml" (builtins.toJSON cfg.configuration)}";
in {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
--config.file=${configFile} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -20,7 +20,6 @@ in
description = "Prometheus exporter for surfboard cable modem";
unitConfig.Documentation = "https://github.com/ipstatic/surfboard_exporter";
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-surfboard-exporter}/bin/surfboard_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -26,7 +26,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-tor-exporter}/bin/prometheus-tor-exporter \
-b ${cfg.listenAddress} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -51,7 +51,6 @@ in
};
serviceOpts = {
serviceConfig = {
DynamicUser = true;
ExecStart = ''
${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
-telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -68,8 +68,8 @@ in
serviceOpts = {
path = [ pkgs.varnish ];
serviceConfig = {
DynamicUser = true;
RestartSec = mkDefault 1;
DynamicUser = false;
ExecStart = ''
${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs }:
{ config, lib, pkgs, options }:
with lib;
@ -23,20 +23,30 @@ in {
to set the peers up.
'';
};
singleSubnetPerField = mkOption {
type = types.bool;
default = false;
description = ''
By default, all allowed IPs and subnets are comma-separated in the
<literal>allowed_ips</literal> field. With this option enabled,
a single IP and subnet will be listed in fields like <literal>allowed_ip_0</literal>,
<literal>allowed_ip_1</literal> and so on.
'';
};
};
serviceOpts = {
script = ''
${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
-p ${toString cfg.port} \
${optionalString cfg.verbose "-v"} \
${optionalString (cfg.wireguardConfig != null) "-n ${cfg.wireguardConfig}"}
'';
path = [ pkgs.wireguard-tools ];
serviceConfig = {
DynamicUser = true;
AmbientCapabilities = [ "CAP_NET_ADMIN" ];
ExecStart = ''
${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
-p ${toString cfg.port} \
${optionalString cfg.verbose "-v"} \
${optionalString cfg.singleSubnetPerField "-s"} \
${optionalString (cfg.wireguardConfig != null) "-n ${cfg.wireguardConfig}"}
'';
};
};
}

View file

@ -0,0 +1,801 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.thanos;
nullOpt = type: description: mkOption {
type = types.nullOr type;
default = null;
inherit description;
};
optionToArgs = opt: v : optional (v != null) ''--${opt}="${toString v}"'';
flagToArgs = opt: v : optional v ''--${opt}'';
listToArgs = opt: vs : map (v: ''--${opt}="${v}"'') vs;
attrsToArgs = opt: kvs: mapAttrsToList (k: v: ''--${opt}=${k}=\"${v}\"'') kvs;
mkParamDef = type: default: description: mkParam type (description + ''
Defaults to <literal>${toString default}</literal> in Thanos
when set to <literal>null</literal>.
'');
mkParam = type: description: {
toArgs = optionToArgs;
option = nullOpt type description;
};
mkFlagParam = description: {
toArgs = flagToArgs;
option = mkOption {
type = types.bool;
default = false;
inherit description;
};
};
mkListParam = opt: description: {
toArgs = _opt: listToArgs opt;
option = mkOption {
type = types.listOf types.str;
default = [];
inherit description;
};
};
mkAttrsParam = opt: description: {
toArgs = _opt: attrsToArgs opt;
option = mkOption {
type = types.attrsOf types.str;
default = {};
inherit description;
};
};
mkStateDirParam = opt: default: description: {
toArgs = _opt: stateDir: optionToArgs opt "/var/lib/${stateDir}";
option = mkOption {
type = types.str;
inherit default;
inherit description;
};
};
toYAML = name: attrs: pkgs.runCommandNoCC name {
preferLocalBuild = true;
json = builtins.toFile "${name}.json" (builtins.toJSON attrs);
nativeBuildInputs = [ pkgs.remarshal ];
} ''json2yaml -i $json -o $out'';
thanos = cmd: "${cfg.package}/bin/thanos ${cmd}" +
(let args = cfg."${cmd}".arguments;
in optionalString (length args != 0) (" \\\n " +
concatStringsSep " \\\n " args));
argumentsOf = cmd: concatLists (collect isList
(flip mapParamsRecursive params."${cmd}" (path: param:
let opt = concatStringsSep "." path;
v = getAttrFromPath path cfg."${cmd}";
in param.toArgs opt v)));
mkArgumentsOption = cmd: mkOption {
type = types.listOf types.str;
default = argumentsOf cmd;
description = ''
Arguments to the <literal>thanos ${cmd}</literal> command.
Defaults to a list of arguments formed by converting the structured
options of <option>services.thanos.${cmd}</option> to a list of arguments.
Overriding this option will cause none of the structured options to have
any effect. So only set this if you know what you're doing!
'';
};
mapParamsRecursive =
let noParam = attr: !(attr ? "toArgs" && attr ? "option");
in mapAttrsRecursiveCond noParam;
paramsToOptions = mapParamsRecursive (_path: param: param.option);
params = {
log = {
log.level = mkParamDef (types.enum ["debug" "info" "warn" "error" "fatal"]) "info" ''
Log filtering level.
'';
log.format = mkParam types.str ''
Log format to use.
'';
};
tracing = cfg: {
tracing.config-file = {
toArgs = _opt: path: optionToArgs "tracing.config-file" path;
option = mkOption {
type = with types; nullOr str;
default = if cfg.tracing.config == null then null
else toString (toYAML "tracing.yaml" cfg.tracing.config);
defaultText = ''
if config.services.thanos.<cmd>.tracing.config == null then null
else toString (toYAML "tracing.yaml" config.services.thanos.<cmd>.tracing.config);
'';
description = ''
Path to YAML file that contains tracing configuration.
'';
};
};
tracing.config =
{
toArgs = _opt: _attrs: [];
option = nullOpt types.attrs ''
Tracing configuration.
When not <literal>null</literal> the attribute set gets converted to
a YAML file and stored in the Nix store. The option
<option>tracing.config-file</option> will default to its path.
If <option>tracing.config-file</option> is set this option has no effect.
'';
};
};
common = cfg: params.log // params.tracing cfg // {
http-address = mkParamDef types.str "0.0.0.0:10902" ''
Listen <literal>host:port</literal> for HTTP endpoints.
'';
grpc-address = mkParamDef types.str "0.0.0.0:10901" ''
Listen <literal>ip:port</literal> address for gRPC endpoints (StoreAPI).
Make sure this address is routable from other components.
'';
grpc-server-tls-cert = mkParam types.str ''
TLS Certificate for gRPC server, leave blank to disable TLS
'';
grpc-server-tls-key = mkParam types.str ''
TLS Key for the gRPC server, leave blank to disable TLS
'';
grpc-server-tls-client-ca = mkParam types.str ''
TLS CA to verify clients against.
If no client CA is specified, there is no client verification on server side.
(tls.NoClientCert)
'';
};
objstore = cfg: {
objstore.config-file = {
toArgs = _opt: path: optionToArgs "objstore.config-file" path;
option = mkOption {
type = with types; nullOr str;
default = if cfg.objstore.config == null then null
else toString (toYAML "objstore.yaml" cfg.objstore.config);
defaultText = ''
if config.services.thanos.<cmd>.objstore.config == null then null
else toString (toYAML "objstore.yaml" config.services.thanos.<cmd>.objstore.config);
'';
description = ''
Path to YAML file that contains object store configuration.
'';
};
};
objstore.config =
{
toArgs = _opt: _attrs: [];
option = nullOpt types.attrs ''
Object store configuration.
When not <literal>null</literal> the attribute set gets converted to
a YAML file and stored in the Nix store. The option
<option>objstore.config-file</option> will default to its path.
If <option>objstore.config-file</option> is set this option has no effect.
'';
};
};
sidecar = params.common cfg.sidecar // params.objstore cfg.sidecar // {
prometheus.url = mkParamDef types.str "http://localhost:9090" ''
URL at which to reach Prometheus's API.
For better performance use local network.
'';
tsdb.path = {
toArgs = optionToArgs;
option = mkOption {
type = types.str;
default = "/var/lib/${config.services.prometheus2.stateDir}/data";
defaultText = "/var/lib/\${config.services.prometheus2.stateDir}/data";
description = ''
Data directory of TSDB.
'';
};
};
reloader.config-file = mkParam types.str ''
Config file watched by the reloader.
'';
reloader.config-envsubst-file = mkParam types.str ''
Output file for environment variable substituted config file.
'';
reloader.rule-dirs = mkListParam "reloader.rule-dir" ''
Rule directories for the reloader to refresh.
'';
};
store = params.common cfg.store // params.objstore cfg.store // {
stateDir = mkStateDirParam "data-dir" "thanos-store" ''
Data directory relative to <literal>/var/lib</literal>
in which to cache remote blocks.
'';
index-cache-size = mkParamDef types.str "250MB" ''
Maximum size of items held in the index cache.
'';
chunk-pool-size = mkParamDef types.str "2GB" ''
Maximum size of concurrently allocatable bytes for chunks.
'';
store.grpc.series-sample-limit = mkParamDef types.int 0 ''
Maximum amount of samples returned via a single Series call.
<literal>0</literal> means no limit.
NOTE: for efficiency we take 120 as the number of samples in chunk (it
cannot be bigger than that), so the actual number of samples might be
lower, even though the maximum could be hit.
'';
store.grpc.series-max-concurrency = mkParamDef types.int 20 ''
Maximum number of concurrent Series calls.
'';
sync-block-duration = mkParamDef types.str "3m" ''
Repeat interval for syncing the blocks between local and remote view.
'';
block-sync-concurrency = mkParamDef types.int 20 ''
Number of goroutines to use when syncing blocks from object storage.
'';
};
query = params.common cfg.query // {
grpc-client-tls-secure = mkFlagParam ''
Use TLS when talking to the gRPC server
'';
grpc-client-tls-cert = mkParam types.str ''
TLS Certificates to use to identify this client to the server
'';
grpc-client-tls-key = mkParam types.str ''
TLS Key for the client's certificate
'';
grpc-client-tls-ca = mkParam types.str ''
TLS CA Certificates to use to verify gRPC servers
'';
grpc-client-server-name = mkParam types.str ''
Server name to verify the hostname on the returned gRPC certificates.
See <link xlink:href="https://tools.ietf.org/html/rfc4366#section-3.1"/>
'';
web.route-prefix = mkParam types.str ''
Prefix for API and UI endpoints.
This allows thanos UI to be served on a sub-path. This option is
analogous to <option>web.route-prefix</option> of Promethus.
'';
web.external-prefix = mkParam types.str ''
Static prefix for all HTML links and redirect URLs in the UI query web
interface.
Actual endpoints are still served on / or the
<option>web.route-prefix</option>. This allows thanos UI to be served
behind a reverse proxy that strips a URL sub-path.
'';
web.prefix-header = mkParam types.str ''
Name of HTTP request header used for dynamic prefixing of UI links and
redirects.
This option is ignored if the option
<literal>web.external-prefix</literal> is set.
Security risk: enable this option only if a reverse proxy in front of
thanos is resetting the header.
The setting <literal>web.prefix-header="X-Forwarded-Prefix"</literal>
can be useful, for example, if Thanos UI is served via Traefik reverse
proxy with <literal>PathPrefixStrip</literal> option enabled, which
sends the stripped prefix value in <literal>X-Forwarded-Prefix</literal>
header. This allows thanos UI to be served on a sub-path.
'';
query.timeout = mkParamDef types.str "2m" ''
Maximum time to process query by query node.
'';
query.max-concurrent = mkParamDef types.int 20 ''
Maximum number of queries processed concurrently by query node.
'';
query.replica-label = mkParam types.str ''
Label to treat as a replica indicator along which data is
deduplicated.
Still you will be able to query without deduplication using
<literal>dedup=false</literal> parameter.
'';
selector-labels = mkAttrsParam "selector-label" ''
Query selector labels that will be exposed in info endpoint.
'';
store.addresses = mkListParam "store" ''
Addresses of statically configured store API servers.
The scheme may be prefixed with <literal>dns+</literal> or
<literal>dnssrv+</literal> to detect store API servers through
respective DNS lookups.
'';
store.sd-files = mkListParam "store.sd-files" ''
Path to files that contain addresses of store API servers. The path
can be a glob pattern.
'';
store.sd-interval = mkParamDef types.str "5m" ''
Refresh interval to re-read file SD files. It is used as a resync fallback.
'';
store.sd-dns-interval = mkParamDef types.str "30s" ''
Interval between DNS resolutions.
'';
store.unhealthy-timeout = mkParamDef types.str "5m" ''
Timeout before an unhealthy store is cleaned from the store UI page.
'';
query.auto-downsampling = mkFlagParam ''
Enable automatic adjustment (step / 5) to what source of data should
be used in store gateways if no
<literal>max_source_resolution</literal> param is specified.
'';
query.partial-response = mkFlagParam ''
Enable partial response for queries if no
<literal>partial_response</literal> param is specified.
'';
query.default-evaluation-interval = mkParamDef types.str "1m" ''
Set default evaluation interval for sub queries.
'';
store.response-timeout = mkParamDef types.str "0ms" ''
If a Store doesn't send any data in this specified duration then a
Store will be ignored and partial data will be returned if it's
enabled. <literal>0</literal> disables timeout.
'';
};
rule = params.common cfg.rule // params.objstore cfg.rule // {
labels = mkAttrsParam "label" ''
Labels to be applied to all generated metrics.
Similar to external labels for Prometheus,
used to identify ruler and its blocks as unique source.
'';
stateDir = mkStateDirParam "data-dir" "thanos-rule" ''
Data directory relative to <literal>/var/lib</literal>.
'';
rule-files = mkListParam "rule-file" ''
Rule files that should be used by rule manager. Can be in glob format.
'';
eval-interval = mkParamDef types.str "30s" ''
The default evaluation interval to use.
'';
tsdb.block-duration = mkParamDef types.str "2h" ''
Block duration for TSDB block.
'';
tsdb.retention = mkParamDef types.str "48h" ''
Block retention time on local disk.
'';
alertmanagers.urls = mkListParam "alertmanagers.url" ''
Alertmanager replica URLs to push firing alerts.
Ruler claims success if push to at least one alertmanager from
discovered succeeds. The scheme may be prefixed with
<literal>dns+</literal> or <literal>dnssrv+</literal> to detect
Alertmanager IPs through respective DNS lookups. The port defaults to
<literal>9093</literal> or the SRV record's value. The URL path is
used as a prefix for the regular Alertmanager API path.
'';
alertmanagers.send-timeout = mkParamDef types.str "10s" ''
Timeout for sending alerts to alertmanager.
'';
alert.query-url = mkParam types.str ''
The external Thanos Query URL that would be set in all alerts 'Source' field.
'';
alert.label-drop = mkListParam "alert.label-drop" ''
Labels by name to drop before sending to alertmanager.
This allows alert to be deduplicated on replica label.
Similar Prometheus alert relabelling
'';
web.route-prefix = mkParam types.str ''
Prefix for API and UI endpoints.
This allows thanos UI to be served on a sub-path.
This option is analogous to <literal>--web.route-prefix</literal> of Promethus.
'';
web.external-prefix = mkParam types.str ''
Static prefix for all HTML links and redirect URLs in the UI query web
interface.
Actual endpoints are still served on / or the
<option>web.route-prefix</option>. This allows thanos UI to be served
behind a reverse proxy that strips a URL sub-path.
'';
web.prefix-header = mkParam types.str ''
Name of HTTP request header used for dynamic prefixing of UI links and
redirects.
This option is ignored if the option
<option>web.external-prefix</option> is set.
Security risk: enable this option only if a reverse proxy in front of
thanos is resetting the header.
The header <literal>X-Forwarded-Prefix</literal> can be useful, for
example, if Thanos UI is served via Traefik reverse proxy with
<literal>PathPrefixStrip</literal> option enabled, which sends the
stripped prefix value in <literal>X-Forwarded-Prefix</literal>
header. This allows thanos UI to be served on a sub-path.
'';
query.addresses = mkListParam "query" ''
Addresses of statically configured query API servers.
The scheme may be prefixed with <literal>dns+</literal> or
<literal>dnssrv+</literal> to detect query API servers through
respective DNS lookups.
'';
query.sd-files = mkListParam "query.sd-files" ''
Path to file that contain addresses of query peers.
The path can be a glob pattern.
'';
query.sd-interval = mkParamDef types.str "5m" ''
Refresh interval to re-read file SD files. (used as a fallback)
'';
query.sd-dns-interval = mkParamDef types.str "30s" ''
Interval between DNS resolutions.
'';
};
compact = params.log // params.tracing cfg.compact // params.objstore cfg.compact // {
http-address = mkParamDef types.str "0.0.0.0:10902" ''
Listen <literal>host:port</literal> for HTTP endpoints.
'';
stateDir = mkStateDirParam "data-dir" "thanos-compact" ''
Data directory relative to <literal>/var/lib</literal>
in which to cache blocks and process compactions.
'';
consistency-delay = mkParamDef types.str "30m" ''
Minimum age of fresh (non-compacted) blocks before they are being
processed. Malformed blocks older than the maximum of consistency-delay
and 30m0s will be removed.
'';
retention.resolution-raw = mkParamDef types.str "0d" ''
How long to retain raw samples in bucket.
<literal>0d</literal> - disables this retention
'';
retention.resolution-5m = mkParamDef types.str "0d" ''
How long to retain samples of resolution 1 (5 minutes) in bucket.
<literal>0d</literal> - disables this retention
'';
retention.resolution-1h = mkParamDef types.str "0d" ''
How long to retain samples of resolution 2 (1 hour) in bucket.
<literal>0d</literal> - disables this retention
'';
startAt = {
toArgs = _opt: startAt: flagToArgs "wait" (startAt == null);
option = nullOpt types.str ''
When this option is set to a <literal>systemd.time</literal>
specification the Thanos compactor will run at the specified period.
When this option is <literal>null</literal> the Thanos compactor service
will run continuously. So it will not exit after all compactions have
been processed but wait for new work.
'';
};
block-sync-concurrency = mkParamDef types.int 20 ''
Number of goroutines to use when syncing block metadata from object storage.
'';
compact.concurrency = mkParamDef types.int 1 ''
Number of goroutines to use when compacting groups.
'';
};
downsample = params.log // params.tracing cfg.downsample // params.objstore cfg.downsample // {
stateDir = mkStateDirParam "data-dir" "thanos-downsample" ''
Data directory relative to <literal>/var/lib</literal>
in which to cache blocks and process downsamplings.
'';
};
receive = params.common cfg.receive // params.objstore cfg.receive // {
remote-write.address = mkParamDef types.str "0.0.0.0:19291" ''
Address to listen on for remote write requests.
'';
stateDir = mkStateDirParam "tsdb.path" "thanos-receive" ''
Data directory relative to <literal>/var/lib</literal> of TSDB.
'';
labels = mkAttrsParam "labels" ''
External labels to announce.
This flag will be removed in the future when handling multiple tsdb
instances is added.
'';
tsdb.retention = mkParamDef types.str "15d" ''
How long to retain raw samples on local storage.
<literal>0d</literal> - disables this retention
'';
};
};
assertRelativeStateDir = cmd: {
assertions = [
{
assertion = !hasPrefix "/" cfg."${cmd}".stateDir;
message =
"The option services.thanos.${cmd}.stateDir should not be an absolute directory." +
" It should be a directory relative to /var/lib.";
}
];
};
in {
options.services.thanos = {
package = mkOption {
type = types.package;
default = pkgs.thanos;
defaultText = "pkgs.thanos";
description = ''
The thanos package that should be used.
'';
};
sidecar = paramsToOptions params.sidecar // {
enable = mkEnableOption
"the Thanos sidecar for Prometheus server";
arguments = mkArgumentsOption "sidecar";
};
store = paramsToOptions params.store // {
enable = mkEnableOption
"the Thanos store node giving access to blocks in a bucket provider.";
arguments = mkArgumentsOption "store";
};
query = paramsToOptions params.query // {
enable = mkEnableOption
("the Thanos query node exposing PromQL enabled Query API " +
"with data retrieved from multiple store nodes");
arguments = mkArgumentsOption "query";
};
rule = paramsToOptions params.rule // {
enable = mkEnableOption
("the Thanos ruler service which evaluates Prometheus rules against" +
" given Query nodes, exposing Store API and storing old blocks in bucket");
arguments = mkArgumentsOption "rule";
};
compact = paramsToOptions params.compact // {
enable = mkEnableOption
"the Thanos compactor which continuously compacts blocks in an object store bucket";
arguments = mkArgumentsOption "compact";
};
downsample = paramsToOptions params.downsample // {
enable = mkEnableOption
"the Thanos downsampler which continuously downsamples blocks in an object store bucket";
arguments = mkArgumentsOption "downsample";
};
receive = paramsToOptions params.receive // {
enable = mkEnableOption
("the Thanos receiver which accept Prometheus remote write API requests " +
"and write to local tsdb (EXPERIMENTAL, this may change drastically without notice)");
arguments = mkArgumentsOption "receive";
};
};
config = mkMerge [
(mkIf cfg.sidecar.enable {
assertions = [
{
assertion = config.services.prometheus2.enable;
message =
"Please enable services.prometheus2 when enabling services.thanos.sidecar.";
}
{
assertion = !(config.services.prometheus2.globalConfig.external_labels == null ||
config.services.prometheus2.globalConfig.external_labels == {});
message =
"services.thanos.sidecar requires uniquely identifying external labels " +
"to be configured in the Prometheus server. " +
"Please set services.prometheus2.globalConfig.external_labels.";
}
];
systemd.services.thanos-sidecar = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "prometheus2.service" ];
serviceConfig = {
User = "prometheus";
Restart = "always";
ExecStart = thanos "sidecar";
};
};
})
(mkIf cfg.store.enable (mkMerge [
(assertRelativeStateDir "store")
{
systemd.services.thanos-store = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
StateDirectory = cfg.store.stateDir;
Restart = "always";
ExecStart = thanos "store";
};
};
}
]))
(mkIf cfg.query.enable {
systemd.services.thanos-query = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
Restart = "always";
ExecStart = thanos "query";
};
};
})
(mkIf cfg.rule.enable (mkMerge [
(assertRelativeStateDir "rule")
{
systemd.services.thanos-rule = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
StateDirectory = cfg.rule.stateDir;
Restart = "always";
ExecStart = thanos "rule";
};
};
}
]))
(mkIf cfg.compact.enable (mkMerge [
(assertRelativeStateDir "compact")
{
systemd.services.thanos-compact =
let wait = cfg.compact.startAt == null; in {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = if wait then "simple" else "oneshot";
Restart = if wait then "always" else "no";
DynamicUser = true;
StateDirectory = cfg.compact.stateDir;
ExecStart = thanos "compact";
};
} // optionalAttrs (!wait) { inherit (cfg.compact) startAt; };
}
]))
(mkIf cfg.downsample.enable (mkMerge [
(assertRelativeStateDir "downsample")
{
systemd.services.thanos-downsample = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
StateDirectory = cfg.downsample.stateDir;
Restart = "always";
ExecStart = thanos "downsample";
};
};
}
]))
(mkIf cfg.receive.enable (mkMerge [
(assertRelativeStateDir "receive")
{
systemd.services.thanos-receive = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
StateDirectory = cfg.receive.stateDir;
Restart = "always";
ExecStart = thanos "receive";
};
};
}
]))
];
}

View file

@ -23,6 +23,7 @@ let
LogType = console
ListenIP = ${cfg.listen.ip}
ListenPort = ${toString cfg.listen.port}
Server = ${cfg.server}
# TODO: set to cfg.database.socket if database type is pgsql?
DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host}
${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"}
@ -50,6 +51,13 @@ in
services.zabbixProxy = {
enable = mkEnableOption "the Zabbix Proxy";
server = mkOption {
type = types.str;
description = ''
The IP address or hostname of the Zabbix server to connect to.
'';
};
package = mkOption {
type = types.package;
default =

View file

@ -48,6 +48,7 @@ in {
requires = [ "keybase.service" ];
after = [ "keybase.service" ];
path = [ "/run/wrappers" ];
unitConfig.ConditionUser = "!@system";
serviceConfig = {
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${cfg.mountPoint}";
ExecStart = "${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}";

View file

@ -33,7 +33,7 @@ let
${cfg.extraConfig}
${ concatMapStrings
({ name, file, master ? true, slaves ? [], masters ? [] }:
({ name, file, master ? true, slaves ? [], masters ? [], extraConfig ? "" }:
''
zone "${name}" {
type ${if master then "master" else "slave"};
@ -52,6 +52,7 @@ let
''
}
allow-query { any; };
${extraConfig}
};
'')
cfg.zones }
@ -131,6 +132,7 @@ in
file = "/var/dns/example.com";
masters = ["192.168.0.1"];
slaves = [];
extraConfig = "";
}];
};
@ -168,7 +170,9 @@ in
###### implementation
config = mkIf config.services.bind.enable {
config = mkIf cfg.enable {
networking.resolvconf.useLocalResolver = mkDefault true;
users.users = singleton
{ name = bindUser;

View file

@ -79,7 +79,7 @@ in
###### implementation
config = mkIf config.services.dnsmasq.enable {
config = mkIf cfg.enable {
networking.nameservers =
optional cfg.resolveLocalQueries "127.0.0.1";
@ -92,6 +92,15 @@ in
description = "Dnsmasq daemon user";
};
networking.resolvconf = mkIf cfg.resolveLocalQueries {
useLocalResolver = mkDefault true;
extraConfig = ''
dnsmasq_conf=/etc/dnsmasq-conf.conf
dnsmasq_resolv=/etc/dnsmasq-resolv.conf
'';
};
systemd.services.dnsmasq = {
description = "Dnsmasq Daemon";
after = [ "network.target" "systemd-resolved.service" ];

View file

@ -26,6 +26,7 @@ in {
systemd.user.services.keybase = {
description = "Keybase service";
unitConfig.ConditionUser = "!@system";
serviceConfig = {
ExecStart = ''
${pkgs.keybase}/bin/keybase service --auto-forked

View file

@ -16,7 +16,8 @@ let
plugins=keyfile
dhcp=${cfg.dhcp}
dns=${cfg.dns}
rc-manager=${cfg.rc-manager}
# If resolvconf is disabled that means that resolv.conf is managed by some other module.
rc-manager=${if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"}
[keyfile]
${optionalString (cfg.unmanaged != [])
@ -176,7 +177,7 @@ in {
basePackages = mkOption {
type = types.attrsOf types.package;
default = { inherit (pkgs)
networkmanager modemmanager wpa_supplicant
networkmanager modemmanager wpa_supplicant crda
networkmanager-openvpn networkmanager-vpnc
networkmanager-openconnect networkmanager-fortisslvpn
networkmanager-l2tp networkmanager-iodine; };
@ -268,25 +269,6 @@ in {
'';
};
rc-manager = mkOption {
type = types.enum [ "symlink" "file" "resolvconf" "netconfig" "unmanaged" "none" ];
default = "resolvconf";
description = ''
Set the <literal>resolv.conf</literal> management mode.
</para>
<para>
A description of these modes can be found in the main section of
<link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
</link>
or in
<citerefentry>
<refentrytitle>NetworkManager.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>.
'';
};
dispatcherScripts = mkOption {
type = types.listOf (types.submodule {
options = {
@ -513,7 +495,7 @@ in {
networking = {
useDHCP = false;
# use mkDefault to trigger the assertion about the conflict above
wireless.enable = lib.mkDefault false;
wireless.enable = mkDefault false;
};
security.polkit.extraConfig = polkitConf;

View file

@ -35,6 +35,11 @@ in
config = mkIf config.services.rdnssd.enable {
assertions = [{
assertion = config.networking.resolvconf.enable;
message = "rdnssd needs resolvconf to work (probably something sets up a static resolv.conf)";
}];
systemd.services.rdnssd = {
description = "RDNSS daemon";
after = [ "network.target" ];

View file

@ -291,7 +291,7 @@ in {
group = mkOption {
type = types.str;
default = "nogroup";
default = defaultUser;
description = ''
Syncthing will be run under this group (group will not be created if it doesn't exist.
This can be your user name).
@ -372,16 +372,18 @@ in {
systemd.packages = [ pkgs.syncthing ];
users = mkIf (cfg.systemService && cfg.user == defaultUser) {
users."${defaultUser}" =
users.users = mkIf (cfg.systemService && cfg.user == defaultUser) {
"${defaultUser}" =
{ group = cfg.group;
home = cfg.dataDir;
createHome = true;
uid = config.ids.uids.syncthing;
description = "Syncthing daemon user";
};
};
groups."${defaultUser}".gid =
users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) {
"${defaultUser}".gid =
config.ids.gids.syncthing;
};
@ -403,18 +405,12 @@ in {
Group = cfg.group;
ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null)
"+${pkgs.writers.writeBash "syncthing-copy-keys" ''
mkdir -p ${cfg.configDir}
chown ${cfg.user}:${cfg.group} ${cfg.configDir}
chmod 700 ${cfg.configDir}
install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir}
${optionalString (cfg.declarative.cert != null) ''
cp ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
chown ${cfg.user}:${cfg.group} ${cfg.configDir}/cert.pem
chmod 400 ${cfg.configDir}/cert.pem
install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
''}
${optionalString (cfg.declarative.key != null) ''
cp ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
chown ${cfg.user}:${cfg.group} ${cfg.configDir}/key.pem
chmod 400 ${cfg.configDir}/key.pem
install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
''}
''}"
;

View file

@ -0,0 +1,75 @@
{ pkgs, lib, config, ... }:
with lib;
let
cfg = config.services.thelounge;
dataDir = "/var/lib/thelounge";
configJsData = "module.exports = " + builtins.toJSON (
{ private = cfg.private; port = cfg.port; } // cfg.extraConfig
);
in {
options.services.thelounge = {
enable = mkEnableOption "The Lounge web IRC client";
private = mkOption {
type = types.bool;
default = false;
description = ''
Make your The Lounge instance private. You will need to configure user
accounts by using the (<command>thelounge</command>) command or by adding
entries in <filename>${dataDir}/users</filename>. You might need to restart
The Lounge after making changes to the state directory.
'';
};
port = mkOption {
type = types.port;
default = 9000;
description = "TCP port to listen on for http connections.";
};
extraConfig = mkOption {
default = {};
type = types.attrs;
example = literalExample ''{
reverseProxy = true;
defaults = {
name = "Your Network";
host = "localhost";
port = 6697;
};
}'';
description = ''
The Lounge's <filename>config.js</filename> contents as attribute set (will be
converted to JSON to generate the configuration file).
The options defined here will be merged to the default configuration file.
Note: In case of duplicate configuration, options from <option>extraConfig</option> have priority.
Documentation: <link xlink:href="https://thelounge.chat/docs/server/configuration" />
'';
};
};
config = mkIf cfg.enable {
users.users.thelounge = {
description = "thelounge service user";
group = "thelounge";
};
users.groups.thelounge = {};
systemd.services.thelounge = {
description = "The Lounge web IRC client";
wantedBy = [ "multi-user.target" ];
environment = { THELOUNGE_HOME = dataDir; };
preStart = "ln -sf ${pkgs.writeText "config.js" configJsData} ${dataDir}/config.js";
serviceConfig = {
User = "thelounge";
StateDirectory = baseNameOf dataDir;
ExecStart = "${pkgs.thelounge}/bin/thelounge start";
};
};
environment.systemPackages = [ pkgs.thelounge ];
};
}

View file

@ -101,6 +101,8 @@ in
isSystemUser = true;
};
networking.resolvconf.useLocalResolver = mkDefault true;
systemd.services.unbound = {
description = "Unbound recursive Domain Name Server";
after = [ "network.target" ];

View file

@ -204,6 +204,7 @@ in {
environment.systemPackages = [ pkgs.wpa_supplicant ];
services.dbus.packages = [ pkgs.wpa_supplicant ];
services.udev.packages = [ pkgs.crda ];
# FIXME: start a separate wpa_supplicant instance per interface.
systemd.services.wpa_supplicant = let

View file

@ -107,8 +107,6 @@ in {
path = with pkgs; [ iptables ipset iproute systemd ];
postStart = ''
${pkgs.ipset}/bin/ipset -quiet create -exist sshguard4 hash:ip family inet
${pkgs.ipset}/bin/ipset -quiet create -exist sshguard6 hash:ip family inet6
${pkgs.iptables}/bin/iptables -I INPUT -m set --match-set sshguard4 src -j DROP
${pkgs.iptables}/bin/ip6tables -I INPUT -m set --match-set sshguard6 src -j DROP
'';

View file

@ -81,7 +81,7 @@ let
${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
BridgeRelay 1
ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${obfs4}/bin/obfs4proxy managed
ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${pkgs.obfs4}/bin/obfs4proxy managed
ExtORPort auto
${optionalString (cfg.relay.role == "private-bridge") ''
ExtraInfoStatistics 0

View file

@ -7,46 +7,28 @@
# is not aware of the path in which the nss modules live. As a workaround, we
# have `enable-cache yes` with an explicit ttl of 0
server-user nscd
threads 1
paranoia no
debug-level 0
enable-cache passwd yes
positive-time-to-live passwd 0
negative-time-to-live passwd 0
suggested-size passwd 211
check-files passwd yes
persistent passwd no
shared passwd yes
enable-cache group yes
positive-time-to-live group 0
negative-time-to-live group 0
suggested-size group 211
check-files group yes
persistent group no
shared group yes
enable-cache netgroup yes
positive-time-to-live netgroup 0
negative-time-to-live netgroup 0
suggested-size netgroup 211
check-files netgroup yes
persistent netgroup no
shared netgroup yes
enable-cache hosts yes
positive-time-to-live hosts 600
negative-time-to-live hosts 0
suggested-size hosts 211
check-files hosts yes
persistent hosts no
shared hosts yes
enable-cache services yes
positive-time-to-live services 0
negative-time-to-live services 0
suggested-size services 211
check-files services yes
persistent services no
shared services yes

View file

@ -39,11 +39,6 @@ in
config = mkIf cfg.enable {
environment.etc."nscd.conf".text = cfg.config;
users.users.nscd =
{ isSystemUser = true;
description = "Name service cache daemon user";
};
systemd.services.nscd =
{ description = "Name Service Cache Daemon";
@ -51,22 +46,23 @@ in
environment = { LD_LIBRARY_PATH = nssModulesPath; };
preStart =
''
mkdir -m 0755 -p /run/nscd
rm -f /run/nscd/nscd.pid
mkdir -m 0755 -p /var/db/nscd
'';
restartTriggers = [
config.environment.etc.hosts.source
config.environment.etc."nsswitch.conf".source
config.environment.etc."nscd.conf".source
];
# We use DynamicUser because in default configurations nscd doesn't
# create any files that need to survive restarts. However, in some
# configurations, nscd needs to be started as root; it will drop
# privileges after all the NSS modules have read their configuration
# files. So prefix the ExecStart command with "!" to prevent systemd
# from dropping privileges early. See ExecStart in systemd.service(5).
serviceConfig =
{ ExecStart = "@${pkgs.glibc.bin}/sbin/nscd nscd";
{ ExecStart = "!@${pkgs.glibc.bin}/sbin/nscd nscd";
Type = "forking";
DynamicUser = true;
RuntimeDirectory = "nscd";
PIDFile = "/run/nscd/nscd.pid";
Restart = "always";
ExecReload =
@ -75,15 +71,6 @@ in
"${pkgs.glibc.bin}/sbin/nscd --invalidate hosts"
];
};
# Urgggggh... Nscd forks before opening its socket and writing
# its pid. So wait until it's ready.
postStart =
''
while ! ${pkgs.glibc.bin}/sbin/nscd -g > /dev/null; do
sleep 0.2
done
'';
};
};

View file

@ -0,0 +1,473 @@
{ config, pkgs, lib, ... }:
let
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
inherit (lib) concatStringsSep literalExample mapAttrsToList optional optionals optionalString types;
cfg = config.services.mediawiki;
fpm = config.services.phpfpm.pools.mediawiki;
user = "mediawiki";
group = config.services.httpd.group;
cacheDir = "/var/cache/mediawiki";
stateDir = "/var/lib/mediawiki";
pkg = pkgs.stdenv.mkDerivation rec {
pname = "mediawiki-full";
version = src.version;
src = cfg.package;
installPhase = ''
mkdir -p $out
cp -r * $out/
rm -rf $out/share/mediawiki/skins/*
rm -rf $out/share/mediawiki/extensions/*
${concatStringsSep "\n" (mapAttrsToList (k: v: ''
ln -s ${v} $out/share/mediawiki/skins/${k}
'') cfg.skins)}
${concatStringsSep "\n" (mapAttrsToList (k: v: ''
ln -s ${v} $out/share/mediawiki/extensions/${k}
'') cfg.extensions)}
'';
};
mediawikiScripts = pkgs.runCommand "mediawiki-scripts" {
buildInputs = [ pkgs.makeWrapper ];
preferLocalBuild = true;
} ''
mkdir -p $out/bin
for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \
--set MEDIAWIKI_CONFIG ${mediawikiConfig} \
--add-flags ${pkg}/share/mediawiki/maintenance/$i
done
'';
mediawikiConfig = pkgs.writeText "LocalSettings.php" ''
<?php
# Protect against web entry
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$wgSitename = "${cfg.name}";
$wgMetaNamespace = false;
## The URL base path to the directory containing the wiki;
## defaults for all runtime URL paths are based off of this.
## For more information on customizing the URLs
## (like /w/index.php/Page_title to /wiki/Page_title) please see:
## https://www.mediawiki.org/wiki/Manual:Short_URL
$wgScriptPath = "";
## The protocol and server name to use in fully-qualified URLs
$wgServer = "${if cfg.virtualHost.enableSSL then "https" else "http"}://${cfg.virtualHost.hostName}";
## The URL path to static resources (images, scripts, etc.)
$wgResourceBasePath = $wgScriptPath;
## The URL path to the logo. Make sure you change this from the default,
## or else you'll overwrite your logo when you upgrade!
$wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
## UPO means: this is also a user preference option
$wgEnableEmail = true;
$wgEnableUserEmail = true; # UPO
$wgEmergencyContact = "${if cfg.virtualHost.adminAddr != null then cfg.virtualHost.adminAddr else config.services.httpd.adminAddr}";
$wgPasswordSender = $wgEmergencyContact;
$wgEnotifUserTalk = false; # UPO
$wgEnotifWatchlist = false; # UPO
$wgEmailAuthentication = true;
## Database settings
$wgDBtype = "${cfg.database.type}";
$wgDBserver = "${cfg.database.host}:${if cfg.database.socket != null then cfg.database.socket else toString cfg.database.port}";
$wgDBname = "${cfg.database.name}";
$wgDBuser = "${cfg.database.user}";
${optionalString (cfg.database.passwordFile != null) "$wgDBpassword = file_get_contents(\"${cfg.database.passwordFile}\");"}
${optionalString (cfg.database.type == "mysql" && cfg.database.tablePrefix != null) ''
# MySQL specific settings
$wgDBprefix = "${cfg.database.tablePrefix}";
''}
${optionalString (cfg.database.type == "mysql") ''
# MySQL table options to use during installation or update
$wgDBTableOptions = "ENGINE=InnoDB, DEFAULT CHARSET=binary";
''}
## Shared memory settings
$wgMainCacheType = CACHE_NONE;
$wgMemCachedServers = [];
${optionalString (cfg.uploadsDir != null) ''
$wgEnableUploads = true;
$wgUploadDirectory = "${cfg.uploadsDir}";
''}
$wgUseImageMagick = true;
$wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert";
# InstantCommons allows wiki to use images from https://commons.wikimedia.org
$wgUseInstantCommons = false;
# Periodically send a pingback to https://www.mediawiki.org/ with basic data
# about this MediaWiki instance. The Wikimedia Foundation shares this data
# with MediaWiki developers to help guide future development efforts.
$wgPingback = true;
## If you use ImageMagick (or any other shell command) on a
## Linux server, this will need to be set to the name of an
## available UTF-8 locale
$wgShellLocale = "C.UTF-8";
## Set $wgCacheDirectory to a writable directory on the web server
## to make your wiki go slightly faster. The directory should not
## be publically accessible from the web.
$wgCacheDirectory = "${cacheDir}";
# Site language code, should be one of the list in ./languages/data/Names.php
$wgLanguageCode = "en";
$wgSecretKey = file_get_contents("${stateDir}/secret.key");
# Changing this will log out all existing sessions.
$wgAuthenticationTokenVersion = "";
## For attaching licensing metadata to pages, and displaying an
## appropriate copyright notice / icon. GNU Free Documentation
## License and Creative Commons licenses are supported so far.
$wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
$wgRightsUrl = "";
$wgRightsText = "";
$wgRightsIcon = "";
# Path to the GNU diff3 utility. Used for conflict resolution.
$wgDiff = "${pkgs.diffutils}/bin/diff";
$wgDiff3 = "${pkgs.diffutils}/bin/diff3";
# Enabled skins.
${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadSkin('${k}');") cfg.skins)}
# Enabled extensions.
${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadExtension('${k}');") cfg.extensions)}
# End of automatically generated settings.
# Add more configuration options below.
${cfg.extraConfig}
'';
in
{
# interface
options = {
services.mediawiki = {
enable = mkEnableOption "MediaWiki";
package = mkOption {
type = types.package;
default = pkgs.mediawiki;
description = "Which MediaWiki package to use.";
};
name = mkOption {
default = "MediaWiki";
example = "Foobar Wiki";
description = "Name of the wiki.";
};
uploadsDir = mkOption {
type = types.nullOr types.path;
default = "${stateDir}/uploads";
description = ''
This directory is used for uploads of pictures. The directory passed here is automatically
created and permissions adjusted as required.
'';
};
passwordFile = mkOption {
type = types.path;
description = "A file containing the initial password for the admin user.";
example = "/run/keys/mediawiki-password";
};
skins = mkOption {
default = {};
type = types.attrsOf types.path;
description = ''
List of paths whose content is copied to the 'skins'
subdirectory of the MediaWiki installation.
'';
};
extensions = mkOption {
default = {};
type = types.attrsOf types.path;
description = ''
List of paths whose content is copied to the 'extensions'
subdirectory of the MediaWiki installation.
'';
};
database = {
type = mkOption {
type = types.enum [ "mysql" "postgres" "sqlite" "mssql" "oracle" ];
default = "mysql";
description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
};
host = mkOption {
type = types.str;
default = "localhost";
description = "Database host address.";
};
port = mkOption {
type = types.port;
default = 3306;
description = "Database host port.";
};
name = mkOption {
type = types.str;
default = "mediawiki";
description = "Database name.";
};
user = mkOption {
type = types.str;
default = "mediawiki";
description = "Database user.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/mediawiki-dbpassword";
description = ''
A file containing the password corresponding to
<option>database.user</option>.
'';
};
tablePrefix = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
If you only have access to a single database and wish to install more than
one version of MediaWiki, or have other applications that also use the
database, you can give the table names a unique prefix to stop any naming
conflicts or confusion.
See <link xlink:href='https://www.mediawiki.org/wiki/Manual:$wgDBprefix'/>.
'';
};
socket = mkOption {
type = types.nullOr types.path;
default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null;
defaultText = "/run/mysqld/mysqld.sock";
description = "Path to the unix socket file to use for authentication.";
};
createLocally = mkOption {
type = types.bool;
default = cfg.database.type == "mysql";
defaultText = "true";
description = ''
Create the database and database user locally.
This currently only applies if database type "mysql" is selected.
'';
};
};
virtualHost = mkOption {
type = types.submodule ({
options = import ../web-servers/apache-httpd/per-server-options.nix {
inherit lib;
forMainServer = false;
};
});
example = literalExample ''
{
hostName = "mediawiki.example.org";
enableSSL = true;
adminAddr = "webmaster@example.org";
sslServerCert = "/var/lib/acme/mediawiki.example.org/full.pem";
sslServerKey = "/var/lib/acme/mediawiki.example.org/key.pem";
}
'';
description = ''
Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
'';
};
poolConfig = mkOption {
type = types.lines;
default = ''
pm = dynamic
pm.max_children = 32
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 500
'';
description = ''
Options for MediaWiki's PHP pool. See the documentation on <literal>php-fpm.conf</literal>
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.lines;
description = ''
Any additional text to be appended to MediaWiki's
LocalSettings.php configuration file. For configuration
settings, see <link xlink:href="https://www.mediawiki.org/wiki/Manual:Configuration_settings"/>.
'';
default = "";
example = ''
$wgEnableEmail = false;
'';
};
};
};
# implementation
config = mkIf cfg.enable {
assertions = [
{ assertion = cfg.database.createLocally -> cfg.database.type == "mysql";
message = "services.mediawiki.createLocally is currently only supported for database type 'mysql'";
}
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true";
}
];
services.mediawiki.skins = {
MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook";
Timeless = "${cfg.package}/share/mediawiki/skins/Timeless";
Vector = "${cfg.package}/share/mediawiki/skins/Vector";
};
services.mysql = mkIf cfg.database.createLocally {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
services.phpfpm.pools.mediawiki = {
listen = "/run/phpfpm/mediawiki.sock";
extraConfig = ''
listen.owner = ${config.services.httpd.user}
listen.group = ${config.services.httpd.group}
user = ${user}
group = ${group}
env[MEDIAWIKI_CONFIG] = ${mediawikiConfig}
${cfg.poolConfig}
'';
};
services.httpd = {
enable = true;
adminAddr = mkDefault cfg.virtualHost.adminAddr;
extraModules = [ "proxy_fcgi" ];
virtualHosts = [ (mkMerge [
cfg.virtualHost {
documentRoot = mkForce "${pkg}/share/mediawiki";
extraConfig = ''
<Directory "${pkg}/share/mediawiki">
<FilesMatch "\.php$">
<If "-f %{REQUEST_FILENAME}">
SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/"
</If>
</FilesMatch>
Require all granted
DirectoryIndex index.php
AllowOverride All
</Directory>
'' + optionalString (cfg.uploadsDir != null) ''
Alias "/images" "${cfg.uploadsDir}"
<Directory "${cfg.uploadsDir}">
Require all granted
</Directory>
'';
}
]) ];
};
systemd.tmpfiles.rules = [
"d '${stateDir}' 0750 ${user} ${group} - -"
"d '${cacheDir}' 0750 ${user} ${group} - -"
] ++ optionals (cfg.uploadsDir != null) [
"d '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
"Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
];
systemd.services.mediawiki-init = {
wantedBy = [ "multi-user.target" ];
before = [ "phpfpm-mediawiki.service" ];
after = optional cfg.database.createLocally "mysql.service";
script = ''
if ! test -e "${stateDir}/secret.key"; then
tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c 64 > ${stateDir}/secret.key
fi
echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \
${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \
${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
--confpath /tmp \
--scriptpath / \
--dbserver ${cfg.database.host}${optionalString (cfg.database.socket != null) ":${cfg.database.socket}"} \
--dbport ${toString cfg.database.port} \
--dbname ${cfg.database.name} \
${optionalString (cfg.database.tablePrefix != null) "--dbprefix ${cfg.database.tablePrefix}"} \
--dbuser ${cfg.database.user} \
${optionalString (cfg.database.passwordFile != null) "--dbpassfile ${cfg.database.passwordFile}"} \
--passfile ${cfg.passwordFile} \
${cfg.name} \
admin
${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick
'';
serviceConfig = {
Type = "oneshot";
User = user;
Group = group;
PrivateTmp = true;
};
};
systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service";
users.users.${user}.group = group;
environment.systemPackages = [ mediawikiScripts ];
};
}

View file

@ -297,8 +297,23 @@ in {
systemd.services = {
"nextcloud-setup" = let
c = cfg.config;
writePhpArrary = a: "[${concatMapStringsSep "," (val: ''"${toString val}"'') a}]";
overrideConfig = pkgs.writeText "nextcloud-config.php" ''
<?php
${optionalString (c.dbpassFile != null) ''
function nix_read_pwd() {
$file = "${c.dbpassFile}";
if (!file_exists($file)) {
throw new \RuntimeException(sprintf(
"Cannot start Nextcloud, dbpass file %s set by NixOS doesn't exist!",
$file
));
}
return trim(file_get_contents($file));
}
''}
$CONFIG = [
'apps_paths' => [
[ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ],
@ -309,19 +324,27 @@ in {
${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"}
'log_type' => 'syslog',
'log_level' => '${builtins.toString cfg.logLevel}',
${optionalString (cfg.config.overwriteProtocol != null) "'overwriteprotocol' => '${cfg.config.overwriteProtocol}',"}
${optionalString (c.overwriteProtocol != null) "'overwriteprotocol' => '${c.overwriteProtocol}',"}
${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"}
${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"}
${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"}
${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"}
${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"}
${optionalString (c.dbpass != null) "'dbpassword' => '${c.dbpass}',"}
${optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_pwd(),"}
'dbtype' => '${c.dbtype}',
'trusted_domains' => ${writePhpArrary ([ cfg.hostName ] ++ c.extraTrustedDomains)},
];
'';
occInstallCmd = let
c = cfg.config;
adminpass = if c.adminpassFile != null
then ''"$(<"${toString c.adminpassFile}")"''
else ''"${toString c.adminpass}"'';
dbpass = if c.dbpassFile != null
then ''"$(<"${toString c.dbpassFile}")"''
else if c.dbpass != null
then ''"${toString c.dbpass}"''
else null;
adminpass = if c.adminpassFile != null
then ''"$(<"${toString c.adminpassFile}")"''
else ''"${toString c.adminpass}"'';
installFlags = concatStringsSep " \\\n "
(mapAttrsToList (k: v: "${k} ${toString v}") {
"--database" = ''"${c.dbtype}"'';

View file

@ -42,10 +42,12 @@
services.postgresql = {
<link linkend="opt-services.postgresql.enable">enable</link> = true;
<link linkend="opt-services.postgresql.initialScript">initialScript</link> = pkgs.writeText "psql-init" ''
CREATE ROLE nextcloud WITH LOGIN;
CREATE DATABASE nextcloud WITH OWNER nextcloud;
'';
<link linkend="opt-services.postgresql.ensureDatabases">ensureDatabases</link> = [ "nextcloud" ];
<link linkend="opt-services.postgresql.ensureUsers">ensureUsers</link> = [
{ name = "nextcloud";
ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
}
];
};
# ensure that postgres is running *before* running the setup
@ -63,17 +65,22 @@
are used internally to configure an HTTP server using
<literal><link xlink:href="https://php-fpm.org/">PHP-FPM</link></literal>
and <literal>nginx</literal>. The <literal>config</literal> attribute set is
used for the <literal>config.php</literal> which is used for the
application's configuration. <emphasis>Beware: this isn't entirely pure
since the config is modified by the application's runtime!</emphasis>
used by the imperative installer and all values are written to an additional file
to ensure that changes can be applied by changing the module's options.
</para>
<para>
In case the application serves multiple hosts (those are checked with
In case the application serves multiple domains (those are checked with
<literal><link xlink:href="http://php.net/manual/en/reserved.variables.server.php">$_SERVER['HTTP_HOST']</link></literal>)
those can be added using
it's needed to add them to
<literal><link linkend="opt-services.nextcloud.config.extraTrustedDomains">services.nextcloud.config.extraTrustedDomains</link></literal>.
</para>
<para>
Auto updates for Nextcloud apps can be enabled using
<literal><link linkend="opt-services.nextcloud.autoUpdateApps.enable">services.nextcloud.autoUpdateApps</link></literal>.
</para>
</section>
<section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
<title>Pitfalls</title>
@ -87,35 +94,24 @@
</para>
<para>
Right now changes to the <literal>services.nextcloud.config</literal>
attribute set won't take effect after the first install (except
<literal><link linkend="opt-services.nextcloud.config.extraTrustedDomains">services.nextcloud.config.extraTrustedDomains</link></literal>)
since the actual configuration file is generated by the NextCloud installer
which also sets up critical parts such as the database structure.
All configuration parameters are also stored in
<literal>/var/lib/nextcloud/config/override.config.php</literal> which is generated by
the module and linked from the store to ensure that all values from <literal>config.php</literal>
can be modified by the module.
However <literal>config.php</literal> manages the application's state and shouldn't be touched
manually because of that.
</para>
<para>
<emphasis>Warning: don't delete <literal>config.php</literal>! This file
<warning>
<para>Don't delete <literal>config.php</literal>! This file
tracks the application's state and a deletion can cause unwanted
side-effects!</emphasis>
</para>
side-effects!</para>
</warning>
<para>
<emphasis>Warning: don't rerun <literal>nextcloud-occ
<warning>
<para>Don't rerun <literal>nextcloud-occ
maintenance:install</literal>! This command tries to install the application
and can cause unwanted side-effects!</emphasis>
</para>
<para>
The issues are known and reported in
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/49783">#49783</link>,
for now it's unfortunately necessary to manually work around these issues.
</para>
<para>
Right now app installation and configuration is done imperatively in the nextcloud web ui or via the <literal>nextcloud-occ</literal> command line utility.
You can activate auto updates for your apps via
<literal><link linkend="opt-services.nextcloud.autoUpdateApps.enable">services.nextcloud.autoUpdateApps</link></literal>.
</para>
and can cause unwanted side-effects!</para>
</warning>
</section>
</chapter>

View file

@ -16,6 +16,9 @@ let
poolName = "tt-rss";
mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
tt-rss-config = pkgs.writeText "config.php" ''
<?php
@ -200,6 +203,12 @@ let
and 3306 for pgsql and mysql respectively).
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = "Create the database and database user locally.";
};
};
auth = {
@ -551,9 +560,13 @@ let
};
};
systemd.services.tt-rss = let
dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service";
in {
systemd.tmpfiles.rules = [
"d '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
"Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
];
systemd.services.tt-rss =
{
description = "Tiny Tiny RSS feeds update daemon";
@ -562,14 +575,14 @@ let
if cfg.database.type == "pgsql" then ''
${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \
${optionalString (cfg.database.passwordFile != null) "PGPASSWORD=$(cat ${cfg.database.passwordFile})"} \
${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.postgresql.package}/bin/psql \
${config.services.postgresql.package}/bin/psql \
-U ${cfg.database.user} \
${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \
-c '${e}' \
${cfg.database.name}''
else if cfg.database.type == "mysql" then ''
echo '${e}' | ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \
echo '${e}' | ${config.services.mysql.package}/bin/mysql \
-u ${cfg.database.user} \
${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
@ -579,7 +592,6 @@ let
in ''
rm -rf "${cfg.root}/*"
mkdir -m 755 -p "${cfg.root}"
cp -r "${pkgs.tt-rss}/"* "${cfg.root}"
${optionalString (cfg.pluginPackages != []) ''
for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
@ -592,19 +604,10 @@ let
done
''}
ln -sf "${tt-rss-config}" "${cfg.root}/config.php"
chown -R "${cfg.user}" "${cfg.root}"
chmod -R 755 "${cfg.root}"
''
+ (optionalString (cfg.database.type == "pgsql") ''
${optionalString (cfg.database.host == null && cfg.database.password == null) ''
if ! [ -e ${cfg.root}/.db-created ]; then
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser ${cfg.database.user}
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O ${cfg.database.user} ${cfg.database.name}
touch ${cfg.root}/.db-created
fi
''}
exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
| tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
@ -628,18 +631,18 @@ let
serviceConfig = {
User = "${cfg.user}";
Group = "tt_rss";
ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon";
StandardOutput = "syslog";
StandardError = "syslog";
PermissionsStartOnly = true;
};
wantedBy = [ "multi-user.target" ];
requires = ["${dbService}"];
after = ["network.target" "${dbService}"];
requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
};
services.mysql = optionalAttrs (cfg.database.type == "mysql") {
services.mysql = mkIf mysqlLocal {
enable = true;
package = mkDefault pkgs.mysql;
ensureDatabases = [ cfg.database.name ];
@ -653,17 +656,22 @@ let
];
};
services.postgresql = optionalAttrs (cfg.database.type == "pgsql") {
services.postgresql = mkIf pgsqlLocal {
enable = mkDefault true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.user;
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
users = optionalAttrs (cfg.user == "tt_rss") {
users.tt_rss = {
description = "tt-rss service user";
isSystemUser = true;
group = "tt_rss";
};
groups.tt_rss = {};
users.users.tt_rss = optionalAttrs (cfg.user == "tt_rss") {
description = "tt-rss service user";
isSystemUser = true;
group = "tt_rss";
};
users.groups.tt_rss = {};
};
}

View file

@ -21,10 +21,9 @@ let
else [{ip = "*"; port = 80;}];
getListen = cfg:
let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen;
in if list == []
then defaultListen cfg
else list;
if cfg.listen == []
then defaultListen cfg
else cfg.listen;
listenToString = l: "${l.ip}:${toString l.port}";
@ -638,7 +637,7 @@ in
message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
];
warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts);
warnings = map (cfg: "apache-httpd's extraSubservices option is deprecated. Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.") (lib.filter (cfg: cfg.extraSubservices != []) allHosts);
users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
{ name = "wwwrun";
@ -672,7 +671,7 @@ in
wantedBy = [ "multi-user.target" ];
wants = [ "keys.target" ];
after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
after = [ "network.target" "fs.target" "keys.target" ];
path =
[ httpd pkgs.coreutils pkgs.gnugrep ]

View file

@ -1,349 +0,0 @@
{ config, lib, pkgs, serverInfo, php, ... }:
with lib;
let
httpd = serverInfo.serverConfig.package;
version24 = !versionOlder httpd.version "2.4";
allGranted = if version24 then ''
Require all granted
'' else ''
Order allow,deny
Allow from all
'';
mediawikiConfig = pkgs.writeText "LocalSettings.php"
''
<?php
# Copied verbatim from the default (generated) LocalSettings.php.
if( defined( 'MW_INSTALL_PATH' ) ) {
$IP = MW_INSTALL_PATH;
} else {
$IP = dirname( __FILE__ );
}
$path = array( $IP, "$IP/includes", "$IP/languages" );
set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() );
require_once( "$IP/includes/DefaultSettings.php" );
if ( $wgCommandLineMode ) {
if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
die( "This script must be run from the command line\n" );
}
}
$wgScriptPath = "${config.urlPrefix}";
# We probably need to set $wgSecretKey and $wgCacheEpoch.
# Paths to external programs.
$wgDiff3 = "${pkgs.diffutils}/bin/diff3";
$wgDiff = "${pkgs.diffutils}/bin/diff";
$wgImageMagickConvertCommand = "${pkgs.imagemagick.out}/bin/convert";
#$wgDebugLogFile = "/tmp/mediawiki_debug_log.txt";
# Database configuration.
$wgDBtype = "${config.dbType}";
$wgDBserver = "${config.dbServer}";
$wgDBuser = "${config.dbUser}";
$wgDBpassword = "${config.dbPassword}";
$wgDBname = "${config.dbName}";
# E-mail.
$wgEmergencyContact = "${config.emergencyContact}";
$wgPasswordSender = "${config.passwordSender}";
$wgSitename = "${config.siteName}";
${optionalString (config.logo != "") ''
$wgLogo = "${config.logo}";
''}
${optionalString (config.articleUrlPrefix != "") ''
$wgArticlePath = "${config.articleUrlPrefix}/$1";
''}
${optionalString config.enableUploads ''
$wgEnableUploads = true;
$wgUploadDirectory = "${config.uploadDir}";
''}
${optionalString (config.defaultSkin != "") ''
$wgDefaultSkin = "${config.defaultSkin}";
''}
${config.extraConfig}
?>
'';
# Unpack Mediawiki and put the config file in its root directory.
mediawikiRoot = pkgs.stdenv.mkDerivation rec {
name= "mediawiki-1.31.1";
src = pkgs.fetchurl {
url = "https://releases.wikimedia.org/mediawiki/1.31/${name}.tar.gz";
sha256 = "13x48clij21cmysjkpnx68vggchrdasqp7b290j87xlfgjhdhnnf";
};
skins = config.skins;
extensions = config.extensions;
buildPhase =
''
for skin in $skins; do
cp -prvd $skin/* skins/
done
for extension in $extensions; do
cp -prvd $extension/* extensions/
done
''; # */
installPhase =
''
mkdir -p $out
cp -r * $out
cp ${mediawikiConfig} $out/LocalSettings.php
sed -i \
-e 's|/bin/bash|${pkgs.bash}/bin/bash|g' \
-e 's|/usr/bin/timeout|${pkgs.coreutils}/bin/timeout|g' \
$out/includes/shell/limit.sh \
$out/includes/GlobalFunctions.php
'';
};
mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts" {
buildInputs = [ pkgs.makeWrapper ];
preferLocalBuild = true;
} ''
mkdir -p $out/bin
for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \
--add-flags ${mediawikiRoot}/maintenance/$i
done
'';
in
{
extraConfig =
''
${optionalString config.enableUploads ''
Alias ${config.urlPrefix}/images ${config.uploadDir}
<Directory ${config.uploadDir}>
${allGranted}
Options -Indexes
</Directory>
''}
${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else ''
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedDirs}
${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedFiles}
RewriteRule ${if config.enableUploads
then "!^/images"
else "^.*\$"
} %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == ""
then ""
else "${config.articleUrlPrefix}/"
}index.php [L]
''}
<Directory ${mediawikiRoot}>
${allGranted}
DirectoryIndex index.php
</Directory>
${optionalString (config.articleUrlPrefix != "") ''
Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php
''}
'';
documentRoot = if config.urlPrefix == "" then mediawikiRoot else null;
enablePHP = true;
options = {
id = mkOption {
default = "main";
description = ''
A unique identifier necessary to keep multiple MediaWiki server
instances on the same machine apart. This is used to
disambiguate the administrative scripts, which get names like
mediawiki-$id-change-password.
'';
};
dbType = mkOption {
default = "postgres";
example = "mysql";
description = "Database type.";
};
dbName = mkOption {
default = "mediawiki";
description = "Name of the database that holds the MediaWiki data.";
};
dbServer = mkOption {
default = ""; # use a Unix domain socket
example = "10.0.2.2";
description = ''
The location of the database server. Leave empty to use a
database server running on the same machine through a Unix
domain socket.
'';
};
dbUser = mkOption {
default = "mediawiki";
description = "The user name for accessing the database.";
};
dbPassword = mkOption {
default = "";
example = "foobar";
description = ''
The password of the database user. Warning: this is stored in
cleartext in the Nix store!
'';
};
emergencyContact = mkOption {
default = serverInfo.serverConfig.adminAddr;
example = "admin@example.com";
description = ''
Emergency contact e-mail address. Defaults to the Apache
admin address.
'';
};
passwordSender = mkOption {
default = serverInfo.serverConfig.adminAddr;
example = "password@example.com";
description = ''
E-mail address from which password confirmations originate.
Defaults to the Apache admin address.
'';
};
siteName = mkOption {
default = "MediaWiki";
example = "Foobar Wiki";
description = "Name of the wiki";
};
logo = mkOption {
default = "";
example = "/images/logo.png";
description = "The URL of the site's logo (which should be a 135x135px image).";
};
urlPrefix = mkOption {
default = "/w";
description = ''
The URL prefix under which the Mediawiki service appears.
'';
};
articleUrlPrefix = mkOption {
default = "/wiki";
example = "";
description = ''
The URL prefix under which article pages appear,
e.g. http://server/wiki/Page. Leave empty to use the main URL
prefix, e.g. http://server/w/index.php?title=Page.
'';
};
enableUploads = mkOption {
default = false;
description = "Whether to enable file uploads.";
};
uploadDir = mkOption {
default = throw "You must specify `uploadDir'.";
example = "/data/mediawiki-upload";
description = "The directory that stores uploaded files.";
};
defaultSkin = mkOption {
default = "";
example = "nostalgia";
description = "Set this value to change the default skin used by MediaWiki.";
};
skins = mkOption {
default = [];
type = types.listOf types.path;
description =
''
List of paths whose content is copied to the skins
subdirectory of the MediaWiki installation.
'';
};
extensions = mkOption {
default = [];
type = types.listOf types.path;
description =
''
List of paths whose content is copied to the 'extensions'
subdirectory of the MediaWiki installation.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
example =
''
$wgEnableEmail = false;
'';
description = ''
Any additional text to be appended to MediaWiki's
configuration file. This is a PHP script. For configuration
settings, see <link xlink:href='https://www.mediawiki.org/wiki/Manual:Configuration_settings'/>.
'';
};
};
extraPath = [ mediawikiScripts ];
# !!! Need to specify that Apache has a dependency on PostgreSQL!
startupScript = pkgs.writeScript "mediawiki_startup.sh"
# Initialise the database automagically if we're using a Postgres
# server on localhost.
(optionalString (config.dbType == "postgres" && config.dbServer == "") ''
if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then
${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}"
( echo 'CREATE LANGUAGE plpgsql;'
cat ${mediawikiRoot}/maintenance/postgres/tables.sql
echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );'
echo COMMIT
) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
fi
${php}/bin/php ${mediawikiRoot}/maintenance/update.php
'');
robotsEntries = optionalString (config.articleUrlPrefix != "")
''
User-agent: *
Disallow: ${config.urlPrefix}/
Disallow: ${config.articleUrlPrefix}/Special:Search
Disallow: ${config.articleUrlPrefix}/Special:Random
'';
}

View file

@ -24,14 +24,6 @@ with lib;
'';
};
port = mkOption {
type = types.int;
default = 0;
description = ''
Port for the server. Option will be removed, use <option>listen</option> instead.
'';
};
listen = mkOption {
type = types.listOf (types.submodule (
{

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