diff options
Diffstat (limited to 'nixos')
59 files changed, 1308 insertions, 298 deletions
diff --git a/nixos/doc/manual/configuration/profiles/hardened.xml b/nixos/doc/manual/configuration/profiles/hardened.xml index dc83fc837e2a3..4a51754cc7ae2 100644 --- a/nixos/doc/manual/configuration/profiles/hardened.xml +++ b/nixos/doc/manual/configuration/profiles/hardened.xml @@ -7,7 +7,7 @@ <para> A profile with most (vanilla) hardening options enabled by default, - potentially at the cost of features and performance. + potentially at the cost of stability, features and performance. </para> <para> @@ -21,4 +21,12 @@ xlink:href="https://github.com/nixos/nixpkgs/tree/master/nixos/modules/profiles/hardened.nix"> profile source</literal> for further detail on which settings are altered. </para> + <warning> + <para> + This profile enables options that are known to affect system + stability. If you experience any stability issues when using the + profile, try disabling it. If you report an issue and use this + profile, always mention that you do. + </para> + </warning> </section> diff --git a/nixos/doc/manual/release-notes/rl-2103.xml b/nixos/doc/manual/release-notes/rl-2103.xml index 1e3ae23b9b387..05daca1d710cb 100644 --- a/nixos/doc/manual/release-notes/rl-2103.xml +++ b/nixos/doc/manual/release-notes/rl-2103.xml @@ -169,6 +169,14 @@ </para> </listitem> <listitem> + <para> + xfsprogs was update from 4.19 to 5.10. It now enables reflink support by default on filesystem creation. + Support for reflinks was added with an experimental status to kernel 4.9 and deemed stable in kernel 4.16. + If you want to be able to mount XFS filesystems created with this release of xfsprogs on kernel releases older than those, you need to format them + with <literal>mkfs.xfs -m reflink=0</literal>. + </para> + </listitem> + <listitem> <para> <package>btc1</package> has been abandoned upstream, and removed. </para> @@ -299,12 +307,52 @@ </listitem> <listitem> <para> + The <literal>wafHook</literal> hook does not wrap Python anymore. + Packages depending on <literal>wafHook</literal> need to include any Python into their <literal>nativeBuildInputs</literal>. + </para> + </listitem> + <listitem> + <para> Starting with version 1.7.0, the project formerly named <literal>CodiMD</literal> is now named <literal>HedgeDoc</literal>. New installations will no longer use the old name for users, state directories and such, this needs to be considered when moving state to a more recent NixOS installation. Based on <xref linkend="opt-system.stateVersion" />, existing installations will continue to work. </para> </listitem> + <listitem> + <para> + <package>fish-foreign-env</package> is now an alias for the + <package>fishPlugins.foreign-env</package> package, in which the fish + functions have been relocated to the + <literal>vendor_functions.d</literal> directory to be loaded automatically. + </para> + </listitem> + <listitem> + <para> + The prometheus json exporter is now managed by the prometheus community. Together with additional features + some backwards incompatibilities were introduced. + Most importantly the exporter no longer accepts a fixed command-line parameter to specify the URL of the + endpoint serving JSON. It now expects this URL to be passed as an URL parameter, when scraping the exporter's + <literal>/probe</literal> endpoint. + In the prometheus scrape configuration the scrape target might look like this: + <programlisting> +http://some.json-exporter.host:7979/probe?target=https://example.com/some/json/endpoint + </programlisting> + </para> + <para> + Existing configuration for the exporter needs to be updated, but can partially be re-used. + Documentation is available in the upstream repository and a small example for NixOS is available + in the corresponding NixOS test. + </para> + <para> + These changes also affect <xref linkend="opt-services.prometheus.exporters.rspamd.enable" />, which is + just a preconfigured instance of the json exporter. + </para> + <para> + For more information, take a look at the <link xlink:href="https://github.com/prometheus-community/json_exporter"> + official documentation</link> of the json_exporter. + </para> + </listitem> </itemizedlist> </section> diff --git a/nixos/modules/config/console.nix b/nixos/modules/config/console.nix index b8d52d1a8fc7c..ab16676051779 100644 --- a/nixos/modules/config/console.nix +++ b/nixos/modules/config/console.nix @@ -145,7 +145,8 @@ in ''; systemd.services.systemd-vconsole-setup = - { before = [ "display-manager.service" ]; + { + before = optional config.services.xserver.enable "display-manager.service"; after = [ "systemd-udev-settle.service" ]; restartTriggers = [ vconsoleConf consoleEnv ]; }; diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl index 7582371529321..44040217b0271 100644 --- a/nixos/modules/config/update-users-groups.pl +++ b/nixos/modules/config/update-users-groups.pl @@ -227,6 +227,15 @@ foreach my $u (@{$spec->{users}}) { $u->{hashedPassword} = hashPassword($u->{password}); } + if (!defined $u->{shell}) { + if (defined $existing) { + $u->{shell} = $existing->{shell}; + } else { + warn "warning: no declarative or previous shell for ‘$name’, setting shell to nologin\n"; + $u->{shell} = "/run/current-system/sw/bin/nologin"; + } + } + $u->{fakePassword} = $existing->{fakePassword} // "x"; $usersOut{$name} = $u; diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index a957633809864..e90a7d567d42c 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -153,7 +153,7 @@ let }; shell = mkOption { - type = types.either types.shellPackage types.path; + type = types.nullOr (types.either types.shellPackage types.path); default = pkgs.shadow; defaultText = "pkgs.shadow"; example = literalExample "pkgs.bashInteractive"; diff --git a/nixos/modules/hardware/keyboard/zsa.nix b/nixos/modules/hardware/keyboard/zsa.nix new file mode 100644 index 0000000000000..5cb09e5af499f --- /dev/null +++ b/nixos/modules/hardware/keyboard/zsa.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) mkOption mkIf types; + cfg = config.hardware.keyboard.zsa; +in +{ + # TODO: make group configurable like in https://github.com/NixOS/nixpkgs/blob/0b2b4b8c4e729535a61db56468809c5c2d3d175c/pkgs/tools/security/nitrokey-app/udev-rules.nix ? + options.hardware.keyboard.zsa = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables udev rules for keyboards from ZSA like the ErgoDox EZ, Planck EZ and Moonlander Mark I. + You need it when you want to flash a new configuration on the keyboard + or use their live training in the browser. + Access to the keyboard is granted to users in the "plugdev" group. + You may want to install the wally-cli package. + ''; + }; + }; + + config = mkIf cfg.enable { + services.udev.packages = [ pkgs.zsa-udev-rules ]; + users.groups.plugdev = {}; + }; +} diff --git a/nixos/modules/hardware/opentabletdriver.nix b/nixos/modules/hardware/opentabletdriver.nix new file mode 100644 index 0000000000000..0f34d903f68bc --- /dev/null +++ b/nixos/modules/hardware/opentabletdriver.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.hardware.opentabletdriver; +in +{ + options = { + hardware.opentabletdriver = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Enable OpenTabletDriver udev rules, user service and blacklist kernel + modules known to conflict with OpenTabletDriver. + ''; + }; + + blacklistedKernelModules = mkOption { + type = types.listOf types.str; + default = [ "hid-uclogic" "wacom" ]; + description = '' + Blacklist of kernel modules known to conflict with OpenTabletDriver. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.opentabletdriver; + defaultText = "pkgs.opentabletdriver"; + description = '' + OpenTabletDriver derivation to use. + ''; + }; + + daemon = { + enable = mkOption { + default = true; + type = types.bool; + description = '' + Whether to start OpenTabletDriver daemon as a systemd user service. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + services.udev.packages = [ cfg.package ]; + + boot.blacklistedKernelModules = cfg.blacklistedKernelModules; + + systemd.user.services.opentabletdriver = with pkgs; mkIf cfg.daemon.enable { + description = "Open source, cross-platform, user-mode tablet driver"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + + serviceConfig = { + Type = "simple"; + ExecStart = "${cfg.package}/bin/otd-daemon -c ${cfg.package}/lib/OpenTabletDriver/Configurations"; + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix index a5bc436be8262..e4ec2d6240d02 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix @@ -17,8 +17,7 @@ # The serial ports listed here are: # - ttyS0: for Tegra (Jetson TX1) # - ttyAMA0: for QEMU's -machine virt - # Also increase the amount of CMA to ensure the virtual console on the RPi3 works. - boot.kernelParams = ["cma=32M" "console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"]; + boot.kernelParams = ["console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"]; boot.initrd.availableKernelModules = [ # Allows early (earlier) modesetting for the Raspberry Pi @@ -30,13 +29,25 @@ sdImage = { populateFirmwareCommands = let configTxt = pkgs.writeText "config.txt" '' + [pi3] kernel=u-boot-rpi3.bin + [pi4] + kernel=u-boot-rpi4.bin + enable_gic=1 + armstub=armstub8-gic.bin + + # Otherwise the resolution will be weird in most cases, compared to + # what the pi3 firmware does by default. + disable_overscan=1 + + [all] # Boot in 64-bit mode. arm_64bit=1 - # U-Boot used to need this to work, regardless of whether UART is actually used or not. - # TODO: check when/if this can be removed. + # U-Boot needs this to work, regardless of whether UART is actually used or not. + # Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still + # a requirement in the future. enable_uart=1 # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel @@ -45,8 +56,17 @@ ''; in '' (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/) - cp ${pkgs.ubootRaspberryPi3_64bit}/u-boot.bin firmware/u-boot-rpi3.bin + + # Add the config cp ${configTxt} firmware/config.txt + + # Add pi3 specific files + cp ${pkgs.ubootRaspberryPi3_64bit}/u-boot.bin firmware/u-boot-rpi3.bin + + # Add pi4 specific files + cp ${pkgs.ubootRaspberryPi4_64bit}/u-boot.bin firmware/u-boot-rpi4.bin + cp ${pkgs.raspberrypi-armstubs}/armstub8-gic.bin firmware/armstub8-gic.bin + cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-4-b.dtb firmware/ ''; populateRootCommands = '' mkdir -p ./files/boot diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix index 87545e8420308..5bdec7de86e8a 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix @@ -3,36 +3,6 @@ { config, lib, pkgs, ... }: { - imports = [ - ../../profiles/base.nix - ../../profiles/installation-device.nix - ./sd-image.nix - ]; - - boot.loader.grub.enable = false; - boot.loader.raspberryPi.enable = true; - boot.loader.raspberryPi.version = 4; + imports = [ ./sd-image-aarch64.nix ]; boot.kernelPackages = pkgs.linuxPackages_rpi4; - - boot.consoleLogLevel = lib.mkDefault 7; - - sdImage = { - firmwareSize = 128; - firmwarePartitionName = "NIXOS_BOOT"; - # This is a hack to avoid replicating config.txt from boot.loader.raspberryPi - populateFirmwareCommands = - "${config.system.build.installBootLoader} ${config.system.build.toplevel} -d ./firmware"; - # As the boot process is done entirely in the firmware partition. - populateRootCommands = ""; - }; - - fileSystems."/boot/firmware" = { - # This effectively "renames" the attrsOf entry set in sd-image.nix - mountPoint = "/boot"; - neededForBoot = true; - }; - - # the installation media is also the installation target, - # so we don't want to provide the installation configuration.nix. - installer.cloneConfig = false; } diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix index 6d4ba96dba0c1..8159576a62ac7 100644 --- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix +++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix @@ -104,7 +104,7 @@ in ''; # Some more help text. - services.mingetty.helpLine = + services.getty.helpLine = '' Log in as "root" with an empty password. ${ diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix index 0e67ae7de6980..95579f3ca06d2 100644 --- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix +++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix @@ -122,7 +122,7 @@ in device = "/dev/something"; }; - services.mingetty = { + services.getty = { # Some more help text. helpLine = '' Log in as "root" with an empty password. ${ diff --git a/nixos/modules/installer/tools/nixos-enter.sh b/nixos/modules/installer/tools/nixos-enter.sh index c72ef6e9c28b3..450d776181489 100644 --- a/nixos/modules/installer/tools/nixos-enter.sh +++ b/nixos/modules/installer/tools/nixos-enter.sh @@ -69,6 +69,9 @@ mount --rbind /sys "$mountPoint/sys" # Run the activation script. Set $LOCALE_ARCHIVE to supress some Perl locale warnings. LOCALE_ARCHIVE="$system/sw/lib/locale/locale-archive" chroot "$mountPoint" "$system/activate" 1>&2 || true + + # Create /tmp + chroot "$mountPoint" systemd-tmpfiles --create --remove --exclude-prefix=/dev 1>&2 || true ) exec chroot "$mountPoint" "${command[@]}" diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix index fe0263f158f43..d81d6c6cb9b8c 100644 --- a/nixos/modules/misc/documentation.nix +++ b/nixos/modules/misc/documentation.nix @@ -261,7 +261,7 @@ in ++ optionals cfg.doc.enable ([ manual.manualHTML nixos-help ] ++ optionals config.services.xserver.enable [ pkgs.nixos-icons ]); - services.mingetty.helpLine = mkIf cfg.doc.enable ( + services.getty.helpLine = mkIf cfg.doc.enable ( "\nRun 'nixos-help' for the NixOS manual." ); }) diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 3b67a8574938c..8fd5d4519fdd8 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -47,6 +47,7 @@ ./hardware/digitalbitbox.nix ./hardware/device-tree.nix ./hardware/sensor/iio.nix + ./hardware/keyboard/zsa.nix ./hardware/ksm.nix ./hardware/ledger.nix ./hardware/logitech.nix @@ -65,6 +66,7 @@ ./hardware/tuxedo-keyboard.nix ./hardware/usb-wwan.nix ./hardware/onlykey.nix + ./hardware/opentabletdriver.nix ./hardware/wooting.nix ./hardware/uinput.nix ./hardware/video/amdgpu.nix @@ -140,6 +142,7 @@ ./programs/light.nix ./programs/mosh.nix ./programs/mininet.nix + ./programs/msmtp.nix ./programs/mtr.nix ./programs/nano.nix ./programs/neovim.nix @@ -537,6 +540,7 @@ ./services/monitoring/do-agent.nix ./services/monitoring/fusion-inventory.nix ./services/monitoring/grafana.nix + ./services/monitoring/grafana-image-renderer.nix ./services/monitoring/grafana-reporter.nix ./services/monitoring/graphite.nix ./services/monitoring/hdaps.nix @@ -742,6 +746,7 @@ ./services/networking/skydns.nix ./services/networking/shadowsocks.nix ./services/networking/shairport-sync.nix + ./services/networking/shellhub-agent.nix ./services/networking/shorewall.nix ./services/networking/shorewall6.nix ./services/networking/shout.nix @@ -847,7 +852,7 @@ ./services/torrent/peerflix.nix ./services/torrent/rtorrent.nix ./services/torrent/transmission.nix - ./services/ttys/agetty.nix + ./services/ttys/getty.nix ./services/ttys/gpm.nix ./services/ttys/kmscon.nix ./services/wayland/cage.nix diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix index 42ed62b063044..680fa40b91195 100644 --- a/nixos/modules/profiles/hardened.nix +++ b/nixos/modules/profiles/hardened.nix @@ -1,5 +1,10 @@ # A profile with most (vanilla) hardening options enabled by default, -# potentially at the cost of features and performance. +# potentially at the cost of stability, features and performance. +# +# This profile enables options that are known to affect system +# stability. If you experience any stability issues when using the +# profile, try disabling it. If you report an issue and use this +# profile, always mention that you do. { config, lib, pkgs, ... }: diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix index e68ea1b08776a..7dc493fb495da 100644 --- a/nixos/modules/profiles/installation-device.nix +++ b/nixos/modules/profiles/installation-device.nix @@ -45,10 +45,10 @@ with lib; }; # Automatically log in at the virtual consoles. - services.mingetty.autologinUser = "nixos"; + services.getty.autologinUser = "nixos"; # Some more help text. - services.mingetty.helpLine = '' + services.getty.helpLine = '' The "nixos" and "root" accounts have empty passwords. An ssh daemon is running. You then must set a password diff --git a/nixos/modules/programs/command-not-found/command-not-found.pl b/nixos/modules/programs/command-not-found/command-not-found.pl index ab7aa204653cd..7515dd975c316 100644 --- a/nixos/modules/programs/command-not-found/command-not-found.pl +++ b/nixos/modules/programs/command-not-found/command-not-found.pl @@ -27,8 +27,8 @@ if (!defined $res || scalar @$res == 0) { my $package = @$res[0]->{package}; if ($ENV{"NIX_AUTO_INSTALL"} // "") { print STDERR <<EOF; -The program ‘$program’ is currently not installed. It is provided by -the package ‘$package’, which I will now install for you. +The program '$program' is currently not installed. It is provided by +the package '$package', which I will now install for you. EOF ; exit 126 if system("nix-env", "-iA", "nixos.$package") == 0; @@ -36,16 +36,17 @@ EOF exec("nix-shell", "-p", $package, "--run", shell_quote("exec", @ARGV)); } else { print STDERR <<EOF; -The program ‘$program’ is currently not installed. You can install it by typing: - nix-env -iA nixos.$package +The program '$program' is not in your PATH. You can make it available in an +ephemeral shell by typing: + nix-shell -p $package EOF } } else { print STDERR <<EOF; -The program ‘$program’ is currently not installed. It is provided by -several packages. You can install it by typing one of the following: +The program '$program' is not in your PATH. It is provided by several packages. +You can make it available in an ephemeral shell by typing one of the following: EOF - print STDERR " nix-env -iA nixos.$_->{package}\n" foreach @$res; + print STDERR " nix-shell -p $_->{package}\n" foreach @$res; } exit 127; diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix index 50d1077dd4108..34a0dc6a2df3d 100644 --- a/nixos/modules/programs/fish.nix +++ b/nixos/modules/programs/fish.nix @@ -112,7 +112,7 @@ in environment.etc."fish/nixos-env-preinit.fish".text = '' # This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently # unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish - set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $__fish_datadir/functions + set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions # source the NixOS environment config if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ] @@ -128,7 +128,7 @@ in # if we haven't sourced the general config, do it if not set -q __fish_nixos_general_config_sourced - set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path + set --prepend fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d fenv source /etc/fish/foreign-env/shellInit > /dev/null set -e fish_function_path[1] @@ -142,7 +142,7 @@ in # if we haven't sourced the login config, do it status --is-login; and not set -q __fish_nixos_login_config_sourced and begin - set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path + set --prepend fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d fenv source /etc/fish/foreign-env/loginShellInit > /dev/null set -e fish_function_path[1] @@ -158,7 +158,7 @@ in and begin ${fishAliases} - set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path + set --prepend fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d fenv source /etc/fish/foreign-env/interactiveShellInit > /dev/null set -e fish_function_path[1] diff --git a/nixos/modules/programs/msmtp.nix b/nixos/modules/programs/msmtp.nix new file mode 100644 index 0000000000000..217060e6b3b32 --- /dev/null +++ b/nixos/modules/programs/msmtp.nix @@ -0,0 +1,104 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.msmtp; + +in { + meta.maintainers = with maintainers; [ pacien ]; + + options = { + programs.msmtp = { + enable = mkEnableOption "msmtp - an SMTP client"; + + setSendmail = mkOption { + type = types.bool; + default = true; + description = '' + Whether to set the system sendmail to msmtp's. + ''; + }; + + defaults = mkOption { + type = types.attrs; + default = {}; + example = { + aliases = "/etc/aliases"; + port = 587; + tls = true; + }; + description = '' + Default values applied to all accounts. + See msmtp(1) for the available options. + ''; + }; + + accounts = mkOption { + type = with types; attrsOf attrs; + default = {}; + example = { + "default" = { + host = "smtp.example"; + auth = true; + user = "someone"; + passwordeval = "cat /secrets/password.txt"; + }; + }; + description = '' + Named accounts and their respective configurations. + The special name "default" allows a default account to be defined. + See msmtp(1) for the available options. + + Use `programs.msmtp.extraConfig` instead of this attribute set-based + option if ordered account inheritance is needed. + + It is advised to use the `passwordeval` setting to read the password + from a secret file to avoid having it written in the world-readable + nix store. The password file must end with a newline (`\n`). + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to add to the msmtp configuration verbatim. + See msmtp(1) for the syntax and available options. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.msmtp ]; + + services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail { + program = "sendmail"; + source = "${pkgs.msmtp}/bin/sendmail"; + setuid = false; + setgid = false; + }; + + environment.etc."msmtprc".text = let + mkValueString = v: + if v == true then "on" + else if v == false then "off" + else generators.mkValueStringDefault {} v; + mkKeyValueString = k: v: "${k} ${mkValueString v}"; + mkInnerSectionString = + attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValueString attrs); + mkAccountString = name: attrs: '' + account ${name} + ${mkInnerSectionString attrs} + ''; + in '' + defaults + ${mkInnerSectionString cfg.defaults} + + ${concatStringsSep "\n" (mapAttrsToList mkAccountString cfg.accounts)} + + ${cfg.extraConfig} + ''; + }; +} diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix index 1f49ddc91bb38..8039763faccc9 100644 --- a/nixos/modules/programs/ssmtp.nix +++ b/nixos/modules/programs/ssmtp.nix @@ -162,15 +162,16 @@ in (mkIf (cfg.authPassFile != null) { AuthPassFile = cfg.authPassFile; }) ]; - environment.etc."ssmtp/ssmtp.conf".source = - let - toStr = value: + # careful here: ssmtp REQUIRES all config lines to end with a newline char! + environment.etc."ssmtp/ssmtp.conf".text = with generators; toKeyValue { + mkKeyValue = mkKeyValueDefault { + mkValueString = value: if value == true then "YES" else if value == false then "NO" - else builtins.toString value + else mkValueStringDefault {} value ; - in - pkgs.writeText "ssmtp.conf" (concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${toStr value}") cfg.settings)); + } "="; + } cfg.settings; environment.systemPackages = [pkgs.ssmtp]; diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml index 517162d1a7bfa..f248112917284 100644 --- a/nixos/modules/security/acme.xml +++ b/nixos/modules/security/acme.xml @@ -268,7 +268,7 @@ chmod 400 /var/lib/secrets/certs.secret <para> It is possible that your account credentials file may become corrupt and need - to be regnerated. In this scenario lego will produce the error <literal>JWS verification error</literal>. + to be regenerated. In this scenario lego will produce the error <literal>JWS verification error</literal>. The solution is to simply delete the associated accounts file and re-run the affected service(s). </para> diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index a428103eaa963..1522111dbddfa 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -394,7 +394,7 @@ let ${optionalString cfg.requireWheel "auth required pam_wheel.so use_uid"} ${optionalString cfg.logFailures - "auth required pam_tally.so"} + "auth required pam_faillock.so"} ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}"} ${optionalString cfg.fprintAuth diff --git a/nixos/modules/services/hardware/throttled.nix b/nixos/modules/services/hardware/throttled.nix index 7617c4492d7c6..1905eb565c6dc 100644 --- a/nixos/modules/services/hardware/throttled.nix +++ b/nixos/modules/services/hardware/throttled.nix @@ -26,5 +26,11 @@ in { if cfg.extraConfig != "" then pkgs.writeText "lenovo_fix.conf" cfg.extraConfig else "${pkgs.throttled}/etc/lenovo_fix.conf"; + + # Kernel 5.9 spams warnings whenever userspace writes to CPU MSRs. + # See https://github.com/erpalma/throttled/issues/215 + boot.kernelParams = + optional (versionAtLeast config.boot.kernelPackages.kernel.version "5.9") + "msr.allow_writes=on"; }; } diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix index 2594ac7437109..8c609bbf825b6 100644 --- a/nixos/modules/services/hardware/trezord.nix +++ b/nixos/modules/services/hardware/trezord.nix @@ -47,7 +47,7 @@ in { services.udev.packages = [ pkgs.trezor-udev-rules ]; systemd.services.trezord = { - description = "TREZOR Bridge"; + description = "Trezor Bridge"; after = [ "systemd-udev-settle.service" "network.target" ]; wantedBy = [ "multi-user.target" ]; path = []; diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 3ee7a81dc3753..35a9dccdff284 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -44,6 +44,9 @@ let [gitlab-shell] dir = "${cfg.packages.gitlab-shell}" + [hooks] + custom_hooks_dir = "${cfg.statePath}/custom_hooks" + [gitlab] secret_file = "${cfg.statePath}/gitlab_shell_secret" url = "http+unix://${pathUrlQuote gitlabSocket}" @@ -65,7 +68,6 @@ let repos_path = "${cfg.statePath}/repositories"; secret_file = "${cfg.statePath}/gitlab_shell_secret"; log_file = "${cfg.statePath}/log/gitlab-shell.log"; - custom_hooks_dir = "${cfg.statePath}/custom_hooks"; redis = { bin = "${pkgs.redis}/bin/redis-cli"; host = "127.0.0.1"; diff --git a/nixos/modules/services/misc/matrix-appservice-discord.nix b/nixos/modules/services/misc/matrix-appservice-discord.nix index 49c41ff637a8f..71d1227f4ff72 100644 --- a/nixos/modules/services/misc/matrix-appservice-discord.nix +++ b/nixos/modules/services/misc/matrix-appservice-discord.nix @@ -5,7 +5,7 @@ with lib; let dataDir = "/var/lib/matrix-appservice-discord"; registrationFile = "${dataDir}/discord-registration.yaml"; - appDir = "${pkgs.matrix-appservice-discord}/lib/node_modules/matrix-appservice-discord"; + appDir = "${pkgs.matrix-appservice-discord}/${pkgs.matrix-appservice-discord.passthru.nodeAppDir}"; cfg = config.services.matrix-appservice-discord; # TODO: switch to configGen.json once RFC42 is implemented settingsFile = pkgs.writeText "matrix-appservice-discord-settings.json" (builtins.toJSON cfg.settings); @@ -22,12 +22,6 @@ in { default = { database = { filename = "${dataDir}/discord.db"; - - # TODO: remove those old config keys once the following issues are solved: - # * https://github.com/Half-Shot/matrix-appservice-discord/issues/490 - # * https://github.com/Half-Shot/matrix-appservice-discord/issues/498 - userStorePath = "${dataDir}/user-store.db"; - roomStorePath = "${dataDir}/room-store.db"; }; # empty values necessary for registration file generation diff --git a/nixos/modules/services/monitoring/grafana-image-renderer.nix b/nixos/modules/services/monitoring/grafana-image-renderer.nix new file mode 100644 index 0000000000000..a010a5316baeb --- /dev/null +++ b/nixos/modules/services/monitoring/grafana-image-renderer.nix @@ -0,0 +1,150 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + cfg = config.services.grafana-image-renderer; + + format = pkgs.formats.json { }; + + configFile = format.generate "grafana-image-renderer-config.json" cfg.settings; +in { + options.services.grafana-image-renderer = { + enable = mkEnableOption "grafana-image-renderer"; + + chromium = mkOption { + type = types.package; + description = '' + The chromium to use for image rendering. + ''; + }; + + verbose = mkEnableOption "verbosity for the service"; + + provisionGrafana = mkEnableOption "Grafana configuration for grafana-image-renderer"; + + settings = mkOption { + type = types.submodule { + freeformType = format.type; + + options = { + service = { + port = mkOption { + type = types.port; + default = 8081; + description = '' + The TCP port to use for the rendering server. + ''; + }; + logging.level = mkOption { + type = types.enum [ "error" "warning" "info" "debug" ]; + default = "info"; + description = '' + The log-level of the <filename>grafana-image-renderer.service</filename>-unit. + ''; + }; + }; + rendering = { + width = mkOption { + default = 1000; + type = types.ints.positive; + description = '' + Width of the PNG used to display the alerting graph. + ''; + }; + height = mkOption { + default = 500; + type = types.ints.positive; + description = '' + Height of the PNG used to display the alerting graph. + ''; + }; + mode = mkOption { + default = "default"; + type = types.enum [ "default" "reusable" "clustered" ]; + description = '' + Rendering mode of <package>grafana-image-renderer</package>: + <itemizedlist> + <listitem><para><literal>default:</literal> Creates on browser-instance + per rendering request.</para></listitem> + <listitem><para><literal>reusable:</literal> One browser instance + will be started and reused for each rendering request.</para></listitem> + <listitem><para><literal>clustered:</literal> allows to precisely + configure how many browser-instances are supposed to be used. The values + for that mode can be declared in <literal>rendering.clustering</literal>. + </para></listitem> + </itemizedlist> + ''; + }; + args = mkOption { + type = types.listOf types.str; + default = [ "--no-sandbox" ]; + description = '' + List of CLI flags passed to <package>chromium</package>. + ''; + }; + }; + }; + }; + + default = {}; + + description = '' + Configuration attributes for <package>grafana-image-renderer</package>. + + See <link xlink:href="https://github.com/grafana/grafana-image-renderer/blob/ce1f81438e5f69c7fd7c73ce08bab624c4c92e25/default.json" /> + for supported values. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { assertion = cfg.provisionGrafana -> config.services.grafana.enable; + message = '' + To provision a Grafana instance to use grafana-image-renderer, + `services.grafana.enable` must be set to `true`! + ''; + } + ]; + + services.grafana.extraOptions = mkIf cfg.provisionGrafana { + RENDERING_SERVER_URL = "http://localhost:${toString cfg.settings.service.port}/render"; + RENDERING_CALLBACK_URL = "http://localhost:${toString config.services.grafana.port}"; + }; + + services.grafana-image-renderer.chromium = mkDefault pkgs.chromium; + + services.grafana-image-renderer.settings = { + rendering = mapAttrs (const mkDefault) { + chromeBin = "${cfg.chromium}/bin/chromium"; + verboseLogging = cfg.verbose; + timezone = config.time.timeZone; + }; + + services = { + logging.level = mkIf cfg.verbose (mkDefault "debug"); + metrics.enabled = mkDefault false; + }; + }; + + systemd.services.grafana-image-renderer = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = " A Grafana backend plugin that handles rendering of panels & dashboards to PNGs using headless browser (Chromium/Chrome)"; + + environment = { + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "true"; + }; + + serviceConfig = { + DynamicUser = true; + PrivateTmp = true; + ExecStart = "${pkgs.grafana-image-renderer}/bin/grafana-image-renderer server --config=${configFile}"; + Restart = "always"; + }; + }; + }; + + meta.maintainers = with maintainers; [ ma27 ]; +} diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index b0c81a46d4d81..c8515c4b8988c 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -5,10 +5,11 @@ with lib; let cfg = config.services.grafana; opt = options.services.grafana; + declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins); envOptions = { PATHS_DATA = cfg.dataDir; - PATHS_PLUGINS = "${cfg.dataDir}/plugins"; + PATHS_PLUGINS = if builtins.isNull cfg.declarativePlugins then "${cfg.dataDir}/plugins" else declarativePlugins; PATHS_LOGS = "${cfg.dataDir}/log"; SERVER_PROTOCOL = cfg.protocol; @@ -260,6 +261,12 @@ in { defaultText = "pkgs.grafana"; type = types.package; }; + declarativePlugins = mkOption { + type = with types; nullOr (listOf path); + default = null; + description = "If non-null, then a list of packages containing Grafana plugins to install. If set, plugins cannot be manually installed."; + example = literalExample "with pkgs.grafanaPlugins; [ grafana-piechart-panel ]"; + }; dataDir = mkOption { description = "Data directory."; diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix index 72428957109c6..4f9be38f7f14a 100644 --- a/nixos/modules/services/monitoring/prometheus/default.nix +++ b/nixos/modules/services/monitoring/prometheus/default.nix @@ -32,6 +32,8 @@ let (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules)) ]); scrape_configs = filterValidPrometheus cfg.scrapeConfigs; + remote_write = filterValidPrometheus cfg.remoteWrite; + remote_read = filterValidPrometheus cfg.remoteRead; alerting = { inherit (cfg) alertmanagers; }; @@ -101,6 +103,157 @@ let }; }; + promTypes.remote_read = types.submodule { + options = { + url = mkOption { + type = types.str; + description = '' + ServerName extension to indicate the name of the server. + http://tools.ietf.org/html/rfc4366#section-3.1 + ''; + }; + name = mkOpt types.string '' + Name of the remote read config, which if specified must be unique among remote read configs. + The name will be used in metrics and logging in place of a generated value to help users distinguish between + remote read configs. + ''; + required_matchers = mkOpt (types.attrsOf types.str) '' + An optional list of equality matchers which have to be + present in a selector to query the remote read endpoint. + ''; + remote_timeout = mkOpt types.str '' + Timeout for requests to the remote read endpoint. + ''; + read_recent = mkOpt types.bool '' + Whether reads should be made for queries for time ranges that + the local storage should have complete data for. + ''; + basic_auth = mkOpt (types.submodule { + options = { + username = mkOption { + type = types.str; + description = '' + HTTP username + ''; + }; + password = mkOpt types.str "HTTP password"; + password_file = mkOpt types.str "HTTP password file"; + }; + }) '' + Sets the `Authorization` header on every remote read request with the + configured username and password. + password and password_file are mutually exclusive. + ''; + bearer_token = mkOpt types.str '' + Sets the `Authorization` header on every remote read request with + the configured bearer token. It is mutually exclusive with `bearer_token_file`. + ''; + bearer_token_file = mkOpt types.str '' + Sets the `Authorization` header on every remote read request with the bearer token + read from the configured file. It is mutually exclusive with `bearer_token`. + ''; + tls_config = mkOpt promTypes.tls_config '' + Configures the remote read request's TLS settings. + ''; + proxy_url = mkOpt types.str "Optional Proxy URL."; + }; + }; + + promTypes.remote_write = types.submodule { + options = { + url = mkOption { + type = types.str; + description = '' + ServerName extension to indicate the name of the server. + http://tools.ietf.org/html/rfc4366#section-3.1 + ''; + }; + remote_timeout = mkOpt types.str '' + Timeout for requests to the remote write endpoint. + ''; + write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) '' + List of remote write relabel configurations. + ''; + name = mkOpt types.string '' + Name of the remote write config, which if specified must be unique among remote write configs. + The name will be used in metrics and logging in place of a generated value to help users distinguish between + remote write configs. + ''; + basic_auth = mkOpt (types.submodule { + options = { + username = mkOption { + type = types.str; + description = '' + HTTP username + ''; + }; + password = mkOpt types.str "HTTP password"; + password_file = mkOpt types.str "HTTP password file"; + }; + }) '' + Sets the `Authorization` header on every remote write request with the + configured username and password. + password and password_file are mutually exclusive. + ''; + bearer_token = mkOpt types.str '' + Sets the `Authorization` header on every remote write request with + the configured bearer token. It is mutually exclusive with `bearer_token_file`. + ''; + bearer_token_file = mkOpt types.str '' + Sets the `Authorization` header on every remote write request with the bearer token + read from the configured file. It is mutually exclusive with `bearer_token`. + ''; + tls_config = mkOpt promTypes.tls_config '' + Configures the remote write request's TLS settings. + ''; + proxy_url = mkOpt types.str "Optional Proxy URL."; + queue_config = mkOpt (types.submodule { + options = { + capacity = mkOpt types.int '' + Number of samples to buffer per shard before we block reading of more + samples from the WAL. It is recommended to have enough capacity in each + shard to buffer several requests to keep throughput up while processing + occasional slow remote requests. + ''; + max_shards = mkOpt types.int '' + Maximum number of shards, i.e. amount of concurrency. + ''; + min_shards = mkOpt types.int '' + Minimum number of shards, i.e. amount of concurrency. + ''; + max_samples_per_send = mkOpt types.int '' + Maximum number of samples per send. + ''; + batch_send_deadline = mkOpt types.str '' + Maximum time a sample will wait in buffer. + ''; + min_backoff = mkOpt types.str '' + Initial retry delay. Gets doubled for every retry. + ''; + max_backoff = mkOpt types.str '' + Maximum retry delay. + ''; + }; + }) '' + Configures the queue used to write to remote storage. + ''; + metadata_config = mkOpt (types.submodule { + options = { + send = mkOpt types.bool '' + Whether metric metadata is sent to remote storage or not. + ''; + send_interval = mkOpt types.str '' + How frequently metric metadata is sent to remote storage. + ''; + }; + }) '' + Configures the sending of series metadata to remote storage. + Metadata configuration is subject to change at any point + or be removed in future releases. + ''; + }; + }; + promTypes.scrape_config = types.submodule { options = { job_name = mkOption { @@ -432,10 +585,10 @@ let regular expression matches. ''; - action = mkDefOpt (types.enum ["replace" "keep" "drop"]) "replace" '' + action = + mkDefOpt (types.enum ["replace" "keep" "drop" "hashmod" "labelmap" "labeldrop" "labelkeep"]) "replace" '' Action to perform based on regex matching. ''; - }; }; @@ -580,6 +733,24 @@ in { ''; }; + remoteRead = mkOption { + type = types.listOf promTypes.remote_read; + default = []; + description = '' + Parameters of the endpoints to query from. + See <link xlink:href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_read">the official documentation</link> for more information. + ''; + }; + + remoteWrite = mkOption { + type = types.listOf promTypes.remote_write; + default = []; + description = '' + Parameters of the endpoints to send samples to. + See <link xlink:href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write">the official documentation</link> for more information. + ''; + }; + rules = mkOption { type = types.listOf types.str; default = []; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 683dc0390c59b..1fd85c66f843d 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -23,6 +23,7 @@ let exporterOpts = genAttrs [ "apcupsd" "bind" + "bird" "blackbox" "collectd" "dnsmasq" @@ -47,6 +48,7 @@ let "rspamd" "rtl_433" "snmp" + "smokeping" "sql" "surfboard" "tor" @@ -234,8 +236,6 @@ in services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000"; services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey; services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey; - })] ++ [(mkIf config.services.rspamd.enable { - services.prometheus.exporters.rspamd.url = mkDefault "http://localhost:11334/stat"; })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable { hardware.rtl-sdr.enable = mkDefault true; })] ++ [(mkIf config.services.nginx.enable { diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bird.nix b/nixos/modules/services/monitoring/prometheus/exporters/bird.nix new file mode 100644 index 0000000000000..d8a526eafcea9 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/bird.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.bird; +in +{ + port = 9324; + extraOpts = { + birdVersion = mkOption { + type = types.enum [ 1 2 ]; + default = 2; + description = '' + Specifies whether BIRD1 or BIRD2 is in use. + ''; + }; + birdSocket = mkOption { + type = types.path; + default = "/var/run/bird.ctl"; + description = '' + Path to BIRD2 (or BIRD1 v4) socket. + ''; + }; + newMetricFormat = mkOption { + type = types.bool; + default = true; + description = '' + Enable the new more-generic metric format. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + SupplementaryGroups = singleton (if cfg.birdVersion == 1 then "bird" else "bird2"); + ExecStart = '' + ${pkgs.prometheus-bird-exporter}/bin/bird_exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -bird.socket ${cfg.birdSocket} \ + -bird.v2=${if cfg.birdVersion == 2 then "true" else "false"} \ + -format.new=${if cfg.newMetricFormat then "true" else "false"} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixos/modules/services/monitoring/prometheus/exporters/json.nix index bd0026b55f721..1800da69a2558 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/json.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/json.nix @@ -8,28 +8,36 @@ in { port = 7979; extraOpts = { - url = mkOption { - type = types.str; - description = '' - URL to scrape JSON from. - ''; - }; configFile = mkOption { type = types.path; description = '' Path to configuration file. ''; }; - listenAddress = {}; # not used }; serviceOpts = { serviceConfig = { ExecStart = '' - ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \ - --port ${toString cfg.port} \ - ${cfg.url} ${escapeShellArg cfg.configFile} \ + ${pkgs.prometheus-json-exporter}/bin/json_exporter \ + --config.file ${escapeShellArg cfg.configFile} \ + --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \ ${concatStringsSep " \\\n " cfg.extraFlags} ''; }; }; + imports = [ + (mkRemovedOptionModule [ "url" ] '' + This option was removed. The URL of the endpoint serving JSON + must now be provided to the exporter by prometheus via the url + parameter `target'. + + In prometheus a scrape URL would look like this: + + http://some.json-exporter.host:7979/probe?target=https://example.com/some/json/endpoint + + For more information, take a look at the official documentation + (https://github.com/prometheus-community/json_exporter) of the json_exporter. + '') + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; } diff --git a/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix index 1f02ae2072499..78fe120e4d932 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix @@ -10,64 +10,55 @@ let echo '${builtins.toJSON conf}' | ${pkgs.buildPackages.jq}/bin/jq '.' > $out ''; - generateConfig = extraLabels: (map (path: { - name = "rspamd_${replaceStrings [ "." " " ] [ "_" "_" ] path}"; - path = "$.${path}"; - labels = extraLabels; - }) [ - "actions.'add header'" - "actions.'no action'" - "actions.'rewrite subject'" - "actions.'soft reject'" - "actions.greylist" - "actions.reject" - "bytes_allocated" - "chunks_allocated" - "chunks_freed" - "chunks_oversized" - "connections" - "control_connections" - "ham_count" - "learned" - "pools_allocated" - "pools_freed" - "read_only" - "scanned" - "shared_chunks_allocated" - "spam_count" - "total_learns" - ]) ++ [{ - name = "rspamd_statfiles"; - type = "object"; - path = "$.statfiles[*]"; - labels = recursiveUpdate { - symbol = "$.symbol"; - type = "$.type"; - } extraLabels; - values = { - revision = "$.revision"; - size = "$.size"; - total = "$.total"; - used = "$.used"; - languages = "$.languages"; - users = "$.users"; - }; - }]; + generateConfig = extraLabels: { + metrics = (map (path: { + name = "rspamd_${replaceStrings [ "." " " ] [ "_" "_" ] path}"; + path = "$.${path}"; + labels = extraLabels; + }) [ + "actions.'add header'" + "actions.'no action'" + "actions.'rewrite subject'" + "actions.'soft reject'" + "actions.greylist" + "actions.reject" + "bytes_allocated" + "chunks_allocated" + "chunks_freed" + "chunks_oversized" + "connections" + "control_connections" + "ham_count" + "learned" + "pools_allocated" + "pools_freed" + "read_only" + "scanned" + "shared_chunks_allocated" + "spam_count" + "total_learns" + ]) ++ [{ + name = "rspamd_statfiles"; + type = "object"; + path = "$.statfiles[*]"; + labels = recursiveUpdate { + symbol = "$.symbol"; + type = "$.type"; + } extraLabels; + values = { + revision = "$.revision"; + size = "$.size"; + total = "$.total"; + used = "$.used"; + languages = "$.languages"; + users = "$.users"; + }; + }]; + }; in { port = 7980; extraOpts = { - listenAddress = {}; # not used - - url = mkOption { - type = types.str; - description = '' - URL to the rspamd metrics endpoint. - Defaults to http://localhost:11334/stat when - <option>services.rspamd.enable</option> is true. - ''; - }; - extraLabels = mkOption { type = types.attrsOf types.str; default = { @@ -84,9 +75,25 @@ in }; }; serviceOpts.serviceConfig.ExecStart = '' - ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \ - --port ${toString cfg.port} \ - ${cfg.url} ${prettyJSON (generateConfig cfg.extraLabels)} \ + ${pkgs.prometheus-json-exporter}/bin/json_exporter \ + --config.file ${prettyJSON (generateConfig cfg.extraLabels)} \ + --web.listen-address "${cfg.listenAddress}:${toString cfg.port}" \ ${concatStringsSep " \\\n " cfg.extraFlags} ''; + + imports = [ + (mkRemovedOptionModule [ "url" ] '' + This option was removed. The URL of the rspamd metrics endpoint + must now be provided to the exporter by prometheus via the url + parameter `target'. + + In prometheus a scrape URL would look like this: + + http://some.rspamd-exporter.host:7980/probe?target=http://some.rspamd.host:11334/stat + + For more information, take a look at the official documentation + (https://github.com/prometheus-community/json_exporter) of the json_exporter. + '') + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; } diff --git a/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix b/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix new file mode 100644 index 0000000000000..0a7bb9c27be28 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix @@ -0,0 +1,60 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.smokeping; + goDuration = types.mkOptionType { + name = "goDuration"; + description = "Go duration (https://golang.org/pkg/time/#ParseDuration)"; + check = x: types.str.check x && builtins.match "(-?[0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+" x != null; + inherit (types.str) merge; + }; +in +{ + port = 9374; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + pingInterval = mkOption { + type = goDuration; + default = "1s"; + description = '' + Interval between pings. + ''; + }; + buckets = mkOption { + type = types.commas; + default = "5e-05,0.0001,0.0002,0.0004,0.0008,0.0016,0.0032,0.0064,0.0128,0.0256,0.0512,0.1024,0.2048,0.4096,0.8192,1.6384,3.2768,6.5536,13.1072,26.2144"; + description = '' + List of buckets to use for the response duration histogram. + ''; + }; + hosts = mkOption { + type = with types; listOf str; + description = '' + List of endpoints to probe. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + AmbientCapabilities = [ "CAP_NET_RAW" ]; + ExecStart = '' + ${pkgs.prometheus-smokeping-prober}/bin/smokeping_prober \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --buckets ${cfg.buckets} \ + --ping.interval ${cfg.pingInterval} \ + --privileged \ + ${concatStringsSep " \\\n " cfg.extraFlags} \ + ${concatStringsSep " " cfg.hosts} + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/corerad.nix b/nixos/modules/services/networking/corerad.nix index d90a5923bc62e..4acdd1d69cc49 100644 --- a/nixos/modules/services/networking/corerad.nix +++ b/nixos/modules/services/networking/corerad.nix @@ -4,13 +4,7 @@ with lib; let cfg = config.services.corerad; - - writeTOML = name: x: - pkgs.runCommandNoCCLocal name { - passAsFile = ["config"]; - config = builtins.toJSON x; - buildInputs = [ pkgs.go-toml ]; - } "jsontoml < $configPath > $out"; + settingsFormat = pkgs.formats.toml {}; in { meta.maintainers = with maintainers; [ mdlayher ]; @@ -19,7 +13,7 @@ in { enable = mkEnableOption "CoreRAD IPv6 NDP RA daemon"; settings = mkOption { - type = types.uniq types.attrs; + type = settingsFormat.type; example = literalExample '' { interfaces = [ @@ -64,7 +58,7 @@ in { config = mkIf cfg.enable { # Prefer the config file over settings if both are set. - services.corerad.configFile = mkDefault (writeTOML "corerad.toml" cfg.settings); + services.corerad.configFile = mkDefault (settingsFormat.generate "corerad.toml" cfg.settings); systemd.services.corerad = { description = "CoreRAD IPv6 NDP RA daemon"; diff --git a/nixos/modules/services/networking/shellhub-agent.nix b/nixos/modules/services/networking/shellhub-agent.nix new file mode 100644 index 0000000000000..4ce4b8250bc3c --- /dev/null +++ b/nixos/modules/services/networking/shellhub-agent.nix @@ -0,0 +1,91 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.shellhub-agent; +in { + + ###### interface + + options = { + + services.shellhub-agent = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the ShellHub Agent daemon, which allows + secure remote logins. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.shellhub-agent; + defaultText = "pkgs.shellhub-agent"; + description = '' + Which ShellHub Agent package to use. + ''; + }; + + tenantId = mkOption { + type = types.str; + example = "ba0a880c-2ada-11eb-a35e-17266ef329d6"; + description = '' + The tenant ID to use when connecting to the ShellHub + Gateway. + ''; + }; + + server = mkOption { + type = types.str; + default = "https://cloud.shellhub.io"; + description = '' + Server address of ShellHub Gateway to connect. + ''; + }; + + privateKey = mkOption { + type = types.path; + default = "/var/lib/shellhub-agent/private.key"; + description = '' + Location where to store the ShellHub Agent private + key. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.shellhub-agent = { + description = "ShellHub Agent"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "local-fs.target" ]; + wants = [ "network-online.target" ]; + after = [ + "local-fs.target" + "network.target" + "network-online.target" + "time-sync.target" + ]; + + environment.SERVER_ADDRESS = cfg.server; + environment.PRIVATE_KEY = cfg.privateKey; + environment.TENANT_ID = cfg.tenantId; + + serviceConfig = { + # The service starts sessions for different users. + User = "root"; + Restart = "on-failure"; + ExecStart = "${cfg.package}/bin/agent"; + }; + }; + + environment.systemPackages = [ cfg.package ]; + }; +} diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/getty.nix index d07746be2377a..68ab81837772a 100644 --- a/nixos/modules/services/ttys/agetty.nix +++ b/nixos/modules/services/ttys/getty.nix @@ -4,7 +4,7 @@ with lib; let - autologinArg = optionalString (config.services.mingetty.autologinUser != null) "--autologin ${config.services.mingetty.autologinUser}"; + autologinArg = optionalString (config.services.getty.autologinUser != null) "--autologin ${config.services.getty.autologinUser}"; gettyCmd = extraArgs: "@${pkgs.util-linux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login ${autologinArg} ${extraArgs}"; in @@ -13,9 +13,13 @@ in ###### interface + imports = [ + (mkRenamedOptionModule [ "services" "mingetty" ] [ "services" "getty" ]) + ]; + options = { - services.mingetty = { + services.getty = { autologinUser = mkOption { type = types.nullOr types.str; @@ -29,7 +33,7 @@ in greetingLine = mkOption { type = types.str; description = '' - Welcome line printed by mingetty. + Welcome line printed by agetty. The default shows current NixOS version label, machine type and tty. ''; }; @@ -38,7 +42,7 @@ in type = types.lines; default = ""; description = '' - Help line printed by mingetty below the welcome line. + Help line printed by agetty below the welcome line. Used by the installation CD to give some hints on how to proceed. ''; @@ -65,7 +69,7 @@ in config = { # Note: this is set here rather than up there so that changing # nixos.label would not rebuild manual pages - services.mingetty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixos.label} (\m) - \l >>>''; + services.getty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixos.label} (\m) - \l >>>''; systemd.services."getty@" = { serviceConfig.ExecStart = [ @@ -76,7 +80,7 @@ in }; systemd.services."serial-getty@" = - let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed); in + let speeds = concatStringsSep "," (map toString config.services.getty.serialSpeed); in { serviceConfig.ExecStart = [ "" # override upstream default with an empty ExecStart (gettyCmd "%I ${speeds} $TERM") @@ -106,8 +110,8 @@ in { # Friendly greeting on the virtual consoles. source = pkgs.writeText "issue" '' - [1;32m${config.services.mingetty.greetingLine}[0m - ${config.services.mingetty.helpLine} + [1;32m${config.services.getty.greetingLine}[0m + ${config.services.getty.helpLine} ''; }; diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix index 0071951283478..e94861a90b5a8 100644 --- a/nixos/modules/services/web-apps/zabbix.nix +++ b/nixos/modules/services/web-apps/zabbix.nix @@ -3,7 +3,7 @@ let inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types; - inherit (lib) literalExample mapAttrs optionalString; + inherit (lib) literalExample mapAttrs optionalString versionAtLeast; cfg = config.services.zabbixWeb; fpm = config.services.phpfpm.pools.zabbix; @@ -28,6 +28,8 @@ let $ZBX_SERVER_PORT = '${toString cfg.server.port}'; $ZBX_SERVER_NAME = '''; $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG; + + ${cfg.extraConfig} ''; in @@ -143,6 +145,14 @@ in ''; }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Additional configuration to be copied verbatim into <filename>zabbix.conf.php</filename>. + ''; + }; + }; }; @@ -150,6 +160,10 @@ in config = mkIf cfg.enable { + services.zabbixWeb.extraConfig = optionalString ((versionAtLeast config.system.stateVersion "20.09") && (versionAtLeast cfg.package.version "5.0.0")) '' + $DB['DOUBLE_IEEE754'] = 'true'; + ''; + systemd.tmpfiles.rules = [ "d '${stateDir}' 0750 ${user} ${group} - -" "d '${stateDir}/session' 0750 ${user} ${config.services.httpd.group} - -" diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix index dc78728d66360..de3c7d693d47b 100644 --- a/nixos/modules/services/web-servers/apache-httpd/default.nix +++ b/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -126,6 +126,13 @@ let </IfModule> ''; + luaSetPaths = '' + <IfModule mod_lua.c> + LuaPackageCPath ${cfg.package.lua5}/lib/lua/${cfg.package.lua5.lua.luaversion}/?.so + LuaPackagePath ${cfg.package.lua5}/share/lua/${cfg.package.lua5.lua.luaversion}/?.lua + </IfModule> + ''; + mkVHostConf = hostOpts: let adminAddr = if hostOpts.adminAddr != null then hostOpts.adminAddr else cfg.adminAddr; @@ -326,6 +333,8 @@ let ${sslConf} + ${if cfg.package.luaSupport then luaSetPaths else ""} + # Fascist default - deny access to everything. <Directory /> Options FollowSymLinks diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 62671e9d74849..7fcd61880ea88 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -27,6 +27,33 @@ let ) cfg.virtualHosts; enableIPv6 = config.networking.enableIPv6; + defaultFastcgiParams = { + SCRIPT_FILENAME = "$document_root$fastcgi_script_name"; + QUERY_STRING = "$query_string"; + REQUEST_METHOD = "$request_method"; + CONTENT_TYPE = "$content_type"; + CONTENT_LENGTH = "$content_length"; + + SCRIPT_NAME = "$fastcgi_script_name"; + REQUEST_URI = "$request_uri"; + DOCUMENT_URI = "$document_uri"; + DOCUMENT_ROOT = "$document_root"; + SERVER_PROTOCOL = "$server_protocol"; + REQUEST_SCHEME = "$scheme"; + HTTPS = "$https if_not_empty"; + + GATEWAY_INTERFACE = "CGI/1.1"; + SERVER_SOFTWARE = "nginx/$nginx_version"; + + REMOTE_ADDR = "$remote_addr"; + REMOTE_PORT = "$remote_port"; + SERVER_ADDR = "$server_addr"; + SERVER_PORT = "$server_port"; + SERVER_NAME = "$server_name"; + + REDIRECT_STATUS = "200"; + }; + recommendedProxyConfig = pkgs.writeText "nginx-recommended-proxy-headers.conf" '' proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -283,6 +310,10 @@ let proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; ''} + ${concatStringsSep "\n" + (mapAttrsToList (n: v: ''fastcgi_param ${n} "${v}";'') + (optionalAttrs (config.fastcgiParams != {}) + (defaultFastcgiParams // config.fastcgiParams)))} ${optionalString (config.index != null) "index ${config.index};"} ${optionalString (config.tryFiles != null) "try_files ${config.tryFiles};"} ${optionalString (config.root != null) "root ${config.root};"} diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix index f2fc072557256..5a7f5188b6cfe 100644 --- a/nixos/modules/services/web-servers/nginx/location-options.nix +++ b/nixos/modules/services/web-servers/nginx/location-options.nix @@ -101,6 +101,16 @@ with lib; ''; }; + fastcgiParams = mkOption { + type = types.attrsOf types.str; + default = {}; + description = '' + FastCGI parameters to override. Unlike in the Nginx + configuration file, overriding only some default parameters + won't unset the default values for other parameters. + ''; + }; + extraConfig = mkOption { type = types.lines; default = ""; diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix index a9b0b8dc23fb7..5f1c099c283d8 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -8,8 +8,7 @@ let cfg = xcfg.desktopManager.plasma5; inherit (pkgs) kdeApplications kdeFrameworks plasma5; - libsForQt5 = pkgs.libsForQt514; - qt5 = pkgs.qt514; + inherit (pkgs) qt5 libsForQt5; inherit (pkgs) writeText; pulseaudio = config.hardware.pulseaudio; diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 23b4d8ffb1376..116994db1c140 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -1,9 +1,7 @@ { config, lib, pkgs, ... }: with lib; - let - xcfg = config.services.xserver; dmcfg = xcfg.displayManager; cfg = dmcfg.sddm; @@ -11,87 +9,86 @@ let sddm = pkgs.libsForQt5.sddm; - xserverWrapper = pkgs.writeScript "xserver-wrapper" '' - #!/bin/sh + iniFmt = pkgs.formats.ini { }; + + xserverWrapper = pkgs.writeShellScript "xserver-wrapper" '' ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)} exec systemd-cat -t xserver-wrapper ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@" ''; - Xsetup = pkgs.writeScript "Xsetup" '' - #!/bin/sh + Xsetup = pkgs.writeShellScript "Xsetup" '' ${cfg.setupScript} ${dmcfg.setupCommands} ''; - Xstop = pkgs.writeScript "Xstop" '' - #!/bin/sh + Xstop = pkgs.writeShellScript "Xstop" '' ${cfg.stopScript} ''; - cfgFile = pkgs.writeText "sddm.conf" '' - [General] - HaltCommand=/run/current-system/systemd/bin/systemctl poweroff - RebootCommand=/run/current-system/systemd/bin/systemctl reboot - ${optionalString cfg.autoNumlock '' - Numlock=on - ''} - - [Theme] - Current=${cfg.theme} - ThemeDir=/run/current-system/sw/share/sddm/themes - FacesDir=/run/current-system/sw/share/sddm/faces - - [Users] - MaximumUid=${toString config.ids.uids.nixbld} - HideUsers=${concatStringsSep "," dmcfg.hiddenUsers} - HideShells=/run/current-system/sw/bin/nologin - - [X11] - MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)} - ServerPath=${xserverWrapper} - XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr - SessionCommand=${dmcfg.sessionData.wrapper} - SessionDir=${dmcfg.sessionData.desktops}/share/xsessions - XauthPath=${pkgs.xorg.xauth}/bin/xauth - DisplayCommand=${Xsetup} - DisplayStopCommand=${Xstop} - EnableHidpi=${boolToString cfg.enableHidpi} - - [Wayland] - EnableHidpi=${boolToString cfg.enableHidpi} - SessionDir=${dmcfg.sessionData.desktops}/share/wayland-sessions - - ${optionalString dmcfg.autoLogin.enable '' - [Autologin] - User=${dmcfg.autoLogin.user} - Session=${autoLoginSessionName}.desktop - Relogin=${boolToString cfg.autoLogin.relogin} - ''} - - ${cfg.extraConfig} - ''; + defaultConfig = { + General = { + HaltCommand = "/run/current-system/systemd/bin/systemctl poweroff"; + RebootCommand = "/run/current-system/systemd/bin/systemctl reboot"; + Numlock = if cfg.autoNumlock then "on" else "none"; # on, off none + }; + + Theme = { + Current = cfg.theme; + ThemeDir = "/run/current-system/sw/share/sddm/themes"; + FacesDir = "/run/current-system/sw/share/sddm/faces"; + }; + + Users = { + MaximumUid = config.ids.uids.nixbld; + HideUsers = concatStringsSep "," dmcfg.hiddenUsers; + HideShells = "/run/current-system/sw/bin/nologin"; + }; - autoLoginSessionName = dmcfg.sessionData.autologinSession; + X11 = { + MinimumVT = if xcfg.tty != null then xcfg.tty else 7; + ServerPath = toString xserverWrapper; + XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr"; + SessionCommand = toString dmcfg.sessionData.wrapper; + SessionDir = "${dmcfg.sessionData.desktops}/share/xsessions"; + XauthPath = "${pkgs.xorg.xauth}/bin/xauth"; + DisplayCommand = toString Xsetup; + DisplayStopCommand = toString Xstop; + EnableHiDPI = cfg.enableHidpi; + }; + + Wayland = { + EnableHiDPI = cfg.enableHidpi; + SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions"; + }; + } // lib.optionalAttrs dmcfg.autoLogin.enable { + Autologin = { + User = dmcfg.autoLogin.user; + Session = autoLoginSessionName; + Relogin = cfg.autoLogin.relogin; + }; + }; + + cfgFile = + iniFmt.generate "sddm.conf" (lib.recursiveUpdate defaultConfig cfg.settings); + + autoLoginSessionName = + "${dmcfg.sessionData.autologinSession}.desktop"; in { imports = [ - (mkRemovedOptionModule [ "services" "xserver" "displayManager" "sddm" "themes" ] + (mkRemovedOptionModule + [ "services" "xserver" "displayManager" "sddm" "themes" ] "Set the option `services.xserver.displayManager.sddm.package' instead.") - (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ] [ - "services" - "xserver" - "displayManager" - "autoLogin" - "enable" - ]) - (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ] [ - "services" - "xserver" - "displayManager" - "autoLogin" - "user" - ]) + (mkRenamedOptionModule + [ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ] + [ "services" "xserver" "displayManager" "autoLogin" "enable" ]) + (mkRenamedOptionModule + [ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ] + [ "services" "xserver" "displayManager" "autoLogin" "user" ]) + (mkRemovedOptionModule + [ "services" "xserver" "displayManager" "sddm" "extraConfig" ] + "Set the option `services.xserver.displayManager.sddm.settings' instead.") ]; options = { @@ -110,22 +107,22 @@ in default = true; description = '' Whether to enable automatic HiDPI mode. - </para> - <para> - Versions up to 0.17 are broken so this only works from 0.18 onwards. ''; }; - extraConfig = mkOption { - type = types.lines; - default = ""; + settings = mkOption { + type = iniFmt.type; + default = { }; example = '' - [Autologin] - User=john - Session=plasma.desktop + { + Autologin = { + User = "john"; + Session = "plasma.desktop"; + }; + } ''; description = '' - Extra lines appended to the configuration of SDDM. + Extra settings merged in and overwritting defaults in sddm.conf. ''; }; @@ -168,28 +165,38 @@ in }; # Configuration for automatic login specific to SDDM - autoLogin.relogin = mkOption { - type = types.bool; - default = false; - description = '' - If true automatic login will kick in again on session exit (logout), otherwise it - will only log in automatically when the display-manager is started. - ''; + autoLogin = { + relogin = mkOption { + type = types.bool; + default = false; + description = '' + If true automatic login will kick in again on session exit (logout), otherwise it + will only log in automatically when the display-manager is started. + ''; + }; + + minimumUid = mkOption { + type = types.ints.u16; + default = 1000; + description = '' + Minimum user ID for auto-login user. + ''; + }; }; - }; - }; config = mkIf cfg.enable { assertions = [ - { assertion = xcfg.enable; + { + assertion = xcfg.enable; message = '' SDDM requires services.xserver.enable to be true ''; } - { assertion = dmcfg.autoLogin.enable -> autoLoginSessionName != null; + { + assertion = dmcfg.autoLogin.enable -> autoLoginSessionName != null; message = '' SDDM auto-login requires that services.xserver.displayManager.defaultSession is set. ''; @@ -230,7 +237,7 @@ in sddm-autologin.text = '' auth requisite pam_nologin.so - auth required pam_succeed_if.so uid >= 1000 quiet + auth required pam_succeed_if.so uid >= ${toString cfg.autoLogin.minimumUid} quiet auth required pam_permit.so account include sddm diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix index b9013ca1ff9f1..2bb4827be9d5d 100644 --- a/nixos/modules/services/x11/window-managers/xmonad.nix +++ b/nixos/modules/services/x11/window-managers/xmonad.nix @@ -5,25 +5,37 @@ let inherit (lib) mkOption mkIf optionals literalExample; cfg = config.services.xserver.windowManager.xmonad; + ghcWithPackages = cfg.haskellPackages.ghcWithPackages; + packages = self: cfg.extraPackages self ++ + optionals cfg.enableContribAndExtras + [ self.xmonad-contrib self.xmonad-extras ]; + xmonad-vanilla = pkgs.xmonad-with-packages.override { - ghcWithPackages = cfg.haskellPackages.ghcWithPackages; - packages = self: cfg.extraPackages self ++ - optionals cfg.enableContribAndExtras - [ self.xmonad-contrib self.xmonad-extras ]; + inherit ghcWithPackages packages; }; - xmonad-config = pkgs.writers.writeHaskellBin "xmonad" { - ghc = cfg.haskellPackages.ghc; - libraries = [ cfg.haskellPackages.xmonad ] ++ - cfg.extraPackages cfg.haskellPackages ++ - optionals cfg.enableContribAndExtras - (with cfg.haskellPackages; [ xmonad-contrib xmonad-extras ]); - inherit (cfg) ghcArgs; - } cfg.config; + xmonad-config = + let + xmonadAndPackages = self: [ self.xmonad ] ++ packages self; + xmonadEnv = ghcWithPackages xmonadAndPackages; + configured = pkgs.writers.writeHaskellBin "xmonad" { + ghc = cfg.haskellPackages.ghc; + libraries = xmonadAndPackages cfg.haskellPackages; + inherit (cfg) ghcArgs; + } cfg.config; + in + pkgs.runCommandLocal "xmonad" { + nativeBuildInputs = [ pkgs.makeWrapper ]; + } '' + install -D ${xmonadEnv}/share/man/man1/xmonad.1.gz $out/share/man/man1/xmonad.1.gz + makeWrapper ${configured}/bin/xmonad $out/bin/xmonad \ + --set NIX_GHC "${xmonadEnv}/bin/ghc" \ + --set XMONAD_XMESSAGE "${pkgs.xorg.xmessage}/bin/xmessage" + ''; xmonad = if (cfg.config != null) then xmonad-config else xmonad-vanilla; in { - meta.maintainers = with maintainers; [ lassulus xaverdh ]; + meta.maintainers = with maintainers; [ lassulus xaverdh ivanbrennan ]; options = { services.xserver.windowManager.xmonad = { @@ -72,13 +84,13 @@ in { This setup is then analogous to other (non-NixOS) linux distributions. If you do set this option, you likely want to use "launch" as your - entry point for xmonad (as in the example), to avoid xmonads + entry point for xmonad (as in the example), to avoid xmonad's recompilation logic on startup. Doing so will render the default "mod+q" restart key binding dysfunctional though, because that attempts to call your binary with the "--restart" command line option, unless you implement that yourself. You way mant to bind "mod+q" to <literal>(restart "xmonad" True)</literal> instead, which will just restart - xmonad from PATH. This allows e.g. switching to the new xmonad binary, + xmonad from PATH. This allows e.g. switching to the new xmonad binary after rebuilding your system with nixos-rebuild. If you actually want to run xmonad with a config specified here, but @@ -91,6 +103,7 @@ in { example = '' import XMonad import XMonad.Util.EZConfig (additionalKeys) + import Control.Monad (when) import Text.Printf (printf) import System.Posix.Process (executeFile) import System.Info (arch,os) @@ -99,16 +112,21 @@ in { compiledConfig = printf "xmonad-%s-%s" arch os - compileRestart = whenX (recompile True) . catchIO $ do - dir <- getXMonadDataDir - args <- getArgs - executeFile (dir </> compiledConfig) False args Nothing + compileRestart resume = + whenX (recompile True) $ + when resume writeStateToFile + *> catchIO + ( do + dir <- getXMonadDataDir + args <- getArgs + executeFile (dir </> compiledConfig) False args Nothing + ) main = launch defaultConfig { modMask = mod4Mask -- Use Super instead of Alt , terminal = "urxvt" } `additionalKeys` - [ ( (mod4Mask,xK_r), compileRestart ) + [ ( (mod4Mask,xK_r), compileRestart True) , ( (mod4Mask,xK_q), restart "xmonad" True ) ] ''; }; diff --git a/nixos/modules/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix index 5fda24adb9782..ac41ba5f93a4b 100644 --- a/nixos/modules/tasks/filesystems/bcachefs.nix +++ b/nixos/modules/tasks/filesystems/bcachefs.nix @@ -49,8 +49,8 @@ in } (mkIf ((elem "bcachefs" config.boot.initrd.supportedFilesystems) || (bootFs != {})) { - # the cryptographic modules are required only for decryption attempts - boot.initrd.availableKernelModules = [ "bcachefs" "chacha20" "poly1305" ]; + # chacha20 and poly1305 are required only for decryption attempts + boot.initrd.availableKernelModules = [ "bcachefs" "sha256" "chacha20" "poly1305" ]; boot.initrd.extraUtilsCommands = '' copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix index 8c12e0e49bf5b..c5470b7af09b0 100644 --- a/nixos/modules/virtualisation/amazon-init.nix +++ b/nixos/modules/virtualisation/amazon-init.nix @@ -7,7 +7,7 @@ let echo "attempting to fetch configuration from EC2 user data..." export HOME=/root - export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused config.system.build.nixos-rebuild]}:$PATH + export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused pkgs.xz config.system.build.nixos-rebuild]}:$PATH export NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels userData=/etc/ec2-metadata/user-data diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix index d493648401872..e47bd59dc016b 100644 --- a/nixos/modules/virtualisation/lxc-container.nix +++ b/nixos/modules/virtualisation/lxc-container.nix @@ -11,7 +11,7 @@ with lib; users.users.root.initialHashedPassword = mkOverride 150 ""; # Some more help text. - services.mingetty.helpLine = + services.getty.helpLine = '' Log in as "root" with an empty password. diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix index 26398afb3cf51..757d73421b8f2 100644 --- a/nixos/modules/virtualisation/nixos-containers.nix +++ b/nixos/modules/virtualisation/nixos-containers.nix @@ -56,10 +56,10 @@ let ip -6 route add $HOST_ADDRESS6 dev eth0 ip -6 route add default via $HOST_ADDRESS6 fi - - ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)} fi + ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)} + # Start the regular stage 1 script. exec "$1" '' @@ -223,8 +223,8 @@ let ${ipcall cfg "ip route" "$LOCAL_ADDRESS" "localAddress"} ${ipcall cfg "ip -6 route" "$LOCAL_ADDRESS6" "localAddress6"} fi - ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)} fi + ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)} '' ); diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix index a46dd65eb4910..ee9fe62187d33 100644 --- a/nixos/modules/virtualisation/oci-containers.nix +++ b/nixos/modules/virtualisation/oci-containers.nix @@ -176,10 +176,10 @@ let description = '' Define which other containers this one depends on. They will be added to both After and Requires for the unit. - Use the same name as the attribute under <literal>virtualisation.oci-containers</literal>. + Use the same name as the attribute under <literal>virtualisation.oci-containers.containers</literal>. ''; example = literalExample '' - virtualisation.oci-containers = { + virtualisation.oci-containers.containers = { node1 = {}; node2 = { dependsOn = [ "node1" ]; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 659e538b31d02..c491b559213c9 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -158,6 +158,7 @@ in home-assistant = handleTest ./home-assistant.nix {}; hostname = handleTest ./hostname.nix {}; hound = handleTest ./hound.nix {}; + hub = handleTest ./git/hub.nix {}; hydra = handleTest ./hydra {}; i3wm = handleTest ./i3wm.nix {}; icingaweb2 = handleTest ./icingaweb2.nix {}; diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix index 8402ba68b7209..369ef94f9fadc 100644 --- a/nixos/tests/docker-tools.nix +++ b/nixos/tests/docker-tools.nix @@ -247,5 +247,12 @@ import ./make-test-python.nix ({ pkgs, ... }: { ).strip() == "${if pkgs.system == "aarch64-linux" then "amd64" else "arm64"}" ) + + with subtest("buildLayeredImage doesn't dereference /nix/store symlink layers"): + docker.succeed( + "docker load --input='${examples.layeredStoreSymlink}'", + "docker run --rm ${examples.layeredStoreSymlink.imageName} bash -c 'test -L ${examples.layeredStoreSymlink.passthru.symlink}'", + "docker rmi ${examples.layeredStoreSymlink.imageName}", + ) ''; }) diff --git a/nixos/tests/dovecot.nix b/nixos/tests/dovecot.nix index bcbe234fd8052..1129e3b45d9d6 100644 --- a/nixos/tests/dovecot.nix +++ b/nixos/tests/dovecot.nix @@ -4,8 +4,11 @@ import ./make-test-python.nix { machine = { pkgs, ... }: { imports = [ common/user-account.nix ]; services.postfix.enable = true; - services.dovecot2.enable = true; - services.dovecot2.protocols = [ "imap" "pop3" ]; + services.dovecot2 = { + enable = true; + protocols = [ "imap" "pop3" ]; + modules = [ pkgs.dovecot_pigeonhole ]; + }; environment.systemPackages = let sendTestMail = pkgs.writeScriptBin "send-testmail" '' #!${pkgs.runtimeShell} diff --git a/nixos/tests/git/hub.nix b/nixos/tests/git/hub.nix new file mode 100644 index 0000000000000..e2359e887efbd --- /dev/null +++ b/nixos/tests/git/hub.nix @@ -0,0 +1,17 @@ +import ../make-test-python.nix ({ pkgs, ...} : { + name = "hub"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ nequissimus ]; + }; + + nodes.hub = { pkgs, ... }: + { + environment.systemPackages = [ pkgs.gitAndTools.hub ]; + }; + + testScript = + '' + assert "git version ${pkgs.git.version}\nhub version ${pkgs.gitAndTools.hub.version}\n" in hub.succeed("hub version") + assert "These GitHub commands are provided by hub" in hub.succeed("hub help") + ''; +}) diff --git a/nixos/tests/grafana.nix b/nixos/tests/grafana.nix index 4b453ece7f1ef..4ba091b893f42 100644 --- a/nixos/tests/grafana.nix +++ b/nixos/tests/grafana.nix @@ -17,6 +17,10 @@ let }; extraNodeConfs = { + declarativePlugins = { + services.grafana.declarativePlugins = [ pkgs.grafanaPlugins.grafana-clock-panel ]; + }; + postgresql = { services.grafana.database = { host = "127.0.0.1:5432"; @@ -52,7 +56,7 @@ let nameValuePair dbName (mkMerge [ baseGrafanaConf (extraNodeConfs.${dbName} or {}) - ])) [ "sqlite" "postgresql" "mysql" ]); + ])) [ "sqlite" "declarativePlugins" "postgresql" "mysql" ]); in { name = "grafana"; @@ -66,6 +70,14 @@ in { testScript = '' start_all() + with subtest("Declarative plugins installed"): + declarativePlugins.wait_for_unit("grafana.service") + declarativePlugins.wait_for_open_port(3000) + declarativePlugins.succeed( + "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/plugins | grep -q grafana-clock-panel" + ) + declarativePlugins.shutdown() + with subtest("Successful API query as admin user with sqlite db"): sqlite.wait_for_unit("grafana.service") sqlite.wait_for_open_port(3000) diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix index d36c1a91be432..ce11e1f942ab1 100644 --- a/nixos/tests/login.nix +++ b/nixos/tests/login.nix @@ -50,7 +50,7 @@ import ./make-test-python.nix ({ pkgs, latestKernel ? false, ... }: with subtest("Virtual console logout"): machine.send_chars("exit\n") machine.wait_until_fails("pgrep -u alice bash") - machine.screenshot("mingetty") + machine.screenshot("getty") with subtest("Check whether ctrl-alt-delete works"): machine.send_key("ctrl-alt-delete") diff --git a/nixos/tests/loki.nix b/nixos/tests/loki.nix index bede775b7d3c3..0c6dff3fdf137 100644 --- a/nixos/tests/loki.nix +++ b/nixos/tests/loki.nix @@ -46,7 +46,9 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: machine.wait_for_open_port(9080) machine.succeed("echo 'Loki Ingestion Test' > /var/log/testlog") # should not have access to journal unless specified - machine.fail("systemctl show --property=SupplementaryGroups promtail | grep -q systemd-journal") + machine.fail( + "systemctl show --property=SupplementaryGroups promtail | grep -q systemd-journal" + ) machine.wait_until_succeeds( "${pkgs.grafana-loki}/bin/logcli --addr='http://localhost:3100' query --no-labels '{job=\"varlogs\",filename=\"/var/log/testlog\"}' | grep -q 'Loki Ingestion Test'" ) diff --git a/nixos/tests/postgresql-wal-receiver.nix b/nixos/tests/postgresql-wal-receiver.nix index 432b46234f9c1..0e8b3bfd6c34f 100644 --- a/nixos/tests/postgresql-wal-receiver.nix +++ b/nixos/tests/postgresql-wal-receiver.nix @@ -1,11 +1,19 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing-python.nix { inherit system pkgs; }; + let + lib = pkgs.lib; + # Makes a test for a PostgreSQL package, given by name and looked up from `pkgs`. makePostgresqlWalReceiverTest = postgresqlPackage: { name = postgresqlPackage; value = - import ./make-test-python.nix ({ pkgs, lib, ... }: let - + let pkg = pkgs."${postgresqlPackage}"; postgresqlDataDir = "/var/lib/postgresql/${pkg.psqlSchema}"; replicationUser = "wal_receiver_user"; @@ -19,7 +27,7 @@ let then pkgs.writeTextDir "recovery.signal" "" else pkgs.writeTextDir "recovery.conf" "restore_command = 'cp ${walBackupDir}/%f %p'"; - in { + in makeTest { name = "postgresql-wal-receiver-${postgresqlPackage}"; meta.maintainers = with lib.maintainers; [ pacien ]; @@ -104,7 +112,7 @@ let "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100" ) ''; - }); + }; }; # Maps the generic function over all attributes of PostgreSQL packages diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index ffa7f420c093d..8fcb0a7aa2c34 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -96,6 +96,31 @@ let ''; }; + bird = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.bird2.enable = true; + services.bird2.config = '' + protocol kernel MyObviousTestString { + ipv4 { + import all; + export none; + }; + } + + protocol device { + } + ''; + }; + exporterTest = '' + wait_for_unit("prometheus-bird-exporter.service") + wait_for_open_port(9324) + succeed("curl -sSf http://localhost:9324/metrics | grep -q 'MyObviousTestString'") + ''; + }; + blackbox = { exporterConfig = { enable = true; @@ -197,10 +222,11 @@ let exporterConfig = { enable = true; url = "http://localhost"; - configFile = pkgs.writeText "json-exporter-conf.json" (builtins.toJSON [{ - name = "json_test_metric"; - path = "$.test"; - }]); + configFile = pkgs.writeText "json-exporter-conf.json" (builtins.toJSON { + metrics = [ + { name = "json_test_metric"; path = "$.test"; } + ]; + }); }; metricProvider = { systemd.services.prometheus-json-exporter.after = [ "nginx.service" ]; @@ -216,7 +242,9 @@ let wait_for_open_port(80) wait_for_unit("prometheus-json-exporter.service") wait_for_open_port(7979) - succeed("curl -sSf localhost:7979/metrics | grep -q 'json_test_metric 1'") + succeed( + "curl -sSf 'localhost:7979/probe?target=http://localhost' | grep -q 'json_test_metric 1'" + ) ''; }; @@ -634,7 +662,7 @@ let wait_for_open_port(11334) wait_for_open_port(7980) wait_until_succeeds( - "curl -sSf localhost:7980/metrics | grep -q 'rspamd_scanned{host=\"rspamd\"} 0'" + "curl -sSf 'localhost:7980/probe?target=http://localhost:11334/stat' | grep -q 'rspamd_scanned{host=\"rspamd\"} 0'" ) ''; }; @@ -670,6 +698,27 @@ let ''; }; + smokeping = { + exporterConfig = { + enable = true; + hosts = ["127.0.0.1"]; + }; + exporterTest = '' + wait_for_unit("prometheus-smokeping-exporter.service") + wait_for_open_port(9374) + wait_until_succeeds( + "curl -sSf localhost:9374/metrics | grep '{}' | grep -qv ' 0$'".format( + 'smokeping_requests_total{host="127.0.0.1",ip="127.0.0.1"} ' + ) + ) + wait_until_succeeds( + "curl -sSf localhost:9374/metrics | grep -q '{}'".format( + 'smokeping_response_ttl{host="127.0.0.1",ip="127.0.0.1"}' + ) + ) + ''; + }; + snmp = { exporterConfig = { enable = true; diff --git a/nixos/tests/shadow.nix b/nixos/tests/shadow.nix index 0cc1d6811400c..8f8cdef7ef9d7 100644 --- a/nixos/tests/shadow.nix +++ b/nixos/tests/shadow.nix @@ -2,6 +2,7 @@ let password1 = "foobar"; password2 = "helloworld"; password3 = "bazqux"; + password4 = "asdf123"; in import ./make-test-python.nix ({ pkgs, ... }: { name = "shadow"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ nequissimus ]; }; @@ -19,6 +20,10 @@ in import ./make-test-python.nix ({ pkgs, ... }: { password = password2; shell = pkgs.shadow; }; + users.ash = { + password = password4; + shell = pkgs.bash; + }; }; }; @@ -41,6 +46,15 @@ in import ./make-test-python.nix ({ pkgs, ... }: { shadow.wait_for_file("/tmp/1") assert "emma" in shadow.succeed("cat /tmp/1") + with subtest("Switch user"): + shadow.send_chars("su - ash\n") + shadow.sleep(2) + shadow.send_chars("${password4}\n") + shadow.sleep(2) + shadow.send_chars("whoami > /tmp/3\n") + shadow.wait_for_file("/tmp/3") + assert "ash" in shadow.succeed("cat /tmp/3") + with subtest("Change password"): shadow.send_key("alt-f3") shadow.wait_until_succeeds(f"[ $(fgconsole) = 3 ]") |