Merge branch 'master' into drbd-kernel-module

This commit is contained in:
Birk 2024-02-14 20:06:21 +00:00 committed by GitHub
commit c3a3dcb418
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2538 changed files with 77005 additions and 36745 deletions

View File

@ -96,3 +96,9 @@ fb0e5be84331188a69b3edd31679ca6576edb75a
# nixos/*: add trivial defaultText for options with simple defaults
25124556397ba17bfd70297000270de1e6523b0a
# systemd: rewrite comments
92dfeb7b3dab820ae307c56c216d175c69ee93cd
# systemd: break too long lines of Nix code
67643f8ec84bef1482204709073e417c9f07eb87

9
.github/CODEOWNERS vendored
View File

@ -52,6 +52,7 @@
/pkgs/pkgs-lib @infinisil
## Format generators/serializers
/pkgs/pkgs-lib/formats/libconfig @ckiee @h7x4
/pkgs/pkgs-lib/formats/hocon @h7x4
# pkgs/by-name
/pkgs/test/nixpkgs-check-by-name @infinisil
@ -67,8 +68,8 @@
/nixos/lib/make-disk-image.nix @raitobezarius
# Nix, the package manager
pkgs/tools/package-management/nix/ @raitobezarius
nixos/modules/installer/tools/nix-fallback-paths.nix @raitobezarius
pkgs/tools/package-management/nix/ @raitobezarius @ma27
nixos/modules/installer/tools/nix-fallback-paths.nix @raitobezarius @ma27
# Nixpkgs documentation
/maintainers/scripts/db-to-md.sh @jtojnar @ryantm
@ -196,6 +197,10 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
/nixos/modules/services/databases/postgresql.nix @thoughtpolice
/nixos/tests/postgresql.nix @thoughtpolice
# Linux kernel
/pkgs/os-specific/linux/kernel @raitobezarius
/pkgs/top-level/linux-kernels.nix @raitobezarius
# Hardened profile & related modules
/nixos/modules/profiles/hardened.nix @joachifm
/nixos/modules/security/hidepid.nix @joachifm

View File

@ -1,4 +1,4 @@
Copyright (c) 2003-2023 Eelco Dolstra and the Nixpkgs/NixOS contributors
Copyright (c) 2003-2024 Eelco Dolstra and the Nixpkgs/NixOS contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1088,238 +1088,482 @@ If you don't specify a `name` attribute, you'll encounter an evaluation error an
## Environment Helpers {#ssec-pkgs-dockerTools-helpers}
Some packages expect certain files to be available globally.
When building an image from scratch (i.e. without `fromImage`), these files are missing.
`pkgs.dockerTools` provides some helpers to set up an environment with the necessary files.
You can include them in `copyToRoot` like this:
When building Docker images with Nix, you might also want to add certain files that are expected to be available globally by the software you're packaging.
Simple examples are the `env` utility in `/usr/bin/env`, or trusted root TLS/SSL certificates.
Such files will most likely not be included if you're building a Docker image from scratch with Nix, and they might also not be included if you're starting from a Docker image that doesn't include them.
The helpers in this section are packages that provide some of these commonly-needed global files.
```nix
buildImage {
name = "environment-example";
copyToRoot = with pkgs.dockerTools; [
usrBinEnv
binSh
caCertificates
fakeNss
];
}
```
Most of these helpers are packages, which means you have to add them to the list of contents to be included in the image (this changes depending on the function you're using to build the image).
[](#ex-dockerTools-helpers-buildImage) and [](#ex-dockerTools-helpers-buildLayeredImage) show how to include these packages on `dockerTools` functions that build an image.
For more details on how that works, see the documentation for the function you're using.
### usrBinEnv {#sssec-pkgs-dockerTools-helpers-usrBinEnv}
This provides the `env` utility at `/usr/bin/env`.
This is currently implemented by linking to the `env` binary from the `coreutils` package, but is considered an implementation detail that could change in the future.
### binSh {#sssec-pkgs-dockerTools-helpers-binSh}
This provides `bashInteractive` at `/bin/sh`.
This provides a `/bin/sh` link to the `bash` binary from the `bashInteractive` package.
Because of this, it supports cases such as running a command interactively in a container (for example by running `docker run -it <image_name>`).
### caCertificates {#sssec-pkgs-dockerTools-helpers-caCertificates}
This sets up `/etc/ssl/certs/ca-certificates.crt`.
This adds trusted root TLS/SSL certificates from the `cacert` package in multiple locations in an attempt to be compatible with binaries built for multiple Linux distributions.
The locations currently used are:
- `/etc/ssl/certs/ca-bundle.crt`
- `/etc/ssl/certs/ca-certificates.crt`
- `/etc/pki/tls/certs/ca-bundle.crt`
[]{#ssec-pkgs-dockerTools-fakeNss}
### fakeNss {#sssec-pkgs-dockerTools-helpers-fakeNss}
Provides `/etc/passwd` and `/etc/group` that contain root and nobody.
Useful when packaging binaries that insist on using nss to look up
username/groups (like nginx).
This is a re-export of the `fakeNss` package from Nixpkgs.
See [](#sec-fakeNss).
### shadowSetup {#ssec-pkgs-dockerTools-shadowSetup}
This constant string is a helper for setting up the base files for managing users and groups, only if such files don't exist already. It is suitable for being used in a [`buildImage` `runAsRoot`](#ex-dockerTools-buildImage-runAsRoot) script for cases like in the example below:
This is a string containing a script that sets up files needed for [`shadow`](https://github.com/shadow-maint/shadow) to work (using the `shadow` package from Nixpkgs), and alters `PATH` to make all its utilities available in the same script.
It is intended to be used with other dockerTools functions in attributes that expect scripts.
After the script in `shadowSetup` runs, you'll then be able to add more commands that make use of the utilities in `shadow`, such as adding any extra users and/or groups.
See [](#ex-dockerTools-shadowSetup-buildImage) and [](#ex-dockerTools-shadowSetup-buildLayeredImage) to better understand how to use it.
`shadowSetup` achieves a result similar to [`fakeNss`](#sssec-pkgs-dockerTools-helpers-fakeNss), but only sets up a `root` user with different values for the home directory and the shell to use, in addition to setting up files for [PAM](https://en.wikipedia.org/wiki/Linux_PAM) and a {manpage}`login.defs(5)` file.
:::{.caution}
Using both `fakeNss` and `shadowSetup` at the same time will either cause your build to break or produce unexpected results.
Use either `fakeNss` or `shadowSetup` depending on your use case, but avoid using both.
:::
:::{.note}
When used with [`buildLayeredImage`](#ssec-pkgs-dockerTools-buildLayeredImage) or [`streamLayeredImage`](#ssec-pkgs-dockerTools-streamLayeredImage), you will have to set the `enableFakechroot` attribute to `true`, or else the script in `shadowSetup` won't run properly.
See [](#ex-dockerTools-shadowSetup-buildLayeredImage).
:::
### Examples {#ssec-pkgs-dockerTools-helpers-examples}
:::{.example #ex-dockerTools-helpers-buildImage}
# Using `dockerTools`'s environment helpers with `buildImage`
This example adds the [`binSh`](#sssec-pkgs-dockerTools-helpers-binSh) helper to a basic Docker image built with [`dockerTools.buildImage`](#ssec-pkgs-dockerTools-buildImage).
This helper makes it possible to enter a shell inside the container.
This is the `buildImage` equivalent of [](#ex-dockerTools-helpers-buildLayeredImage).
```nix
buildImage {
name = "shadow-basic";
{ dockerTools, hello }:
dockerTools.buildImage {
name = "env-helpers";
tag = "latest";
runAsRoot = ''
#!${pkgs.runtimeShell}
${pkgs.dockerTools.shadowSetup}
groupadd -r redis
useradd -r -g redis redis
mkdir /data
chown redis:redis /data
'';
}
copyToRoot = [
hello
dockerTools.binSh
];
```
Creating base files like `/etc/passwd` or `/etc/login.defs` is necessary for shadow-utils to manipulate users and groups.
After building the image and loading it in Docker, we can create a container based on it and enter a shell inside the container.
This is made possible by `binSh`.
When using `buildLayeredImage`, you can put this in `fakeRootCommands` if you `enableFakechroot`:
```nix
buildLayeredImage {
name = "shadow-layered";
fakeRootCommands = ''
${pkgs.dockerTools.shadowSetup}
'';
enableFakechroot = true;
}
```shell
$ nix-build
(some output removed for clarity)
/nix/store/2p0i3i04cgjlk71hsn7ll4kxaxxiv4qg-docker-image-env-helpers.tar.gz
$ docker load -i /nix/store/2p0i3i04cgjlk71hsn7ll4kxaxxiv4qg-docker-image-env-helpers.tar.gz
(output removed for clarity)
$ docker run --rm -it env-helpers:latest /bin/sh
sh-5.2# help
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
(rest of output removed for clarity)
```
:::
## fakeNss {#ssec-pkgs-dockerTools-fakeNss}
:::{.example #ex-dockerTools-helpers-buildLayeredImage}
# Using `dockerTools`'s environment helpers with `buildLayeredImage`
If your primary goal is providing a basic skeleton for user lookups to work,
and/or a lesser privileged user, adding `pkgs.fakeNss` to
the container image root might be the better choice than a custom script
running `useradd` and friends.
It provides a `/etc/passwd` and `/etc/group`, containing `root` and `nobody`
users and groups.
It also provides a `/etc/nsswitch.conf`, configuring NSS host resolution to
first check `/etc/hosts`, before checking DNS, as the default in the absence of
a config file (`dns [!UNAVAIL=return] files`) is quite unexpected.
You can pair it with `binSh`, which provides `bin/sh` as a symlink
to `bashInteractive` (as `/bin/sh` is configured as a shell).
This example adds the [`binSh`](#sssec-pkgs-dockerTools-helpers-binSh) helper to a basic Docker image built with [`dockerTools.buildLayeredImage`](#ssec-pkgs-dockerTools-buildLayeredImage).
This helper makes it possible to enter a shell inside the container.
This is the `buildLayeredImage` equivalent of [](#ex-dockerTools-helpers-buildImage).
```nix
buildImage {
name = "shadow-basic";
{ dockerTools, hello }:
dockerTools.buildLayeredImage {
name = "env-helpers";
tag = "latest";
copyToRoot = pkgs.buildEnv {
name = "image-root";
paths = [ binSh pkgs.fakeNss ];
pathsToLink = [ "/bin" "/etc" "/var" ];
contents = [
hello
dockerTools.binSh
];
config = {
Cmd = [ "/bin/hello" ];
};
}
```
## buildNixShellImage {#ssec-pkgs-dockerTools-buildNixShellImage}
After building the image and loading it in Docker, we can create a container based on it and enter a shell inside the container.
This is made possible by `binSh`.
Create a Docker image that sets up an environment similar to that of running `nix-shell` on a derivation.
When run in Docker, this environment somewhat resembles the Nix sandbox typically used by `nix-build`, with a major difference being that access to the internet is allowed.
It additionally also behaves like an interactive `nix-shell`, running things like `shellHook` and setting an interactive prompt.
If the derivation is fully buildable (i.e. `nix-build` can be used on it), running `buildDerivation` inside such a Docker image will build the derivation, with all its outputs being available in the correct `/nix/store` paths, pointed to by the respective environment variables like `$out`, etc.
::: {.warning}
The behavior doesn't match `nix-shell` or `nix-build` exactly and this function is known not to work correctly for e.g. fixed-output derivations, content-addressed derivations, impure derivations and other special types of derivations.
```shell
$ nix-build
(some output removed for clarity)
/nix/store/rpf47f4z5b9qr4db4ach9yr4b85hjhxq-env-helpers.tar.gz
$ docker load -i /nix/store/rpf47f4z5b9qr4db4ach9yr4b85hjhxq-env-helpers.tar.gz
(output removed for clarity)
$ docker run --rm -it env-helpers:latest /bin/sh
sh-5.2# help
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
(rest of output removed for clarity)
```
:::
### Arguments {#ssec-pkgs-dockerTools-buildNixShellImage-arguments}
:::{.example #ex-dockerTools-shadowSetup-buildImage}
# Using `dockerTools.shadowSetup` with `dockerTools.buildImage`
`drv`
: The derivation on which to base the Docker image.
Adding packages to the Docker image is possible by e.g. extending the list of `nativeBuildInputs` of this derivation like
```nix
buildNixShellImage {
drv = someDrv.overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs or [] ++ [
somethingExtra
];
});
# ...
}
```
Similarly, you can extend the image initialization script by extending `shellHook`
`name` _optional_
: The name of the resulting image.
*Default:* `drv.name + "-env"`
`tag` _optional_
: Tag of the generated image.
*Default:* the resulting image derivation output path's hash
`uid`/`gid` _optional_
: The user/group ID to run the container as. This is like a `nixbld` build user.
*Default:* 1000/1000
`homeDirectory` _optional_
: The home directory of the user the container is running as
*Default:* `/build`
`shell` _optional_
: The path to the `bash` binary to use as the shell. This shell is started when running the image.
*Default:* `pkgs.bashInteractive + "/bin/bash"`
`command` _optional_
: Run this command in the environment of the derivation, in an interactive shell. See the `--command` option in the [`nix-shell` documentation](https://nixos.org/manual/nix/stable/command-ref/nix-shell.html?highlight=nix-shell#options).
*Default:* (none)
`run` _optional_
: Same as `command`, but runs the command in a non-interactive shell instead. See the `--run` option in the [`nix-shell` documentation](https://nixos.org/manual/nix/stable/command-ref/nix-shell.html?highlight=nix-shell#options).
*Default:* (none)
### Example {#ssec-pkgs-dockerTools-buildNixShellImage-example}
The following shows how to build the `pkgs.hello` package inside a Docker container built with `buildNixShellImage`.
This is an example that shows how to use `shadowSetup` with `dockerTools.buildImage`.
Note that the extra script in `runAsRoot` uses `groupadd` and `useradd`, which are binaries provided by the `shadow` package.
These binaries are added to the `PATH` by the `shadowSetup` script, but only for the duration of `runAsRoot`.
```nix
with import <nixpkgs> {};
{ dockerTools, hello }:
dockerTools.buildImage {
name = "shadow-basic";
tag = "latest";
copyToRoot = [ hello ];
runAsRoot = ''
${dockerTools.shadowSetup}
groupadd -r hello
useradd -r -g hello hello
mkdir /data
chown hello:hello /data
'';
config = {
Cmd = [ "/bin/hello" ];
WorkingDir = "/data";
};
}
```
:::
:::{.example #ex-dockerTools-shadowSetup-buildLayeredImage}
# Using `dockerTools.shadowSetup` with `dockerTools.buildLayeredImage`
It accomplishes the same thing as [](#ex-dockerTools-shadowSetup-buildImage), but using `buildLayeredImage` instead.
Note that the extra script in `fakeRootCommands` uses `groupadd` and `useradd`, which are binaries provided by the `shadow` package.
These binaries are added to the `PATH` by the `shadowSetup` script, but only for the duration of `fakeRootCommands`.
```nix
{ dockerTools, hello }:
dockerTools.buildLayeredImage {
name = "shadow-basic";
tag = "latest";
contents = [ hello ];
fakeRootCommands = ''
${dockerTools.shadowSetup}
groupadd -r hello
useradd -r -g hello hello
mkdir /data
chown hello:hello /data
'';
enableFakechroot = true;
config = {
Cmd = [ "/bin/hello" ];
WorkingDir = "/data";
};
}
```
:::
[]{#ssec-pkgs-dockerTools-buildNixShellImage-arguments}
## buildNixShellImage {#ssec-pkgs-dockerTools-buildNixShellImage}
`buildNixShellImage` uses [`streamNixShellImage`](#ssec-pkgs-dockerTools-streamNixShellImage) underneath to build a compressed Docker-compatible repository tarball of an image that sets up an environment similar to that of running `nix-shell` on a derivation.
Basically, `buildNixShellImage` runs the script created by `streamNixShellImage` to save the compressed image in the Nix store.
`buildNixShellImage` supports the same options as `streamNixShellImage`, see [`streamNixShellImage`](#ssec-pkgs-dockerTools-streamNixShellImage) for details.
[]{#ssec-pkgs-dockerTools-buildNixShellImage-example}
### Examples {#ssec-pkgs-dockerTools-buildNixShellImage-examples}
:::{.example #ex-dockerTools-buildNixShellImage-hello}
# Building a Docker image with `buildNixShellImage` with the build environment for the `hello` package
This example shows how to build the `hello` package inside a Docker container built with `buildNixShellImage`.
The Docker image generated will have a name like `hello-<version>-env` and tag `latest`.
This example is the `buildNixShellImage` equivalent of [](#ex-dockerTools-streamNixShellImage-hello).
```nix
{ dockerTools, hello }:
dockerTools.buildNixShellImage {
drv = hello;
tag = "latest";
}
```
Build the derivation:
The result of building this package is a `.tar.gz` file that can be loaded into Docker:
```console
nix-build hello.nix
```shell
$ nix-build
(some output removed for clarity)
/nix/store/pkj1sgzaz31wl0pbvbg3yp5b3kxndqms-hello-2.12.1-env.tar.gz
$ docker load -i /nix/store/pkj1sgzaz31wl0pbvbg3yp5b3kxndqms-hello-2.12.1-env.tar.gz
(some output removed for clarity)
Loaded image: hello-2.12.1-env:latest
```
these 8 derivations will be built:
/nix/store/xmw3a5ln29rdalavcxk1w3m4zb2n7kk6-nix-shell-rc.drv
...
Creating layer 56 from paths: ['/nix/store/crpnj8ssz0va2q0p5ibv9i6k6n52gcya-stdenv-linux']
Creating layer 57 with customisation...
Adding manifests...
Done.
/nix/store/cpyn1lc897ghx0rhr2xy49jvyn52bazv-hello-2.12-env.tar.gz
After starting an interactive container, the derivation can be built by running `buildDerivation`, and the output can be executed as expected:
Load the image:
```shell
$ docker run -it hello-2.12.1-env:latest
[nix-shell:~]$ buildDerivation
Running phase: unpackPhase
unpacking source archive /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
source root is hello-2.12.1
(some output removed for clarity)
Running phase: fixupPhase
shrinking RPATHs of ELF executables and libraries in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1
shrinking /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1/bin/hello
checking for references to /build/ in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1...
gzipping man pages under /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1/share/man/
patching script interpreter paths in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1
stripping (with command strip and flags -S -p) in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1/bin
```console
docker load -i result
[nix-shell:~]$ $out/bin/hello
Hello, world!
```
:::
## streamNixShellImage {#ssec-pkgs-dockerTools-streamNixShellImage}
`streamNixShellImage` builds a **script** which, when run, will stream to stdout a Docker-compatible repository tarball of an image that sets up an environment similar to that of running `nix-shell` on a derivation.
This means that `streamNixShellImage` does not output an image into the Nix store, but only a script that builds the image, saving on IO and disk/cache space, particularly with large images.
See [](#ex-dockerTools-streamNixShellImage-hello) to understand how to load in Docker the image generated by this script.
The environment set up by `streamNixShellImage` somewhat resembles the Nix sandbox typically used by `nix-build`, with a major difference being that access to the internet is allowed.
It also behaves like an interactive `nix-shell`, running things like `shellHook` (see [](#ex-dockerTools-streamNixShellImage-addingShellHook)) and setting an interactive prompt.
If the derivation is buildable (i.e. `nix-build` can be used on it), running `buildDerivation` in the container will build the derivation, with all its outputs being available in the correct `/nix/store` paths, pointed to by the respective environment variables (e.g. `$out`).
::: {.caution}
The environment in the image doesn't match `nix-shell` or `nix-build` exactly, and this function is known not to work correctly for fixed-output derivations, content-addressed derivations, impure derivations and other special types of derivations.
:::
### Inputs {#ssec-pkgs-dockerTools-streamNixShellImage-inputs}
`streamNixShellImage` expects one argument with the following attributes:
`drv` (Attribute Set)
: The derivation for which the environment in the image will be set up.
Adding packages to the Docker image is possible by extending the list of `nativeBuildInputs` of this derivation.
See [](#ex-dockerTools-streamNixShellImage-extendingBuildInputs) for how to do that.
Similarly, you can extend the image initialization script by extending `shellHook`.
[](#ex-dockerTools-streamNixShellImage-addingShellHook) shows how to do that.
`name` (String; _optional_)
: The name of the generated image.
_Default value:_ the value of `drv.name + "-env"`.
`tag` (String or Null; _optional_)
: Tag of the generated image.
If `null`, the hash of the nix derivation that builds the Docker image will be used as the tag.
_Default value:_ `null`.
`uid` (Number; _optional_)
: The user ID to run the container as.
This can be seen as a `nixbld` build user.
_Default value:_ 1000.
`gid` (Number; _optional_)
: The group ID to run the container as.
This can be seen as a `nixbld` build group.
_Default value:_ 1000.
`homeDirectory` (String; _optional_)
: The home directory of the user the container is running as.
_Default value:_ `/build`.
`shell` (String; _optional_)
: The path to the `bash` binary to use as the shell.
This shell is started when running the image.
This can be seen as an equivalent of the `NIX_BUILD_SHELL` [environment variable](https://nixos.org/manual/nix/stable/command-ref/nix-shell.html#environment-variables) for {manpage}`nix-shell(1)`.
_Default value:_ the `bash` binary from the `bashInteractive` package.
`command` (String or Null; _optional_)
: If specified, this command will be run in the environment of the derivation in an interactive shell.
A call to `exit` will be added after the command if it is specified, so the shell will exit after it's finished running.
This can be seen as an equivalent of the `--command` option in {manpage}`nix-shell(1)`.
_Default value:_ `null`.
`run` (String or Null; _optional_)
: Similar to the `command` attribute, but runs the command in a non-interactive shell instead.
A call to `exit` will be added after the command if it is specified, so the shell will exit after it's finished running.
This can be seen as an equivalent of the `--run` option in {manpage}`nix-shell(1)`.
_Default value:_ `null`.
### Examples {#ssec-pkgs-dockerTools-streamNixShellImage-examples}
:::{.example #ex-dockerTools-streamNixShellImage-hello}
# Building a Docker image with `streamNixShellImage` with the build environment for the `hello` package
This example shows how to build the `hello` package inside a Docker container built with `streamNixShellImage`.
The Docker image generated will have a name like `hello-<version>-env` and tag `latest`.
This example is the `streamNixShellImage` equivalent of [](#ex-dockerTools-buildNixShellImage-hello).
```nix
{ dockerTools, hello }:
dockerTools.streamNixShellImage {
drv = hello;
tag = "latest";
}
```
0d9f4c4cd109: Loading layer [==================================================>] 2.56MB/2.56MB
...
ab1d897c0697: Loading layer [==================================================>] 10.24kB/10.24kB
Loaded image: hello-2.12-env:pgj9h98nal555415faa43vsydg161bdz
The result of building this package is a script.
Running this script and piping it into `docker load` gives you the same image that was built in [](#ex-dockerTools-buildNixShellImage-hello).
Run the container:
```shell
$ nix-build
(some output removed for clarity)
/nix/store/8vhznpz2frqazxnd8pgdvf38jscdypax-stream-hello-2.12.1-env
```console
docker run -it hello-2.12-env:pgj9h98nal555415faa43vsydg161bdz
$ /nix/store/8vhznpz2frqazxnd8pgdvf38jscdypax-stream-hello-2.12.1-env | docker load
(some output removed for clarity)
Loaded image: hello-2.12.1-env:latest
```
[nix-shell:/build]$
After starting an interactive container, the derivation can be built by running `buildDerivation`, and the output can be executed as expected:
In the running container, run the build:
```shell
$ docker run -it hello-2.12.1-env:latest
[nix-shell:~]$ buildDerivation
Running phase: unpackPhase
unpacking source archive /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
source root is hello-2.12.1
(some output removed for clarity)
Running phase: fixupPhase
shrinking RPATHs of ELF executables and libraries in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1
shrinking /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1/bin/hello
checking for references to /build/ in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1...
gzipping man pages under /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1/share/man/
patching script interpreter paths in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1
stripping (with command strip and flags -S -p) in /nix/store/f2vs29jibd7lwxyj35r9h87h6brgdysz-hello-2.12.1/bin
```console
buildDerivation
[nix-shell:~]$ $out/bin/hello
Hello, world!
```
:::
:::{.example #ex-dockerTools-streamNixShellImage-extendingBuildInputs}
# Adding extra packages to a Docker image built with `streamNixShellImage`
This example shows how to add extra packages to an image built with `streamNixShellImage`.
In this case, we'll add the `cowsay` package.
The Docker image generated will have a name like `hello-<version>-env` and tag `latest`.
This example uses [](#ex-dockerTools-streamNixShellImage-hello) as a starting point.
```nix
{ dockerTools, cowsay, hello }:
dockerTools.streamNixShellImage {
tag = "latest";
drv = hello.overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs or [] ++ [
cowsay
];
});
}
```
unpacking sources
unpacking source archive /nix/store/8nqv6kshb3vs5q5bs2k600xpj5bkavkc-hello-2.12.tar.gz
...
patching script interpreter paths in /nix/store/z5wwy5nagzy15gag42vv61c2agdpz2f2-hello-2.12
checking for references to /build/ in /nix/store/z5wwy5nagzy15gag42vv61c2agdpz2f2-hello-2.12...
The result of building this package is a script which can be run and piped into `docker load` to load the generated image.
Check the build result:
```shell
$ nix-build
(some output removed for clarity)
/nix/store/h5abh0vljgzg381lna922gqknx6yc0v7-stream-hello-2.12.1-env
```console
$out/bin/hello
$ /nix/store/h5abh0vljgzg381lna922gqknx6yc0v7-stream-hello-2.12.1-env | docker load
(some output removed for clarity)
Loaded image: hello-2.12.1-env:latest
```
Hello, world!
After starting an interactive container, we can verify the extra package is available by running `cowsay`:
```shell
$ docker run -it hello-2.12.1-env:latest
[nix-shell:~]$ cowsay "Hello, world!"
_______________
< Hello, world! >
---------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
:::
:::{.example #ex-dockerTools-streamNixShellImage-addingShellHook}
# Adding a `shellHook` to a Docker image built with `streamNixShellImage`
This example shows how to add a `shellHook` command to an image built with `streamNixShellImage`.
In this case, we'll simply output the string `Hello, world!`.
The Docker image generated will have a name like `hello-<version>-env` and tag `latest`.
This example uses [](#ex-dockerTools-streamNixShellImage-hello) as a starting point.
```nix
{ dockerTools, hello }:
dockerTools.streamNixShellImage {
tag = "latest";
drv = hello.overrideAttrs (old: {
shellHook = ''
${old.shellHook or ""}
echo "Hello, world!"
'';
});
}
```
The result of building this package is a script which can be run and piped into `docker load` to load the generated image.
```shell
$ nix-build
(some output removed for clarity)
/nix/store/iz4dhdvgzazl5vrgyz719iwjzjy6xlx1-stream-hello-2.12.1-env
$ /nix/store/iz4dhdvgzazl5vrgyz719iwjzjy6xlx1-stream-hello-2.12.1-env | docker load
(some output removed for clarity)
Loaded image: hello-2.12.1-env:latest
```
After starting an interactive container, we can see the result of the `shellHook`:
```shell
$ docker run -it hello-2.12.1-env:latest
Hello, world!
[nix-shell:~]$
```
:::

View File

@ -3,6 +3,7 @@
This chapter describes several special build helpers.
```{=include=} sections
special/fakenss.section.md
special/fhs-environments.section.md
special/makesetuphook.section.md
special/mkshell.section.md

View File

@ -0,0 +1,77 @@
# fakeNss {#sec-fakeNss}
Provides `/etc/passwd` and `/etc/group` files that contain `root` and `nobody`, allowing user/group lookups to work in binaries that insist on doing those.
This might be a better choice than a custom script running `useradd` and related utilities if you only need those files to exist with some entries.
`fakeNss` also provides `/etc/nsswitch.conf`, configuring NSS host resolution to first check `/etc/hosts` before checking DNS, since the default in the absence of a config file (`dns [!UNAVAIL=return] files`) is quite unexpected.
It also creates an empty directory at `/var/empty` because it uses that as the home directory for the `root` and `nobody` users.
The `/var/empty` directory can also be used as a `chroot` target to prevent file access in processes that do not need to access files, if your container runs such processes.
The user entries created by `fakeNss` use the `/bin/sh` shell, which is not provided by `fakeNss` because in most cases it won't be used.
If you need that to be available, see [`dockerTools.binSh`](#sssec-pkgs-dockerTools-helpers-binSh) or provide your own.
## Inputs {#sec-fakeNss-inputs}
`fakeNss` is made available in Nixpkgs as a package rather than a function, but it has two attributes that can be overridden and might be useful in particular cases.
For more details on how overriding works, see [](#ex-fakeNss-overriding) and [](#sec-pkg-override).
`extraPasswdLines` (List of Strings; _optional_)
: A list of lines that will be added to `/etc/passwd`.
Useful if extra users need to exist in the output of `fakeNss`.
If `extraPasswdLines` is specified, it will **not** override the `root` and `nobody` entries created by `fakeNss`.
Those entries will always exist.
Lines specified here must follow the format in {manpage}`passwd(5)`.
_Default value:_ `[]`.
`extraGroupLines` (List of Strings; _optional_)
: A list of lines that will be added to `/etc/group`.
Useful if extra groups need to exist in the output of `fakeNss`.
If `extraGroupLines` is specified, it will **not** override the `root` and `nobody` entries created by `fakeNss`.
Those entries will always exist.
Lines specified here must follow the format in {manpage}`group(5)`.
_Default value:_ `[]`.
## Examples {#sec-fakeNss-examples}
:::{.example #ex-fakeNss-dockerTools-buildImage}
# Using `fakeNss` with `dockerTools.buildImage`
This example shows how to use `fakeNss` as-is.
It is useful with functions in `dockerTools` to allow building Docker images that have the `/etc/passwd` and `/etc/group` files.
This example includes the `hello` binary in the image so it can do something besides just have the extra files.
```nix
{ dockerTools, fakeNss, hello }:
dockerTools.buildImage {
name = "image-with-passwd";
tag = "latest";
copyToRoot = [ fakeNss hello ];
config = {
Cmd = [ "/bin/hello" ];
};
}
```
:::
:::{.example #ex-fakeNss-overriding}
# Using `fakeNss` with an override to add extra lines
The following code uses `override` to add extra lines to `/etc/passwd` and `/etc/group` to create another user and group entry.
```nix
{ fakeNss }:
fakeNss.override {
extraPasswdLines = ["newuser:x:9001:9001:new user:/var/empty:/bin/sh"];
extraGroupLines = ["newuser:x:9001:"];
}
```
:::

View File

@ -25,6 +25,7 @@ let
{ name = "gvariant"; description = "GVariant formatted string serialization functions"; }
{ name = "customisation"; description = "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; }
{ name = "meta"; description = "functions for derivation metadata"; }
{ name = "derivations"; description = "miscellaneous derivation-specific functions"; }
];
};

View File

@ -2,7 +2,7 @@
This hook helps with installing manpages and shell completion files. It exposes 2 shell functions `installManPage` and `installShellCompletion` that can be used from your `postInstall` hook.
The `installManPage` function takes one or more paths to manpages to install. The manpages must have a section suffix, and may optionally be compressed (with `.gz` suffix). This function will place them into the correct directory.
The `installManPage` function takes one or more paths to manpages to install. The manpages must have a section suffix, and may optionally be compressed (with `.gz` suffix). This function will place them into the correct `share/man/man<section>/` directory, in [`outputMan`](#outputman).
The `installShellCompletion` function takes one or more paths to shell completion files. By default it will autodetect the shell type from the completion file extension, but you may also specify it by passing one of `--bash`, `--fish`, or `--zsh`. These flags apply to all paths listed after them (up until another shell flag is given). Each path may also have a custom installation name provided by providing a flag `--name NAME` before the path. If this flag is not provided, zsh completions will be renamed automatically such that `foobar.zsh` becomes `_foobar`. A root name may be provided for all paths using the flag `--cmd NAME`; this synthesizes the appropriate name depending on the shell (e.g. `--cmd foo` will synthesize the name `foo.bash` for bash and `_foo` for zsh). The path may also be a fifo or named fd (such as produced by `<(cmd)`), in which case the shell and name must be provided.

View File

@ -45,6 +45,7 @@ Bash-only variables:
- `postgresqlTestSetupCommands`: bash commands to run after database start, defaults to running `$postgresqlTestSetupSQL` as database administrator.
- `postgresqlEnableTCP`: set to `1` to enable TCP listening. Flaky; not recommended.
- `postgresqlStartCommands`: defaults to `pg_ctl start`.
- `postgresqlExtraSettings`: Additional configuration to add to `postgresql.conf`
## Hooks {#sec-postgresqlTestHook-hooks}

View File

@ -103,7 +103,7 @@ See the [Dart documentation](#ssec-dart-applications) for more details on requir
flutter.buildFlutterApplication {
pname = "firmware-updater";
version = "unstable-2023-04-30";
version = "0-unstable-2023-04-30";
# To build for the Web, use the targetFlutterPlatform argument.
# targetFlutterPlatform = "web";

View File

@ -93,7 +93,11 @@ The `dotnetCorePackages.sdk` contains both a runtime and the full sdk of a given
To package Dotnet applications, you can use `buildDotnetModule`. This has similar arguments to `stdenv.mkDerivation`, with the following additions:
* `projectFile` is used for specifying the dotnet project file, relative to the source root. These have `.sln` (entire solution) or `.csproj` (single project) file extensions. This can be a list of multiple projects as well. When omitted, will attempt to find and build the solution (`.sln`). If running into problems, make sure to set it to a file (or a list of files) with the `.csproj` extension - building applications as entire solutions is not fully supported by the .NET CLI.
* `nugetDeps` takes either a path to a `deps.nix` file, or a derivation. The `deps.nix` file can be generated using the script attached to `passthru.fetch-deps`. This file can also be generated manually using `nuget-to-nix` tool, which is available in nixpkgs. If the argument is a derivation, it will be used directly and assume it has the same output as `mkNugetDeps`.
* `nugetDeps` takes either a path to a `deps.nix` file, or a derivation. The `deps.nix` file can be generated using the script attached to `passthru.fetch-deps`. If the argument is a derivation, it will be used directly and assume it has the same output as `mkNugetDeps`.
::: {.note}
For more detail about managing the `deps.nix` file, see [Generating and updating NuGet dependencies](#generating-and-updating-nuget-dependencies)
:::
* `packNupkg` is used to pack project as a `nupkg`, and installs it to `$out/share`. If set to `true`, the derivation can be used as a dependency for another dotnet project by adding it to `projectReferences`.
* `projectReferences` can be used to resolve `ProjectReference` project items. Referenced projects can be packed with `buildDotnetModule` by setting the `packNupkg = true` attribute and passing a list of derivations to `projectReferences`. Since we are sharing referenced projects as NuGets they must be added to csproj/fsproj files as `PackageReference` as well.
For example, your project has a local dependency:
@ -156,6 +160,8 @@ in buildDotnetModule rec {
}
```
Keep in mind that you can tag the [`@NixOS/dotnet`](https://github.com/orgs/nixos/teams/dotnet) team for help and code review.
## Dotnet global tools {#dotnet-global-tools}
[.NET Global tools](https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools) are a mechanism provided by the dotnet CLI to install .NET binaries from Nuget packages.
@ -212,5 +218,43 @@ buildDotnetGlobalTool {
};
}
```
## Generating and updating NuGet dependencies {#generating-and-updating-nuget-dependencies}
First, restore the packages to the `out` directory, ensure you have cloned
the upstream repository and you are inside it.
```bash
$ dotnet restore --packages out
Determining projects to restore...
Restored /home/lychee/Celeste64/Celeste64.csproj (in 1.21 sec).
```
Next, use `nuget-to-nix` tool provided in nixpkgs to generate a lockfile to `deps.nix` from
the packages inside the `out` directory.
```bash
$ nuget-to-nix out > deps.nix
```
Which `nuget-to-nix` will generate an output similar to below
```
{ fetchNuGet }: [
(fetchNuGet { pname = "FosterFramework"; version = "0.1.15-alpha"; sha256 = "0pzsdfbsfx28xfqljcwy100xhbs6wyx0z1d5qxgmv3l60di9xkll"; })
(fetchNuGet { pname = "Microsoft.AspNetCore.App.Runtime.linux-x64"; version = "8.0.1"; sha256 = "1gjz379y61ag9whi78qxx09bwkwcznkx2mzypgycibxk61g11da1"; })
(fetchNuGet { pname = "Microsoft.NET.ILLink.Tasks"; version = "8.0.1"; sha256 = "1drbgqdcvbpisjn8mqfgba1pwb6yri80qc4mfvyczqwrcsj5k2ja"; })
(fetchNuGet { pname = "Microsoft.NETCore.App.Runtime.linux-x64"; version = "8.0.1"; sha256 = "1g5b30f4l8a1zjjr3b8pk9mcqxkxqwa86362f84646xaj4iw3a4d"; })
(fetchNuGet { pname = "SharpGLTF.Core"; version = "1.0.0-alpha0031"; sha256 = "0ln78mkhbcxqvwnf944hbgg24vbsva2jpih6q3x82d3h7rl1pkh6"; })
(fetchNuGet { pname = "SharpGLTF.Runtime"; version = "1.0.0-alpha0031"; sha256 = "0lvb3asi3v0n718qf9y367km7qpkb9wci38y880nqvifpzllw0jg"; })
(fetchNuGet { pname = "Sledge.Formats"; version = "1.2.2"; sha256 = "1y0l66m9rym0p1y4ifjlmg3j9lsmhkvbh38frh40rpvf1axn2dyh"; })
(fetchNuGet { pname = "Sledge.Formats.Map"; version = "1.1.5"; sha256 = "1bww60hv9xcyxpvkzz5q3ybafdxxkw6knhv97phvpkw84pd0jil6"; })
(fetchNuGet { pname = "System.Numerics.Vectors"; version = "4.5.0"; sha256 = "1kzrj37yzawf1b19jq0253rcs8hsq1l2q8g69d7ipnhzb0h97m59"; })
]
```
Finally, you move the `deps.nix` file to the appropriate location to be used by `nugetDeps`, then you're all set!
If you ever need to update the dependencies of a package, you instead do
* `nix-build -A package.fetch-deps` to generate the update script for `package`
* Run `./result deps.nix` to regenerate the lockfile to `deps.nix`, keep in mind if a location isn't provided, it will write to a temporary path instead
* Finally, move the file where needed and look at its contents to confirm it has updated the dependencies.
When packaging a new .NET application in nixpkgs, you can tag the [`@NixOS/dotnet`](https://github.com/orgs/nixos/teams/dotnet) team for help and code review.

View File

@ -3,72 +3,61 @@
Writing Nix expressions for Qt libraries and applications is largely similar as for other C++ software.
This section assumes some knowledge of the latter.
The major caveat with Qt applications is that Qt uses a plugin system to load additional modules at runtime,
from a list of well-known locations. In Nixpkgs, we patch QtCore to instead use an environment variable,
and wrap Qt applications to set it to the right paths. This effectively makes the runtime dependencies
pure and explicit at build-time, at the cost of introducing an extra indirection.
The major caveat with Qt applications is that Qt uses a plugin system to load additional modules at runtime.
In Nixpkgs, we wrap Qt applications to inject environment variables telling Qt where to discover the required plugins and QML modules.
This effectively makes the runtime dependencies pure and explicit at build-time, at the cost of introducing
an extra indirection.
## Nix expression for a Qt package (default.nix) {#qt-default-nix}
```nix
{ stdenv, lib, qtbase, wrapQtAppsHook }:
{ stdenv, lib, qt6, wrapQtAppsHook }:
stdenv.mkDerivation {
pname = "myapp";
version = "1.0";
buildInputs = [ qtbase ];
nativeBuildInputs = [ wrapQtAppsHook ];
buildInputs = [ qt6.qtbase ];
nativeBuildInputs = [ qt6.wrapQtAppsHook ];
}
```
It is important to import Qt modules directly, that is: `qtbase`, `qtdeclarative`, etc. *Do not* import Qt package sets such as `qt5` because the Qt versions of dependencies may not be coherent, causing build and runtime failures.
Any Qt package should include `wrapQtAppsHook` in `nativeBuildInputs`, or explicitly set `dontWrapQtApps` to bypass generating the wrappers.
Additionally all Qt packages must include `wrapQtAppsHook` in `nativeBuildInputs`, or you must explicitly set `dontWrapQtApps`.
::: {.note}
Graphical Linux applications should also include `qtwayland` in `buildInputs`, to ensure the Wayland platform plugin is available.
`pkgs.callPackage` does not provide injections for `qtbase` or the like.
Instead you want to either use `pkgs.libsForQt5.callPackage`, or `pkgs.qt6Packages.callPackage`, depending on the Qt version you want to use.
This may become default in the future, see [NixOS/nixpkgs#269674](https://github.com/NixOS/nixpkgs/pull/269674).
:::
For example (from [here](https://github.com/NixOS/nixpkgs/blob/2f9286912cb215969ece465147badf6d07aa43fe/pkgs/top-level/all-packages.nix#L30106))
## Packages supporting multiple Qt versions {#qt-versions}
```nix
zeal-qt5 = libsForQt5.callPackage ../data/documentation/zeal { };
zeal-qt6 = qt6Packages.callPackage ../data/documentation/zeal { };
zeal = zeal-qt5;
```
If your package is a library that can be built with multiple Qt versions, you may want to take Qt modules as separate arguments (`qtbase`, `qtdeclarative` etc.), and invoke the package from `pkgs/top-level/qt5-packages.nix` or `pkgs/top-level/qt6-packages.nix` using the respective `callPackage` functions.
## Locating runtime dependencies {#qt-runtime-dependencies}
Applications should generally be built with upstream's preferred Qt version.
Qt applications must be wrapped to find runtime dependencies.
Include `wrapQtAppsHook` in `nativeBuildInputs`:
```nix
{ stdenv, wrapQtAppsHook }:
stdenv.mkDerivation {
# ...
nativeBuildInputs = [ wrapQtAppsHook ];
}
```
## Locating additional runtime dependencies {#qt-runtime-dependencies}
Add entries to `qtWrapperArgs` are to modify the wrappers created by
`wrapQtAppsHook`:
```nix
{ stdenv, wrapQtAppsHook }:
{ stdenv, qt6 }:
stdenv.mkDerivation {
# ...
nativeBuildInputs = [ wrapQtAppsHook ];
nativeBuildInputs = [ qt6.wrapQtAppsHook ];
qtWrapperArgs = [ ''--prefix PATH : /path/to/bin'' ];
}
```
The entries are passed as arguments to [wrapProgram](#fun-wrapProgram).
Set `dontWrapQtApps` to stop applications from being wrapped automatically.
Wrap programs manually with `wrapQtApp`, using the syntax of
[wrapProgram](#fun-wrapProgram):
If you need more control over the wrapping process, set `dontWrapQtApps` to disable automatic wrapper generation,
and then create wrappers manually in `fixupPhase`, using `wrapQtApp`, which itself is a small wrapper over [wrapProgram](#fun-wrapProgram):
The `makeWrapper` arguments required for Qt are also exposed in the environment as `$qtWrapperArgs`.
```nix
{ stdenv, lib, wrapQtAppsHook }:

View File

@ -314,5 +314,9 @@
"systemd-veritysetup@.service(8)": "https://www.freedesktop.org/software/systemd/man/systemd-veritysetup@.service.html",
"systemd-volatile-root(8)": "https://www.freedesktop.org/software/systemd/man/systemd-volatile-root.html",
"systemd-xdg-autostart-generator(8)": "https://www.freedesktop.org/software/systemd/man/systemd-xdg-autostart-generator.html",
"udevadm(8)": "https://www.freedesktop.org/software/systemd/man/udevadm.html"
"udevadm(8)": "https://www.freedesktop.org/software/systemd/man/udevadm.html",
"passwd(5)": "https://man.archlinux.org/man/passwd.5",
"group(5)": "https://man.archlinux.org/man/group.5",
"login.defs(5)": "https://man.archlinux.org/man/login.defs.5",
"nix-shell(1)": "https://nixos.org/manual/nix/stable/command-ref/nix-shell.html"
}

View File

@ -3,7 +3,7 @@
let
inherit (builtins) head tail length;
inherit (lib.trivial) id mergeAttrs;
inherit (lib.trivial) id mergeAttrs warn;
inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName;
inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl;
in
@ -1197,9 +1197,10 @@ rec {
(x // y) // mask;
# DEPRECATED
zipWithNames = zipAttrsWithNames;
zipWithNames = warn
"lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." zipAttrsWithNames;
# DEPRECATED
zip = builtins.trace
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
zip = warn
"lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith;
}

View File

@ -116,7 +116,7 @@ let
inherit (self.customisation) overrideDerivation makeOverridable
callPackageWith callPackagesWith extendDerivation hydraJob
makeScope makeScopeWithSplicing makeScopeWithSplicing';
inherit (self.derivations) lazyDerivation;
inherit (self.derivations) lazyDerivation optionalDrvAttr;
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
hiPrioSet getLicenseFromSpdxId getExe getExe';

View File

@ -98,4 +98,30 @@ in
# `lazyDerivation` caller knew a shortcut, be taken from there.
meta = args.meta or checked.meta;
} // passthru;
/* Conditionally set a derivation attribute.
Because `mkDerivation` sets `__ignoreNulls = true`, a derivation
attribute set to `null` will not impact the derivation output hash.
Thus, this function passes through its `value` argument if the `cond`
is `true`, but returns `null` if not.
Type: optionalDrvAttr :: Bool -> a -> a | Null
Example:
(stdenv.mkDerivation {
name = "foo";
x = optionalDrvAttr true 1;
y = optionalDrvAttr false 1;
}).drvPath == (stdenv.mkDerivation {
name = "foo";
x = 1;
}).drvPath
=> true
*/
optionalDrvAttr =
# Condition
cond:
# Attribute value
value: if cond then value else null;
}

View File

@ -854,6 +854,11 @@ in mkLicense lset) ({
fullName = "Mozilla Public License 2.0";
};
mplus = {
spdxId = "mplus";
fullName = "M+ Font License";
};
mspl = {
spdxId = "MS-PL";
fullName = "Microsoft Public License";

View File

@ -2,7 +2,7 @@
{ lib }:
let
inherit (lib.strings) toInt;
inherit (lib.trivial) compare min id;
inherit (lib.trivial) compare min id warn;
inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) sort;
in
@ -848,8 +848,8 @@ rec {
crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
=> [ "13" "14" "23" "24" ]
*/
crossLists = builtins.trace
"lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
crossLists = warn
"lib.crossLists is deprecated, use lib.cartesianProductOfSets instead."
(f: foldl (fs: args: concatMap (f: map f args) fs) [f]);

View File

@ -1256,7 +1256,7 @@ let
(opt.highestPrio or defaultOverridePriority)
(f opt.value);
doRename = { from, to, visible, warn, use, withPriority ? true }:
doRename = { from, to, visible, warn, use, withPriority ? true, condition ? true }:
{ config, options, ... }:
let
fromOpt = getAttrFromPath from options;
@ -1272,7 +1272,7 @@ let
} // optionalAttrs (toType != null) {
type = toType;
});
config = mkMerge [
config = mkIf condition (mkMerge [
(optionalAttrs (options ? warnings) {
warnings = optional (warn && fromOpt.isDefined)
"The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'.";
@ -1280,7 +1280,7 @@ let
(if withPriority
then mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt
else mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt)
];
]);
};
/* Use this function to import a JSON file as NixOS configuration.

View File

@ -254,13 +254,31 @@ rec {
else if all isInt list && all (x: x == head list) list then head list
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
/*
Require a single definition.
WARNING: Does not perform nested checks, as this does not run the merge function!
*/
mergeOneOption = mergeUniqueOption { message = ""; };
mergeUniqueOption = { message }: loc: defs:
if length defs == 1
then (head defs).value
else assert length defs > 1;
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
/*
Require a single definition.
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
*/
mergeUniqueOption = args@{
message,
# WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be
# - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc)
# - if you want attribute values to be checked, or list items
# - if you want coercedTo-like behavior to work
merge ? loc: defs: (head defs).value }:
loc: defs:
if length defs == 1
then merge loc defs
else
assert length defs > 1;
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
/* "Merge" option definitions by checking that they all have the same value. */
mergeEqualOption = loc: defs:
@ -379,7 +397,7 @@ rec {
if ! isString text then throw "literalExpression expects a string."
else { _type = "literalExpression"; inherit text; };
literalExample = lib.warn "literalExample is deprecated, use literalExpression instead, or use literalMD for a non-Nix description." literalExpression;
literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression;
/* Transition marker for documentation that's already migrated to markdown
syntax. This is a no-op and no longer needed.

View File

@ -561,7 +561,7 @@ rec {
["&quot;" "&apos;" "&lt;" "&gt;" "&amp;"];
# warning added 12-12-2022
replaceChars = lib.warn "replaceChars is a deprecated alias of replaceStrings, replace usages of it with replaceStrings." builtins.replaceStrings;
replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
# Case conversion utilities.
lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
@ -1133,7 +1133,7 @@ rec {
"/prefix/nix-profiles-library-paths.patch"
"/prefix/compose-search-path.patch" ]
*/
readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead"
readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead."
(rootPath: file:
let
lines = lib.splitString "\n" (readFile file);

View File

@ -48,6 +48,7 @@ rec {
isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
isRx = { cpu = { family = "rx"; }; };
isSparc = { cpu = { family = "sparc"; }; };
isSparc64 = { cpu = { family = "sparc"; bits = 64; }; };
isWasm = { cpu = { family = "wasm"; }; };
isMsp430 = { cpu = { family = "msp430"; }; };
isVc4 = { cpu = { family = "vc4"; }; };

View File

@ -1902,7 +1902,7 @@ runTests {
expected = true;
};
# lazyDerivation
# DERIVATIONS
testLazyDerivationIsLazyInDerivationForAttrNames = {
expr = attrNames (lazyDerivation {
@ -1955,6 +1955,24 @@ runTests {
expected = derivation;
};
testOptionalDrvAttr = let
mkDerivation = args: derivation (args // {
builder = "builder";
system = "system";
__ignoreNulls = true;
});
in {
expr = (mkDerivation {
name = "foo";
x = optionalDrvAttr true 1;
y = optionalDrvAttr false 1;
}).drvPath;
expected = (mkDerivation {
name = "foo";
x = 1;
}).drvPath;
};
testTypeDescriptionInt = {
expr = (with types; int).description;
expected = "signed integer";

View File

@ -407,6 +407,16 @@ checkConfigOutput "{}" config.submodule.a ./emptyValues.nix
checkConfigError 'The option .int.a. is used but not defined' config.int.a ./emptyValues.nix
checkConfigError 'The option .nonEmptyList.a. is used but not defined' config.nonEmptyList.a ./emptyValues.nix
# types.unique
# requires a single definition
checkConfigError 'The option .examples\.merged. is defined multiple times while it.s expected to be unique' config.examples.merged.a ./types-unique.nix
# user message is printed
checkConfigError 'We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system.' config.examples.merged.a ./types-unique.nix
# let the inner merge function check the values (on demand)
checkConfigError 'A definition for option .examples\.badLazyType\.a. is not of type .string.' config.examples.badLazyType.a ./types-unique.nix
# overriding still works (unlike option uniqueness)
checkConfigOutput '^"bee"$' config.examples.override.b ./types-unique.nix
## types.raw
checkConfigOutput '^true$' config.unprocessedNestingEvaluates.success ./raw.nix
checkConfigOutput "10" config.processedToplevel ./raw.nix
@ -465,6 +475,9 @@ checkConfigOutput '^1234$' config.c.d.e ./doRename-basic.nix
checkConfigOutput '^"The option `a\.b. defined in `.*/doRename-warnings\.nix. has been renamed to `c\.d\.e.\."$' \
config.result \
./doRename-warnings.nix
checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-enable.nix
checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-no-enable.nix
checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-migrated.nix
# Anonymous modules get deduplicated by key
checkConfigOutput '^"pear"$' config.once.raw ./merge-module-with-key.nix

View File

@ -0,0 +1,10 @@
{ config, lib, ... }:
{
config = {
services.foo.enable = true;
services.foo.bar = "baz";
result =
assert config.services.foos == { "" = { bar = "baz"; }; };
true;
};
}

View File

@ -0,0 +1,10 @@
{ config, lib, ... }:
{
config = {
services.foos."".bar = "baz";
result =
assert config.services.foos == { "" = { bar = "baz"; }; };
assert config.services.foo.bar == "baz";
true;
};
}

View File

@ -0,0 +1,9 @@
{ config, lib, options, ... }:
{
config = {
result =
assert config.services.foos == { };
assert ! options.services.foo.bar.isDefined;
true;
};
}

View File

@ -0,0 +1,42 @@
/*
Simulate a migration from a single-instance `services.foo` to a multi instance
`services.foos.<name>` module, where `name = ""` serves as the legacy /
compatibility instance.
- No instances must exist, unless one is defined in the multi-instance module,
or if the legacy enable option is set to true.
- The legacy instance options must be renamed to the new instance, if it exists.
The relevant scenarios are tested in separate files:
- ./doRename-condition-enable.nix
- ./doRename-condition-no-enable.nix
*/
{ config, lib, ... }:
let
inherit (lib) mkOption mkEnableOption types doRename;
in
{
options = {
services.foo.enable = mkEnableOption "foo";
services.foos = mkOption {
type = types.attrsOf (types.submodule {
options = {
bar = mkOption { type = types.str; };
};
});
default = { };
};
result = mkOption {};
};
imports = [
(doRename {
from = [ "services" "foo" "bar" ];
to = [ "services" "foos" "" "bar" ];
visible = true;
warn = false;
use = x: x;
withPriority = true;
condition = config.services.foo.enable;
})
];
}

View File

@ -0,0 +1,27 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.examples = mkOption {
type = types.lazyAttrsOf
(types.unique
{ message = "We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system."; }
(types.attrsOf types.str));
};
imports = [
{ examples.merged = { b = "bee"; }; }
{ examples.override = lib.mkForce { b = "bee"; }; }
];
config.examples = {
merged = {
a = "aye";
};
override = {
a = "aye";
};
badLazyType = {
a = true;
};
};
}

View File

@ -230,7 +230,7 @@ in {
else if lib.pathExists revisionFile then lib.fileContents revisionFile
else default;
nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version;
nixpkgsVersion = warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version;
/* Determine whether the function is being called from inside a Nix
shell.

View File

@ -614,23 +614,12 @@ rec {
nestedTypes.elemType = elemType;
};
# Value of given type but with no merging (i.e. `uniq list`s are not concatenated).
uniq = elemType: mkOptionType rec {
name = "uniq";
inherit (elemType) description descriptionClass check;
merge = mergeOneOption;
emptyValue = elemType.emptyValue;
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: uniq (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
uniq = unique { message = ""; };
unique = { message }: type: mkOptionType rec {
name = "unique";
inherit (type) description descriptionClass check;
merge = mergeUniqueOption { inherit message; };
merge = mergeUniqueOption { inherit message; inherit (type) merge; };
emptyValue = type.emptyValue;
getSubOptions = type.getSubOptions;
getSubModules = type.getSubModules;

View File

@ -241,6 +241,12 @@
fingerprint = "DBF4 E6D0 90B8 BEA4 4BFE 1F1C 3442 4321 39B5 0691";
}];
};
_9R = {
email = "nix@9-r.net";
github = "9R";
githubId = 381298;
name = "9R";
};
a1russell = {
email = "adamlr6+pub@gmail.com";
github = "a1russell";
@ -361,6 +367,12 @@
githubId = 13504599;
name = "Adam Boseley";
};
abueide = {
email = "andrea@abueide.com";
github = "abueide";
githubId = 19354425;
name = "Andrea Bueide";
};
abuibrahim = {
email = "ruslan@babayev.com";
github = "abuibrahim";
@ -2329,6 +2341,12 @@
fingerprint = "D35E C9CE E631 638F F1D8 B401 6F0E 410D C3EE D02";
}];
};
benhiemer = {
name = "Benedikt Hiemer";
email = "ben.email@posteo.de";
github = "benhiemer";
githubId = 16649926;
};
benjaminedwardwebb = {
name = "Ben Webb";
email = "benjaminedwardwebb@gmail.com";
@ -3954,6 +3972,15 @@
githubId = 6821729;
github = "criyle";
};
croissong = {
email = "jan.moeller0@pm.me";
name = "Jan Möller";
github = "Croissong";
githubId = 4162215;
keys = [{
fingerprint = "CE97 9DEE 904C 26AA 3716 78C2 96A4 38F9 EE72 572F";
}];
};
crschnick = {
email = "crschnick@xpipe.io";
name = "Christopher Schnick";
@ -4371,6 +4398,12 @@
githubId = 49904992;
name = "Dawid Sowa";
};
daylinmorgan = {
email = "daylinmorgan@gmail.com";
github = "daylinmorgan";
githubId = 47667941;
name = "Daylin Morgan";
};
dbalan = {
email = "nix@dbalan.in";
github = "dbalan";
@ -4419,6 +4452,12 @@
githubId = 14032;
name = "Daniel Brockman";
};
DCsunset = {
email = "DCsunset@protonmail.com";
github = "DCsunset";
githubId = 23468812;
name = "DCsunset";
};
ddelabru = {
email = "ddelabru@redhat.com";
github = "ddelabru";
@ -4663,6 +4702,12 @@
githubId = 30475873;
name = "Andrei Hava";
};
devplayer0 = {
email = "dev@nul.ie";
github = "devplayer0";
githubId = 1427254;
name = "Jack O'Sullivan";
};
devusb = {
email = "mhelton@devusb.us";
github = "devusb";
@ -4761,6 +4806,11 @@
githubId = 32810399;
name = "Diffumist";
};
DimitarNestorov = {
name = "Dimitar Nestorov";
github = "DimitarNestorov";
githubId = 8790386;
};
diogotcorreia = {
name = "Diogo Correia";
email = "me@diogotc.com";
@ -4948,6 +4998,14 @@
fingerprint = "EE7D 158E F9E7 660E 0C33 86B2 8FC5 F7D9 0A5D 8F4D";
}];
};
donteatoreo = {
name = "DontEatOreo";
github = "DontEatOreo";
githubId = 57304299;
keys = [{
fingerprint = "33CD 5C0A 673C C54D 661E 5E4C 0DB5 361B EEE5 30AB";
}];
};
doriath = {
email = "tomasz.zurkowski@gmail.com";
github = "doriath";
@ -5893,6 +5951,12 @@
githubId = 2512008;
name = "Even Brenden";
};
evey = {
email = "nix@lubdub.nl";
github = "lub-dub";
githubId = 159288204;
name = "evey";
};
evilmav = {
email = "elenskiy.ilya@gmail.com";
github = "evilmav";
@ -6236,6 +6300,13 @@
fingerprint = "2F93 661D AC17 EA98 A104 F780 ECC7 55EE 583C 1672";
}];
};
flandweber = {
email = "finn@landweber.xyz";
github = "flandweber";
githubId = 110117466;
matrix = "@flandweber:envs.net";
name = "Finn Landweber";
};
fleaz = {
email = "mail@felixbreidenstein.de";
matrix = "@fleaz:rainbownerds.de";
@ -6684,6 +6755,12 @@
githubId = 293586;
name = "Adam Gamble";
};
gangaram = {
email = "Ganga.Ram@tii.ae";
github = "gangaram-tii";
githubId = 131853076;
name = "Ganga Ram";
};
garaiza-93 = {
email = "araizagustavo93@gmail.com";
github = "garaiza-93";
@ -6969,6 +7046,11 @@
githubId = 615606;
name = "Glenn Searby";
};
Gliczy = {
name = "Gliczy";
github = "Gliczy";
githubId = 129636582;
};
glittershark = {
name = "Griffin Smith";
email = "root@gws.fyi";
@ -9142,6 +9224,12 @@
fingerprint = "7249 70E6 A661 D84E 8B47 678A 0590 93B1 A278 BCD0";
}];
};
jokatzke = {
email = "jokatzke@fastmail.com";
github = "jokatzke";
githubId = 46931073;
name = "Jonas Katzke";
};
joko = {
email = "ioannis.koutras@gmail.com";
github = "jokogr";
@ -9230,6 +9318,12 @@
github = "josephst";
githubId = 1269177;
};
josephsurin = {
name = "Joseph Surin";
email = "nix@jsur.in";
github = "josephsurin";
githubId = 14977484;
};
joshniemela = {
name = "Joshua Niemelä";
email = "josh@jniemela.dk";
@ -9667,6 +9761,11 @@
matrix = "@katexochen:matrix.org";
name = "Paul Meyer";
};
katrinafyi = {
name = "katrinafyi";
github = "katrinafyi";
githubId = 39479354;
};
kayhide = {
email = "kayhide@gmail.com";
github = "kayhide";
@ -9772,6 +9871,9 @@
github = "kevincox";
githubId = 494012;
name = "Kevin Cox";
keys = [{
fingerprint = "B66B 891D D83B 0E67 7D84 FC30 9BB9 2CC1 552E 99AA";
}];
};
kevingriffin = {
email = "me@kevin.jp";
@ -10279,6 +10381,12 @@
githubId = 894884;
name = "Jakub Kozłowski";
};
kud = {
email = "kasa7qi@gmail.com";
github = "KUD-00";
githubId = 70764075;
name = "kud";
};
kupac = {
github = "Kupac";
githubId = 8224569;
@ -10679,6 +10787,15 @@
githubId = 1769386;
name = "Liam Diprose";
};
liassica = {
email = "git-commit.jingle869@aleeas.com";
github = "Liassica";
githubId = 115422798;
name = "Liassica";
keys = [{
fingerprint = "83BE 3033 6164 B971 FA82 7036 0D34 0E59 4980 7BDD";
}];
};
liberatys = {
email = "liberatys@hey.com";
name = "Nick Anthony Flueckiger";
@ -10717,6 +10834,12 @@
name = "Yanning Chen";
matrix = "@self:lightquantum.me";
};
Ligthiago = {
email = "donets.andre@gmail.com";
github = "Ligthiago";
githubId = 142721811;
name = "Andrey Donets";
};
lihop = {
email = "nixos@leroy.geek.nz";
github = "lihop";
@ -11275,6 +11398,12 @@
githubId = 7910815;
name = "Alex McGrath";
};
lychee = {
email = "itslychee+nixpkgs@protonmail.com";
githubId = 82718618;
github = "itslychee";
name = "Lychee";
};
lynty = {
email = "ltdong93+nix@gmail.com";
github = "Lynty";
@ -11622,12 +11751,6 @@
githubId = 1729331;
name = "Dominique Martinet";
};
martingms = {
email = "martin@mg.am";
github = "martingms";
githubId = 458783;
name = "Martin Gammelsæter";
};
martinjlowm = {
email = "martin@martinjlowm.dk";
github = "martinjlowm";
@ -12779,6 +12902,16 @@
githubId = 10601196;
name = "Jérémie Ferry";
};
motiejus = {
email = "motiejus@jakstys.lt";
github = "motiejus";
githubId = 107720;
keys = [{
fingerprint = "5F6B 7A8A 92A2 60A4 3704 9BEB 6F13 3A0C 1C28 48D7";
}];
matrix = "@motiejus:jakstys.lt";
name = "Motiejus Jakštys";
};
mounium = {
email = "muoniurn@gmail.com";
github = "Mounium";
@ -13923,6 +14056,12 @@
github = "numkem";
githubId = 332423;
};
nu-nu-ko = {
email = "host@nuko.city";
github = "nu-nu-ko";
githubId = 153512689;
name = "nuko";
};
nviets = {
email = "nathan.g.viets@gmail.com";
github = "nviets";
@ -14465,6 +14604,12 @@
githubId = 72527881;
name = "PassiveLemon";
};
patka = {
email = "patka@patka.dev";
github = "patka-123";
githubId = 69802930;
name = "patka";
};
patricksjackson = {
email = "patrick@jackson.dev";
github = "patricksjackson";
@ -14622,6 +14767,11 @@
github = "pennae";
githubId = 82953136;
};
peret = {
name = "Peter Retzlaff";
github = "peret";
githubId = 617977;
};
periklis = {
email = "theopompos@gmail.com";
github = "periklis";
@ -15707,6 +15857,12 @@
githubId = 1891350;
name = "Michael Raskin";
};
raspher = {
email = "raspher@protonmail.com";
github = "raspher";
githubId = 23345803;
name = "Szymon Scholz";
};
ratcornu = {
email = "ratcornu@skaven.org";
github = "RatCornu";
@ -15876,6 +16032,12 @@
githubId = 801525;
name = "rembo10";
};
remexre = {
email = "nathan+nixpkgs@remexre.com";
github = "remexre";
githubId = 4196789;
name = "Nathan Ringo";
};
renatoGarcia = {
email = "fgarcia.renato@gmail.com";
github = "renatoGarcia";
@ -15929,7 +16091,7 @@
};
RGBCube = {
name = "RGBCube";
email = "rgbsphere+nixpkgs@gmail.com";
email = "nixpkgs@rgbcu.be";
github = "RGBCube";
githubId = 78925721;
keys = [{
@ -17294,6 +17456,12 @@
github = "shymega";
githubId = 1334592;
};
siddharthdhakane = {
email = "siddharthdhakane@gmail.com";
github = "siddharthdhakane";
githubId = 28101092;
name = "Siddharth Dhakane";
};
siddharthist = {
email = "langston.barrett@gmail.com";
github = "langston-barrett";
@ -18086,6 +18254,12 @@
githubId = 38893265;
name = "StrikerLulu";
};
struan = {
email = "contact@struanrobertson.co.uk";
github = "struan-robertson";
githubId = 7543617;
name = "Struan Robertson";
};
stteague = {
email = "stteague505@yahoo.com";
github = "stteague";
@ -19106,6 +19280,7 @@
tomasajt = {
github = "TomaSajt";
githubId = 62384384;
matrix = "@tomasajt:matrix.org";
name = "TomaSajt";
keys = [{
fingerprint = "8CA9 8016 F44D B717 5B44 6032 F011 163C 0501 22A1";

View File

@ -100,9 +100,11 @@ moonscript,https://github.com/leafo/moonscript.git,dev-1,,,,arobyn
nlua,,,,,,teto
nui.nvim,,,,,,mrcjkb
nvim-cmp,https://github.com/hrsh7th/nvim-cmp,,,,,
nvim-nio,,,,,,mrcjkb
penlight,https://github.com/lunarmodules/Penlight.git,,,,,alerque
plenary.nvim,https://github.com/nvim-lua/plenary.nvim.git,,,,5.1,
rapidjson,https://github.com/xpol/lua-rapidjson.git,,,,,
rocks.nvim,,,,,5.1,teto mrcjkb
rest.nvim,,,,,5.1,teto
rustaceanvim,,,,,,mrcjkb
say,https://github.com/Olivine-Labs/say.git,,,,,
@ -119,3 +121,4 @@ toml,,,,,,mrcjkb
toml-edit,,,,,5.1,mrcjkb
vstruct,https://github.com/ToxicFrog/vstruct.git,,,,,
vusted,,,,,,figsoda
xml2lua,,,,,,teto

1 name src ref server version luaversion maintainers
100 nlua teto
101 nui.nvim mrcjkb
102 nvim-cmp https://github.com/hrsh7th/nvim-cmp
103 nvim-nio mrcjkb
104 penlight https://github.com/lunarmodules/Penlight.git alerque
105 plenary.nvim https://github.com/nvim-lua/plenary.nvim.git 5.1
106 rapidjson https://github.com/xpol/lua-rapidjson.git
107 rocks.nvim 5.1 teto mrcjkb
108 rest.nvim 5.1 teto
109 rustaceanvim mrcjkb
110 say https://github.com/Olivine-Labs/say.git
121 toml-edit 5.1 mrcjkb
122 vstruct https://github.com/ToxicFrog/vstruct.git
123 vusted figsoda
124 xml2lua teto

View File

@ -339,7 +339,6 @@ with lib.maintainers; {
geospatial = {
members = [
das-g
imincik
nh2
nialov

View File

@ -39,4 +39,5 @@ and non-critical by adding `options = [ "nofail" ];`.
```{=include=} sections
luks-file-systems.section.md
sshfs-file-systems.section.md
overlayfs.section.md
```

View File

@ -0,0 +1,27 @@
# Overlayfs {#sec-overlayfs}
NixOS offers a convenient abstraction to create both read-only as well writable
overlays.
```nix
fileSystems = {
"/writable-overlay" = {
overlay = {
lowerdir = [ writableOverlayLowerdir ];
upperdir = "/.rw-writable-overlay/upper";
workdir = "/.rw-writable-overlay/work";
};
# Mount the writable overlay in the initrd.
neededForBoot = true;
};
"/readonly-overlay".overlay.lowerdir = [
writableOverlayLowerdir
writableOverlayLowerdir2
];
};
```
If `upperdir` and `workdir` are not null, they will be created before the
overlay is mounted.
To mount an overlay as read-only, you need to provide at least two `lowerdir`s.

View File

@ -326,7 +326,7 @@ Composed types are types that take a type as parameter. `listOf
`types.uniq` *`t`*
: Ensures that type *`t`* cannot be merged. It is used to ensure option
definitions are declared only once.
definitions are provided only once.
`types.unique` `{ message = m }` *`t`*

View File

@ -75,9 +75,10 @@ image with a new one or by updating partitions via an A/B scheme. See the
[Chrome OS update process][chrome-os-update] for an example of how to achieve
this. The appliance image built in the following example does not contain a
`configuration.nix` and thus you will not be able to call `nixos-rebuild` from
this system.
this system. Furthermore, it uses a [Unified Kernel Image][unified-kernel-image].
[chrome-os-update]: https://chromium.googlesource.com/aosp/platform/system/update_engine/+/HEAD/README.md
[unified-kernel-image]: https://uapi-group.org/specifications/specs/unified_kernel_image/
```nix
let
@ -101,18 +102,8 @@ in
"/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
"${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
"/loader/entries/nixos.conf".source = pkgs.writeText "nixos.conf" ''
title NixOS
linux /EFI/nixos/kernel.efi
initrd /EFI/nixos/initrd.efi
options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
'';
"/EFI/nixos/kernel.efi".source =
"${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
"/EFI/nixos/initrd.efi".source =
"${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
"/EFI/Linux/${config.system.boot.loader.ukiFile}".source =
"${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
};
repartConfig = {
Type = "esp";

View File

@ -8,6 +8,10 @@ In addition to numerous new and upgraded packages, this release has the followin
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
- `cryptsetup` has been upgraded from 2.6.1 to 2.7.0. Cryptsetup is a critical component enabling LUKS-based (but not only) full disk encryption.
Take the time to review [the release notes](https://gitlab.com/cryptsetup/cryptsetup/-/raw/v2.7.0/docs/v2.7.0-ReleaseNotes).
One of the highlight is that it is now possible to use hardware OPAL-based encryption of your disk with `cryptsetup`, it has a lot of caveats, see the above notes for the full details.
- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.
- `linuxPackages_testing_bcachefs` is now fully deprecated by `linuxPackages_latest`, and is therefore no longer available.
@ -93,6 +97,10 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- `idris2` was updated to v0.7.0. This version introduces breaking changes. Check out the [changelog](https://github.com/idris-lang/Idris2/blob/v0.7.0/CHANGELOG.md#v070) for details.
- `neo4j` has been updated to 5, you may want to read the [release notes for Neo4j 5](https://neo4j.com/release-notes/database/neo4j-5/)
- `services.neo4j.allowUpgrade` was removed and no longer has any effect. Neo4j 5 supports automatic rolling upgrades.
- `nitter` requires a `guest_accounts.jsonl` to be provided as a path or loaded into the default location at `/var/lib/nitter/guest_accounts.jsonl`. See [Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) for details.
- `services.aria2.rpcSecret` has been replaced with `services.aria2.rpcSecretFile`.
@ -134,6 +142,9 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
'';
```
- The package `optparse-bash` is now dropped due to upstream inactivity. Alternatives available in Nixpkgs include [`argc`](https://github.com/sigoden/argc), [`argbash`](https://github.com/matejak/argbash), [`bashly`](https://github.com/DannyBen/bashly) and [`gum`](https://github.com/charmbracelet/gum), to name a few.
- The `kanata` package has been updated to v1.5.0, which includes [breaking changes](https://github.com/jtroo/kanata/releases/tag/v1.5.0).
- The `craftos-pc` package has been updated to v2.8, which includes [breaking changes](https://github.com/MCJack123/craftos2/releases/tag/v2.8).
@ -156,6 +167,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- `xxd` has been moved from `vim` default output to its own output to reduce closure size. The canonical way to reference it across all platforms is `unixtools.xxd`.
- The `stalwart-mail` package has been updated to v0.5.3, which includes [breaking changes](https://github.com/stalwartlabs/mail-server/blob/v0.5.3/UPGRADING.md).
- `services.avahi.nssmdns` got split into `services.avahi.nssmdns4` and `services.avahi.nssmdns6` which enable the mDNS NSS switch for IPv4 and IPv6 respectively.
Since most mDNS responders only register IPv4 addresses, most users want to keep the IPv6 support disabled to avoid long timeouts.
@ -239,6 +252,9 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- `services.postgresql.extraPlugins` changed its type from just a list of packages to also a function that returns such a list.
For example a config line like ``services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ postgis ];`` is recommended to be changed to ``services.postgresql.extraPlugins = ps: with ps; [ postgis ];``;
- The Matrix homeserver [Synapse](https://element-hq.github.io/synapse/) module now supports configuring UNIX domain socket [listeners](#opt-services.matrix-synapse.settings.listeners) through the `path` option.
The default replication worker on the main instance has been migrated away from TCP sockets to UNIX domain sockets.
- Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles.
The `nimPackages` and `nim2Packages` sets have been removed.
See https://nixos.org/manual/nixpkgs/unstable#nim for more information.
@ -248,6 +264,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
After upgrading, follow the instructions on the [upstream release notes](https://github.com/majewsky/portunus/releases/tag/v2.0.0) to upgrade all user accounts to strong password hashes.
Support for weak password hashes will be removed in NixOS 24.11.
- A stdenv's default set of hardening flags can now be set via its `bintools-wrapper`'s `defaultHardeningFlags` argument. A convenient stdenv adapter, `withDefaultHardeningFlags`, can be used to override an existing stdenv's `defaultHardeningFlags`.
- `libass` now uses the native CoreText backend on Darwin, which may fix subtitle rendering issues with `mpv`, `ffmpeg`, etc.
- [Lilypond](https://lilypond.org/index.html) and [Denemo](https://www.denemo.org) are now compiled with Guile 3.0.
@ -265,11 +283,24 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- The option [`services.nextcloud.config.dbport`] of the Nextcloud module was removed to match upstream.
The port can be specified in [`services.nextcloud.config.dbhost`](#opt-services.nextcloud.config.dbhost).
- A new abstraction to create both read-only as well as writable overlay file
systems was added. Available via
[fileSystems.overlay](#opt-fileSystems._name_.overlay.lowerdir). See also the
[NixOS docs](#sec-overlayfs).
- systemd units can now specify the `Upholds=` and `UpheldBy=` unit dependencies via the aptly
named `upholds` and `upheldBy` options. These options get systemd to enforce that the
dependencies remain continuosly running for as long as the dependent unit is in a running state.
- `stdenv`: The `--replace` flag in `substitute`, `substituteInPlace`, `substituteAll`, `substituteAllStream`, and `substituteStream` is now deprecated if favor of the new `--replace-fail`, `--replace-warn` and `--replace-quiet`. The deprecated `--replace` equates to `--replace-warn`.
- A new hardening flag, `zerocallusedregs` was made available, corresponding to the gcc/clang option `-fzero-call-used-regs=used-gpr`.
- New options were added to the dnsdist module to enable and configure a DNSCrypt endpoint (see `services.dnsdist.dnscrypt.enable`, etc.).
The module can generate the DNSCrypt provider key pair, certificates and also performs their rotation automatically with no downtime.
- With a bump to `sonarr` v4, existing config database files will be upgraded automatically, but note that some old apparently-working configs [might actually be corrupt and fail to upgrade cleanly](https://forums.sonarr.tv/t/sonarr-v4-released/33089).
- The Yama LSM is now enabled by default in the kernel, which prevents ptracing
non-child processes. This means you will not be able to attach gdb to an
existing process, but will need to start that process from gdb (so it is a
@ -281,6 +312,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
`globalRedirect` can now have redirect codes other than 301 through
`redirectCode`.
- `libjxl` 0.9.0 [dropped support for the butteraugli API](https://github.com/libjxl/libjxl/pull/2576). You will no longer be able to set `enableButteraugli` on `libaom`.
- The source of the `mockgen` package has changed to the [go.uber.org/mock](https://github.com/uber-go/mock) fork because [the original repository is no longer maintained](https://github.com/golang/mock#gomock).
- `security.pam.enableSSHAgentAuth` was renamed to `security.pam.sshAgentAuth.enable` and an `authorizedKeysFiles`
@ -289,6 +322,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- [](#opt-boot.kernel.sysctl._net.core.wmem_max_) changed from a string to an integer because of the addition of a custom merge option (taking the highest value defined to avoid conflicts between 2 services trying to set that value), just as [](#opt-boot.kernel.sysctl._net.core.rmem_max_) since 22.11.
- A new top-level package set, `pkgsExtraHardening` is added. This is a set of packages built with stricter hardening flags - those that have not yet received enough testing to be applied universally, those that are more likely to cause build failures or those that have drawbacks to their use (e.g. performance or required hardware features).
- `services.zfs.zed.enableMail` now uses the global `sendmail` wrapper defined by an email module
(such as msmtp or Postfix). It no longer requires using a special ZFS build with email support.
@ -304,6 +339,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- The `hardware.pulseaudio` module now sets permission of pulse user home directory to 755 when running in "systemWide" mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399).
- The module `services.github-runner` has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration.
- The `btrbk` module now automatically selects and provides required compression
program depending on the configured `stream_compress` option. Since this
replaces the need for the `extraPackages` option, this option will be
@ -317,3 +354,5 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
The previous native backends remain available but are now minimally maintained. Refer to [upstream documentation](https://doc.qt.io/qt-6/qtmultimedia-index.html#ffmpeg-as-the-default-backend) for further details about each platform.
- The `drbd` out-of-tree Linux kernel driver has been added in version `9.2.7`. With it the DRBD 9.x features can be used instead of the 8.x features provided by the `8.4.11` in-tree driver.
- The oil shell is now using the c++ version by default. The python based build is still available as `oil-python`

View File

@ -242,7 +242,7 @@ in rec {
ln -sfn '${name}' $out/'${name2}'
'') (unit.aliases or [])) units)}
# Create .wants and .requires symlinks from the wantedBy and
# Create .wants, .upholds and .requires symlinks from the wantedBy, upheldBy and
# requiredBy options.
${concatStrings (mapAttrsToList (name: unit:
concatMapStrings (name2: ''
@ -250,6 +250,12 @@ in rec {
ln -sfn '../${name}' $out/'${name2}.wants'/
'') (unit.wantedBy or [])) units)}
${concatStrings (mapAttrsToList (name: unit:
concatMapStrings (name2: ''
mkdir -p $out/'${name2}.upholds'
ln -sfn '../${name}' $out/'${name2}.upholds'/
'') (unit.upheldBy or [])) units)}
${concatStrings (mapAttrsToList (name: unit:
concatMapStrings (name2: ''
mkdir -p $out/'${name2}.requires'
@ -289,6 +295,8 @@ in rec {
{ Requires = toString config.requires; }
// optionalAttrs (config.wants != [])
{ Wants = toString config.wants; }
// optionalAttrs (config.upholds != [])
{ Upholds = toString config.upholds; }
// optionalAttrs (config.after != [])
{ After = toString config.after; }
// optionalAttrs (config.before != [])

View File

@ -74,6 +74,15 @@ in rec {
'';
};
upheldBy = mkOption {
default = [];
type = types.listOf unitNameType;
description = lib.mdDoc ''
Keep this unit running as long as the listed units are running. This is a continuously
enforced version of wantedBy.
'';
};
wantedBy = mkOption {
default = [];
type = types.listOf unitNameType;
@ -147,6 +156,14 @@ in rec {
'';
};
upholds = mkOption {
default = [];
type = types.listOf unitNameType;
description = lib.mdDoc ''
Keeps the specified running while this unit is running. A continuous version of `wants`.
'';
};
after = mkOption {
default = [];
type = types.listOf unitNameType;

View File

@ -0,0 +1,7 @@
# Amazon images
* The `create-amis.sh` script will be replaced by https://github.com/NixOS/amis which will regularly upload AMIs per NixOS channel bump.
* @arianvp is planning to drop zfs support
* @arianvp is planning to rewrite the image builder to use the repart-based image builder.

View File

@ -157,4 +157,6 @@ in {
'';
};
in if config.ec2.zfs.enable then zfsBuilder else extBuilder;
meta.maintainers = with maintainers; [ arianvp ];
}

View File

@ -13,8 +13,23 @@
./lxd.nix
];
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
networking = {
dhcpcd.enable = false;
useDHCP = false;
useHostResolvConf = false;
};
systemd.network = {
enable = true;
networks."50-eth0" = {
matchConfig.Name = "eth0";
networkConfig = {
DHCP = "ipv4";
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
};
};
system.stateVersion = "@stateVersion@"; # Did you read the comment?
}

View File

@ -25,7 +25,21 @@
fi
'';
# Network
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
networking = {
dhcpcd.enable = false;
useDHCP = false;
useHostResolvConf = false;
};
systemd.network = {
enable = true;
networks."50-eth0" = {
matchConfig.Name = "eth0";
networkConfig = {
DHCP = "ipv4";
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
};
};
}

View File

@ -13,8 +13,23 @@
./lxd.nix
];
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
networking = {
dhcpcd.enable = false;
useDHCP = false;
useHostResolvConf = false;
};
systemd.network = {
enable = true;
networks."50-enp5s0" = {
matchConfig.Name = "enp5s0";
networkConfig = {
DHCP = "ipv4";
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
};
};
system.stateVersion = "@stateVersion@"; # Did you read the comment?
}

View File

@ -26,6 +26,21 @@
'';
# Network
networking.useDHCP = false;
networking.interfaces.enp5s0.useDHCP = true;
networking = {
dhcpcd.enable = false;
useDHCP = false;
useHostResolvConf = false;
};
systemd.network = {
enable = true;
networks."50-enp5s0" = {
matchConfig.Name = "enp5s0";
networkConfig = {
DHCP = "ipv4";
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
};
};
}

View File

@ -30,6 +30,7 @@ with lib;
beam = super.beam_nox;
cairo = super.cairo.override { x11Support = false; };
dbus = super.dbus.override { x11Support = false; };
fastfetch = super.fastfetch.override { vulkanSupport = false; waylandSupport = false; x11Support = false; };
ffmpeg_4 = super.ffmpeg_4.override { ffmpegVariant = "headless"; };
ffmpeg_5 = super.ffmpeg_5.override { ffmpegVariant = "headless"; };
# dep of graphviz, libXpm is optional for Xpm support

View File

@ -313,7 +313,7 @@ in
kanboard = 281;
# pykms = 282; # DynamicUser = true
kodi = 283;
restya-board = 284;
# restya-board = 284; # removed 2024-01-22
mighttpd2 = 285;
hass = 286;
#monero = 287; # dynamically allocated as of 2021-05-08
@ -623,7 +623,7 @@ in
kanboard = 281;
# pykms = 282; # DynamicUser = true
kodi = 283;
restya-board = 284;
# restya-board = 284; # removed 2024-01-22
mighttpd2 = 285;
hass = 286;
# monero = 287; # dynamically allocated as of 2021-05-08

View File

@ -5,34 +5,39 @@ let
opt = options.system.nixos;
inherit (lib)
concatStringsSep mapAttrsToList toLower
concatStringsSep mapAttrsToList toLower optionalString
literalExpression mkRenamedOptionModule mkDefault mkOption trivial types;
needsEscaping = s: null != builtins.match "[a-zA-Z0-9]+" s;
escapeIfNecessary = s: if needsEscaping s then s else ''"${lib.escape [ "\$" "\"" "\\" "\`" ] s}"'';
attrsToText = attrs:
concatStringsSep "\n" (
mapAttrsToList (n: v: ''${n}=${escapeIfNecessary (toString v)}'') attrs
) + "\n";
concatStringsSep "\n"
(mapAttrsToList (n: v: ''${n}=${escapeIfNecessary (toString v)}'') attrs)
+ "\n";
osReleaseContents = {
NAME = "${cfg.distroName}";
ID = "${cfg.distroId}";
VERSION = "${cfg.release} (${cfg.codeName})";
VERSION_CODENAME = toLower cfg.codeName;
VERSION_ID = cfg.release;
BUILD_ID = cfg.version;
PRETTY_NAME = "${cfg.distroName} ${cfg.release} (${cfg.codeName})";
LOGO = "nix-snowflake";
HOME_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/";
DOCUMENTATION_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/learn.html";
SUPPORT_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/community.html";
BUG_REPORT_URL = lib.optionalString (cfg.distroId == "nixos") "https://github.com/NixOS/nixpkgs/issues";
IMAGE_ID = lib.optionalString (config.system.image.id != null) config.system.image.id;
IMAGE_VERSION = lib.optionalString (config.system.image.version != null) config.system.image.version;
} // lib.optionalAttrs (cfg.variant_id != null) {
VARIANT_ID = cfg.variant_id;
};
osReleaseContents =
let
isNixos = cfg.distroId == "nixos";
in
{
NAME = "${cfg.distroName}";
ID = "${cfg.distroId}";
VERSION = "${cfg.release} (${cfg.codeName})";
VERSION_CODENAME = toLower cfg.codeName;
VERSION_ID = cfg.release;
BUILD_ID = cfg.version;
PRETTY_NAME = "${cfg.distroName} ${cfg.release} (${cfg.codeName})";
LOGO = "nix-snowflake";
HOME_URL = optionalString isNixos "https://nixos.org/";
DOCUMENTATION_URL = optionalString isNixos "https://nixos.org/learn.html";
SUPPORT_URL = optionalString isNixos "https://nixos.org/community.html";
BUG_REPORT_URL = optionalString isNixos "https://github.com/NixOS/nixpkgs/issues";
ANSI_COLOR = optionalString isNixos "1;34";
IMAGE_ID = optionalString (config.system.image.id != null) config.system.image.id;
IMAGE_VERSION = optionalString (config.system.image.version != null) config.system.image.version;
} // lib.optionalAttrs (cfg.variant_id != null) {
VARIANT_ID = cfg.variant_id;
};
initrdReleaseContents = (removeAttrs osReleaseContents [ "BUILD_ID" ]) // {
PRETTY_NAME = "${osReleaseContents.PRETTY_NAME} (Initrd)";
@ -56,60 +61,61 @@ in
};
options.system = {
nixos = {
version = mkOption {
internal = true;
type = types.str;
description = lib.mdDoc "The full NixOS version (e.g. `16.03.1160.f2d4ee1`).";
};
nixos.version = mkOption {
internal = true;
type = types.str;
description = lib.mdDoc "The full NixOS version (e.g. `16.03.1160.f2d4ee1`).";
};
release = mkOption {
readOnly = true;
type = types.str;
default = trivial.release;
description = lib.mdDoc "The NixOS release (e.g. `16.03`).";
};
nixos.release = mkOption {
readOnly = true;
type = types.str;
default = trivial.release;
description = lib.mdDoc "The NixOS release (e.g. `16.03`).";
};
versionSuffix = mkOption {
internal = true;
type = types.str;
default = trivial.versionSuffix;
description = lib.mdDoc "The NixOS version suffix (e.g. `1160.f2d4ee1`).";
};
nixos.versionSuffix = mkOption {
internal = true;
type = types.str;
default = trivial.versionSuffix;
description = lib.mdDoc "The NixOS version suffix (e.g. `1160.f2d4ee1`).";
};
revision = mkOption {
internal = true;
type = types.nullOr types.str;
default = trivial.revisionWithDefault null;
description = lib.mdDoc "The Git revision from which this NixOS configuration was built.";
};
nixos.revision = mkOption {
internal = true;
type = types.nullOr types.str;
default = trivial.revisionWithDefault null;
description = lib.mdDoc "The Git revision from which this NixOS configuration was built.";
};
codeName = mkOption {
readOnly = true;
type = types.str;
default = trivial.codeName;
description = lib.mdDoc "The NixOS release code name (e.g. `Emu`).";
};
nixos.codeName = mkOption {
readOnly = true;
type = types.str;
default = trivial.codeName;
description = lib.mdDoc "The NixOS release code name (e.g. `Emu`).";
};
distroId = mkOption {
internal = true;
type = types.str;
default = "nixos";
description = lib.mdDoc "The id of the operating system";
};
nixos.distroId = mkOption {
internal = true;
type = types.str;
default = "nixos";
description = lib.mdDoc "The id of the operating system";
};
distroName = mkOption {
internal = true;
type = types.str;
default = "NixOS";
description = lib.mdDoc "The name of the operating system";
};
nixos.distroName = mkOption {
internal = true;
type = types.str;
default = "NixOS";
description = lib.mdDoc "The name of the operating system";
};
nixos.variant_id = mkOption {
type = types.nullOr (types.strMatching "^[a-z0-9._-]+$");
default = null;
description = lib.mdDoc "A lower-case string identifying a specific variant or edition of the operating system";
example = "installer";
variant_id = mkOption {
type = types.nullOr (types.strMatching "^[a-z0-9._-]+$");
default = null;
description = lib.mdDoc "A lower-case string identifying a specific variant or edition of the operating system";
example = "installer";
};
};
image = {

View File

@ -219,6 +219,7 @@
./programs/msmtp.nix
./programs/mtr.nix
./programs/nano.nix
./programs/nautilus-open-any-terminal.nix
./programs/nbd.nix
./programs/neovim.nix
./programs/nethoscope.nix
@ -316,7 +317,6 @@
./security/oath.nix
./security/pam.nix
./security/pam_mount.nix
./security/pam_usb.nix
./security/please.nix
./security/polkit.nix
./security/rngd.nix
@ -410,7 +410,6 @@
./services/continuous-integration/buildbot/worker.nix
./services/continuous-integration/buildkite-agents.nix
./services/continuous-integration/gitea-actions-runner.nix
./services/continuous-integration/github-runner.nix
./services/continuous-integration/github-runners.nix
./services/continuous-integration/gitlab-runner.nix
./services/continuous-integration/gocd-agent/default.nix
@ -429,6 +428,7 @@
./services/databases/couchdb.nix
./services/databases/dgraph.nix
./services/databases/dragonflydb.nix
./services/databases/etcd.nix
./services/databases/ferretdb.nix
./services/databases/firebird.nix
./services/databases/foundationdb.nix
@ -577,6 +577,7 @@
./services/home-automation/ebusd.nix
./services/home-automation/esphome.nix
./services/home-automation/evcc.nix
./services/home-automation/govee2mqtt.nix
./services/home-automation/home-assistant.nix
./services/home-automation/homeassistant-satellite.nix
./services/home-automation/zigbee2mqtt.nix
@ -679,7 +680,6 @@
./services/misc/dwm-status.nix
./services/misc/dysnomia.nix
./services/misc/errbot.nix
./services/misc/etcd.nix
./services/misc/etebase-server.nix
./services/misc/etesync-dav.nix
./services/misc/evdevremapkeys.nix
@ -1202,6 +1202,7 @@
./services/security/hologram-agent.nix
./services/security/hologram-server.nix
./services/security/infnoise.nix
./services/security/intune.nix
./services/security/jitterentropy-rngd.nix
./services/security/kanidm.nix
./services/security/munge.nix
@ -1346,7 +1347,6 @@
./services/web-apps/powerdns-admin.nix
./services/web-apps/pretalx.nix
./services/web-apps/prosody-filer.nix
./services/web-apps/restya-board.nix
./services/web-apps/rimgo.nix
./services/web-apps/sftpgo.nix
./services/web-apps/suwayomi-server.nix
@ -1525,6 +1525,7 @@
./tasks/filesystems/jfs.nix
./tasks/filesystems/nfs.nix
./tasks/filesystems/ntfs.nix
./tasks/filesystems/overlayfs.nix
./tasks/filesystems/reiserfs.nix
./tasks/filesystems/sshfs.nix
./tasks/filesystems/squashfs.nix

View File

@ -1,4 +1,4 @@
{ config, lib, ... }:
{ config, lib, pkgs, ... }:
with lib;
@ -21,8 +21,12 @@ in
programs.chromium = {
enable = mkEnableOption (lib.mdDoc "{command}`chromium` policies");
enablePlasmaBrowserIntegration = mkEnableOption (lib.mdDoc "Native Messaging Host for Plasma Browser Integration");
plasmaBrowserIntegrationPackage = mkPackageOption pkgs [ "plasma5Packages" "plasma-browser-integration" ] { };
extensions = mkOption {
type = types.listOf types.str;
type = with types; nullOr (listOf str);
description = lib.mdDoc ''
List of chromium extensions to install.
For list of plugins ids see id in url of extensions on
@ -33,7 +37,7 @@ in
[ExtensionInstallForcelist](https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExtensionInstallForcelist)
for additional details.
'';
default = [];
default = null;
example = literalExpression ''
[
"chlffgpmiacpedhhbkiomidkjlcfhogd" # pushbullet
@ -62,16 +66,14 @@ in
type = types.nullOr types.str;
description = lib.mdDoc "Chromium default search provider url.";
default = null;
example =
"https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
example = "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
};
defaultSearchProviderSuggestURL = mkOption {
type = types.nullOr types.str;
description = lib.mdDoc "Chromium default search provider url for suggestions.";
default = null;
example =
"https://encrypted.google.com/complete/search?output=chrome&q={searchTerms}";
example = "https://encrypted.google.com/complete/search?output=chrome&q={searchTerms}";
};
extraOpts = mkOption {
@ -90,9 +92,9 @@ in
"PasswordManagerEnabled" = false;
"SpellcheckEnabled" = true;
"SpellcheckLanguage" = [
"de"
"en-US"
];
"de"
"en-US"
];
}
'';
};
@ -101,15 +103,21 @@ in
###### implementation
config = lib.mkIf cfg.enable {
# for chromium
environment.etc."chromium/policies/managed/default.json".text = builtins.toJSON defaultProfile;
environment.etc."chromium/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
# for google-chrome https://www.chromium.org/administrators/linux-quick-start
environment.etc."opt/chrome/policies/managed/default.json".text = builtins.toJSON defaultProfile;
environment.etc."opt/chrome/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
# for brave
environment.etc."brave/policies/managed/default.json".text = builtins.toJSON defaultProfile;
environment.etc."brave/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
config = {
environment.etc = lib.mkIf cfg.enable {
# for chromium
"chromium/native-messaging-hosts/org.kde.plasma.browser_integration.json" = lib.mkIf cfg.enablePlasmaBrowserIntegration
{ source = "${cfg.plasmaBrowserIntegrationPackage}/etc/chromium/native-messaging-hosts/org.kde.plasma.browser_integration.json"; };
"chromium/policies/managed/default.json" = lib.mkIf (defaultProfile != {}) { text = builtins.toJSON defaultProfile; };
"chromium/policies/managed/extra.json" = lib.mkIf (cfg.extraOpts != {}) { text = builtins.toJSON cfg.extraOpts; };
# for google-chrome https://www.chromium.org/administrators/linux-quick-start
"opt/chrome/native-messaging-hosts/org.kde.plasma.browser_integration.json" = lib.mkIf cfg.enablePlasmaBrowserIntegration
{ source = "${cfg.plasmaBrowserIntegrationPackage}/etc/opt/chrome/native-messaging-hosts/org.kde.plasma.browser_integration.json"; };
"opt/chrome/policies/managed/default.json" = lib.mkIf (defaultProfile != {}) { text = builtins.toJSON defaultProfile; };
"opt/chrome/policies/managed/extra.json" = lib.mkIf (cfg.extraOpts != {}) { text = builtins.toJSON cfg.extraOpts; };
# for brave
"brave/policies/managed/default.json" = lib.mkIf (defaultProfile != {}) { text = builtins.toJSON defaultProfile; };
"brave/policies/managed/extra.json" = lib.mkIf (cfg.extraOpts != {}) { text = builtins.toJSON cfg.extraOpts; };
};
};
}

View File

@ -22,7 +22,7 @@ let
serverOptions = { name, config, ... }: {
freeformType = attrsOf (either scalarType (listOf scalarType));
# Client system-options file directives are explained here:
# https://www.ibm.com/docs/en/storage-protect/8.1.20?topic=commands-processing-options
# https://www.ibm.com/docs/en/storage-protect/8.1.21?topic=commands-processing-options
options.servername = mkOption {
type = servernameType;
default = name;

View File

@ -112,6 +112,7 @@ in
(mkRemovedOptionModule [ "services" "cryptpad" ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "services" "rtsp-simple-server" ] "Package has been completely rebranded by upstream as mediamtx, and thus the service and the package were renamed in NixOS as well.")
(mkRemovedOptionModule [ "services" "prayer" ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "services" "restya-board" ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "i18n" "inputMethod" "fcitx" ] "The fcitx module has been removed. Please use fcitx5 instead")
(mkRemovedOptionModule [ "services" "dhcpd4" ] ''

View File

@ -545,12 +545,14 @@ let
};
server = mkOption {
type = types.nullOr types.str;
inherit (defaultAndText "server" null) default defaultText;
type = types.str;
inherit (defaultAndText "server" "https://acme-v02.api.letsencrypt.org/directory") default defaultText;
example = "https://acme-staging-v02.api.letsencrypt.org/directory";
description = lib.mdDoc ''
ACME Directory Resource URI. Defaults to Let's Encrypt's
production endpoint,
<https://acme-v02.api.letsencrypt.org/directory>, if unset.
ACME Directory Resource URI.
Defaults to Let's Encrypt's production endpoint.
For testing Let's Encrypt's [staging endpoint](https://letsencrypt.org/docs/staging-environment/)
should be used to avoid the rather tight rate limit on the production endpoint.
'';
};

View File

@ -205,17 +205,6 @@ let
};
};
usbAuth = mkOption {
default = config.security.pam.usb.enable;
defaultText = literalExpression "config.security.pam.usb.enable";
type = types.bool;
description = lib.mdDoc ''
If set, users listed in
{file}`/etc/pamusb.conf` are able to log in
with the associated USB key.
'';
};
otpwAuth = mkOption {
default = config.security.pam.enableOTPW;
defaultText = literalExpression "config.security.pam.enableOTPW";
@ -665,7 +654,6 @@ let
authfile = u2f.authFile;
appid = u2f.appId;
}; })
{ name = "usb"; enable = cfg.usbAuth; control = "sufficient"; modulePath = "${pkgs.pam_usb}/lib/security/pam_usb.so"; }
(let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; control = ussh.control; modulePath = "${pkgs.pam_ussh}/lib/security/pam_ussh.so"; settings = {
ca_file = ussh.caFile;
authorized_principals = ussh.authorizedPrincipals;
@ -700,6 +688,7 @@ let
|| cfg.pamMount
|| cfg.enableKwallet
|| cfg.enableGnomeKeyring
|| config.services.intune.enable
|| cfg.googleAuthenticator.enable
|| cfg.gnupg.enable
|| cfg.failDelay.enable
@ -726,6 +715,7 @@ let
kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5";
}; }
{ name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; }
{ name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
{ name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
store-only = cfg.gnupg.storeOnly;
}; }
@ -867,6 +857,7 @@ let
{ name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
no-autostart = cfg.gnupg.noAutostart;
}; }
{ name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
];
};
};

View File

@ -1,51 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.security.pam.usb;
anyUsbAuth = any (attrByPath ["usbAuth"] false) (attrValues config.security.pam.services);
in
{
options = {
security.pam.usb = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable USB login for all login systems that support it. For
more information, visit <https://github.com/aluzzardi/pam_usb/wiki/Getting-Started#setting-up-devices-and-users>.
'';
};
};
};
config = mkIf (cfg.enable || anyUsbAuth) {
# Make sure pmount and pumount are setuid wrapped.
security.wrappers = {
pmount =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pmount.out}/bin/pmount";
};
pumount =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pmount.out}/bin/pumount";
};
};
environment.systemPackages = [ pkgs.pmount ];
};
}

View File

@ -14,6 +14,15 @@ let
in
{
imports = [
(mkRemovedOptionModule [ "services" "rabbitmq" "cookie" ] ''
This option wrote the Erlang cookie to the store, while it should be kept secret.
Please remove it from your NixOS configuration and deploy a cookie securely instead.
The renamed `unsafeCookie` must ONLY be used in isolated non-production environments such as NixOS VM tests.
'')
];
###### interface
options = {
services.rabbitmq = {
@ -62,13 +71,18 @@ in
'';
};
cookie = mkOption {
unsafeCookie = mkOption {
default = "";
type = types.str;
description = lib.mdDoc ''
Erlang cookie is a string of arbitrary length which must
be the same for several nodes to be allowed to communicate.
Leave empty to generate automatically.
Setting the cookie via this option exposes the cookie to the store, which
is not recommended for security reasons.
Only use this option in an isolated non-production environment such as
NixOS VM tests.
'';
};
@ -209,9 +223,8 @@ in
};
preStart = ''
${optionalString (cfg.cookie != "") ''
echo -n ${cfg.cookie} > ${cfg.dataDir}/.erlang.cookie
chmod 600 ${cfg.dataDir}/.erlang.cookie
${optionalString (cfg.unsafeCookie != "") ''
install -m 600 <(echo -n ${cfg.unsafeCookie}) ${cfg.dataDir}/.erlang.cookie
''}
'';
};

View File

@ -90,7 +90,7 @@ in
environment.HOME = "/var/lib/tsm-backup";
serviceConfig = {
# for exit status description see
# https://www.ibm.com/docs/en/storage-protect/8.1.20?topic=clients-client-return-codes
# https://www.ibm.com/docs/en/storage-protect/8.1.21?topic=clients-client-return-codes
SuccessExitStatus = "4 8";
# The `-se` option must come after the command.
# The `-optfile` option suppresses a `dsm.opt`-not-found warning.

View File

@ -174,9 +174,8 @@ in
'')
(optionalString cfg.genCfsslAPIToken ''
if [ ! -f "${cfsslAPITokenPath}" ]; then
head -c ${toString (cfsslAPITokenLength / 2)} /dev/urandom | od -An -t x | tr -d ' ' >"${cfsslAPITokenPath}"
install -o cfssl -m 400 <(head -c ${toString (cfsslAPITokenLength / 2)} /dev/urandom | od -An -t x | tr -d ' ') "${cfsslAPITokenPath}"
fi
chown cfssl "${cfsslAPITokenPath}" && chmod 400 "${cfsslAPITokenPath}"
'')]);
systemd.services.kube-certmgr-bootstrap = {
@ -194,7 +193,7 @@ in
if [ -f "${cfsslAPITokenPath}" ]; then
ln -fs "${cfsslAPITokenPath}" "${certmgrAPITokenPath}"
else
touch "${certmgrAPITokenPath}" && chmod 600 "${certmgrAPITokenPath}"
install -m 600 /dev/null "${certmgrAPITokenPath}"
fi
''
(optionalString (cfg.pkiTrustOnBootstrap) ''
@ -220,7 +219,6 @@ in
inherit (cert) action;
authority = {
inherit remote;
file.path = cert.caCert;
root_ca = cert.caCert;
profile = "default";
auth_key_file = certmgrAPITokenPath;
@ -297,8 +295,7 @@ in
exit 1
fi
echo $token > ${certmgrAPITokenPath}
chmod 600 ${certmgrAPITokenPath}
install -m 0600 <(echo $token) ${certmgrAPITokenPath}
echo "Restarting certmgr..." >&1
systemctl restart certmgr

View File

@ -1,25 +0,0 @@
{ config
, pkgs
, lib
, ...
}@args:
with lib;
let
cfg = config.services.github-runner;
in
{
options.services.github-runner = import ./github-runner/options.nix (args // {
# Users don't need to specify options.services.github-runner.name; it will default
# to the hostname.
includeNameDefault = true;
});
config = mkIf cfg.enable {
services.github-runners.${cfg.name} = cfg;
};
meta.maintainers = with maintainers; [ veehaitch newam thomasjm ];
}

View File

@ -1,213 +1,266 @@
{ config
, lib
{ lib
, pkgs
, includeNameDefault
, ...
}:
with lib;
{
enable = mkOption {
default = false;
example = true;
description = lib.mdDoc ''
Whether to enable GitHub Actions runner.
Note: GitHub recommends using self-hosted runners with private repositories only. Learn more here:
[About self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners).
'';
type = lib.types.bool;
};
url = mkOption {
type = types.str;
description = lib.mdDoc ''
Repository to add the runner to.
Changing this option triggers a new runner registration.
IMPORTANT: If your token is org-wide (not per repository), you need to
provide a github org link, not a single repository, so do it like this
`https://github.com/nixos`, not like this
`https://github.com/nixos/nixpkgs`.
Otherwise, you are going to get a `404 NotFound`
from `POST https://api.github.com/actions/runner-registration`
in the configure script.
'';
example = "https://github.com/nixos/nixpkgs";
};
tokenFile = mkOption {
type = types.path;
description = lib.mdDoc ''
The full path to a file which contains either
* a fine-grained personal access token (PAT),
* a classic PAT
* or a runner registration token
Changing this option or the `tokenFile`s content triggers a new runner registration.
We suggest using the fine-grained PATs. A runner registration token is valid
only for 1 hour after creation, so the next time the runner configuration changes
this will give you hard-to-debug HTTP 404 errors in the configure step.
The file should contain exactly one line with the token without any newline.
(Use `echo -n 'token' > token file` to make sure no newlines sneak in.)
If the file contains a PAT, the service creates a new registration token
on startup as needed.
If a registration token is given, it can be used to re-register a runner of the same
name but is time-limited as noted above.
For fine-grained PATs:
Give it "Read and Write access to organization/repository self hosted runners",
depending on whether it is organization wide or per-repository. You might have to
experiment a little, fine-grained PATs are a `beta` Github feature and still subject
to change; nonetheless they are the best option at the moment.
For classic PATs:
Make sure the PAT has a scope of `admin:org` for organization-wide registrations
or a scope of `repo` for a single repository.
For runner registration tokens:
Nothing special needs to be done, but updating will break after one hour,
so these are not recommended.
'';
example = "/run/secrets/github-runner/nixos.token";
};
name = let
# Same pattern as for `networking.hostName`
baseType = types.strMatching "^$|^[[:alnum:]]([[:alnum:]_-]{0,61}[[:alnum:]])?$";
in mkOption {
type = if includeNameDefault then baseType else types.nullOr baseType;
description = lib.mdDoc ''
Name of the runner to configure. Defaults to the hostname.
Changing this option triggers a new runner registration.
'';
example = "nixos";
} // (if includeNameDefault then {
default = config.networking.hostName;
defaultText = literalExpression "config.networking.hostName";
} else {
default = null;
});
runnerGroup = mkOption {
type = types.nullOr types.str;
description = lib.mdDoc ''
Name of the runner group to add this runner to (defaults to the default runner group).
Changing this option triggers a new runner registration.
'';
default = null;
};
extraLabels = mkOption {
type = types.listOf types.str;
description = lib.mdDoc ''
Extra labels in addition to the default (`["self-hosted", "Linux", "X64"]`).
Changing this option triggers a new runner registration.
'';
example = literalExpression ''[ "nixos" ]'';
default = [ ];
};
replace = mkOption {
type = types.bool;
description = lib.mdDoc ''
Replace any existing runner with the same name.
Without this flag, registering a new runner with the same name fails.
'';
default = false;
};
extraPackages = mkOption {
type = types.listOf types.package;
description = lib.mdDoc ''
Extra packages to add to `PATH` of the service to make them available to workflows.
'';
default = [ ];
};
extraEnvironment = mkOption {
type = types.attrs;
description = lib.mdDoc ''
Extra environment variables to set for the runner, as an attrset.
'';
example = {
GIT_CONFIG = "/path/to/git/config";
};
default = {};
};
serviceOverrides = mkOption {
type = types.attrs;
description = lib.mdDoc ''
Modify the systemd service. Can be used to, e.g., adjust the sandboxing options.
See {manpage}`systemd.exec(5)` for more options.
'';
example = {
ProtectHome = false;
RestrictAddressFamilies = [ "AF_PACKET" ];
};
default = {};
};
package = mkPackageOption pkgs "github-runner" { };
ephemeral = mkOption {
type = types.bool;
description = lib.mdDoc ''
If enabled, causes the following behavior:
- Passes the `--ephemeral` flag to the runner configuration script
- De-registers and stops the runner with GitHub after it has processed one job
- On stop, systemd wipes the runtime directory (this always happens, even without using the ephemeral option)
- Restarts the service after its successful exit
- On start, wipes the state directory and configures a new runner
You should only enable this option if `tokenFile` points to a file which contains a
personal access token (PAT). If you're using the option with a registration token, restarting the
service will fail as soon as the registration token expired.
'';
default = false;
};
user = mkOption {
type = types.nullOr types.str;
description = lib.mdDoc ''
User under which to run the service. If null, will use a systemd dynamic user.
'';
default = null;
defaultText = literalExpression "username";
};
workDir = mkOption {
type = with types; nullOr str;
description = lib.mdDoc ''
Working directory, available as `$GITHUB_WORKSPACE` during workflow runs
and used as a default for [repository checkouts](https://github.com/actions/checkout).
The service cleans this directory on every service start.
A value of `null` will default to the systemd `RuntimeDirectory`.
'';
default = null;
};
nodeRuntimes = mkOption {
type = with types; nonEmptyListOf (enum [ "node16" "node20" ]);
default = [ "node20" ];
options.services.github-runners = mkOption {
description = mdDoc ''
List of Node.js runtimes the runner should support.
Multiple GitHub Runners.
'';
example = {
runner1 = {
enable = true;
url = "https://github.com/owner/repo";
name = "runner1";
tokenFile = "/secrets/token1";
};
runner2 = {
enable = true;
url = "https://github.com/owner/repo";
name = "runner2";
tokenFile = "/secrets/token2";
};
};
default = { };
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
enable = mkOption {
default = false;
example = true;
description = mdDoc ''
Whether to enable GitHub Actions runner.
Note: GitHub recommends using self-hosted runners with private repositories only. Learn more here:
[About self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners).
'';
type = types.bool;
};
url = mkOption {
type = types.str;
description = mdDoc ''
Repository to add the runner to.
Changing this option triggers a new runner registration.
IMPORTANT: If your token is org-wide (not per repository), you need to
provide a github org link, not a single repository, so do it like this
`https://github.com/nixos`, not like this
`https://github.com/nixos/nixpkgs`.
Otherwise, you are going to get a `404 NotFound`
from `POST https://api.github.com/actions/runner-registration`
in the configure script.
'';
example = "https://github.com/nixos/nixpkgs";
};
tokenFile = mkOption {
type = types.path;
description = mdDoc ''
The full path to a file which contains either
* a fine-grained personal access token (PAT),
* a classic PAT
* or a runner registration token
Changing this option or the `tokenFile`s content triggers a new runner registration.
We suggest using the fine-grained PATs. A runner registration token is valid
only for 1 hour after creation, so the next time the runner configuration changes
this will give you hard-to-debug HTTP 404 errors in the configure step.
The file should contain exactly one line with the token without any newline.
(Use `echo -n 'token' > token file` to make sure no newlines sneak in.)
If the file contains a PAT, the service creates a new registration token
on startup as needed.
If a registration token is given, it can be used to re-register a runner of the same
name but is time-limited as noted above.
For fine-grained PATs:
Give it "Read and Write access to organization/repository self hosted runners",
depending on whether it is organization wide or per-repository. You might have to
experiment a little, fine-grained PATs are a `beta` Github feature and still subject
to change; nonetheless they are the best option at the moment.
For classic PATs:
Make sure the PAT has a scope of `admin:org` for organization-wide registrations
or a scope of `repo` for a single repository.
For runner registration tokens:
Nothing special needs to be done, but updating will break after one hour,
so these are not recommended.
'';
example = "/run/secrets/github-runner/nixos.token";
};
name = mkOption {
type = types.nullOr types.str;
description = mdDoc ''
Name of the runner to configure. If null, defaults to the hostname.
Changing this option triggers a new runner registration.
'';
example = "nixos";
default = name;
};
runnerGroup = mkOption {
type = types.nullOr types.str;
description = mdDoc ''
Name of the runner group to add this runner to (defaults to the default runner group).
Changing this option triggers a new runner registration.
'';
default = null;
};
extraLabels = mkOption {
type = types.listOf types.str;
description = mdDoc ''
Extra labels in addition to the default (unless disabled through the `noDefaultLabels` option).
Changing this option triggers a new runner registration.
'';
example = literalExpression ''[ "nixos" ]'';
default = [ ];
};
noDefaultLabels = mkOption {
type = types.bool;
description = mdDoc ''
Disables adding the default labels. Also see the `extraLabels` option.
Changing this option triggers a new runner registration.
'';
default = false;
};
replace = mkOption {
type = types.bool;
description = mdDoc ''
Replace any existing runner with the same name.
Without this flag, registering a new runner with the same name fails.
'';
default = false;
};
extraPackages = mkOption {
type = types.listOf types.package;
description = mdDoc ''
Extra packages to add to `PATH` of the service to make them available to workflows.
'';
default = [ ];
};
extraEnvironment = mkOption {
type = types.attrs;
description = mdDoc ''
Extra environment variables to set for the runner, as an attrset.
'';
example = {
GIT_CONFIG = "/path/to/git/config";
};
default = { };
};
serviceOverrides = mkOption {
type = types.attrs;
description = mdDoc ''
Modify the systemd service. Can be used to, e.g., adjust the sandboxing options.
See {manpage}`systemd.exec(5)` for more options.
'';
example = {
ProtectHome = false;
RestrictAddressFamilies = [ "AF_PACKET" ];
};
default = { };
};
package = mkPackageOption pkgs "github-runner" { };
ephemeral = mkOption {
type = types.bool;
description = mdDoc ''
If enabled, causes the following behavior:
- Passes the `--ephemeral` flag to the runner configuration script
- De-registers and stops the runner with GitHub after it has processed one job
- On stop, systemd wipes the runtime directory (this always happens, even without using the ephemeral option)
- Restarts the service after its successful exit
- On start, wipes the state directory and configures a new runner
You should only enable this option if `tokenFile` points to a file which contains a
personal access token (PAT). If you're using the option with a registration token, restarting the
service will fail as soon as the registration token expired.
Changing this option triggers a new runner registration.
'';
default = false;
};
user = mkOption {
type = types.nullOr types.str;
description = mdDoc ''
User under which to run the service.
If this option and the `group` option is set to `null`,
the service runs as a dynamically allocated user.
Also see the `group` option for an overview on the effects of the `user` and `group` settings.
'';
default = null;
defaultText = literalExpression "username";
};
group = mkOption {
type = types.nullOr types.str;
description = mdDoc ''
Group under which to run the service.
The effect of this option depends on the value of the `user` option:
- `group == null` and `user == null`:
The service runs with a dynamically allocated user and group.
- `group == null` and `user != null`:
The service runs as the given user and its default group.
- `group != null` and `user == null`:
This configuration is invalid. In this case, the service would use the given group
but run as root implicitly. If this is really what you want, set `user = "root"` explicitly.
'';
default = null;
defaultText = literalExpression "groupname";
};
workDir = mkOption {
type = with types; nullOr str;
description = mdDoc ''
Working directory, available as `$GITHUB_WORKSPACE` during workflow runs
and used as a default for [repository checkouts](https://github.com/actions/checkout).
The service cleans this directory on every service start.
A value of `null` will default to the systemd `RuntimeDirectory`.
Changing this option triggers a new runner registration.
'';
default = null;
};
nodeRuntimes = mkOption {
type = with types; nonEmptyListOf (enum [ "node20" ]);
default = [ "node20" ];
description = mdDoc ''
List of Node.js runtimes the runner should support.
'';
};
};
}));
};
}

View File

@ -1,268 +1,299 @@
{ config
, lib
, pkgs
, cfg ? config.services.github-runner
, svcName
, systemdDir ? "${svcName}/${cfg.name}"
# %t: Runtime directory root (usually /run); see systemd.unit(5)
, runtimeDir ? "%t/${systemdDir}"
# %S: State directory root (usually /var/lib); see systemd.unit(5)
, stateDir ? "%S/${systemdDir}"
# %L: Log directory root (usually /var/log); see systemd.unit(5)
, logsDir ? "%L/${systemdDir}"
# Name of file stored in service state directory
, currentConfigTokenFilename ? ".current-token"
, ...
}:
with lib;
let
workDir = if cfg.workDir == null then runtimeDir else cfg.workDir;
package = cfg.package.override { inherit (cfg) nodeRuntimes; };
in
{
description = "GitHub Actions runner";
config.assertions = flatten (
flip mapAttrsToList config.services.github-runners (name: cfg: map (mkIf cfg.enable) [
{
assertion = !cfg.noDefaultLabels || (cfg.extraLabels != [ ]);
message = "`services.github-runners.${name}`: The `extraLabels` option is mandatory if `noDefaultLabels` is set";
}
{
assertion = cfg.group == null || cfg.user != null;
message = ''`services.github-runners.${name}`: Setting `group` while leaving `user` unset runs the service as `root`. If this is really what you want, set `user = "root"` explicitly'';
}
])
);
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
config.systemd.services = flip mapAttrs' config.services.github-runners (name: cfg:
let
svcName = "github-runner-${name}";
systemdDir = "github-runner/${name}";
environment = {
HOME = workDir;
RUNNER_ROOT = stateDir;
} // cfg.extraEnvironment;
# %t: Runtime directory root (usually /run); see systemd.unit(5)
runtimeDir = "%t/${systemdDir}";
# %S: State directory root (usually /var/lib); see systemd.unit(5)
stateDir = "%S/${systemdDir}";
# %L: Log directory root (usually /var/log); see systemd.unit(5)
logsDir = "%L/${systemdDir}";
# Name of file stored in service state directory
currentConfigTokenFilename = ".current-token";
path = (with pkgs; [
bash
coreutils
git
gnutar
gzip
]) ++ [
config.nix.package
] ++ cfg.extraPackages;
workDir = if cfg.workDir == null then runtimeDir else cfg.workDir;
# Support old github-runner versions which don't have the `nodeRuntimes` arg yet.
package = cfg.package.override (old: optionalAttrs (hasAttr "nodeRuntimes" old) { inherit (cfg) nodeRuntimes; });
in
nameValuePair svcName {
description = "GitHub Actions runner";
serviceConfig = mkMerge [
{
ExecStart = "${package}/bin/Runner.Listener run --startuptype service";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
# Does the following, sequentially:
# - If the module configuration or the token has changed, purge the state directory,
# and create the current and the new token file with the contents of the configured
# token. While both files have the same content, only the later is accessible by
# the service user.
# - Configure the runner using the new token file. When finished, delete it.
# - Set up the directory structure by creating the necessary symlinks.
ExecStartPre =
let
# Wrapper script which expects the full path of the state, working and logs
# directory as arguments. Overrides the respective systemd variables to provide
# unambiguous directory names. This becomes relevant, for example, if the
# caller overrides any of the StateDirectory=, RuntimeDirectory= or LogDirectory=
# to contain more than one directory. This causes systemd to set the respective
# environment variables with the path of all of the given directories, separated
# by a colon.
writeScript = name: lines: pkgs.writeShellScript "${svcName}-${name}.sh" ''
set -euo pipefail
environment = {
HOME = workDir;
RUNNER_ROOT = stateDir;
} // cfg.extraEnvironment;
STATE_DIRECTORY="$1"
WORK_DIRECTORY="$2"
LOGS_DIRECTORY="$3"
path = (with pkgs; [
bash
coreutils
git
gnutar
gzip
]) ++ [
config.nix.package
] ++ cfg.extraPackages;
${lines}
'';
runnerRegistrationConfig = getAttrs [ "name" "tokenFile" "url" "runnerGroup" "extraLabels" "ephemeral" "workDir" ] cfg;
newConfigPath = builtins.toFile "${svcName}-config.json" (builtins.toJSON runnerRegistrationConfig);
currentConfigPath = "$STATE_DIRECTORY/.nixos-current-config.json";
newConfigTokenPath = "$STATE_DIRECTORY/.new-token";
currentConfigTokenPath = "$STATE_DIRECTORY/${currentConfigTokenFilename}";
serviceConfig = mkMerge [
{
ExecStart = "${package}/bin/Runner.Listener run --startuptype service";
runnerCredFiles = [
".credentials"
".credentials_rsaparams"
".runner"
# Does the following, sequentially:
# - If the module configuration or the token has changed, purge the state directory,
# and create the current and the new token file with the contents of the configured
# token. While both files have the same content, only the later is accessible by
# the service user.
# - Configure the runner using the new token file. When finished, delete it.
# - Set up the directory structure by creating the necessary symlinks.
ExecStartPre =
let
# Wrapper script which expects the full path of the state, working and logs
# directory as arguments. Overrides the respective systemd variables to provide
# unambiguous directory names. This becomes relevant, for example, if the
# caller overrides any of the StateDirectory=, RuntimeDirectory= or LogDirectory=
# to contain more than one directory. This causes systemd to set the respective
# environment variables with the path of all of the given directories, separated
# by a colon.
writeScript = name: lines: pkgs.writeShellScript "${svcName}-${name}.sh" ''
set -euo pipefail
STATE_DIRECTORY="$1"
WORK_DIRECTORY="$2"
LOGS_DIRECTORY="$3"
${lines}
'';
runnerRegistrationConfig = getAttrs [
"ephemeral"
"extraLabels"
"name"
"noDefaultLabels"
"runnerGroup"
"tokenFile"
"url"
"workDir"
]
cfg;
newConfigPath = builtins.toFile "${svcName}-config.json" (builtins.toJSON runnerRegistrationConfig);
currentConfigPath = "$STATE_DIRECTORY/.nixos-current-config.json";
newConfigTokenPath = "$STATE_DIRECTORY/.new-token";
currentConfigTokenPath = "$STATE_DIRECTORY/${currentConfigTokenFilename}";
runnerCredFiles = [
".credentials"
".credentials_rsaparams"
".runner"
];
unconfigureRunner = writeScript "unconfigure" ''
copy_tokens() {
# Copy the configured token file to the state dir and allow the service user to read the file
install --mode=666 ${escapeShellArg cfg.tokenFile} "${newConfigTokenPath}"
# Also copy current file to allow for a diff on the next start
install --mode=600 ${escapeShellArg cfg.tokenFile} "${currentConfigTokenPath}"
}
clean_state() {
find "$STATE_DIRECTORY/" -mindepth 1 -delete
copy_tokens
}
diff_config() {
changed=0
# Check for module config changes
[[ -f "${currentConfigPath}" ]] \
&& ${pkgs.diffutils}/bin/diff -q '${newConfigPath}' "${currentConfigPath}" >/dev/null 2>&1 \
|| changed=1
# Also check the content of the token file
[[ -f "${currentConfigTokenPath}" ]] \
&& ${pkgs.diffutils}/bin/diff -q "${currentConfigTokenPath}" ${escapeShellArg cfg.tokenFile} >/dev/null 2>&1 \
|| changed=1
# If the config has changed, remove old state and copy tokens
if [[ "$changed" -eq 1 ]]; then
echo "Config has changed, removing old runner state."
echo "The old runner will still appear in the GitHub Actions UI." \
"You have to remove it manually."
clean_state
fi
}
if [[ "${optionalString cfg.ephemeral "1"}" ]]; then
# In ephemeral mode, we always want to start with a clean state
clean_state
elif [[ "$(ls -A "$STATE_DIRECTORY")" ]]; then
# There are state files from a previous run; diff them to decide if we need a new registration
diff_config
else
# The state directory is entirely empty which indicates a first start
copy_tokens
fi
# Always clean workDir
find -H "$WORK_DIRECTORY" -mindepth 1 -delete
'';
configureRunner = writeScript "configure" ''
if [[ -e "${newConfigTokenPath}" ]]; then
echo "Configuring GitHub Actions Runner"
args=(
--unattended
--disableupdate
--work "$WORK_DIRECTORY"
--url ${escapeShellArg cfg.url}
--labels ${escapeShellArg (concatStringsSep "," cfg.extraLabels)}
${optionalString (cfg.name != null ) "--name ${escapeShellArg cfg.name}"}
${optionalString cfg.replace "--replace"}
${optionalString (cfg.runnerGroup != null) "--runnergroup ${escapeShellArg cfg.runnerGroup}"}
${optionalString cfg.ephemeral "--ephemeral"}
${optionalString cfg.noDefaultLabels "--no-default-labels"}
)
# If the token file contains a PAT (i.e., it starts with "ghp_" or "github_pat_"), we have to use the --pat option,
# if it is not a PAT, we assume it contains a registration token and use the --token option
token=$(<"${newConfigTokenPath}")
if [[ "$token" =~ ^ghp_* ]] || [[ "$token" =~ ^github_pat_* ]]; then
args+=(--pat "$token")
else
args+=(--token "$token")
fi
${package}/bin/Runner.Listener configure "''${args[@]}"
# Move the automatically created _diag dir to the logs dir
mkdir -p "$STATE_DIRECTORY/_diag"
cp -r "$STATE_DIRECTORY/_diag/." "$LOGS_DIRECTORY/"
rm -rf "$STATE_DIRECTORY/_diag/"
# Cleanup token from config
rm "${newConfigTokenPath}"
# Symlink to new config
ln -s '${newConfigPath}' "${currentConfigPath}"
fi
'';
setupWorkDir = writeScript "setup-work-dirs" ''
# Link _diag dir
ln -s "$LOGS_DIRECTORY" "$WORK_DIRECTORY/_diag"
# Link the runner credentials to the work dir
ln -s "$STATE_DIRECTORY"/{${lib.concatStringsSep "," runnerCredFiles}} "$WORK_DIRECTORY/"
'';
in
map (x: "${x} ${escapeShellArgs [ stateDir workDir logsDir ]}") [
"+${unconfigureRunner}" # runs as root
configureRunner
setupWorkDir
];
# If running in ephemeral mode, restart the service on-exit (i.e., successful de-registration of the runner)
# to trigger a fresh registration.
Restart = if cfg.ephemeral then "on-success" else "no";
# If the runner exits with `ReturnCode.RetryableError = 2`, always restart the service:
# https://github.com/actions/runner/blob/40ed7f8/src/Runner.Common/Constants.cs#L146
RestartForceExitStatus = [ 2 ];
# Contains _diag
LogsDirectory = [ systemdDir ];
# Default RUNNER_ROOT which contains ephemeral Runner data
RuntimeDirectory = [ systemdDir ];
# Home of persistent runner data, e.g., credentials
StateDirectory = [ systemdDir ];
StateDirectoryMode = "0700";
WorkingDirectory = workDir;
InaccessiblePaths = [
# Token file path given in the configuration, if visible to the service
"-${cfg.tokenFile}"
# Token file in the state directory
"${stateDir}/${currentConfigTokenFilename}"
];
unconfigureRunner = writeScript "unconfigure" ''
copy_tokens() {
# Copy the configured token file to the state dir and allow the service user to read the file
install --mode=666 ${escapeShellArg cfg.tokenFile} "${newConfigTokenPath}"
# Also copy current file to allow for a diff on the next start
install --mode=600 ${escapeShellArg cfg.tokenFile} "${currentConfigTokenPath}"
}
clean_state() {
find "$STATE_DIRECTORY/" -mindepth 1 -delete
copy_tokens
}
diff_config() {
changed=0
# Check for module config changes
[[ -f "${currentConfigPath}" ]] \
&& ${pkgs.diffutils}/bin/diff -q '${newConfigPath}' "${currentConfigPath}" >/dev/null 2>&1 \
|| changed=1
# Also check the content of the token file
[[ -f "${currentConfigTokenPath}" ]] \
&& ${pkgs.diffutils}/bin/diff -q "${currentConfigTokenPath}" ${escapeShellArg cfg.tokenFile} >/dev/null 2>&1 \
|| changed=1
# If the config has changed, remove old state and copy tokens
if [[ "$changed" -eq 1 ]]; then
echo "Config has changed, removing old runner state."
echo "The old runner will still appear in the GitHub Actions UI." \
"You have to remove it manually."
clean_state
fi
}
if [[ "${optionalString cfg.ephemeral "1"}" ]]; then
# In ephemeral mode, we always want to start with a clean state
clean_state
elif [[ "$(ls -A "$STATE_DIRECTORY")" ]]; then
# There are state files from a previous run; diff them to decide if we need a new registration
diff_config
else
# The state directory is entirely empty which indicates a first start
copy_tokens
fi
# Always clean workDir
find -H "$WORK_DIRECTORY" -mindepth 1 -delete
'';
configureRunner = writeScript "configure" ''
if [[ -e "${newConfigTokenPath}" ]]; then
echo "Configuring GitHub Actions Runner"
args=(
--unattended
--disableupdate
--work "$WORK_DIRECTORY"
--url ${escapeShellArg cfg.url}
--labels ${escapeShellArg (concatStringsSep "," cfg.extraLabels)}
--name ${escapeShellArg cfg.name}
${optionalString cfg.replace "--replace"}
${optionalString (cfg.runnerGroup != null) "--runnergroup ${escapeShellArg cfg.runnerGroup}"}
${optionalString cfg.ephemeral "--ephemeral"}
)
# If the token file contains a PAT (i.e., it starts with "ghp_" or "github_pat_"), we have to use the --pat option,
# if it is not a PAT, we assume it contains a registration token and use the --token option
token=$(<"${newConfigTokenPath}")
if [[ "$token" =~ ^ghp_* ]] || [[ "$token" =~ ^github_pat_* ]]; then
args+=(--pat "$token")
else
args+=(--token "$token")
fi
${package}/bin/Runner.Listener configure "''${args[@]}"
# Move the automatically created _diag dir to the logs dir
mkdir -p "$STATE_DIRECTORY/_diag"
cp -r "$STATE_DIRECTORY/_diag/." "$LOGS_DIRECTORY/"
rm -rf "$STATE_DIRECTORY/_diag/"
# Cleanup token from config
rm "${newConfigTokenPath}"
# Symlink to new config
ln -s '${newConfigPath}' "${currentConfigPath}"
fi
'';
setupWorkDir = writeScript "setup-work-dirs" ''
# Link _diag dir
ln -s "$LOGS_DIRECTORY" "$WORK_DIRECTORY/_diag"
# Link the runner credentials to the work dir
ln -s "$STATE_DIRECTORY"/{${lib.concatStringsSep "," runnerCredFiles}} "$WORK_DIRECTORY/"
'';
in
map (x: "${x} ${escapeShellArgs [ stateDir workDir logsDir ]}") [
"+${unconfigureRunner}" # runs as root
configureRunner
setupWorkDir
];
KillSignal = "SIGINT";
# If running in ephemeral mode, restart the service on-exit (i.e., successful de-registration of the runner)
# to trigger a fresh registration.
Restart = if cfg.ephemeral then "on-success" else "no";
# If the runner exits with `ReturnCode.RetryableError = 2`, always restart the service:
# https://github.com/actions/runner/blob/40ed7f8/src/Runner.Common/Constants.cs#L146
RestartForceExitStatus = [ 2 ];
# Hardening (may overlap with DynamicUser=)
# The following options are only for optimizing:
# systemd-analyze security github-runner
AmbientCapabilities = mkBefore [ "" ];
CapabilityBoundingSet = mkBefore [ "" ];
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = mkBefore [ "" ];
NoNewPrivileges = mkDefault true;
PrivateDevices = mkDefault true;
PrivateMounts = mkDefault true;
PrivateTmp = mkDefault true;
PrivateUsers = mkDefault true;
ProtectClock = mkDefault true;
ProtectControlGroups = mkDefault true;
ProtectHome = mkDefault true;
ProtectHostname = mkDefault true;
ProtectKernelLogs = mkDefault true;
ProtectKernelModules = mkDefault true;
ProtectKernelTunables = mkDefault true;
ProtectSystem = mkDefault "strict";
RemoveIPC = mkDefault true;
RestrictNamespaces = mkDefault true;
RestrictRealtime = mkDefault true;
RestrictSUIDSGID = mkDefault true;
UMask = mkDefault "0066";
ProtectProc = mkDefault "invisible";
SystemCallFilter = mkBefore [
"~@clock"
"~@cpu-emulation"
"~@module"
"~@mount"
"~@obsolete"
"~@raw-io"
"~@reboot"
"~capset"
"~setdomainname"
"~sethostname"
];
RestrictAddressFamilies = mkBefore [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ];
# Contains _diag
LogsDirectory = [ systemdDir ];
# Default RUNNER_ROOT which contains ephemeral Runner data
RuntimeDirectory = [ systemdDir ];
# Home of persistent runner data, e.g., credentials
StateDirectory = [ systemdDir ];
StateDirectoryMode = "0700";
WorkingDirectory = workDir;
BindPaths = lib.optionals (cfg.workDir != null) [ cfg.workDir ];
InaccessiblePaths = [
# Token file path given in the configuration, if visible to the service
"-${cfg.tokenFile}"
# Token file in the state directory
"${stateDir}/${currentConfigTokenFilename}"
# Needs network access
PrivateNetwork = mkDefault false;
# Cannot be true due to Node
MemoryDenyWriteExecute = mkDefault false;
# The more restrictive "pid" option makes `nix` commands in CI emit
# "GC Warning: Couldn't read /proc/stat"
# You may want to set this to "pid" if not using `nix` commands
ProcSubset = mkDefault "all";
# Coverage programs for compiled code such as `cargo-tarpaulin` disable
# ASLR (address space layout randomization) which requires the
# `personality` syscall
# You may want to set this to `true` if not using coverage tooling on
# compiled code
LockPersonality = mkDefault false;
DynamicUser = mkDefault true;
}
(mkIf (cfg.user != null) {
DynamicUser = false;
User = cfg.user;
})
(mkIf (cfg.group != null) {
DynamicUser = false;
Group = cfg.group;
})
cfg.serviceOverrides
];
KillSignal = "SIGINT";
# Hardening (may overlap with DynamicUser=)
# The following options are only for optimizing:
# systemd-analyze security github-runner
AmbientCapabilities = mkBefore [ "" ];
CapabilityBoundingSet = mkBefore [ "" ];
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = mkBefore [ "" ];
NoNewPrivileges = mkDefault true;
PrivateDevices = mkDefault true;
PrivateMounts = mkDefault true;
PrivateTmp = mkDefault true;
PrivateUsers = mkDefault true;
ProtectClock = mkDefault true;
ProtectControlGroups = mkDefault true;
ProtectHome = mkDefault true;
ProtectHostname = mkDefault true;
ProtectKernelLogs = mkDefault true;
ProtectKernelModules = mkDefault true;
ProtectKernelTunables = mkDefault true;
ProtectSystem = mkDefault "strict";
RemoveIPC = mkDefault true;
RestrictNamespaces = mkDefault true;
RestrictRealtime = mkDefault true;
RestrictSUIDSGID = mkDefault true;
UMask = mkDefault "0066";
ProtectProc = mkDefault "invisible";
SystemCallFilter = mkBefore [
"~@clock"
"~@cpu-emulation"
"~@module"
"~@mount"
"~@obsolete"
"~@raw-io"
"~@reboot"
"~capset"
"~setdomainname"
"~sethostname"
];
RestrictAddressFamilies = mkBefore [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ];
BindPaths = lib.optionals (cfg.workDir != null) [ cfg.workDir ];
# Needs network access
PrivateNetwork = mkDefault false;
# Cannot be true due to Node
MemoryDenyWriteExecute = mkDefault false;
# The more restrictive "pid" option makes `nix` commands in CI emit
# "GC Warning: Couldn't read /proc/stat"
# You may want to set this to "pid" if not using `nix` commands
ProcSubset = mkDefault "all";
# Coverage programs for compiled code such as `cargo-tarpaulin` disable
# ASLR (address space layout randomization) which requires the
# `personality` syscall
# You may want to set this to `true` if not using coverage tooling on
# compiled code
LockPersonality = mkDefault false;
# Note that this has some interactions with the User setting; so you may
# want to consult the systemd docs if using both.
DynamicUser = mkDefault true;
}
(mkIf (cfg.user != null) { User = cfg.user; })
cfg.serviceOverrides
];
);
}

View File

@ -1,58 +1,10 @@
{ config
, pkgs
, lib
, ...
}@args:
with lib;
let
cfg = config.services.github-runners;
in
{ lib, ... }:
{
options.services.github-runners = mkOption {
default = {};
type = with types; attrsOf (submodule { options = import ./github-runner/options.nix (args // {
# services.github-runners.${name}.name doesn't have a default; it falls back to ${name} below.
includeNameDefault = false;
}); });
example = {
runner1 = {
enable = true;
url = "https://github.com/owner/repo";
name = "runner1";
tokenFile = "/secrets/token1";
};
imports = [
(lib.mkRemovedOptionModule [ "services" "github-runner" ] "Use `services.github-runners.*` instead")
./github-runner/options.nix
./github-runner/service.nix
];
runner2 = {
enable = true;
url = "https://github.com/owner/repo";
name = "runner2";
tokenFile = "/secrets/token2";
};
};
description = lib.mdDoc ''
Multiple GitHub Runners.
'';
};
config = {
systemd.services = flip mapAttrs' cfg (n: v:
let
svcName = "github-runner-${n}";
in
nameValuePair svcName
(import ./github-runner/service.nix (args // {
inherit svcName;
cfg = v // {
name = if v.name != null then v.name else n;
};
systemdDir = "github-runner/${n}";
}))
);
};
meta.maintainers = with maintainers; [ veehaitch newam ];
meta.maintainers = with lib.maintainers; [ veehaitch newam ];
}

View File

@ -99,6 +99,17 @@ in {
type = types.nullOr types.path;
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Open etcd ports in the firewall.
Ports opened:
- 2379/tcp for client requests
- 2380/tcp for peer communication
'';
};
peerCertFile = mkOption {
description = lib.mdDoc "Cert file to use for peer to peer communication";
default = cfg.certFile;
@ -160,7 +171,10 @@ in {
systemd.services.etcd = {
description = "etcd key-value store";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
after = [ "network-online.target" ]
++ lib.optional config.networking.firewall.enable "firewall.service";
wants = [ "network-online.target" ]
++ lib.optional config.networking.firewall.enable "firewall.service";
environment = (filterAttrs (n: v: v != null) {
ETCD_NAME = cfg.name;
@ -190,6 +204,8 @@ in {
serviceConfig = {
Type = "notify";
Restart = "always";
RestartSec = "30s";
ExecStart = "${cfg.package}/bin/etcd";
User = "etcd";
LimitNOFILE = 40000;
@ -198,6 +214,13 @@ in {
environment.systemPackages = [ cfg.package ];
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [
2379 # for client requests
2380 # for peer communication
];
};
users.users.etcd = {
isSystemUser = true;
group = "etcd";

View File

@ -35,65 +35,64 @@ let
serverConfig = pkgs.writeText "neo4j.conf" ''
# General
dbms.allow_upgrade=${boolToString cfg.allowUpgrade}
dbms.default_listen_address=${cfg.defaultListenAddress}
dbms.databases.default_to_read_only=${boolToString cfg.readOnly}
server.default_listen_address=${cfg.defaultListenAddress}
server.databases.default_to_read_only=${boolToString cfg.readOnly}
${optionalString (cfg.workerCount > 0) ''
dbms.threads.worker_count=${toString cfg.workerCount}
''}
# Directories (readonly)
dbms.directories.certificates=${cfg.directories.certificates}
dbms.directories.plugins=${cfg.directories.plugins}
dbms.directories.lib=${cfg.package}/share/neo4j/lib
# dbms.directories.certificates=${cfg.directories.certificates}
server.directories.plugins=${cfg.directories.plugins}
server.directories.lib=${cfg.package}/share/neo4j/lib
${optionalString (cfg.constrainLoadCsv) ''
dbms.directories.import=${cfg.directories.imports}
server.directories.import=${cfg.directories.imports}
''}
# Directories (read and write)
dbms.directories.data=${cfg.directories.data}
dbms.directories.logs=${cfg.directories.home}/logs
dbms.directories.run=${cfg.directories.home}/run
server.directories.data=${cfg.directories.data}
server.directories.logs=${cfg.directories.home}/logs
server.directories.run=${cfg.directories.home}/run
# HTTP Connector
${optionalString (cfg.http.enable) ''
dbms.connector.http.enabled=${boolToString cfg.http.enable}
dbms.connector.http.listen_address=${cfg.http.listenAddress}
dbms.connector.http.advertised_address=${cfg.http.listenAddress}
server.http.enabled=${boolToString cfg.http.enable}
server.http.listen_address=${cfg.http.listenAddress}
server.http.advertised_address=${cfg.http.listenAddress}
''}
# HTTPS Connector
dbms.connector.https.enabled=${boolToString cfg.https.enable}
dbms.connector.https.listen_address=${cfg.https.listenAddress}
dbms.connector.https.advertised_address=${cfg.https.listenAddress}
server.https.enabled=${boolToString cfg.https.enable}
server.https.listen_address=${cfg.https.listenAddress}
server.https.advertised_address=${cfg.https.listenAddress}
# BOLT Connector
dbms.connector.bolt.enabled=${boolToString cfg.bolt.enable}
dbms.connector.bolt.listen_address=${cfg.bolt.listenAddress}
dbms.connector.bolt.advertised_address=${cfg.bolt.listenAddress}
dbms.connector.bolt.tls_level=${cfg.bolt.tlsLevel}
server.bolt.enabled=${boolToString cfg.bolt.enable}
server.bolt.listen_address=${cfg.bolt.listenAddress}
server.bolt.advertised_address=${cfg.bolt.listenAddress}
server.bolt.tls_level=${cfg.bolt.tlsLevel}
# SSL Policies
${concatStringsSep "\n" sslPolicies}
# Default retention policy from neo4j.conf
dbms.tx_log.rotation.retention_policy=1 days
db.tx_log.rotation.retention_policy=1 days
# Default JVM parameters from neo4j.conf
dbms.jvm.additional=-XX:+UseG1GC
dbms.jvm.additional=-XX:-OmitStackTraceInFastThrow
dbms.jvm.additional=-XX:+AlwaysPreTouch
dbms.jvm.additional=-XX:+UnlockExperimentalVMOptions
dbms.jvm.additional=-XX:+TrustFinalNonStaticFields
dbms.jvm.additional=-XX:+DisableExplicitGC
dbms.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048
dbms.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true
dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
server.jvm.additional=-XX:+UseG1GC
server.jvm.additional=-XX:-OmitStackTraceInFastThrow
server.jvm.additional=-XX:+AlwaysPreTouch
server.jvm.additional=-XX:+UnlockExperimentalVMOptions
server.jvm.additional=-XX:+TrustFinalNonStaticFields
server.jvm.additional=-XX:+DisableExplicitGC
server.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048
server.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true
server.jvm.additional=-Dunsupported.dbms.udc.source=tarball
#dbms.memory.heap.initial_size=12000m
#dbms.memory.heap.max_size=12000m
#dbms.memory.pagecache.size=4g
#dbms.tx_state.max_off_heap_memory=8000m
#server.memory.off_heap.transaction_max_size=12000m
#server.memory.heap.max_size=12000m
#server.memory.pagecache.size=4g
#server.tx_state.max_off_heap_memory=8000m
# Extra Configuration
${cfg.extraServerConfig}
@ -127,14 +126,6 @@ in {
'';
};
allowUpgrade = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Allow upgrade of Neo4j database files from an older version.
'';
};
constrainLoadCsv = mkOption {
type = types.bool;
default = true;

View File

@ -20,10 +20,11 @@ let
mkBot = n: c:
format.generate "${n}.json" (c.settings // {
SteamLogin = if c.username == "" then n else c.username;
Enabled = c.enabled;
} // lib.optionalAttrs (c.passwordFile != null) {
SteamPassword = c.passwordFile;
# sets the password format to file (https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Security#file)
PasswordFormat = 4;
Enabled = c.enabled;
});
in
{
@ -127,8 +128,12 @@ in
default = "";
};
passwordFile = lib.mkOption {
type = lib.types.path;
description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `archisteamfarm` user/group.";
type = with lib.types; nullOr path;
default = null;
description = lib.mdDoc ''
Path to a file containing the password. The file must be readable by the `archisteamfarm` user/group.
Omit or set to null to provide the password a different way, such as through the web-ui.
'';
};
enabled = lib.mkOption {
type = lib.types.bool;

View File

@ -34,6 +34,7 @@ in {
users.users.brltty = {
description = "BRLTTY daemon user";
group = "brltty";
isSystemUser = true;
};
users.groups = {
brltty = { };

View File

@ -51,7 +51,9 @@ let
# to install it because it would create a cyclic dependency between
# the outputs. We also need to enable the remote,
# which should not be done by default.
lib.optionalAttrs cfg.enableTestRemote (enableRemote cfg.package.installedTests "fwupd-tests")
lib.optionalAttrs
(cfg.daemonSettings.TestDevices or false)
(enableRemote cfg.package.installedTests "fwupd-tests")
);
in {
@ -86,15 +88,6 @@ in {
'';
};
enableTestRemote = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Whether to enable test remote. This is used by
[installed tests](https://github.com/fwupd/fwupd/blob/master/data/installed-tests/README.md).
'';
};
package = mkPackageOption pkgs "fwupd" { };
daemonSettings = mkOption {
@ -128,6 +121,16 @@ in {
or if this partition is not mounted at /boot/efi, /boot, or /efi
'';
};
TestDevices = mkOption {
internal = true;
type = types.bool;
default = false;
description = lib.mdDoc ''
Create virtual test devices and remote for validating daemon flows.
This is only intended for CI testing and development purposes.
'';
};
};
};
default = {};
@ -153,13 +156,13 @@ in {
(mkRenamedOptionModule [ "services" "fwupd" "blacklistPlugins"] [ "services" "fwupd" "daemonSettings" "DisabledPlugins" ])
(mkRenamedOptionModule [ "services" "fwupd" "disabledDevices" ] [ "services" "fwupd" "daemonSettings" "DisabledDevices" ])
(mkRenamedOptionModule [ "services" "fwupd" "disabledPlugins" ] [ "services" "fwupd" "daemonSettings" "DisabledPlugins" ])
(mkRemovedOptionModule [ "services" "fwupd" "enableTestRemote" ] "This option was removed after being removed upstream. It only provided a method for testing fwupd functionality, and should not have been exposed for use outside of nix tests.")
];
###### implementation
config = mkIf cfg.enable {
# Disable test related plug-ins implicitly so that users do not have to care about them.
services.fwupd.daemonSettings = {
DisabledPlugins = cfg.package.defaultDisabledPlugins;
EspLocation = config.boot.loader.efi.efiSysMountPoint;
};

View File

@ -46,8 +46,8 @@ in
config = mkIf config.services.pcscd.enable {
environment.etc."reader.conf".source = cfgFile;
environment.systemPackages = [ package.out ];
systemd.packages = [ (getBin package) ];
environment.systemPackages = [ package ];
systemd.packages = [ package ];
services.pcscd.plugins = [ pkgs.ccid ];
@ -64,7 +64,7 @@ in
# around it, we force the path to the cfgFile.
#
# https://github.com/NixOS/nixpkgs/issues/121088
serviceConfig.ExecStart = [ "" "${getBin package}/bin/pcscd -f -x -c ${cfgFile}" ];
serviceConfig.ExecStart = [ "" "${package}/bin/pcscd -f -x -c ${cfgFile}" ];
};
};
}

View File

@ -12,6 +12,7 @@ let
inherit (cfg)
verbose
temp
turbo
;
# `core` and `cache` are both intentionally set to `cfg.coreOffset` as according to the undervolt docs:
#
@ -105,6 +106,14 @@ in
'';
};
turbo = mkOption {
type = types.nullOr types.int;
default = null;
description = lib.mdDoc ''
Changes the Intel Turbo feature status (1 is disabled and 0 is enabled).
'';
};
p1.limit = mkOption {
type = with types; nullOr int;
default = null;

View File

@ -0,0 +1,90 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.govee2mqtt;
in {
meta.maintainers = with lib.maintainers; [ SuperSandro2000 ];
options.services.govee2mqtt = {
enable = lib.mkEnableOption "Govee2MQTT";
package = lib.mkPackageOption pkgs "govee2mqtt" { };
user = lib.mkOption {
type = lib.types.str;
default = "govee2mqtt";
description = "User under which Govee2MQTT should run.";
};
group = lib.mkOption {
type = lib.types.str;
default = "govee2mqtt";
description = "Group under which Govee2MQTT should run.";
};
environmentFile = lib.mkOption {
type = lib.types.path;
example = "/var/lib/govee2mqtt/govee2mqtt.env";
description = ''
Environment file as defined in {manpage}`systemd.exec(5)`.
See upstream documentation <https://github.com/wez/govee2mqtt/blob/main/docs/CONFIG.md>.
'';
};
};
config = lib.mkIf cfg.enable {
users = {
groups.${cfg.group} = { };
users.${cfg.user} = {
description = "Govee2MQTT service user";
inherit (cfg) group;
isSystemUser = true;
};
};
systemd.services.govee2mqtt = {
description = "Govee2MQTT Service";
wantedBy = [ "multi-user.target" ];
after = [ "networking.target" ];
serviceConfig = {
CacheDirectory = "govee2mqtt";
Environment = [
"GOVEE_CACHE_DIR=/var/cache/govee2mqtt"
];
EnvironmentFile = cfg.environmentFile;
ExecStart = "${lib.getExe cfg.package} serve --govee-iot-key=/var/lib/govee2mqtt/iot.key --govee-iot-cert=/var/lib/govee2mqtt/iot.cert"
+ " --amazon-root-ca=${pkgs.cacert.unbundled}/etc/ssl/certs/Amazon_Root_CA_1:66c9fcf99bf8c0a39e2f0788a43e696365bca.crt";
Group = cfg.group;
Restart = "on-failure";
StateDirectory = "govee2mqtt";
User = cfg.user;
# Hardening
AmbientCapabilities = "";
CapabilityBoundingSet = "";
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
};
};
};
}

View File

@ -52,7 +52,7 @@ let
hasAttrByPath (splitString "." component) cfg.config
|| useComponentPlatform component
|| useExplicitComponent component
|| builtins.elem component cfg.extraComponents;
|| builtins.elem component (cfg.extraComponents ++ cfg.defaultIntegrations);
# Final list of components passed into the package to include required dependencies
extraComponents = filter useComponent availableComponents;
@ -103,6 +103,45 @@ in {
description = lib.mdDoc "The config directory, where your {file}`configuration.yaml` is located.";
};
defaultIntegrations = mkOption {
type = types.listOf (types.enum availableComponents);
# https://github.com/home-assistant/core/blob/dev/homeassistant/bootstrap.py#L109
default = [
"application_credentials"
"frontend"
"hardware"
"logger"
"network"
"system_health"
# key features
"automation"
"person"
"scene"
"script"
"tag"
"zone"
# built-in helpers
"counter"
"input_boolean"
"input_button"
"input_datetime"
"input_number"
"input_select"
"input_text"
"schedule"
"timer"
# non-supervisor
"backup"
];
readOnly = true;
description = ''
List of integrations set are always set up, unless in recovery mode.
'';
};
extraComponents = mkOption {
type = types.listOf (types.enum availableComponents);
default = [
@ -533,6 +572,7 @@ in {
"inkbird"
"improv_ble"
"keymitt_ble"
"leaone-ble"
"led_ble"
"medcom_ble"
"melnor"

View File

@ -126,8 +126,9 @@ then enable `services.matrix-synapse.settings.enable_registration = true;`.
Otherwise, or you can generate a registration secret with
{command}`pwgen -s 64 1` and set it with
[](#opt-services.matrix-synapse.settings.registration_shared_secret).
To create a new user or admin, run the following after you have set the secret
and have rebuilt NixOS:
To create a new user or admin from the terminal your client listener
must be configured to use TCP sockets. Then you can run the following
after you have set the secret and have rebuilt NixOS:
```ShellSession
$ nix-shell -p matrix-synapse
$ register_new_matrix_user -k your-registration-shared-secret http://localhost:8008

View File

@ -6,8 +6,16 @@ let
cfg = config.services.matrix-synapse;
format = pkgs.formats.yaml { };
filterRecursiveNull = o:
if isAttrs o then
mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o)
else if isList o then
map filterRecursiveNull (filter (v: v != null) o)
else
o;
# remove null values from the final configuration
finalSettings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings;
finalSettings = filterRecursiveNull cfg.settings;
configFile = format.generate "homeserver.yaml" finalSettings;
usePostgresql = cfg.settings.database.name == "psycopg2";
@ -105,6 +113,19 @@ let
SYSLOG_IDENTIFIER = logName;
};
});
toIntBase8 = str:
lib.pipe str [
lib.stringToCharacters
(map lib.toInt)
(lib.foldl (acc: digit: acc * 8 + digit) 0)
];
toDecimalFilePermission = value:
if value == null then
null
else
toIntBase8 value;
in {
imports = [
@ -192,10 +213,11 @@ in {
];
options = let
listenerType = workerContext: types.submodule {
listenerType = workerContext: types.submodule ({ config, ... }: {
options = {
port = mkOption {
type = types.port;
type = types.nullOr types.port;
default = null;
example = 8448;
description = lib.mdDoc ''
The port to listen for HTTP(S) requests on.
@ -203,11 +225,20 @@ in {
};
bind_addresses = mkOption {
type = types.listOf types.str;
default = [
type = types.nullOr (types.listOf types.str);
default = if config.path != null then null else [
"::1"
"127.0.0.1"
];
defaultText = literalExpression ''
if path != null then
null
else
[
"::1"
"127.0.0.1"
]
'';
example = literalExpression ''
[
"::"
@ -219,6 +250,35 @@ in {
'';
};
path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Unix domain socket path to bind this listener to.
::: {.note}
This option is incompatible with {option}`bind_addresses`, {option}`port`, {option}`tls`
and also does not support the `metrics` and `manhole` listener {option}`type`.
:::
'';
};
mode = mkOption {
type = types.nullOr (types.strMatching "^[0,2-7]{3,4}$");
default = if config.path != null then "660" else null;
defaultText = literalExpression ''
if path != null then
"660"
else
null
'';
example = "660";
description = ''
File permissions on the UNIX domain socket.
'';
apply = toDecimalFilePermission;
};
type = mkOption {
type = types.enum [
"http"
@ -234,17 +294,30 @@ in {
};
tls = mkOption {
type = types.bool;
default = !workerContext;
type = types.nullOr types.bool;
default = if config.path != null then
null
else
!workerContext;
defaultText = ''
Enabled for the main instance listener, unless it is configured with a UNIX domain socket path.
'';
example = false;
description = lib.mdDoc ''
Whether to enable TLS on the listener socket.
::: {.note}
This option will be ignored for UNIX domain sockets.
:::
'';
};
x_forwarded = mkOption {
type = types.bool;
default = false;
default = config.path != null;
defaultText = ''
Enabled if the listener is configured with a UNIX domain socket path
'';
example = true;
description = lib.mdDoc ''
Use the X-Forwarded-For (XFF) header as the client IP and not the
@ -291,11 +364,28 @@ in {
'';
};
};
};
});
in {
services.matrix-synapse = {
enable = mkEnableOption (lib.mdDoc "matrix.org synapse");
enableRegistrationScript = mkOption {
type = types.bool;
default = clientListener.bind_addresses != [];
example = false;
defaultText = ''
Enabled if the client listener uses TCP sockets
'';
description = ''
Whether to install the `register_new_matrix_user` script, that
allows account creation on the terminal.
::: {.note}
This script does not work when the client listener uses UNIX domain sockets
:::
'';
};
serviceUnit = lib.mkOption {
type = lib.types.str;
readOnly = true;
@ -616,11 +706,8 @@ in {
compress = false;
}];
}] ++ lib.optional hasWorkers {
port = 9093;
bind_addresses = [ "127.0.0.1" ];
path = "/run/matrix-synapse/main_replication.sock";
type = "http";
tls = false;
x_forwarded = false;
resources = [{
names = [ "replication" ];
compress = false;
@ -630,7 +717,7 @@ in {
List of ports that Synapse should listen on, their purpose and their configuration.
By default, synapse will be configured for client and federation traffic on port 8008, and
for worker replication traffic on port 9093. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers)
use a UNIX domain socket for worker replication. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers)
for more details.
'';
};
@ -1006,9 +1093,15 @@ in {
listener = lib.findFirst
(
listener:
listener.port == main.port
(
lib.hasAttr "port" main && listener.port or null == main.port
|| lib.hasAttr "path" main && listener.path or null == main.path
)
&& listenerSupportsResource "replication" listener
&& (lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses)
&& (
lib.hasAttr "host" main && lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses
|| lib.hasAttr "path" main
)
)
null
cfg.settings.listeners;
@ -1022,15 +1115,44 @@ in {
This is done by default unless you manually configure either of those settings.
'';
}
];
{
assertion = cfg.enableRegistrationScript -> clientListener.path == null;
message = ''
The client listener on matrix-synapse is configured to use UNIX domain sockets.
This configuration is incompatible with the `register_new_matrix_user` script.
Disable `services.mastrix-synapse.enableRegistrationScript` to continue.
'';
}
]
++ (map (listener: {
assertion = (listener.path == null) != (listener.bind_addresses == null);
message = ''
Listeners require either a UNIX domain socket `path` or `bind_addresses` for a TCP socket.
'';
}) cfg.settings.listeners)
++ (map (listener: {
assertion = listener.path != null -> (listener.bind_addresses == null && listener.port == null && listener.tls == null);
message = let
formatKeyValue = key: value: lib.optionalString (value != null) " - ${key}=${toString value}\n";
in ''
Listener configured with UNIX domain socket (${toString listener.path}) ignores the following options:
${formatKeyValue "bind_addresses" listener.bind_addresses}${formatKeyValue "port" listener.port}${formatKeyValue "tls" listener.tls}
'';
}) cfg.settings.listeners)
++ (map (listener: {
assertion = listener.path == null || listener.type == "http";
message = ''
Listener configured with UNIX domain socket (${toString listener.path}) only supports the "http" listener type.
'';
}) cfg.settings.listeners);
services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally {
enabled = true;
path = config.services.redis.servers.matrix-synapse.unixSocket;
};
services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault {
host = "127.0.0.1";
port = 9093;
path = "/run/matrix-synapse/main_replication.sock";
});
services.matrix-synapse.serviceUnit = if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service";
@ -1086,6 +1208,8 @@ in {
User = "matrix-synapse";
Group = "matrix-synapse";
WorkingDirectory = cfg.dataDir;
RuntimeDirectory = "matrix-synapse";
RuntimeDirectoryPreserve = true;
ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
Restart = "on-failure";
UMask = "0077";
@ -1178,7 +1302,9 @@ in {
user = "matrix-synapse";
};
environment.systemPackages = [ registerNewMatrixUser ];
environment.systemPackages = lib.optionals cfg.enableRegistrationScript [
registerNewMatrixUser
];
};
meta = {

View File

@ -681,6 +681,11 @@ in
optional (cfg.database.password != "") "config.services.gitea.database.password will be stored as plaintext in the Nix store. Use database.passwordFile instead." ++
optional (cfg.extraConfig != null) ''
services.gitea.`extraConfig` is deprecated, please use services.gitea.`settings`.
'' ++
optional (lib.getName cfg.package == "forgejo") ''
Running forgejo via services.gitea.package is no longer supported.
Please use services.forgejo instead.
See https://nixos.org/manual/nixos/unstable/#module-forgejo for migration instructions.
'';
# Create database passwordFile default when password is configured.

View File

@ -1386,10 +1386,8 @@ in {
systemd.services.gitlab-db-config = {
after = [ "gitlab-config.service" "gitlab-postgresql.service" "postgresql.service" ];
bindsTo = [
"gitlab-config.service"
] ++ optional (cfg.databaseHost == "") "postgresql.service"
++ optional databaseActuallyCreateLocally "gitlab-postgresql.service";
wants = optional (cfg.databaseHost == "") "postgresql.service" ++ optional databaseActuallyCreateLocally "gitlab-postgresql.service";
bindsTo = [ "gitlab-config.service" ];
wantedBy = [ "gitlab.target" ];
partOf = [ "gitlab.target" ];
serviceConfig = {
@ -1422,10 +1420,10 @@ in {
"gitlab-db-config.service"
];
bindsTo = [
"redis-gitlab.service"
"gitlab-config.service"
"gitlab-db-config.service"
] ++ optional (cfg.databaseHost == "") "postgresql.service";
];
wants = [ "redis-gitlab.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service";
wantedBy = [ "gitlab.target" ];
partOf = [ "gitlab.target" ];
environment = gitlabEnv // (optionalAttrs cfg.sidekiq.memoryKiller.enable {
@ -1612,10 +1610,10 @@ in {
"gitlab-db-config.service"
];
bindsTo = [
"redis-gitlab.service"
"gitlab-config.service"
"gitlab-db-config.service"
] ++ optional (cfg.databaseHost == "") "postgresql.service";
];
wants = [ "redis-gitlab.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service";
requiredBy = [ "gitlab.target" ];
partOf = [ "gitlab.target" ];
environment = gitlabEnv;

View File

@ -1,109 +1,149 @@
{ config, pkgs, lib, ... }:
with lib;
let
inherit (lib) mkIf getExe maintainers mkEnableOption mkOption mkPackageOption;
inherit (lib.types) str path bool;
cfg = config.services.jellyfin;
in
{
options = {
services.jellyfin = {
enable = mkEnableOption (lib.mdDoc "Jellyfin Media Server");
user = mkOption {
type = types.str;
default = "jellyfin";
description = lib.mdDoc "User account under which Jellyfin runs.";
};
enable = mkEnableOption "Jellyfin Media Server";
package = mkPackageOption pkgs "jellyfin" { };
group = mkOption {
type = types.str;
user = mkOption {
type = str;
default = "jellyfin";
description = lib.mdDoc "Group under which jellyfin runs.";
description = "User account under which Jellyfin runs.";
};
group = mkOption {
type = str;
default = "jellyfin";
description = "Group under which jellyfin runs.";
};
dataDir = mkOption {
type = path;
default = "/var/lib/jellyfin";
description = ''
Base data directory,
passed with `--datadir` see [#data-directory](https://jellyfin.org/docs/general/administration/configuration/#data-directory)
'';
};
configDir = mkOption {
type = path;
default = "${cfg.dataDir}/config";
defaultText = "\${cfg.dataDir}/config";
description = ''
Directory containing the server configuration files,
passed with `--configdir` see [configuration-directory](https://jellyfin.org/docs/general/administration/configuration/#configuration-directory)
'';
};
cacheDir = mkOption {
type = path;
default = "/var/cache/jellyfin";
description = ''
Directory containing the jellyfin server cache,
passed with `--cachedir` see [#cache-directory](https://jellyfin.org/docs/general/administration/configuration/#cache-directory)
'';
};
logDir = mkOption {
type = path;
default = "${cfg.dataDir}/log";
defaultText = "\${cfg.dataDir}/log";
description = ''
Directory where the Jellyfin logs will be stored,
passed with `--logdir` see [#log-directory](https://jellyfin.org/docs/general/administration/configuration/#log-directory)
'';
};
openFirewall = mkOption {
type = types.bool;
type = bool;
default = false;
description = lib.mdDoc ''
description = ''
Open the default ports in the firewall for the media server. The
HTTP/HTTPS ports can be changed in the Web UI, so this option should
only be used if they are unchanged.
only be used if they are unchanged, see [Port Bindings](https://jellyfin.org/docs/general/networking/#port-bindings).
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.jellyfin = {
description = "Jellyfin Media Server";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
systemd = {
tmpfiles.settings.jellyfinDirs = {
"${cfg.dataDir}"."d" = {
mode = "700";
inherit (cfg) user group;
};
"${cfg.configDir}"."d" = {
mode = "700";
inherit (cfg) user group;
};
"${cfg.logDir}"."d" = {
mode = "700";
inherit (cfg) user group;
};
"${cfg.cacheDir}"."d" = {
mode = "700";
inherit (cfg) user group;
};
};
services.jellyfin = {
description = "Jellyfin Media Server";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
# This is mostly follows: https://github.com/jellyfin/jellyfin/blob/master/fedora/jellyfin.service
# Upstream also disable some hardenings when running in LXC, we do the same with the isContainer option
serviceConfig = rec {
Type = "simple";
User = cfg.user;
Group = cfg.group;
StateDirectory = "jellyfin";
StateDirectoryMode = "0700";
CacheDirectory = "jellyfin";
CacheDirectoryMode = "0700";
UMask = "0077";
WorkingDirectory = "/var/lib/jellyfin";
ExecStart = "${cfg.package}/bin/jellyfin --datadir '/var/lib/${StateDirectory}' --cachedir '/var/cache/${CacheDirectory}'";
Restart = "on-failure";
TimeoutSec = 15;
SuccessExitStatus = ["0" "143"];
# This is mostly follows: https://github.com/jellyfin/jellyfin/blob/master/fedora/jellyfin.service
# Upstream also disable some hardenings when running in LXC, we do the same with the isContainer option
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
UMask = "0077";
WorkingDirectory = cfg.dataDir;
ExecStart = "${getExe cfg.package} --datadir '${cfg.dataDir}' --configdir '${cfg.configDir}' --cachedir '${cfg.cacheDir}' --logdir '${cfg.logDir}'";
Restart = "on-failure";
TimeoutSec = 15;
SuccessExitStatus = ["0" "143"];
# Security options:
NoNewPrivileges = true;
SystemCallArchitectures = "native";
# AF_NETLINK needed because Jellyfin monitors the network connection
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
RestrictNamespaces = !config.boot.isContainer;
RestrictRealtime = true;
RestrictSUIDSGID = true;
ProtectControlGroups = !config.boot.isContainer;
ProtectHostname = true;
ProtectKernelLogs = !config.boot.isContainer;
ProtectKernelModules = !config.boot.isContainer;
ProtectKernelTunables = !config.boot.isContainer;
LockPersonality = true;
PrivateTmp = !config.boot.isContainer;
# needed for hardware acceleration
PrivateDevices = false;
PrivateUsers = true;
RemoveIPC = true;
# Security options:
NoNewPrivileges = true;
SystemCallArchitectures = "native";
# AF_NETLINK needed because Jellyfin monitors the network connection
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
RestrictNamespaces = !config.boot.isContainer;
RestrictRealtime = true;
RestrictSUIDSGID = true;
ProtectControlGroups = !config.boot.isContainer;
ProtectHostname = true;
ProtectKernelLogs = !config.boot.isContainer;
ProtectKernelModules = !config.boot.isContainer;
ProtectKernelTunables = !config.boot.isContainer;
LockPersonality = true;
PrivateTmp = !config.boot.isContainer;
# needed for hardware acceleration
PrivateDevices = false;
PrivateUsers = true;
RemoveIPC = true;
SystemCallFilter = [
"~@clock"
"~@aio"
"~@chown"
"~@cpu-emulation"
"~@debug"
"~@keyring"
"~@memlock"
"~@module"
"~@mount"
"~@obsolete"
"~@privileged"
"~@raw-io"
"~@reboot"
"~@setuid"
"~@swap"
];
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"~@clock" "~@aio" "~@chown" "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@module" "~@mount" "~@obsolete" "~@privileged" "~@raw-io" "~@reboot" "~@setuid" "~@swap"
];
SystemCallErrorNumber = "EPERM";
};
};
};
users.users = mkIf (cfg.user == "jellyfin") {
jellyfin = {
group = cfg.group;
inherit (cfg) group;
isSystemUser = true;
};
};
@ -120,5 +160,5 @@ in
};
meta.maintainers = with lib.maintainers; [ minijackson ];
meta.maintainers = with maintainers; [ minijackson nu-nu-ko ];
}

View File

@ -64,8 +64,7 @@ in
example = "--max-freed $((64 * 1024**3))";
type = lib.types.singleLineStr;
description = lib.mdDoc ''
Options given to {file}`nix-collect-garbage` when the
garbage collector is run automatically.
Options given to [`nix-collect-garbage`](https://nixos.org/manual/nix/stable/command-ref/nix-collect-garbage) when the garbage collector is run automatically.
'';
};

View File

@ -1370,5 +1370,5 @@ in
];
meta.doc = ./default.md;
meta.maintainers = with maintainers; [ tomberek nessdoor ];
meta.maintainers = with maintainers; [ tomberek nessdoor christoph-heiss ];
}

View File

@ -24,6 +24,24 @@ in {
The public facing IP of the RustDesk relay.
'';
};
extraSignalArgs = mkOption {
type = listOf str;
default = [];
example = [ "-k" "_" ];
description = ''
A list of extra command line arguments to pass to the `hbbs` process.
'';
};
extraRelayArgs = mkOption {
type = listOf str;
default = [];
example = [ "-k" "_" ];
description = ''
A list of extra command line arguments to pass to the `hbbr` process.
'';
};
};
config = let
@ -83,11 +101,11 @@ in {
};
systemd.services.rustdesk-signal = lib.mkMerge [ serviceDefaults {
serviceConfig.ExecStart = "${cfg.package}/bin/hbbs -r ${cfg.relayIP}";
serviceConfig.ExecStart = "${cfg.package}/bin/hbbs -r ${cfg.relayIP} ${lib.escapeShellArgs cfg.extraSignalArgs}";
} ];
systemd.services.rustdesk-relay = lib.mkMerge [ serviceDefaults {
serviceConfig.ExecStart = "${cfg.package}/bin/hbbr";
serviceConfig.ExecStart = "${cfg.package}/bin/hbbr ${lib.escapeShellArgs cfg.extraRelayArgs}";
} ];
};

View File

@ -276,9 +276,11 @@ in
ingressesSet = filterIngressSet tunnel.ingress;
ingressesStr = filterIngressStr tunnel.ingress;
fullConfig = {
fullConfig = filterConfig {
tunnel = name;
"credentials-file" = tunnel.credentialsFile;
warp-routing = filterConfig tunnel.warp-routing;
originRequest = filterConfig tunnel.originRequest;
ingress =
(map
(key: {
@ -294,6 +296,7 @@ in
(attrNames ingressesStr))
++ [{ service = tunnel.default; }];
};
mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig);
in
nameValuePair "cloudflared-tunnel-${name}" ({
@ -322,5 +325,5 @@ in
};
};
meta.maintainers = with maintainers; [ bbigras ];
meta.maintainers = with maintainers; [ bbigras anpin ];
}

View File

@ -219,6 +219,8 @@ in
'';
} ];
environment.etc."dhcpcd.conf".source = dhcpcdConf;
systemd.services.dhcpcd = let
cfgN = config.networking;
hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "")

View File

@ -909,7 +909,7 @@ in {
in {
settings = {
ssid = bssCfg.ssid;
utf8_ssid = bssCfg.ssid;
utf8_ssid = bssCfg.utf8Ssid;
logger_syslog = mkDefault (-1);
logger_syslog_level = bssCfg.logLevel;
@ -1197,8 +1197,6 @@ in {
environment.systemPackages = [cfg.package];
services.udev.packages = with pkgs; [crda];
systemd.services.hostapd = {
description = "IEEE 802.11 Host Access-Point Daemon";

View File

@ -5,12 +5,7 @@ with lib;
let
cfg = config.services.jibri;
# Copied from the jitsi-videobridge.nix file.
toHOCON = x:
if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}")
else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}"
else if isList x then "[${ concatMapStringsSep "," toHOCON x }]"
else builtins.toJSON x;
format = pkgs.formats.hocon { };
# We're passing passwords in environment variables that have names generated
# from an attribute name, which may not be a valid bash identifier.
@ -38,13 +33,13 @@ let
control-login = {
domain = env.control.login.domain;
username = env.control.login.username;
password.__hocon_envvar = toVarName "${name}_control";
password = format.lib.mkSubstitution (toVarName "${name}_control");
};
call-login = {
domain = env.call.login.domain;
username = env.call.login.username;
password.__hocon_envvar = toVarName "${name}_call";
password = format.lib.mkSubstitution (toVarName "${name}_call");
};
strip-from-room-domain = env.stripFromRoomDomain;
@ -85,13 +80,13 @@ let
};
# Allow overriding leaves of the default config despite types.attrs not doing any merging.
jibriConfig = recursiveUpdate defaultJibriConfig cfg.config;
configFile = pkgs.writeText "jibri.conf" (toHOCON { jibri = jibriConfig; });
configFile = format.generate "jibri.conf" { jibri = jibriConfig; };
in
{
options.services.jibri = with types; {
enable = mkEnableOption (lib.mdDoc "Jitsi BRoadcasting Infrastructure. Currently Jibri must be run on a host that is also running {option}`services.jitsi-meet.enable`, so for most use cases it will be simpler to run {option}`services.jitsi-meet.jibri.enable`");
config = mkOption {
type = attrs;
type = format.type;
default = { };
description = lib.mdDoc ''
Jibri configuration.

View File

@ -5,14 +5,9 @@ with lib;
let
cfg = config.services.jicofo;
# HOCON is a JSON superset that some jitsi-meet components use for configuration
toHOCON = x: if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}")
else if isAttrs x && x ? __hocon_unquoted_string then x.__hocon_unquoted_string
else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}"
else if isList x then "[${ concatMapStringsSep "," toHOCON x }]"
else builtins.toJSON x;
format = pkgs.formats.hocon { };
configFile = pkgs.writeText "jicofo.conf" (toHOCON cfg.config);
configFile = format.generate "jicofo.conf" cfg.config;
in
{
options.services.jicofo = with types; {
@ -77,7 +72,7 @@ in
};
config = mkOption {
type = (pkgs.formats.json {}).type;
type = format.type;
default = { };
example = literalExpression ''
{
@ -99,7 +94,7 @@ in
hostname = cfg.xmppHost;
username = cfg.userName;
domain = cfg.userDomain;
password = { __hocon_envvar = "JICOFO_AUTH_PASS"; };
password = format.lib.mkSubstitution "JICOFO_AUTH_PASS";
xmpp-domain = if cfg.xmppDomain == null then cfg.xmppHost else cfg.xmppDomain;
};
service = client;

View File

@ -6,16 +6,7 @@ let
cfg = config.services.jitsi-videobridge;
attrsToArgs = a: concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") a);
# HOCON is a JSON superset that videobridge2 uses for configuration.
# It can substitute environment variables which we use for passwords here.
# https://github.com/lightbend/config/blob/master/README.md
#
# Substitution for environment variable FOO is represented as attribute set
# { __hocon_envvar = "FOO"; }
toHOCON = x: if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}")
else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}"
else if isList x then "[${ concatMapStringsSep "," toHOCON x }]"
else builtins.toJSON x;
format = pkgs.formats.hocon { };
# We're passing passwords in environment variables that have names generated
# from an attribute name, which may not be a valid bash identifier.
@ -38,7 +29,7 @@ let
hostname = xmppConfig.hostName;
domain = xmppConfig.domain;
username = xmppConfig.userName;
password = { __hocon_envvar = toVarName name; };
password = format.lib.mkSubstitution (toVarName name);
muc_jids = xmppConfig.mucJids;
muc_nickname = xmppConfig.mucNickname;
disable_certificate_verification = xmppConfig.disableCertificateVerification;
@ -221,7 +212,7 @@ in
"-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi";
"-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "videobridge";
"-Djava.util.logging.config.file" = "/etc/jitsi/videobridge/logging.properties";
"-Dconfig.file" = pkgs.writeText "jvb.conf" (toHOCON jvbConfig);
"-Dconfig.file" = format.generate "jvb.conf" jvbConfig;
# Mitigate CVE-2021-44228
"-Dlog4j2.formatMsgNoLookups" = true;
} // (mapAttrs' (k: v: nameValuePair "-D${k}" v) cfg.extraProperties);

View File

@ -133,9 +133,6 @@ in
"ipsec.d/01-nixos.conf".source = configFile;
} // policyFiles;
# Create NSS database directory
systemd.tmpfiles.rules = [ "d /var/lib/ipsec/nss 755 root root -" ];
systemd.services.ipsec = {
description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec";
wantedBy = [ "multi-user.target" ];
@ -153,6 +150,10 @@ in
echo 0 | tee /proc/sys/net/ipv4/conf/*/send_redirects
echo 0 | tee /proc/sys/net/ipv{4,6}/conf/*/accept_redirects
'';
serviceConfig = {
StateDirectory = "ipsec/nss";
StateDirectoryMode = 0700;
};
};
};

View File

@ -326,6 +326,29 @@ in
RuntimeDirectoryMode = "0700";
User = "murmur";
Group = "murmur";
# service hardening
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "full";
RestrictAddressFamilies = "~AF_PACKET AF_NETLINK";
RestrictNamespaces = true;
RestrictSUIDSGID = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = "@system-service";
};
};

View File

@ -185,6 +185,19 @@ in
can be loaded using "nft -f". The ruleset is updated atomically.
'';
};
networking.nftables.flattenRulesetFile = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Use `builtins.readFile` rather than `include` to handle {option}`networking.nftables.rulesetFile`. It is useful when you want to apply {option}`networking.nftables.preCheckRuleset` to {option}`networking.nftables.rulesetFile`.
::: {.note}
It is expected that {option}`networking.nftables.rulesetFile` can be accessed from the build sandbox.
:::
'';
};
networking.nftables.tables = mkOption {
type = types.attrsOf (types.submodule tableSubmodule);
@ -252,8 +265,10 @@ in
networking.nftables.flushRuleset = mkDefault (versionOlder config.system.stateVersion "23.11" || (cfg.rulesetFile != null || cfg.ruleset != ""));
systemd.services.nftables = {
description = "nftables firewall";
before = [ "network-pre.target" ];
wants = [ "network-pre.target" ];
after = [ "sysinit.target" ];
before = [ "network-pre.target" "shutdown.target" ];
conflicts = [ "shutdown.target" ];
wants = [ "network-pre.target" "sysinit.target" ];
wantedBy = [ "multi-user.target" ];
reloadIfChanged = true;
serviceConfig = let
@ -293,9 +308,13 @@ in
}
'') enabledTables)}
${cfg.ruleset}
${lib.optionalString (cfg.rulesetFile != null) ''
include "${cfg.rulesetFile}"
''}
${if cfg.rulesetFile != null then
if cfg.flattenRulesetFile then
builtins.readFile cfg.rulesetFile
else ''
include "${cfg.rulesetFile}"
''
else ""}
'';
checkPhase = lib.optionalString cfg.checkRuleset ''
cp $out ruleset.conf
@ -315,6 +334,7 @@ in
ExecStop = [ deletionsScriptVar cleanupDeletionsScript ];
StateDirectory = "nftables";
};
unitConfig.DefaultDependencies = false;
};
};
}

View File

@ -34,6 +34,18 @@ in
description = "Directory to store downloads.";
};
user = mkOption {
type = types.str;
default = "pyload";
description = "User under which pyLoad runs, and which owns the download directory.";
};
group = mkOption {
type = types.str;
default = "pyload";
description = "Group under which pyLoad runs, and which owns the download directory.";
};
credentialsFile = mkOption {
type = with types; nullOr path;
default = null;
@ -52,7 +64,7 @@ in
config = lib.mkIf cfg.enable {
systemd.tmpfiles.settings.pyload = {
${cfg.downloadDirectory}.d = { };
${cfg.downloadDirectory}.d = { inherit (cfg) user group; };
};
systemd.services.pyload = {
@ -80,9 +92,8 @@ in
cfg.downloadDirectory
];
User = "pyload";
Group = "pyload";
DynamicUser = true;
User = cfg.user;
Group = cfg.group;
EnvironmentFile = lib.optional (cfg.credentialsFile != null) cfg.credentialsFile;
@ -143,5 +154,13 @@ in
];
};
};
users.users.pyload = lib.mkIf (cfg.user == "pyload") {
isSystemUser = true;
group = cfg.group;
home = stateDir;
};
users.groups.pyload = lib.mkIf (cfg.group == "pyload") { };
};
}

View File

@ -0,0 +1,32 @@
{ config
, pkgs
, lib
, ...
}:
let
cfg = config.services.intune;
in
{
options.services.intune = {
enable = lib.mkEnableOption (lib.mdDoc "Microsoft Intune");
};
config = lib.mkIf cfg.enable {
users.users.microsoft-identity-broker = {
group = "microsoft-identity-broker";
isSystemUser = true;
};
users.groups.microsoft-identity-broker = { };
environment.systemPackages = [ pkgs.microsoft-identity-broker pkgs.intune-portal ];
systemd.packages = [ pkgs.microsoft-identity-broker pkgs.intune-portal ];
systemd.tmpfiles.packages = [ pkgs.intune-portal ];
services.dbus.packages = [ pkgs.microsoft-identity-broker ];
};
meta = {
maintainers = with lib.maintainers; [ rhysmdnz ];
};
}

View File

@ -165,10 +165,17 @@ in
type = lib.types.submodule {
freeformType = settingsFormat.type;
options.pam_allowed_login_groups = lib.mkOption {
description = lib.mdDoc "Kanidm groups that are allowed to login using PAM.";
example = "my_pam_group";
type = lib.types.listOf lib.types.str;
options = {
pam_allowed_login_groups = lib.mkOption {
description = lib.mdDoc "Kanidm groups that are allowed to login using PAM.";
example = "my_pam_group";
type = lib.types.listOf lib.types.str;
};
hsm_pin_path = lib.mkOption {
description = lib.mdDoc "Path to a HSM pin.";
default = "/var/cache/kanidm-unixd/hsm-pin";
type = lib.types.path;
};
};
};
description = lib.mdDoc ''

View File

@ -36,7 +36,8 @@ in {
description = mdDoc ''
Declarative configuration of firewall rules.
All rules will be stored in `/var/lib/opensnitch/rules`.
All rules will be stored in `/var/lib/opensnitch/rules` by default.
Rules path can be configured with `settings.Rules.Path`.
See [upstream documentation](https://github.com/evilsocket/opensnitch/wiki/Rules)
for available options.
'';
@ -79,15 +80,6 @@ in {
'';
};
DefaultDuration = mkOption {
type = types.enum [
"once" "always" "until restart" "30s" "5m" "15m" "30m" "1h"
];
description = mdDoc ''
Default duration of firewall rule.
'';
};
InterceptUnknown = mkOption {
type = types.bool;
description = mdDoc ''
@ -134,6 +126,30 @@ in {
};
};
Ebpf.ModulesPath = mkOption {
type = types.path;
default = if cfg.settings.ProcMonitorMethod == "ebpf" then "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd" else null;
defaultText = literalExpression ''
if cfg.settings.ProcMonitorMethod == "ebpf" then
"\\$\\{config.boot.kernelPackages.opensnitch-ebpf\\}/etc/opensnitchd"
else null;
'';
description = mdDoc ''
Configure eBPF modules path. Used when
`settings.ProcMonitorMethod` is set to `ebpf`.
'';
};
Rules.Path = mkOption {
type = types.path;
default = "/var/lib/opensnitch/rules";
description = mdDoc ''
Path to the directory where firewall rules can be found and will
get stored by the NixOS module.
'';
};
};
};
description = mdDoc ''
@ -151,40 +167,42 @@ in {
systemd = {
packages = [ pkgs.opensnitch ];
services.opensnitchd.wantedBy = [ "multi-user.target" ];
services.opensnitchd = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = [
""
"${pkgs.opensnitch}/bin/opensnitchd --config-file ${format.generate "default-config.json" cfg.settings}"
];
};
preStart = mkIf (cfg.rules != {}) (let
rules = flip mapAttrsToList predefinedRules (file: content: {
inherit (content) file;
local = "${cfg.settings.Rules.Path}/${file}.json";
});
in ''
# Remove all firewall rules from rules path (configured with
# cfg.settings.Rules.Path) that are symlinks to a store-path, but aren't
# declared in `cfg.rules` (i.e. all networks that were "removed" from
# `cfg.rules`).
find ${cfg.settings.Rules.Path} -type l -lname '${builtins.storeDir}/*' ${optionalString (rules != {}) ''
-not \( ${concatMapStringsSep " -o " ({ local, ... }:
"-name '${baseNameOf local}*'")
rules} \) \
''} -delete
${concatMapStrings ({ file, local }: ''
ln -sf '${file}' "${local}"
'') rules}
'');
};
tmpfiles.rules = [
"d ${cfg.settings.Rules.Path} 0750 root root - -"
"L+ /etc/opensnitchd/system-fw.json - - - - ${pkgs.opensnitch}/etc/opensnitchd/system-fw.json"
];
};
systemd.services.opensnitchd.preStart = mkIf (cfg.rules != {}) (let
rules = flip mapAttrsToList predefinedRules (file: content: {
inherit (content) file;
local = "/var/lib/opensnitch/rules/${file}.json";
});
in ''
# Remove all firewall rules from `/var/lib/opensnitch/rules` that are symlinks to a store-path,
# but aren't declared in `cfg.rules` (i.e. all networks that were "removed" from
# `cfg.rules`).
find /var/lib/opensnitch/rules -type l -lname '${builtins.storeDir}/*' ${optionalString (rules != {}) ''
-not \( ${concatMapStringsSep " -o " ({ local, ... }:
"-name '${baseNameOf local}*'")
rules} \) \
''} -delete
${concatMapStrings ({ file, local }: ''
ln -sf '${file}' "${local}"
'') rules}
if [ ! -f /etc/opensnitchd/system-fw.json ]; then
cp "${pkgs.opensnitch}/etc/opensnitchd/system-fw.json" "/etc/opensnitchd/system-fw.json"
fi
'');
environment.etc = mkMerge [ ({
"opensnitchd/default-config.json".source = format.generate "default-config.json" cfg.settings;
}) (mkIf (cfg.settings.ProcMonitorMethod == "ebpf") {
"opensnitchd/opensnitch.o".source = "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd/opensnitch.o";
"opensnitchd/opensnitch-dns.o".source = "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd/opensnitch-dns.o";
"opensnitchd/opensnitch-procs.o".source = "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd/opensnitch-procs.o";
})];
};
meta.maintainers = with lib.maintainers; [ onny ];
}

View File

@ -17,7 +17,7 @@ let
cfg = config.services.frigate;
format = pkgs.formats.yaml {};
format = pkgs.formats.yaml { };
filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! lib.elem v [ null ])) cfg.settings;
@ -112,7 +112,7 @@ in
};
};
};
default = {};
default = { };
description = mdDoc ''
Frigate configuration as a nix attribute set.
@ -125,7 +125,7 @@ in
config = mkIf cfg.enable {
services.nginx = {
enable =true;
enable = true;
additionalModules = with pkgs.nginxModules; [
secure-token
rtmp
@ -133,31 +133,64 @@ in
];
recommendedProxySettings = mkDefault true;
recommendedGzipSettings = mkDefault true;
mapHashBucketSize = mkDefault 128;
upstreams = {
frigate-api.servers = {
"127.0.0.1:5001" = {};
"127.0.0.1:5001" = { };
};
frigate-mqtt-ws.servers = {
"127.0.0.1:5002" = {};
"127.0.0.1:5002" = { };
};
frigate-jsmpeg.servers = {
"127.0.0.1:8082" = {};
"127.0.0.1:8082" = { };
};
frigate-go2rtc.servers = {
"127.0.0.1:1984" = {};
"127.0.0.1:1984" = { };
};
};
# Based on https://github.com/blakeblackshear/frigate/blob/v0.12.0/docker/rootfs/usr/local/nginx/conf/nginx.conf
proxyCachePath."frigate" = {
enable = true;
keysZoneSize = "10m";
keysZoneName = "frigate_api_cache";
maxSize = "10m";
inactive = "1m";
levels = "1:2";
};
# Based on https://github.com/blakeblackshear/frigate/blob/v0.13.1/docker/main/rootfs/usr/local/nginx/conf/nginx.conf
virtualHosts."${cfg.hostname}" = {
locations = {
"/api/" = {
proxyPass = "http://frigate-api/";
extraConfig = ''
proxy_cache frigate_api_cache;
proxy_cache_lock on;
proxy_cache_use_stale updating;
proxy_cache_valid 200 5s;
proxy_cache_bypass $http_x_cache_bypass;
proxy_no_cache $should_not_cache;
add_header X-Cache-Status $upstream_cache_status;
location /api/vod/ {
proxy_pass http://frigate-api/vod/;
proxy_cache off;
}
location /api/stats {
access_log off;
rewrite ^/api/(.*)$ $1 break;
proxy_pass http://frigate-api;
}
location /api/version {
access_log off;
rewrite ^/api/(.*)$ $1 break;
proxy_pass http://frigate-api;
}
'';
};
"~* /api/.*\.(jpg|jpeg|png)$" = {
proxyPass = "http://frigate-api";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
rewrite ^/api/(.*)$ $1 break;
'';
};
@ -169,10 +202,6 @@ in
secure_token $args;
secure_token_types application/vnd.apple.mpegurl;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
add_header Access-Control-Allow-Origin '*';
add_header Cache-Control "no-store";
expires off;
'';
@ -192,27 +221,64 @@ in
proxyPass = "http://frigate-go2rtc/";
proxyWebsockets = true;
};
# frigate lovelace card uses this path
"/live/mse/api/ws" = {
proxyPass = "http://frigate-go2rtc/api/ws";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
"/live/webrtc/" = {
proxyPass = "http://frigate-go2rtc/";
proxyWebsockets = true;
};
"/live/webrtc/api/ws" = {
proxyPass = "http://frigate-go2rtc/api/ws";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
# pass through go2rtc player
"/live/webrtc/webrtc.html" = {
proxyPass = "http://frigate-go2rtc/webrtc.html";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
"/api/go2rtc/api" = {
proxyPass = "http://frigate-go2rtc/api";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
# integrationn uses this to add webrtc candidate
"/api/go2rtc/webrtc" = {
proxyPass = "http://frigate-go2rtc/api/webrtc";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
"/cache/" = {
alias = "/var/cache/frigate/";
};
"/clips/" = {
root = "/var/lib/frigate";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Expose-Headers' 'Content-Length';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
types {
video/mp4 mp4;
image/jpeg jpg;
@ -224,17 +290,6 @@ in
"/recordings/" = {
root = "/var/lib/frigate";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Expose-Headers' 'Content-Length';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
types {
video/mp4 mp4;
}
@ -315,6 +370,12 @@ in
}
}
'';
appendHttpConfig = ''
map $sent_http_content_type $should_not_cache {
'application/json' 0;
default 1;
}
'';
};
systemd.services.nginx.serviceConfig.SupplementaryGroups = [
@ -325,7 +386,7 @@ in
isSystemUser = true;
group = "frigate";
};
users.groups.frigate = {};
users.groups.frigate = { };
systemd.services.frigate = {
after = [

View File

@ -873,9 +873,11 @@ in {
{ systemd.timers.nextcloud-cron = {
wantedBy = [ "timers.target" ];
after = [ "nextcloud-setup.service" ];
timerConfig.OnBootSec = "5m";
timerConfig.OnUnitActiveSec = "5m";
timerConfig.Unit = "nextcloud-cron.service";
timerConfig = {
OnBootSec = "5m";
OnUnitActiveSec = "5m";
Unit = "nextcloud-cron.service";
};
};
systemd.tmpfiles.rules = map (dir: "d ${dir} 0750 nextcloud nextcloud - -") [
@ -992,15 +994,21 @@ in {
nextcloud-cron = {
after = [ "nextcloud-setup.service" ];
environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config";
serviceConfig.Type = "oneshot";
serviceConfig.User = "nextcloud";
serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${webroot}/cron.php";
serviceConfig = {
Type = "oneshot";
User = "nextcloud";
ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e";
ExecStart = "${lib.getExe phpPackage} -f ${webroot}/cron.php";
KillMode = "process";
};
};
nextcloud-update-plugins = mkIf cfg.autoUpdateApps.enable {
after = [ "nextcloud-setup.service" ];
serviceConfig.Type = "oneshot";
serviceConfig.ExecStart = "${occ}/bin/nextcloud-occ app:update --all";
serviceConfig.User = "nextcloud";
serviceConfig = {
Type = "oneshot";
ExecStart = "${occ}/bin/nextcloud-occ app:update --all";
User = "nextcloud";
};
startAt = cfg.autoUpdateApps.startAt;
};
};

View File

@ -21,7 +21,7 @@ let
eval "$(${config.systemd.package}/bin/systemctl show -pUID,MainPID photoprism.service | ${pkgs.gnused}/bin/sed "s/UID/ServiceUID/")"
exec ${pkgs.util-linux}/bin/nsenter \
-t $MainPID -m -S $ServiceUID -G $ServiceUID --wdns=${cfg.storagePath} \
exec ${cfg.package}/bin/photoprism "$@"
${cfg.package}/bin/photoprism "$@"
'';
in
{

View File

@ -1,380 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
# TODO: are these php-packages needed?
#imagick
#php-geoip -> php.ini: extension = geoip.so
#expat
let
cfg = config.services.restya-board;
fpm = config.services.phpfpm.pools.${poolName};
runDir = "/run/restya-board";
poolName = "restya-board";
in
{
###### interface
options = {
services.restya-board = {
enable = mkEnableOption (lib.mdDoc "restya-board");
dataDir = mkOption {
type = types.path;
default = "/var/lib/restya-board";
description = lib.mdDoc ''
Data of the application.
'';
};
user = mkOption {
type = types.str;
default = "restya-board";
description = lib.mdDoc ''
User account under which the web-application runs.
'';
};
group = mkOption {
type = types.str;
default = "nginx";
description = lib.mdDoc ''
Group account under which the web-application runs.
'';
};
virtualHost = {
serverName = mkOption {
type = types.str;
default = "restya.board";
description = lib.mdDoc ''
Name of the nginx virtualhost to use.
'';
};
listenHost = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc ''
Listen address for the virtualhost to use.
'';
};
listenPort = mkOption {
type = types.port;
default = 3000;
description = lib.mdDoc ''
Listen port for the virtualhost to use.
'';
};
};
database = {
host = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Host of the database. Leave 'null' to use a local PostgreSQL database.
A local PostgreSQL database is initialized automatically.
'';
};
port = mkOption {
type = types.nullOr types.int;
default = 5432;
description = lib.mdDoc ''
The database's port.
'';
};
name = mkOption {
type = types.str;
default = "restya_board";
description = lib.mdDoc ''
Name of the database. The database must exist.
'';
};
user = mkOption {
type = types.str;
default = "restya_board";
description = lib.mdDoc ''
The database user. The user must exist and have access to
the specified database.
'';
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
description = lib.mdDoc ''
The database user's password. 'null' if no password is set.
'';
};
};
email = {
server = mkOption {
type = types.nullOr types.str;
default = null;
example = "localhost";
description = lib.mdDoc ''
Hostname to send outgoing mail. Null to use the system MTA.
'';
};
port = mkOption {
type = types.port;
default = 25;
description = lib.mdDoc ''
Port used to connect to SMTP server.
'';
};
login = mkOption {
type = types.str;
default = "";
description = lib.mdDoc ''
SMTP authentication login used when sending outgoing mail.
'';
};
password = mkOption {
type = types.str;
default = "";
description = lib.mdDoc ''
SMTP authentication password used when sending outgoing mail.
ATTENTION: The password is stored world-readable in the nix-store!
'';
};
};
timezone = mkOption {
type = types.lines;
default = "GMT";
description = lib.mdDoc ''
Timezone the web-app runs in.
'';
};
};
};
###### implementation
config = mkIf cfg.enable {
services.phpfpm.pools = {
${poolName} = {
inherit (cfg) user group;
phpOptions = ''
date.timezone = "CET"
${optionalString (cfg.email.server != null) ''
SMTP = ${cfg.email.server}
smtp_port = ${toString cfg.email.port}
auth_username = ${cfg.email.login}
auth_password = ${cfg.email.password}
''}
'';
settings = mapAttrs (name: mkDefault) {
"listen.owner" = "nginx";
"listen.group" = "nginx";
"listen.mode" = "0600";
"pm" = "dynamic";
"pm.max_children" = 75;
"pm.start_servers" = 10;
"pm.min_spare_servers" = 5;
"pm.max_spare_servers" = 20;
"pm.max_requests" = 500;
"catch_workers_output" = 1;
};
};
};
services.nginx.enable = true;
services.nginx.virtualHosts.${cfg.virtualHost.serverName} = {
listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ];
serverName = cfg.virtualHost.serverName;
root = runDir;
extraConfig = ''
index index.html index.php;
gzip on;
gzip_comp_level 6;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss;
client_max_body_size 300M;
rewrite ^/oauth/authorize$ /server/php/authorize.php last;
rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last;
rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last;
rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last;
rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last;
rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last;
'';
locations."/".root = "${runDir}/client";
locations."~ \\.php$" = {
tryFiles = "$uri =404";
extraConfig = ''
include ${config.services.nginx.package}/conf/fastcgi_params;
fastcgi_pass unix:${fpm.socket};
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PHP_VALUE "upload_max_filesize=9G \n post_max_size=9G \n max_execution_time=200 \n max_input_time=200 \n memory_limit=256M";
'';
};
locations."~* \\.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
root = "${runDir}/client";
extraConfig = ''
if (-f $request_filename) {
break;
}
rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last;
add_header Cache-Control public;
add_header Cache-Control must-revalidate;
expires 7d;
'';
};
};
systemd.services.restya-board-init = {
description = "Restya board initialization";
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
wantedBy = [ "multi-user.target" ];
requires = lib.optional (cfg.database.host != null) "postgresql.service";
after = [ "network.target" ] ++ (lib.optional (cfg.database.host != null) "postgresql.service");
script = ''
rm -rf "${runDir}"
mkdir -m 750 -p "${runDir}"
cp -r "${pkgs.restya-board}/"* "${runDir}"
sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql"
rm -rf "${runDir}/media"
rm -rf "${runDir}/client/img"
chmod -R 0750 "${runDir}"
sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh
${if (cfg.database.host == null) then ''
sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php"
sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php"
'' else ''
sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php"
sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', ${if cfg.database.passwordFile == null then "''" else "'$(cat ${cfg.database.passwordFile})');/g"}" "${runDir}/server/php/config.inc.php"
''}
sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php"
sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php"
sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php"
chmod 0400 "${runDir}/server/php/config.inc.php"
ln -sf "${cfg.dataDir}/media" "${runDir}/media"
ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
chmod g+w "${runDir}/tmp/cache"
chown -R "${cfg.user}":"${cfg.group}" "${runDir}"
mkdir -m 0750 -p "${cfg.dataDir}"
mkdir -m 0750 -p "${cfg.dataDir}/media"
mkdir -m 0750 -p "${cfg.dataDir}/client/img"
cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
chown "${cfg.user}":"${cfg.group}" "${cfg.dataDir}"
chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/media"
chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/client/img"
${optionalString (cfg.database.host == null) ''
if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
-c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'"
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
-c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0"
${pkgs.sudo}/bin/sudo -u ${cfg.user} \
${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \
-d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql"
touch "${cfg.dataDir}/.db-initialized"
fi
''}
'';
};
systemd.timers.restya-board = {
description = "restya-board scripts for e.g. email notification";
wantedBy = [ "timers.target" ];
after = [ "restya-board-init.service" ];
requires = [ "restya-board-init.service" ];
timerConfig = {
OnUnitInactiveSec = "60s";
Unit = "restya-board-timers.service";
};
};
systemd.services.restya-board-timers = {
description = "restya-board scripts for e.g. email notification";
serviceConfig.Type = "oneshot";
serviceConfig.User = cfg.user;
after = [ "restya-board-init.service" ];
requires = [ "restya-board-init.service" ];
script = ''
/bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true
/bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true
/bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true
/bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true
/bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true
'';
};
users.users.restya-board = {
isSystemUser = true;
createHome = false;
home = runDir;
group = "restya-board";
};
users.groups.restya-board = {};
services.postgresql.enable = mkIf (cfg.database.host == null) true;
services.postgresql.identMap = optionalString (cfg.database.host == null)
''
restya-board-users restya-board restya_board
'';
services.postgresql.authentication = optionalString (cfg.database.host == null)
''
local restya_board all ident map=restya-board-users
'';
};
}

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