diff options
Diffstat (limited to 'nixos')
104 files changed, 2797 insertions, 975 deletions
diff --git a/nixos/doc/manual/configuration/gpu-accel.chapter.md b/nixos/doc/manual/configuration/gpu-accel.chapter.md index dfccdf291b736..aa63aec61669b 100644 --- a/nixos/doc/manual/configuration/gpu-accel.chapter.md +++ b/nixos/doc/manual/configuration/gpu-accel.chapter.md @@ -65,12 +65,10 @@ hardware.opengl.extraPackages = [ [Intel Gen8 and later GPUs](https://en.wikipedia.org/wiki/List_of_Intel_graphics_processing_units#Gen8) are supported by the Intel NEO OpenCL runtime that is provided by the -intel-compute-runtime package. For Gen7 GPUs, the deprecated Beignet -runtime can be used, which is provided by the beignet package. The -proprietary Intel OpenCL runtime, in the intel-ocl package, is an -alternative for Gen7 GPUs. +intel-compute-runtime package. The proprietary Intel OpenCL runtime, in +the intel-ocl package, is an alternative for Gen7 GPUs. -The intel-compute-runtime, beignet, or intel-ocl package can be added to +The intel-compute-runtime or intel-ocl package can be added to [](#opt-hardware.opengl.extraPackages) to enable OpenCL support. For example, for Gen8 and later GPUs, the following configuration can be used: diff --git a/nixos/doc/manual/installation/installing-usb.section.md b/nixos/doc/manual/installation/installing-usb.section.md index adfe22ea2f00e..3b9e2f492f04d 100644 --- a/nixos/doc/manual/installation/installing-usb.section.md +++ b/nixos/doc/manual/installation/installing-usb.section.md @@ -35,7 +35,7 @@ select the image, select the USB flash drive and click "Write". 4. Then use the `dd` utility to write the image to the USB flash drive. ```ShellSession - sudo dd if=<path-to-image> of=/dev/sdX bs=4M conv=fsync + sudo dd bs=4M conv=fsync oflag=direct status=progress if=<path-to-image> of=/dev/sdX ``` ## Creating bootable USB flash drive from a Terminal on macOS {#sec-booting-from-usb-macos} diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index d238e92271c54..aba4d3d72d1df 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -10,6 +10,8 @@ In addition to numerous new and upgraded packages, this release has the followin - `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_testing`, and is therefore no longer available. + - NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS. - This can be disabled through the `environment.stub-ld.enable` option. - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically. @@ -24,6 +26,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable). +- systemd's gateway, upload, and remote services, which provides ways of sending journals across the network. Enable using [services.journald.gateway](#opt-services.journald.gateway.enable), [services.journald.upload](#opt-services.journald.upload.enable), and [services.journald.remote](#opt-services.journald.remote.enable). + - [GNS3](https://www.gns3.com/), a network software emulator. Available as [services.gns3-server](#opt-services.gns3-server.enable). - [rspamd-trainer](https://gitlab.com/onlime/rspamd-trainer), script triggered by a helper which reads mails from a specific mail inbox and feeds them into rspamd for spam/ham training. @@ -43,11 +47,14 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> +- `himalaya` was updated to v1.0.0-beta, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta) for details. + - The `power.ups` module now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS. -- `k9s` was updated to v0.30. There have been various breaking changes in the config file format, - check out the changelog of [v0.29](https://github.com/derailed/k9s/releases/tag/v0.29.0) and - [v0.30](https://github.com/derailed/k9s/releases/tag/v0.30.0) for details. It is recommended +- `k9s` was updated to v0.31. There have been various breaking changes in the config file format, + check out the changelog of [v0.29](https://github.com/derailed/k9s/releases/tag/v0.29.0), + [v0.30](https://github.com/derailed/k9s/releases/tag/v0.30.0) and + [v0.31](https://github.com/derailed/k9s/releases/tag/v0.31.0) for details. It is recommended to back up your current configuration and let k9s recreate the new base configuration. - `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. @@ -58,11 +65,31 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - `paperless`' `services.paperless.extraConfig` setting has been removed and converted to the freeform type and option named `services.paperless.settings`. +- The legacy and long deprecated systemd target `network-interfaces.target` has been removed. Use `network.target` instead. + - `mkosi` was updated to v19. Parts of the user interface have changed. Consult the [release notes](https://github.com/systemd/mkosi/releases/tag/v19) for a list of changes. +- `services.nginx` will no longer advertise HTTP/3 availability automatically. This must now be manually added, preferably to each location block. + Example: + + ```nix + locations."/".extraConfig = '' + add_header Alt-Svc 'h3=":$server_port"; ma=86400'; + ''; + locations."^~ /assets/".extraConfig = '' + add_header Alt-Svc 'h3=":$server_port"; ma=86400'; + ''; + + ``` - 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). + - Files are now handled in binary mode; this could break programs with embedded UTF-8 characters. + - The ROM was updated to match ComputerCraft version v1.109.2. + - The bundled Lua was updated to Lua v5.2, which includes breaking changes. See the [Lua manual](https://www.lua.org/manual/5.2/manual.html#8) for more information. + - The WebSocket API [was rewritten](https://github.com/MCJack123/craftos2/issues/337), which introduced breaking changes. + - The latest available version of Nextcloud is v28 (available as `pkgs.nextcloud28`). The installation logic is as follows: - If [`services.nextcloud.package`](#opt-services.nextcloud.package) is specified explicitly, this package will be installed (**recommended**) - If [`system.stateVersion`](#opt-system.stateVersion) is >=24.05, `pkgs.nextcloud28` will be installed by default. @@ -80,6 +107,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m `CONFIG_FILE_NAME` includes `bpf_pinning`, `ematch_map`, `group`, `nl_protos`, `rt_dsfield`, `rt_protos`, `rt_realms`, `rt_scopes`, and `rt_tables`. +- The executable file names for `firefox-devedition`, `firefox-beta`, `firefox-esr` now matches their package names, which is consistent with the `firefox-*-bin` packages. The desktop entries are also updated so that you can have multiple editions of firefox in your app launcher. + - The `systemd.oomd` module behavior is changed as: - Raise ManagedOOMMemoryPressureLimit from 50% to 80%. This should make systemd-oomd kill things less often, and fix issues like [this](https://pagure.io/fedora-workstation/issue/358). @@ -91,6 +120,9 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - `systemd.oomd.enableUserServices` is renamed to `systemd.oomd.enableUserSlices`. +- `security.pam.enableSSHAgentAuth` now requires `services.openssh.authorizedKeysFiles` to be non-empty, + which is the case when `services.openssh.enable` is true. Previously, `pam_ssh_agent_auth` silently failed to work. + ## Other Notable Changes {#sec-release-24.05-notable-changes} <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> @@ -113,6 +145,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - `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. + - The following options of the Nextcloud module were moved into [`services.nextcloud.extraOptions`](#opt-services.nextcloud.extraOptions) and renamed to match the name from Nextcloud's `config.php`: - `logLevel` -> [`loglevel`](#opt-services.nextcloud.extraOptions.loglevel), - `logType` -> [`log_type`](#opt-services.nextcloud.extraOptions.log_type), @@ -123,6 +157,9 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - `extraTrustedDomains` -> [`trusted_domains`](#opt-services.nextcloud.extraOptions.trusted_domains) and - `trustedProxies` -> [`trusted_proxies`](#opt-services.nextcloud.extraOptions.trusted_proxies). +- 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). + - 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 @@ -134,11 +171,17 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - 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` + option was added, to control which `authorized_keys` files are trusted. It defaults to the previous behaviour, + **which is insecure**: see [#31611](https://github.com/NixOS/nixpkgs/issues/31611). + - [](#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. - `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. +- The `krb5` module has been rewritten and moved to `security.krb5`, moving all options but `security.krb5.enable` and `security.krb5.package` into `security.krb5.settings`. + - Gitea 1.21 upgrade has several breaking changes, including: - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*` - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command. diff --git a/nixos/lib/make-options-doc/default.nix b/nixos/lib/make-options-doc/default.nix index 99515b5b8276e..284934a7608ef 100644 --- a/nixos/lib/make-options-doc/default.nix +++ b/nixos/lib/make-options-doc/default.nix @@ -120,7 +120,7 @@ in rec { { meta.description = "List of NixOS options in JSON format"; nativeBuildInputs = [ pkgs.brotli - pkgs.python3Minimal + pkgs.python3 ]; options = builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON optionsNix)); diff --git a/nixos/lib/make-single-disk-zfs-image.nix b/nixos/lib/make-single-disk-zfs-image.nix index a3564f9a8b68e..585fa93b7fa0f 100644 --- a/nixos/lib/make-single-disk-zfs-image.nix +++ b/nixos/lib/make-single-disk-zfs-image.nix @@ -21,6 +21,9 @@ , # size of the FAT partition, in megabytes. bootSize ? 1024 + , # memory allocated for virtualized build instance + memSize ? 1024 + , # The size of the root partition, in megabytes. rootSize ? 2048 @@ -230,7 +233,7 @@ let ).runInLinuxVM ( pkgs.runCommand name { - memSize = 1024; + inherit memSize; QEMU_OPTS = "-drive file=$rootDiskImage,if=virtio,cache=unsafe,werror=report"; preVM = '' PATH=$PATH:${pkgs.qemu_kvm}/bin diff --git a/nixos/lib/make-squashfs.nix b/nixos/lib/make-squashfs.nix index 4b6b567399484..f28e2c6715805 100644 --- a/nixos/lib/make-squashfs.nix +++ b/nixos/lib/make-squashfs.nix @@ -14,6 +14,7 @@ let pseudoFilesArgs = lib.concatMapStrings (f: ''-p "${f}" '') pseudoFiles; + compFlag = if comp == null then "-no-compression" else "-comp ${comp}"; in stdenv.mkDerivation { name = "${fileName}.img"; @@ -39,7 +40,7 @@ stdenv.mkDerivation { # Generate the squashfs image. mksquashfs nix-path-registration $(cat $closureInfo/store-paths) $out ${pseudoFilesArgs} \ - -no-hardlinks ${lib.optionalString noStrip "-no-strip"} -keep-as-directory -all-root -b 1048576 -comp ${comp} \ + -no-hardlinks ${lib.optionalString noStrip "-no-strip"} -keep-as-directory -all-root -b 1048576 ${compFlag} \ -processors $NIX_BUILD_CORES ''; } diff --git a/nixos/lib/test-driver/default.nix b/nixos/lib/test-driver/default.nix index 09d80deb85467..1acdaacc4e658 100644 --- a/nixos/lib/test-driver/default.nix +++ b/nixos/lib/test-driver/default.nix @@ -18,7 +18,7 @@ python3Packages.buildPythonApplication { pname = "nixos-test-driver"; version = "1.1"; src = ./.; - format = "pyproject"; + pyproject = true; propagatedBuildInputs = [ coreutils @@ -32,6 +32,10 @@ python3Packages.buildPythonApplication { ++ (lib.optionals enableOCR [ imagemagick_light tesseract4 ]) ++ extraPythonPackages python3Packages; + nativeBuildInputs = [ + python3Packages.setuptools + ]; + passthru.tests = { inherit (nixosTests.nixos-test-driver) driver-timeout; }; diff --git a/nixos/maintainers/scripts/openstack/openstack-image-zfs.nix b/nixos/maintainers/scripts/openstack/openstack-image-zfs.nix index 936dcee12949e..60f0535854dd5 100644 --- a/nixos/maintainers/scripts/openstack/openstack-image-zfs.nix +++ b/nixos/maintainers/scripts/openstack/openstack-image-zfs.nix @@ -20,6 +20,12 @@ in default = "nixos-openstack-image-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}"; }; + ramMB = mkOption { + type = types.int; + default = 1024; + description = lib.mdDoc "RAM allocation for build VM"; + }; + sizeMB = mkOption { type = types.int; default = 8192; @@ -64,7 +70,7 @@ in includeChannel = copyChannel; bootSize = 1000; - + memSize = cfg.ramMB; rootSize = cfg.sizeMB; rootPoolProperties = { ashift = 12; diff --git a/nixos/modules/config/krb5/default.nix b/nixos/modules/config/krb5/default.nix deleted file mode 100644 index df7a3f48236f0..0000000000000 --- a/nixos/modules/config/krb5/default.nix +++ /dev/null @@ -1,369 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - cfg = config.krb5; - - # This is to provide support for old configuration options (as much as is - # reasonable). This can be removed after 18.03 was released. - defaultConfig = { - libdefaults = optionalAttrs (cfg.defaultRealm != null) - { default_realm = cfg.defaultRealm; }; - - realms = optionalAttrs (lib.all (value: value != null) [ - cfg.defaultRealm cfg.kdc cfg.kerberosAdminServer - ]) { - ${cfg.defaultRealm} = { - kdc = cfg.kdc; - admin_server = cfg.kerberosAdminServer; - }; - }; - - domain_realm = optionalAttrs (lib.all (value: value != null) [ - cfg.domainRealm cfg.defaultRealm - ]) { - ".${cfg.domainRealm}" = cfg.defaultRealm; - ${cfg.domainRealm} = cfg.defaultRealm; - }; - }; - - mergedConfig = (recursiveUpdate defaultConfig { - inherit (config.krb5) - kerberos libdefaults realms domain_realm capaths appdefaults plugins - extraConfig config; - }); - - filterEmbeddedMetadata = value: if isAttrs value then - (filterAttrs - (attrName: attrValue: attrName != "_module" && attrValue != null) - value) - else value; - - indent = " "; - - mkRelation = name: value: - if (isList value) then - concatMapStringsSep "\n" (mkRelation name) value - else "${name} = ${mkVal value}"; - - mkVal = value: - if (value == true) then "true" - else if (value == false) then "false" - else if (isInt value) then (toString value) - else if (isAttrs value) then - let configLines = concatLists - (map (splitString "\n") - (mapAttrsToList mkRelation value)); - in - (concatStringsSep "\n${indent}" - ([ "{" ] ++ configLines)) - + "\n}" - else value; - - mkMappedAttrsOrString = value: concatMapStringsSep "\n" - (line: if builtins.stringLength line > 0 - then "${indent}${line}" - else line) - (splitString "\n" - (if isAttrs value then - concatStringsSep "\n" - (mapAttrsToList mkRelation value) - else value)); - -in { - - ###### interface - - options = { - krb5 = { - enable = mkEnableOption (lib.mdDoc "building krb5.conf, configuration file for Kerberos V"); - - kerberos = mkOption { - type = types.package; - default = pkgs.krb5; - defaultText = literalExpression "pkgs.krb5"; - example = literalExpression "pkgs.heimdal"; - description = lib.mdDoc '' - The Kerberos implementation that will be present in - `environment.systemPackages` after enabling this - service. - ''; - }; - - libdefaults = mkOption { - type = with types; either attrs lines; - default = {}; - apply = attrs: filterEmbeddedMetadata attrs; - example = literalExpression '' - { - default_realm = "ATHENA.MIT.EDU"; - }; - ''; - description = lib.mdDoc '' - Settings used by the Kerberos V5 library. - ''; - }; - - realms = mkOption { - type = with types; either attrs lines; - default = {}; - example = literalExpression '' - { - "ATHENA.MIT.EDU" = { - admin_server = "athena.mit.edu"; - kdc = [ - "athena01.mit.edu" - "athena02.mit.edu" - ]; - }; - }; - ''; - apply = attrs: filterEmbeddedMetadata attrs; - description = lib.mdDoc "Realm-specific contact information and settings."; - }; - - domain_realm = mkOption { - type = with types; either attrs lines; - default = {}; - example = literalExpression '' - { - "example.com" = "EXAMPLE.COM"; - ".example.com" = "EXAMPLE.COM"; - }; - ''; - apply = attrs: filterEmbeddedMetadata attrs; - description = lib.mdDoc '' - Map of server hostnames to Kerberos realms. - ''; - }; - - capaths = mkOption { - type = with types; either attrs lines; - default = {}; - example = literalExpression '' - { - "ATHENA.MIT.EDU" = { - "EXAMPLE.COM" = "."; - }; - "EXAMPLE.COM" = { - "ATHENA.MIT.EDU" = "."; - }; - }; - ''; - apply = attrs: filterEmbeddedMetadata attrs; - description = lib.mdDoc '' - Authentication paths for non-hierarchical cross-realm authentication. - ''; - }; - - appdefaults = mkOption { - type = with types; either attrs lines; - default = {}; - example = literalExpression '' - { - pam = { - debug = false; - ticket_lifetime = 36000; - renew_lifetime = 36000; - max_timeout = 30; - timeout_shift = 2; - initial_timeout = 1; - }; - }; - ''; - apply = attrs: filterEmbeddedMetadata attrs; - description = lib.mdDoc '' - Settings used by some Kerberos V5 applications. - ''; - }; - - plugins = mkOption { - type = with types; either attrs lines; - default = {}; - example = literalExpression '' - { - ccselect = { - disable = "k5identity"; - }; - }; - ''; - apply = attrs: filterEmbeddedMetadata attrs; - description = lib.mdDoc '' - Controls plugin module registration. - ''; - }; - - extraConfig = mkOption { - type = with types; nullOr lines; - default = null; - example = '' - [logging] - kdc = SYSLOG:NOTICE - admin_server = SYSLOG:NOTICE - default = SYSLOG:NOTICE - ''; - description = lib.mdDoc '' - These lines go to the end of `krb5.conf` verbatim. - `krb5.conf` may include any of the relations that are - valid for `kdc.conf` (see `man kdc.conf`), - but it is not a recommended practice. - ''; - }; - - config = mkOption { - type = with types; nullOr lines; - default = null; - example = '' - [libdefaults] - default_realm = EXAMPLE.COM - - [realms] - EXAMPLE.COM = { - admin_server = kerberos.example.com - kdc = kerberos.example.com - default_principal_flags = +preauth - } - - [domain_realm] - example.com = EXAMPLE.COM - .example.com = EXAMPLE.COM - - [logging] - kdc = SYSLOG:NOTICE - admin_server = SYSLOG:NOTICE - default = SYSLOG:NOTICE - ''; - description = lib.mdDoc '' - Verbatim `krb5.conf` configuration. Note that this - is mutually exclusive with configuration via - `libdefaults`, `realms`, - `domain_realm`, `capaths`, - `appdefaults`, `plugins` and - `extraConfig` configuration options. Consult - `man krb5.conf` for documentation. - ''; - }; - - defaultRealm = mkOption { - type = with types; nullOr str; - default = null; - example = "ATHENA.MIT.EDU"; - description = lib.mdDoc '' - DEPRECATED, please use - `krb5.libdefaults.default_realm`. - ''; - }; - - domainRealm = mkOption { - type = with types; nullOr str; - default = null; - example = "athena.mit.edu"; - description = lib.mdDoc '' - DEPRECATED, please create a map of server hostnames to Kerberos realms - in `krb5.domain_realm`. - ''; - }; - - kdc = mkOption { - type = with types; nullOr str; - default = null; - example = "kerberos.mit.edu"; - description = lib.mdDoc '' - DEPRECATED, please pass a `kdc` attribute to a realm - in `krb5.realms`. - ''; - }; - - kerberosAdminServer = mkOption { - type = with types; nullOr str; - default = null; - example = "kerberos.mit.edu"; - description = lib.mdDoc '' - DEPRECATED, please pass an `admin_server` attribute - to a realm in `krb5.realms`. - ''; - }; - }; - }; - - ###### implementation - - config = mkIf cfg.enable { - - environment.systemPackages = [ cfg.kerberos ]; - - environment.etc."krb5.conf".text = if isString cfg.config - then cfg.config - else ('' - [libdefaults] - ${mkMappedAttrsOrString mergedConfig.libdefaults} - - [realms] - ${mkMappedAttrsOrString mergedConfig.realms} - - [domain_realm] - ${mkMappedAttrsOrString mergedConfig.domain_realm} - - [capaths] - ${mkMappedAttrsOrString mergedConfig.capaths} - - [appdefaults] - ${mkMappedAttrsOrString mergedConfig.appdefaults} - - [plugins] - ${mkMappedAttrsOrString mergedConfig.plugins} - '' + optionalString (mergedConfig.extraConfig != null) - ("\n" + mergedConfig.extraConfig)); - - warnings = flatten [ - (optional (cfg.defaultRealm != null) '' - The option krb5.defaultRealm is deprecated, please use - krb5.libdefaults.default_realm. - '') - (optional (cfg.domainRealm != null) '' - The option krb5.domainRealm is deprecated, please use krb5.domain_realm. - '') - (optional (cfg.kdc != null) '' - The option krb5.kdc is deprecated, please pass a kdc attribute to a - realm in krb5.realms. - '') - (optional (cfg.kerberosAdminServer != null) '' - The option krb5.kerberosAdminServer is deprecated, please pass an - admin_server attribute to a realm in krb5.realms. - '') - ]; - - assertions = [ - { assertion = !((builtins.any (value: value != null) [ - cfg.defaultRealm cfg.domainRealm cfg.kdc cfg.kerberosAdminServer - ]) && ((builtins.any (value: value != {}) [ - cfg.libdefaults cfg.realms cfg.domain_realm cfg.capaths - cfg.appdefaults cfg.plugins - ]) || (builtins.any (value: value != null) [ - cfg.config cfg.extraConfig - ]))); - message = '' - Configuration of krb5.conf by deprecated options is mutually exclusive - with configuration by section. Please migrate your config using the - attributes suggested in the warnings. - ''; - } - { assertion = !(cfg.config != null - && ((builtins.any (value: value != {}) [ - cfg.libdefaults cfg.realms cfg.domain_realm cfg.capaths - cfg.appdefaults cfg.plugins - ]) || (builtins.any (value: value != null) [ - cfg.extraConfig cfg.defaultRealm cfg.domainRealm cfg.kdc - cfg.kerberosAdminServer - ]))); - message = '' - Configuration of krb5.conf using krb.config is mutually exclusive with - configuration by section. If you want to mix the two, you can pass - lines to any configuration section or lines to krb5.extraConfig. - ''; - } - ]; - }; -} diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix index 0f5888f5ea3b0..4727e5b85ef22 100644 --- a/nixos/modules/config/no-x-libs.nix +++ b/nixos/modules/config/no-x-libs.nix @@ -35,6 +35,7 @@ with lib; # dep of graphviz, libXpm is optional for Xpm support gd = super.gd.override { withXorg = false; }; ghostscript = super.ghostscript.override { cupsSupport = false; x11Support = false; }; + gjs = super.gjs.overrideAttrs { doCheck = false; installTests = false; }; # avoid test dependency on gtk3 gobject-introspection = super.gobject-introspection.override { x11Support = false; }; gpsd = super.gpsd.override { guiSupport = false; }; graphviz = super.graphviz-nox; diff --git a/nixos/modules/hardware/usb-storage.nix b/nixos/modules/hardware/usb-storage.nix index 9c1b7a125fd18..3cb2c60d7ccd5 100644 --- a/nixos/modules/hardware/usb-storage.nix +++ b/nixos/modules/hardware/usb-storage.nix @@ -14,7 +14,7 @@ with lib; config = mkIf config.hardware.usbStorage.manageStartStop { services.udev.extraRules = '' - ACTION=="add|change", SUBSYSTEM=="scsi_disk", DRIVERS=="usb-storage", ATTR{manage_start_stop}="1" + ACTION=="add|change", SUBSYSTEM=="scsi_disk", DRIVERS=="usb-storage", ATTR{manage_system_start_stop}="1" ''; }; } diff --git a/nixos/modules/hardware/video/webcam/ipu6.nix b/nixos/modules/hardware/video/webcam/ipu6.nix index fce78cda34c71..c2dbdc217bd60 100644 --- a/nixos/modules/hardware/video/webcam/ipu6.nix +++ b/nixos/modules/hardware/video/webcam/ipu6.nix @@ -13,11 +13,12 @@ in enable = mkEnableOption (lib.mdDoc "support for Intel IPU6/MIPI cameras"); platform = mkOption { - type = types.enum [ "ipu6" "ipu6ep" ]; + type = types.enum [ "ipu6" "ipu6ep" "ipu6epmtl" ]; description = lib.mdDoc '' Choose the version for your hardware platform. - Use `ipu6` for Tiger Lake and `ipu6ep` for Alder Lake respectively. + Use `ipu6` for Tiger Lake, `ipu6ep` for Alder Lake or Raptor Lake, + and `ipu6epmtl` for Meteor Lake. ''; }; @@ -29,9 +30,7 @@ in ipu6-drivers ]; - hardware.firmware = with pkgs; [ ] - ++ optional (cfg.platform == "ipu6") ipu6-camera-bin - ++ optional (cfg.platform == "ipu6ep") ipu6ep-camera-bin; + hardware.firmware = [ pkgs.ipu6-camera-bins ]; services.udev.extraRules = '' SUBSYSTEM=="intel-ipu6-psys", MODE="0660", GROUP="video" @@ -44,14 +43,13 @@ in extraPackages = with pkgs.gst_all_1; [ ] ++ optional (cfg.platform == "ipu6") icamerasrc-ipu6 - ++ optional (cfg.platform == "ipu6ep") icamerasrc-ipu6ep; + ++ optional (cfg.platform == "ipu6ep") icamerasrc-ipu6ep + ++ optional (cfg.platform == "ipu6epmtl") icamerasrc-ipu6epmtl; input = { pipeline = "icamerasrc"; - format = mkIf (cfg.platform == "ipu6ep") (mkDefault "NV12"); + format = mkIf (cfg.platform != "ipu6") (mkDefault "NV12"); }; }; - }; - } diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index 0b5135c088eaf..6adb94e09aff3 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -512,9 +512,10 @@ in + lib.optionalString isAarch "-Xbcj arm" + lib.optionalString (isPower && is32bit && isBigEndian) "-Xbcj powerpc" + lib.optionalString (isSparc) "-Xbcj sparc"; - type = lib.types.str; + type = lib.types.nullOr lib.types.str; description = lib.mdDoc '' Compression settings to use for the squashfs nix store. + `null` disables compression. ''; example = "zstd -Xcompression-level 6"; }; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 65047bdd110a7..4e3ce4d088968 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -10,7 +10,6 @@ ./config/gtk/gtk-icon-cache.nix ./config/i18n.nix ./config/iproute2.nix - ./config/krb5/default.nix ./config/ldap.nix ./config/ldso.nix ./config/locale.nix @@ -273,6 +272,7 @@ ./programs/virt-manager.nix ./programs/wavemon.nix ./programs/wayland/cardboard.nix + ./programs/wayland/labwc.nix ./programs/wayland/river.nix ./programs/wayland/sway.nix ./programs/wayland/waybar.nix @@ -308,6 +308,7 @@ ./security/duosec.nix ./security/google_oslogin.nix ./security/ipa.nix + ./security/krb5 ./security/lock-kernel-modules.nix ./security/misc.nix ./security/oath.nix @@ -497,6 +498,7 @@ ./services/development/jupyterhub/default.nix ./services/development/livebook.nix ./services/development/lorri.nix + ./services/development/nixseparatedebuginfod.nix ./services/development/rstudio-server/default.nix ./services/development/zammad.nix ./services/display-managers/greetd.nix @@ -832,6 +834,7 @@ ./services/monitoring/riemann.nix ./services/monitoring/scollector.nix ./services/monitoring/smartd.nix + ./services/monitoring/snmpd.nix ./services/monitoring/statsd.nix ./services/monitoring/sysstat.nix ./services/monitoring/teamviewer.nix @@ -1175,6 +1178,7 @@ ./services/search/typesense.nix ./services/security/aesmd.nix ./services/security/authelia.nix + ./services/security/bitwarden-directory-connector-cli.nix ./services/security/certmgr.nix ./services/security/cfssl.nix ./services/security/clamav.nix @@ -1472,6 +1476,9 @@ ./system/boot/systemd/initrd-secrets.nix ./system/boot/systemd/initrd.nix ./system/boot/systemd/journald.nix + ./system/boot/systemd/journald-gateway.nix + ./system/boot/systemd/journald-remote.nix + ./system/boot/systemd/journald-upload.nix ./system/boot/systemd/logind.nix ./system/boot/systemd/nspawn.nix ./system/boot/systemd/oomd.nix diff --git a/nixos/modules/programs/firefox.nix b/nixos/modules/programs/firefox.nix index 1edf935d1649e..29c567783e27a 100644 --- a/nixos/modules/programs/firefox.nix +++ b/nixos/modules/programs/firefox.nix @@ -284,6 +284,7 @@ in # Preferences are converted into a policy programs.firefox.policies = { + DisableAppUpdate = true; Preferences = (mapAttrs (_: value: { Value = value; Status = cfg.preferencesStatus; }) cfg.preferences); diff --git a/nixos/modules/programs/partition-manager.nix b/nixos/modules/programs/partition-manager.nix index c18598b7c25d6..cf0491ff028fc 100644 --- a/nixos/modules/programs/partition-manager.nix +++ b/nixos/modules/programs/partition-manager.nix @@ -14,6 +14,6 @@ with lib; config = mkIf config.programs.partition-manager.enable { services.dbus.packages = [ pkgs.libsForQt5.kpmcore ]; # `kpmcore` need to be installed to pull in polkit actions. - environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.partition-manager ]; + environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.libsForQt5.partitionmanager ]; }; } diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix index 9fd37e1793a7f..7f285ab05537a 100644 --- a/nixos/modules/programs/singularity.nix +++ b/nixos/modules/programs/singularity.nix @@ -61,7 +61,12 @@ in }; enableSuid = mkOption { type = types.bool; - default = true; + # SingularityCE requires SETUID for most things. Apptainer prefers user + # namespaces, e.g. `apptainer exec --nv` would fail if built + # `--with-suid`: + # > `FATAL: nvidia-container-cli not allowed in setuid mode` + default = cfg.package.projectName != "apptainer"; + defaultText = literalExpression ''config.services.singularity.package.projectName != "apptainer"''; example = false; description = mdDoc '' Whether to enable the SUID support of Singularity/Apptainer. diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix index bec3900496fd9..34f6f0882c617 100644 --- a/nixos/modules/programs/starship.nix +++ b/nixos/modules/programs/starship.nix @@ -1,13 +1,21 @@ { config, lib, pkgs, ... }: -with lib; - let cfg = config.programs.starship; settingsFormat = pkgs.formats.toml { }; - settingsFile = settingsFormat.generate "starship.toml" cfg.settings; + userSettingsFile = settingsFormat.generate "starship.toml" cfg.settings; + + settingsFile = if cfg.presets == [] then userSettingsFile else pkgs.runCommand "starship.toml" + { + nativeBuildInputs = [ pkgs.yq ]; + } '' + tomlq -s -t 'reduce .[] as $item ({}; . * $item)' \ + ${lib.concatStringsSep " " (map (f: "${pkgs.starship}/share/starship/presets/${f}.toml") cfg.presets)} \ + ${userSettingsFile} \ + > $out + ''; initOption = if cfg.interactiveOnly then @@ -18,19 +26,28 @@ let in { options.programs.starship = { - enable = mkEnableOption (lib.mdDoc "the Starship shell prompt"); + enable = lib.mkEnableOption (lib.mdDoc "the Starship shell prompt"); - interactiveOnly = mkOption { + interactiveOnly = lib.mkOption { default = true; example = false; - type = types.bool; + type = lib.types.bool; description = lib.mdDoc '' Whether to enable starship only when the shell is interactive. Some plugins require this to be set to false to function correctly. ''; }; - settings = mkOption { + presets = lib.mkOption { + default = [ ]; + example = [ "nerd-font-symbols" ]; + type = with lib.types; listOf str; + description = lib.mdDoc '' + Presets files to be merged with settings in order. + ''; + }; + + settings = lib.mkOption { inherit (settingsFormat) type; default = { }; description = lib.mdDoc '' @@ -41,7 +58,7 @@ in }; }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { programs.bash.${initOption} = '' if [[ $TERM != "dumb" ]]; then # don't set STARSHIP_CONFIG automatically if there's a user-specified diff --git a/nixos/modules/programs/wayland/labwc.nix b/nixos/modules/programs/wayland/labwc.nix new file mode 100644 index 0000000000000..d0806c3aa5d0e --- /dev/null +++ b/nixos/modules/programs/wayland/labwc.nix @@ -0,0 +1,25 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.labwc; +in +{ + meta.maintainers = with lib.maintainers; [ AndersonTorres ]; + + options.programs.labwc = { + enable = lib.mkEnableOption (lib.mdDoc "labwc"); + package = lib.mkPackageOption pkgs "labwc" { }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + environment.systemPackages = [ cfg.package ]; + + xdg.portal.config.wlroots.default = lib.mkDefault [ "wlr" "gtk" ]; + + # To make a labwc session available for certain DMs like SDDM + services.xserver.displayManager.sessionPackages = [ cfg.package ]; + } + (import ./wayland-session.nix { inherit lib pkgs; }) + ]); +} diff --git a/nixos/modules/security/ipa.nix b/nixos/modules/security/ipa.nix index 49226ec38199c..3bf8b11f86261 100644 --- a/nixos/modules/security/ipa.nix +++ b/nixos/modules/security/ipa.nix @@ -117,8 +117,8 @@ in { config = mkIf cfg.enable { assertions = [ { - assertion = !config.krb5.enable; - message = "krb5 must be disabled through `krb5.enable` for FreeIPA integration to work."; + assertion = !config.security.krb5.enable; + message = "krb5 must be disabled through `security.krb5.enable` for FreeIPA integration to work."; } { assertion = !config.users.ldap.enable; diff --git a/nixos/modules/security/krb5/default.nix b/nixos/modules/security/krb5/default.nix new file mode 100644 index 0000000000000..5921982f954ca --- /dev/null +++ b/nixos/modules/security/krb5/default.nix @@ -0,0 +1,90 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) mdDoc mkIf mkOption mkPackageOption mkRemovedOptionModule; + inherit (lib.types) bool; + + mkRemovedOptionModule' = name: reason: mkRemovedOptionModule ["krb5" name] reason; + mkRemovedOptionModuleCfg = name: mkRemovedOptionModule' name '' + The option `krb5.${name}' has been removed. Use + `security.krb5.settings.${name}' for structured configuration. + ''; + + cfg = config.security.krb5; + format = import ./krb5-conf-format.nix { inherit pkgs lib; } { }; +in { + imports = [ + (mkRemovedOptionModuleCfg "libdefaults") + (mkRemovedOptionModuleCfg "realms") + (mkRemovedOptionModuleCfg "domain_realm") + (mkRemovedOptionModuleCfg "capaths") + (mkRemovedOptionModuleCfg "appdefaults") + (mkRemovedOptionModuleCfg "plugins") + (mkRemovedOptionModuleCfg "config") + (mkRemovedOptionModuleCfg "extraConfig") + (mkRemovedOptionModule' "kerberos" '' + The option `krb5.kerberos' has been moved to `security.krb5.package'. + '') + ]; + + options = { + security.krb5 = { + enable = mkOption { + default = false; + description = mdDoc "Enable and configure Kerberos utilities"; + type = bool; + }; + + package = mkPackageOption pkgs "krb5" { + example = "heimdal"; + }; + + settings = mkOption { + default = { }; + type = format.type; + description = mdDoc '' + Structured contents of the {file}`krb5.conf` file. See + {manpage}`krb5.conf(5)` for details about configuration. + ''; + example = { + include = [ "/run/secrets/secret-krb5.conf" ]; + includedir = [ "/run/secrets/secret-krb5.conf.d" ]; + + libdefaults = { + default_realm = "ATHENA.MIT.EDU"; + }; + + realms = { + "ATHENA.MIT.EDU" = { + admin_server = "athena.mit.edu"; + kdc = [ + "athena01.mit.edu" + "athena02.mit.edu" + ]; + }; + }; + + domain_realm = { + "mit.edu" = "ATHENA.MIT.EDU"; + }; + + logging = { + kdc = "SYSLOG:NOTICE"; + admin_server = "SYSLOG:NOTICE"; + default = "SYSLOG:NOTICE"; + }; + }; + }; + }; + }; + + config = mkIf cfg.enable { + environment = { + systemPackages = [ cfg.package ]; + etc."krb5.conf".source = format.generate "krb5.conf" cfg.settings; + }; + }; + + meta.maintainers = builtins.attrValues { + inherit (lib.maintainers) dblsaiko h7x4; + }; +} diff --git a/nixos/modules/security/krb5/krb5-conf-format.nix b/nixos/modules/security/krb5/krb5-conf-format.nix new file mode 100644 index 0000000000000..d01e47a40be05 --- /dev/null +++ b/nixos/modules/security/krb5/krb5-conf-format.nix @@ -0,0 +1,88 @@ +{ pkgs, lib, ... }: + +# Based on +# - https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html +# - https://manpages.debian.org/unstable/heimdal-docs/krb5.conf.5heimdal.en.html + +let + inherit (lib) boolToString concatMapStringsSep concatStringsSep filter + isAttrs isBool isList mapAttrsToList mdDoc mkOption singleton splitString; + inherit (lib.types) attrsOf bool coercedTo either int listOf oneOf path + str submodule; +in +{ }: { + type = let + section = attrsOf relation; + relation = either (attrsOf value) value; + value = either (listOf atom) atom; + atom = oneOf [int str bool]; + in submodule { + freeformType = attrsOf section; + options = { + include = mkOption { + default = [ ]; + description = mdDoc '' + Files to include in the Kerberos configuration. + ''; + type = coercedTo path singleton (listOf path); + }; + includedir = mkOption { + default = [ ]; + description = mdDoc '' + Directories containing files to include in the Kerberos configuration. + ''; + type = coercedTo path singleton (listOf path); + }; + module = mkOption { + default = [ ]; + description = mdDoc '' + Modules to obtain Kerberos configuration from. + ''; + type = coercedTo path singleton (listOf path); + }; + }; + }; + + generate = let + indent = str: concatMapStringsSep "\n" (line: " " + line) (splitString "\n" str); + + formatToplevel = args @ { + include ? [ ], + includedir ? [ ], + module ? [ ], + ... + }: let + sections = removeAttrs args [ "include" "includedir" "module" ]; + in concatStringsSep "\n" (filter (x: x != "") [ + (concatStringsSep "\n" (mapAttrsToList formatSection sections)) + (concatMapStringsSep "\n" (m: "module ${m}") module) + (concatMapStringsSep "\n" (i: "include ${i}") include) + (concatMapStringsSep "\n" (i: "includedir ${i}") includedir) + ]); + + formatSection = name: section: '' + [${name}] + ${indent (concatStringsSep "\n" (mapAttrsToList formatRelation section))} + ''; + + formatRelation = name: relation: + if isAttrs relation + then '' + ${name} = { + ${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))} + }'' + else formatValue name relation; + + formatValue = name: value: + if isList value + then concatMapStringsSep "\n" (formatAtom name) value + else formatAtom name value; + + formatAtom = name: atom: let + v = if isBool atom then boolToString atom else toString atom; + in "${name} = ${v}"; + in + name: value: pkgs.writeText name '' + ${formatToplevel value} + ''; +} diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index c99615d5a6362..111be7057afc0 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -654,8 +654,8 @@ let { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { config_file = "/etc/security/pam_mysql.conf"; }; } - { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = { - file = lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles; + { name = "ssh_agent_auth"; enable = config.security.pam.sshAgentAuth.enable && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = { + file = lib.concatStringsSep ":" config.security.pam.sshAgentAuth.authorizedKeysFiles; }; } (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so"; args = [ "${pkgs.opensc}/lib/opensc-pkcs11.so" @@ -943,7 +943,7 @@ let value.source = pkgs.writeText "${name}.pam" service.text; }; - optionalSudoConfigForSSHAgentAuth = optionalString config.security.pam.enableSSHAgentAuth '' + optionalSudoConfigForSSHAgentAuth = optionalString config.security.pam.sshAgentAuth.enable '' # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic. Defaults env_keep+=SSH_AUTH_SOCK ''; @@ -956,6 +956,7 @@ in imports = [ (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ]) + (mkRenamedOptionModule [ "security" "pam" "enableSSHAgentAuth" ] [ "security" "pam" "sshAgentAuth" "enable" ]) ]; ###### interface @@ -1025,16 +1026,34 @@ in ''; }; - security.pam.enableSSHAgentAuth = mkOption { - type = types.bool; - default = false; - description = - lib.mdDoc '' - Enable sudo logins if the user's SSH agent provides a key - present in {file}`~/.ssh/authorized_keys`. - This allows machines to exclusively use SSH keys instead of - passwords. + security.pam.sshAgentAuth = { + enable = mkEnableOption '' + authenticating using a signature performed by the ssh-agent. + This allows using SSH keys exclusively, instead of passwords, for instance on remote machines + ''; + + authorizedKeysFiles = mkOption { + type = with types; listOf str; + description = '' + A list of paths to files in OpenSSH's `authorized_keys` format, containing + the keys that will be trusted by the `pam_ssh_agent_auth` module. + + The following patterns are expanded when interpreting the path: + - `%f` and `%H` respectively expand to the fully-qualified and short hostname ; + - `%u` expands to the username ; + - `~` or `%h` expands to the user's home directory. + + ::: {.note} + Specifying user-writeable files here result in an insecure configuration: a malicious process + can then edit such an authorized_keys file and bypass the ssh-agent-based authentication. + + See [issue #31611](https://github.com/NixOS/nixpkgs/issues/31611) + ::: ''; + example = [ "/etc/ssh/authorized_keys.d/%u" ]; + default = config.services.openssh.authorizedKeysFiles; + defaultText = literalExpression "config.services.openssh.authorizedKeysFiles"; + }; }; security.pam.enableOTPW = mkEnableOption (lib.mdDoc "the OTPW (one-time password) PAM module"); @@ -1067,8 +1086,8 @@ in security.pam.krb5 = { enable = mkOption { - default = config.krb5.enable; - defaultText = literalExpression "config.krb5.enable"; + default = config.security.krb5.enable; + defaultText = literalExpression "config.security.krb5.enable"; type = types.bool; description = lib.mdDoc '' Enables Kerberos PAM modules (`pam-krb5`, @@ -1076,7 +1095,7 @@ in If set, users can authenticate with their Kerberos password. This requires a valid Kerberos configuration - (`config.krb5.enable` should be set to + (`config.security.krb5.enable` should be set to `true`). Note that the Kerberos PAM modules are not necessary when using SSS @@ -1456,8 +1475,25 @@ in `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled` or `boot.zfs.enableUnstable`). ''; } + { + assertion = with config.security.pam.sshAgentAuth; enable -> authorizedKeysFiles != []; + message = '' + `security.pam.enableSSHAgentAuth` requires `services.openssh.authorizedKeysFiles` to be a non-empty list. + Did you forget to set `services.openssh.enable` ? + ''; + } ]; + warnings = optional + (with lib; with config.security.pam.sshAgentAuth; + enable && any (s: hasPrefix "%h" s || hasPrefix "~" s) authorizedKeysFiles) + ''config.security.pam.sshAgentAuth.authorizedKeysFiles contains files in the user's home directory. + + Specifying user-writeable files there result in an insecure configuration: + a malicious process can then edit such an authorized_keys file and bypass the ssh-agent-based authentication. + See https://github.com/NixOS/nixpkgs/issues/31611 + ''; + environment.systemPackages = # Include the PAM modules in the system path mostly for the manpages. [ pkgs.pam ] diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix index 3dd5d2e525d91..6aa9445eab65e 100644 --- a/nixos/modules/security/sudo.nix +++ b/nixos/modules/security/sudo.nix @@ -6,8 +6,6 @@ let cfg = config.security.sudo; - inherit (config.security.pam) enableSSHAgentAuth; - toUserString = user: if (isInt user) then "#${toString user}" else "${user}"; toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}"; diff --git a/nixos/modules/services/admin/pgadmin.nix b/nixos/modules/services/admin/pgadmin.nix index 5eaa911e37f18..ceb5655dc562f 100644 --- a/nixos/modules/services/admin/pgadmin.nix +++ b/nixos/modules/services/admin/pgadmin.nix @@ -44,12 +44,19 @@ in initialPasswordFile = mkOption { description = lib.mdDoc '' - Initial password file for the pgAdmin account. + Initial password file for the pgAdmin account. Minimum length by default is 6. + Please see `services.pgadmin.minimumPasswordLength`. NOTE: Should be string not a store path, to prevent the password from being world readable ''; type = types.path; }; + minimumPasswordLength = mkOption { + description = lib.mdDoc "Minimum length of the password"; + type = types.int; + default = 6; + }; + emailServer = { enable = mkOption { description = lib.mdDoc '' @@ -116,7 +123,9 @@ in services.pgadmin.settings = { DEFAULT_SERVER_PORT = cfg.port; + PASSWORD_LENGTH_MIN = cfg.minimumPasswordLength; SERVER_MODE = true; + UPGRADE_CHECK_ENABLED = false; } // (optionalAttrs cfg.openFirewall { DEFAULT_SERVER = mkDefault "::"; }) // (optionalAttrs cfg.emailServer.enable { @@ -140,6 +149,14 @@ in preStart = '' # NOTE: this is idempotent (aka running it twice has no effect) + # Check here for password length to prevent pgadmin from starting + # and presenting a hard to find error message + # see https://github.com/NixOS/nixpkgs/issues/270624 + PW_LENGTH=$(wc -m < ${escapeShellArg cfg.initialPasswordFile}) + if [ $PW_LENGTH -lt ${toString cfg.minimumPasswordLength} ]; then + echo "Password must be at least ${toString cfg.minimumPasswordLength} characters long" + exit 1 + fi ( # Email address: echo ${escapeShellArg cfg.initialEmail} diff --git a/nixos/modules/services/databases/influxdb.nix b/nixos/modules/services/databases/influxdb.nix index 34b4139e7c580..adb212ab08d0d 100644 --- a/nixos/modules/services/databases/influxdb.nix +++ b/nixos/modules/services/databases/influxdb.nix @@ -161,6 +161,7 @@ in ExecStart = ''${cfg.package}/bin/influxd -config "${configFile}"''; User = cfg.user; Group = cfg.group; + Restart = "on-failure"; }; postStart = let diff --git a/nixos/modules/services/development/nixseparatedebuginfod.nix b/nixos/modules/services/development/nixseparatedebuginfod.nix new file mode 100644 index 0000000000000..daf85153d339f --- /dev/null +++ b/nixos/modules/services/development/nixseparatedebuginfod.nix @@ -0,0 +1,105 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.services.nixseparatedebuginfod; + url = "127.0.0.1:${toString cfg.port}"; +in +{ + options = { + services.nixseparatedebuginfod = { + enable = lib.mkEnableOption "separatedebuginfod, a debuginfod server providing source and debuginfo for nix packages"; + port = lib.mkOption { + description = "port to listen"; + default = 1949; + type = lib.types.port; + }; + nixPackage = lib.mkOption { + type = lib.types.package; + default = pkgs.nix; + defaultText = lib.literalExpression "pkgs.nix"; + description = '' + The version of nix that nixseparatedebuginfod should use as client for the nix daemon. It is strongly advised to use nix version >= 2.18, otherwise some debug info may go missing. + ''; + }; + allowOldNix = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Do not fail evaluation when {option}`services.nixseparatedebuginfod.nixPackage` is older than nix 2.18. + ''; + }; + }; + }; + config = lib.mkIf cfg.enable { + assertions = [ { + assertion = cfg.allowOldNix || (lib.versionAtLeast cfg.nixPackage.version "2.18"); + message = "nixseparatedebuginfod works better when `services.nixseparatedebuginfod.nixPackage` is set to nix >= 2.18 (instead of ${cfg.nixPackage.name}). Set `services.nixseparatedebuginfod.allowOldNix` to bypass."; + } ]; + + systemd.services.nixseparatedebuginfod = { + wantedBy = [ "multi-user.target" ]; + wants = [ "nix-daemon.service" ]; + after = [ "nix-daemon.service" ]; + path = [ cfg.nixPackage ]; + serviceConfig = { + ExecStart = [ "${pkgs.nixseparatedebuginfod}/bin/nixseparatedebuginfod -l ${url}" ]; + Restart = "on-failure"; + CacheDirectory = "nixseparatedebuginfod"; + # nix does not like DynamicUsers in allowed-users + User = "nixseparatedebuginfod"; + Group = "nixseparatedebuginfod"; + + # hardening + # Filesystem stuff + ProtectSystem = "strict"; # Prevent writing to most of / + ProtectHome = true; # Prevent accessing /home and /root + PrivateTmp = true; # Give an own directory under /tmp + PrivateDevices = true; # Deny access to most of /dev + ProtectKernelTunables = true; # Protect some parts of /sys + ProtectControlGroups = true; # Remount cgroups read-only + RestrictSUIDSGID = true; # Prevent creating SETUID/SETGID files + PrivateMounts = true; # Give an own mount namespace + RemoveIPC = true; + UMask = "0077"; + + # Capabilities + CapabilityBoundingSet = ""; # Allow no capabilities at all + NoNewPrivileges = true; # Disallow getting more capabilities. This is also implied by other options. + + # Kernel stuff + ProtectKernelModules = true; # Prevent loading of kernel modules + SystemCallArchitectures = "native"; # Usually no need to disable this + ProtectKernelLogs = true; # Prevent access to kernel logs + ProtectClock = true; # Prevent setting the RTC + + # Networking + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + + # Misc + LockPersonality = true; # Prevent change of the personality + ProtectHostname = true; # Give an own UTS namespace + RestrictRealtime = true; # Prevent switching to RT scheduling + MemoryDenyWriteExecute = true; # Maybe disable this for interpreters like python + RestrictNamespaces = true; + }; + }; + + users.users.nixseparatedebuginfod = { + isSystemUser = true; + group = "nixseparatedebuginfod"; + }; + + users.groups.nixseparatedebuginfod = { }; + + nix.settings.extra-allowed-users = [ "nixseparatedebuginfod" ]; + + environment.variables.DEBUGINFOD_URLS = "http://${url}"; + + environment.systemPackages = [ + # valgrind support requires debuginfod-find on PATH + (lib.getBin pkgs.elfutils) + ]; + + environment.etc."gdb/gdbinit.d/nixseparatedebuginfod.gdb".text = "set debuginfod enabled on"; + + }; +} diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix index a9e4998efe37a..58b94dae5734e 100644 --- a/nixos/modules/services/hardware/pcscd.nix +++ b/nixos/modules/services/hardware/pcscd.nix @@ -16,16 +16,13 @@ let in { - - ###### interface - options.services.pcscd = { enable = mkEnableOption (lib.mdDoc "PCSC-Lite daemon"); plugins = mkOption { type = types.listOf types.package; defaultText = literalExpression "[ pkgs.ccid ]"; - example = literalExpression "[ pkgs.pcsc-cyberjack ]"; + example = literalExpression "with pkgs; [ pcsc-cyberjack yubikey-personalization ]"; description = lib.mdDoc "Plugin packages to be used for PCSC-Lite."; }; @@ -46,10 +43,7 @@ in }; }; - ###### implementation - config = mkIf config.services.pcscd.enable { - environment.etc."reader.conf".source = cfgFile; environment.systemPackages = [ package ]; @@ -61,7 +55,6 @@ in systemd.services.pcscd = { environment.PCSCLITE_HP_DROPDIR = pluginEnv; - restartTriggers = [ "/etc/reader.conf" ]; # If the cfgFile is empty and not specified (in which case the default # /etc/reader.conf is assumed), pcscd will happily start going through the diff --git a/nixos/modules/services/hardware/vdr.nix b/nixos/modules/services/hardware/vdr.nix index 5feb379b50d1a..689d83f7eedcd 100644 --- a/nixos/modules/services/hardware/vdr.nix +++ b/nixos/modules/services/hardware/vdr.nix @@ -1,18 +1,15 @@ { config, lib, pkgs, ... }: - -with lib; - let cfg = config.services.vdr; - libDir = "/var/lib/vdr"; -in { - - ###### interface + inherit (lib) + mkEnableOption mkPackageOption mkOption types mkIf optional mdDoc; +in +{ options = { services.vdr = { - enable = mkEnableOption (lib.mdDoc "VDR. Please put config into ${libDir}"); + enable = mkEnableOption (mdDoc "Start VDR"); package = mkPackageOption pkgs "vdr" { example = "wrapVdr.override { plugins = with pkgs.vdrPlugins; [ hello ]; }"; @@ -21,59 +18,84 @@ in { videoDir = mkOption { type = types.path; default = "/srv/vdr/video"; - description = lib.mdDoc "Recording directory"; + description = mdDoc "Recording directory"; }; extraArguments = mkOption { type = types.listOf types.str; - default = []; - description = lib.mdDoc "Additional command line arguments to pass to VDR."; + default = [ ]; + description = mdDoc "Additional command line arguments to pass to VDR."; + }; + + enableLirc = mkEnableOption (mdDoc "LIRC"); + + user = mkOption { + type = types.str; + default = "vdr"; + description = mdDoc '' + User under which the VDR service runs. + ''; }; - enableLirc = mkEnableOption (lib.mdDoc "LIRC"); + group = mkOption { + type = types.str; + default = "vdr"; + description = mdDoc '' + Group under which the VDRvdr service runs. + ''; + }; }; + }; - ###### implementation + config = mkIf cfg.enable { - config = mkIf cfg.enable (mkMerge [{ systemd.tmpfiles.rules = [ - "d ${cfg.videoDir} 0755 vdr vdr -" - "Z ${cfg.videoDir} - vdr vdr -" + "d ${cfg.videoDir} 0755 ${cfg.user} ${cfg.group} -" + "Z ${cfg.videoDir} - ${cfg.user} ${cfg.group} -" ]; systemd.services.vdr = { description = "VDR"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + wants = optional cfg.enableLirc "lircd.service"; + after = [ "network.target" ] + ++ optional cfg.enableLirc "lircd.service"; serviceConfig = { - ExecStart = '' - ${cfg.package}/bin/vdr \ - --video="${cfg.videoDir}" \ - --config="${libDir}" \ - ${escapeShellArgs cfg.extraArguments} - ''; - User = "vdr"; + ExecStart = + let + args = [ + "--video=${cfg.videoDir}" + ] + ++ optional cfg.enableLirc "--lirc=${config.passthru.lirc.socket}" + ++ cfg.extraArguments; + in + "${cfg.package}/bin/vdr ${lib.escapeShellArgs args}"; + User = cfg.user; + Group = cfg.group; CacheDirectory = "vdr"; StateDirectory = "vdr"; + RuntimeDirectory = "vdr"; Restart = "on-failure"; }; }; - users.users.vdr = { - group = "vdr"; - home = libDir; - isSystemUser = true; + environment.systemPackages = [ cfg.package ]; + + users.users = mkIf (cfg.user == "vdr") { + vdr = { + inherit (cfg) group; + home = "/run/vdr"; + isSystemUser = true; + extraGroups = [ + "video" + "audio" + ] + ++ optional cfg.enableLirc "lirc"; + }; }; - users.groups.vdr = {}; - } + users.groups = mkIf (cfg.group == "vdr") { vdr = { }; }; - (mkIf cfg.enableLirc { - services.lirc.enable = true; - users.users.vdr.extraGroups = [ "lirc" ]; - services.vdr.extraArguments = [ - "--lirc=${config.passthru.lirc.socket}" - ]; - })]); + }; } diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index abbb2f32e6ccc..25c7017a1d258 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -1,8 +1,11 @@ { options, config, lib, pkgs, ... }: -with lib; - let + inherit (lib) any attrValues concatMapStringsSep concatStrings + concatStringsSep flatten imap1 isList literalExpression mapAttrsToList + mkEnableOption mkIf mkOption mkRemovedOptionModule optional optionalAttrs + optionalString singleton types; + cfg = config.services.dovecot2; dovecotPkg = pkgs.dovecot; @@ -113,6 +116,36 @@ let '' ) + '' + plugin { + sieve_plugins = ${concatStringsSep " " cfg.sieve.plugins} + sieve_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions)} + sieve_global_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions)} + '' + (optionalString (cfg.imapsieve.mailbox != []) '' + ${ + concatStringsSep "\n" (flatten (imap1 ( + idx: el: + singleton "imapsieve_mailbox${toString idx}_name = ${el.name}" + ++ optional (el.from != null) "imapsieve_mailbox${toString idx}_from = ${el.from}" + ++ optional (el.causes != null) "imapsieve_mailbox${toString idx}_causes = ${el.causes}" + ++ optional (el.before != null) "imapsieve_mailbox${toString idx}_before = file:${stateDir}/imapsieve/before/${baseNameOf el.before}" + ++ optional (el.after != null) "imapsieve_mailbox${toString idx}_after = file:${stateDir}/imapsieve/after/${baseNameOf el.after}" + ) + cfg.imapsieve.mailbox)) + } + '') + (optionalString (cfg.sieve.pipeBins != []) '' + sieve_pipe_bin_dir = ${pkgs.linkFarm "sieve-pipe-bins" (map (el: { + name = builtins.unsafeDiscardStringContext (baseNameOf el); + path = el; + }) + cfg.sieve.pipeBins)} + '') + '' + } + '' + cfg.extraConfig ]; @@ -343,6 +376,104 @@ in description = lib.mdDoc "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %."; }; + imapsieve.mailbox = mkOption { + default = []; + description = "Configure Sieve filtering rules on IMAP actions"; + type = types.listOf (types.submodule ({ config, ... }: { + options = { + name = mkOption { + description = '' + This setting configures the name of a mailbox for which administrator scripts are configured. + + The settings defined hereafter with matching sequence numbers apply to the mailbox named by this setting. + + This setting supports wildcards with a syntax compatible with the IMAP LIST command, meaning that this setting can apply to multiple or even all ("*") mailboxes. + ''; + example = "Junk"; + type = types.str; + }; + + from = mkOption { + default = null; + description = '' + Only execute the administrator Sieve scripts for the mailbox configured with services.dovecot2.imapsieve.mailbox.<name>.name when the message originates from the indicated mailbox. + + This setting supports wildcards with a syntax compatible with the IMAP LIST command, meaning that this setting can apply to multiple or even all ("*") mailboxes. + ''; + example = "*"; + type = types.nullOr types.str; + }; + + causes = mkOption { + default = null; + description = '' + Only execute the administrator Sieve scripts for the mailbox configured with services.dovecot2.imapsieve.mailbox.<name>.name when one of the listed IMAPSIEVE causes apply. + + This has no effect on the user script, which is always executed no matter the cause. + ''; + example = "COPY"; + type = types.nullOr (types.enum [ "APPEND" "COPY" "FLAG" ]); + }; + + before = mkOption { + default = null; + description = '' + When an IMAP event of interest occurs, this sieve script is executed before any user script respectively. + + This setting each specify the location of a single sieve script. The semantics of this setting is similar to sieve_before: the specified scripts form a sequence together with the user script in which the next script is only executed when an (implicit) keep action is executed. + ''; + example = literalExpression "./report-spam.sieve"; + type = types.nullOr types.path; + }; + + after = mkOption { + default = null; + description = '' + When an IMAP event of interest occurs, this sieve script is executed after any user script respectively. + + This setting each specify the location of a single sieve script. The semantics of this setting is similar to sieve_after: the specified scripts form a sequence together with the user script in which the next script is only executed when an (implicit) keep action is executed. + ''; + example = literalExpression "./report-spam.sieve"; + type = types.nullOr types.path; + }; + }; + })); + }; + + sieve = { + plugins = mkOption { + default = []; + example = [ "sieve_extprograms" ]; + description = "Sieve plugins to load"; + type = types.listOf types.str; + }; + + extensions = mkOption { + default = []; + description = "Sieve extensions for use in user scripts"; + example = [ "notify" "imapflags" "vnd.dovecot.filter" ]; + type = types.listOf types.str; + }; + + globalExtensions = mkOption { + default = []; + example = [ "vnd.dovecot.environment" ]; + description = "Sieve extensions for use in global scripts"; + type = types.listOf types.str; + }; + + pipeBins = mkOption { + default = []; + example = literalExpression '' + map lib.getExe [ + (pkgs.writeShellScriptBin "learn-ham.sh" "exec ''${pkgs.rspamd}/bin/rspamc learn_ham") + (pkgs.writeShellScriptBin "learn-spam.sh" "exec ''${pkgs.rspamd}/bin/rspamc learn_spam") + ] + ''; + description = "Programs available for use by the vnd.dovecot.pipe extension"; + type = types.listOf types.path; + }; + }; }; @@ -353,14 +484,23 @@ in enable = true; params.dovecot2 = {}; }; - services.dovecot2.protocols = - optional cfg.enableImap "imap" - ++ optional cfg.enablePop3 "pop3" - ++ optional cfg.enableLmtp "lmtp"; - - services.dovecot2.mailPlugins = mkIf cfg.enableQuota { - globally.enable = [ "quota" ]; - perProtocol.imap.enable = [ "imap_quota" ]; + + services.dovecot2 = { + protocols = + optional cfg.enableImap "imap" + ++ optional cfg.enablePop3 "pop3" + ++ optional cfg.enableLmtp "lmtp"; + + mailPlugins = mkIf cfg.enableQuota { + globally.enable = [ "quota" ]; + perProtocol.imap.enable = [ "imap_quota" ]; + }; + + sieve.plugins = + optional (cfg.imapsieve.mailbox != []) "sieve_imapsieve" + ++ optional (cfg.sieve.pipeBins != []) "sieve_extprograms"; + + sieve.globalExtensions = optional (cfg.sieve.pipeBins != []) "vnd.dovecot.pipe"; }; users.users = { @@ -415,7 +555,7 @@ in # (should be 0) so that the compiled sieve script is newer than # the source file and Dovecot won't try to compile it. preStart = '' - rm -rf ${stateDir}/sieve + rm -rf ${stateDir}/sieve ${stateDir}/imapsieve '' + optionalString (cfg.sieveScripts != {}) '' mkdir -p ${stateDir}/sieve ${concatStringsSep "\n" ( @@ -432,6 +572,29 @@ in ) cfg.sieveScripts )} chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve' + '' + + optionalString (cfg.imapsieve.mailbox != []) '' + mkdir -p ${stateDir}/imapsieve/{before,after} + + ${ + concatMapStringsSep "\n" + (el: + optionalString (el.before != null) '' + cp -p ${el.before} ${stateDir}/imapsieve/before/${baseNameOf el.before} + ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/before/${baseNameOf el.before}' + '' + + optionalString (el.after != null) '' + cp -p ${el.after} ${stateDir}/imapsieve/after/${baseNameOf el.after} + ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/after/${baseNameOf el.after}' + '' + ) + cfg.imapsieve.mailbox + } + + ${ + optionalString (cfg.mailUser != null && cfg.mailGroup != null) + "chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/imapsieve'" + } ''; }; @@ -459,4 +622,5 @@ in }; + meta.maintainers = [ lib.maintainers.dblsaiko ]; } diff --git a/nixos/modules/services/mail/nullmailer.nix b/nixos/modules/services/mail/nullmailer.nix index f6befe246b12a..4fd0026dbe4eb 100644 --- a/nixos/modules/services/mail/nullmailer.nix +++ b/nixos/modules/services/mail/nullmailer.nix @@ -120,7 +120,7 @@ with lib; }; maxpause = mkOption { - type = types.nullOr types.str; + type = with types; nullOr (oneOf [ str int ]); default = null; description = lib.mdDoc '' The maximum time to pause between successive queue runs, in seconds. @@ -138,7 +138,7 @@ with lib; }; pausetime = mkOption { - type = types.nullOr types.str; + type = with types; nullOr (oneOf [ str int ]); default = null; description = lib.mdDoc '' The minimum time to pause between successive queue runs when there @@ -168,7 +168,7 @@ with lib; }; sendtimeout = mkOption { - type = types.nullOr types.str; + type = with types; nullOr (oneOf [ str int ]); default = null; description = lib.mdDoc '' The time to wait for a remote module listed above to complete sending @@ -194,7 +194,7 @@ with lib; environment = { systemPackages = [ pkgs.nullmailer ]; etc = let - validAttrs = filterAttrs (name: value: value != null) cfg.config; + validAttrs = lib.mapAttrs (_: toString) (filterAttrs (_: value: value != null) cfg.config); in (foldl' (as: name: as // { "nullmailer/${name}".text = validAttrs.${name}; }) {} (attrNames validAttrs)) // optionalAttrs (cfg.remotesFile != null) { "nullmailer/remotes".source = cfg.remotesFile; }; diff --git a/nixos/modules/services/misc/llama-cpp.nix b/nixos/modules/services/misc/llama-cpp.nix new file mode 100644 index 0000000000000..4d76456fb2fd5 --- /dev/null +++ b/nixos/modules/services/misc/llama-cpp.nix @@ -0,0 +1,111 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.services.llama-cpp; +in { + + options = { + + services.llama-cpp = { + enable = lib.mkEnableOption "LLaMA C++ server"; + + package = lib.mkPackageOption pkgs "llama-cpp" { }; + + model = lib.mkOption { + type = lib.types.path; + example = "/models/mistral-instruct-7b/ggml-model-q4_0.gguf"; + description = "Model path."; + }; + + extraFlags = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "Extra flags passed to llama-cpp-server."; + example = ["-c" "4096" "-ngl" "32" "--numa"]; + default = []; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + example = "0.0.0.0"; + description = "IP address the LLaMA C++ server listens on."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "Listen port for LLaMA C++ server."; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Open ports in the firewall for LLaMA C++ server."; + }; + }; + + }; + + config = lib.mkIf cfg.enable { + + systemd.services.llama-cpp = { + description = "LLaMA C++ server"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + + serviceConfig = { + Type = "idle"; + KillSignal = "SIGINT"; + ExecStart = "${cfg.package}/bin/llama-cpp-server --log-disable --host ${cfg.host} --port ${builtins.toString cfg.port} -m ${cfg.model} ${utils.escapeSystemdExecArgs cfg.extraFlags}"; + Restart = "on-failure"; + RestartSec = 300; + + # for GPU acceleration + PrivateDevices = false; + + # hardening + DynamicUser = true; + CapabilityBoundingSet = ""; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + NoNewPrivileges = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + MemoryDenyWriteExecute = true; + LockPersonality = true; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + SystemCallErrorNumber = "EPERM"; + ProtectProc = "invisible"; + ProtectHostname = true; + ProcSubset = "pid"; + }; + }; + + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + + }; + + meta.maintainers = with lib.maintainers; [ newam ]; +} diff --git a/nixos/modules/services/misc/ntfy-sh.nix b/nixos/modules/services/misc/ntfy-sh.nix index 98134e94eeede..b8b0772401156 100644 --- a/nixos/modules/services/misc/ntfy-sh.nix +++ b/nixos/modules/services/misc/ntfy-sh.nix @@ -79,12 +79,6 @@ in cache-file = mkDefault "/var/lib/ntfy-sh/cache-file.db"; }; - systemd.tmpfiles.rules = [ - "f ${cfg.settings.auth-file} 0600 ${cfg.user} ${cfg.group} - -" - "d ${cfg.settings.attachment-cache-dir} 0700 ${cfg.user} ${cfg.group} - -" - "f ${cfg.settings.cache-file} 0600 ${cfg.user} ${cfg.group} - -" - ]; - systemd.services.ntfy-sh = { description = "Push notifications server"; diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix index ec6aa56150397..5cf3c096397cb 100644 --- a/nixos/modules/services/monitoring/netdata.nix +++ b/nixos/modules/services/monitoring/netdata.nix @@ -206,7 +206,15 @@ in { description = "Real time performance monitoring"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = (with pkgs; [ curl gawk iproute2 which procps bash ]) + path = (with pkgs; [ + curl + gawk + iproute2 + which + procps + bash + util-linux # provides logger command; required for syslog health alarms + ]) ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages) ++ lib.optional config.virtualisation.libvirtd.enable (config.virtualisation.libvirtd.package); environment = { diff --git a/nixos/modules/services/monitoring/snmpd.nix b/nixos/modules/services/monitoring/snmpd.nix new file mode 100644 index 0000000000000..f2d3953e6a620 --- /dev/null +++ b/nixos/modules/services/monitoring/snmpd.nix @@ -0,0 +1,83 @@ +{ pkgs, config, lib, ... }: + +let + cfg = config.services.snmpd; + configFile = if cfg.configText != "" then + pkgs.writeText "snmpd.cfg" '' + ${cfg.configText} + '' else null; +in { + options.services.snmpd = { + enable = lib.mkEnableOption "snmpd"; + + package = lib.mkPackageOption pkgs "net-snmp" {}; + + listenAddress = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + The address to listen on for SNMP and AgentX messages. + ''; + example = "127.0.0.1"; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 161; + description = lib.mdDoc '' + The port to listen on for SNMP and AgentX messages. + ''; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc '' + Open port in firewall for snmpd. + ''; + }; + + configText = lib.mkOption { + type = lib.types.lines; + default = ""; + description = lib.mdDoc '' + The contents of the snmpd.conf. If the {option}`configFile` option + is set, this value will be ignored. + + Note that the contents of this option will be added to the Nix + store as world-readable plain text, {option}`configFile` can be used in + addition to a secret management tool to protect sensitive data. + ''; + }; + + configFile = lib.mkOption { + type = lib.types.path; + default = configFile; + defaultText = lib.literalMD "The value of {option}`configText`."; + description = lib.mdDoc '' + Path to the snmpd.conf file. By default, if {option}`configText` is set, + a config file will be automatically generated. + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + systemd.services."snmpd" = { + description = "Simple Network Management Protocol (SNMP) daemon."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${lib.getExe' cfg.package "snmpd"} -f -Lo -c ${cfg.configFile} ${cfg.listenAddress}:${toString cfg.port}"; + }; + }; + + networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [ + cfg.port + ]; + }; + + meta.maintainers = [ lib.maintainers.eliandoran ]; + +} diff --git a/nixos/modules/services/network-filesystems/kubo.nix b/nixos/modules/services/network-filesystems/kubo.nix index 8226fc614bc47..126e0902d5b4a 100644 --- a/nixos/modules/services/network-filesystems/kubo.nix +++ b/nixos/modules/services/network-filesystems/kubo.nix @@ -282,8 +282,9 @@ in environment.systemPackages = [ cfg.package ]; environment.variables.IPFS_PATH = fakeKuboRepo; - # https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size + # https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000; + boot.kernel.sysctl."net.core.wmem_max" = mkDefault 2500000; programs.fuse = mkIf cfg.autoMount { userAllowOther = true; diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix index 89b30996e8faa..782681018116c 100644 --- a/nixos/modules/services/networking/avahi-daemon.nix +++ b/nixos/modules/services/networking/avahi-daemon.nix @@ -95,7 +95,6 @@ in ipv6 = mkOption { type = types.bool; default = false; - defaultText = literalExpression "config.networking.enableIPv6"; description = lib.mdDoc "Whether to use IPv6."; }; @@ -274,17 +273,17 @@ in system.nssModules = optional (cfg.nssmdns4 || cfg.nssmdns6) pkgs.nssmdns; system.nssDatabases.hosts = let - mdnsMinimal = if (cfg.nssmdns4 && cfg.nssmdns6) then - "mdns_minimal" + mdns = if (cfg.nssmdns4 && cfg.nssmdns6) then + "mdns" else if (!cfg.nssmdns4 && cfg.nssmdns6) then - "mdns6_minimal" + "mdns6" else if (cfg.nssmdns4 && !cfg.nssmdns6) then - "mdns4_minimal" + "mdns4" else ""; in optionals (cfg.nssmdns4 || cfg.nssmdns6) (mkMerge [ - (mkBefore [ "${mdnsMinimal} [NOTFOUND=return]" ]) # before resolve - (mkAfter [ "mdns" ]) # after dns + (mkBefore [ "${mdns}_minimal [NOTFOUND=return]" ]) # before resolve + (mkAfter [ "${mdns}" ]) # after dns ]); environment.systemPackages = [ cfg.package ]; diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index a67f0c5de9ba3..18f205b8d99ef 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -217,7 +217,7 @@ with lib; inherit RuntimeDirectory; inherit StateDirectory; Type = "oneshot"; - ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}"; + ExecStartPre = [ "!${pkgs.writeShellScript "ddclient-prestart" preStart}" ]; ExecStart = "${lib.getExe cfg.package} -file /run/${RuntimeDirectory}/ddclient.conf"; }; }; diff --git a/nixos/modules/services/networking/dnsmasq.md b/nixos/modules/services/networking/dnsmasq.md new file mode 100644 index 0000000000000..6fc9178b1c0d5 --- /dev/null +++ b/nixos/modules/services/networking/dnsmasq.md @@ -0,0 +1,68 @@ +# Dnsmasq {#module-services-networking-dnsmasq} + +Dnsmasq is an integrated DNS, DHCP and TFTP server for small networks. + +## Configuration {#module-services-networking-dnsmasq-configuration} + +### An authoritative DHCP and DNS server on a home network {#module-services-networking-dnsmasq-configuration-home} + +On a home network, you can use Dnsmasq as a DHCP and DNS server. New devices on +your network will be configured by Dnsmasq, and instructed to use it as the DNS +server by default. This allows you to rely on your own server to perform DNS +queries and caching, with DNSSEC enabled. + +The following example assumes that + +- you have disabled your router's integrated DHCP server, if it has one +- your router's address is set in [](#opt-networking.defaultGateway.address) +- your system's Ethernet interface is `eth0` +- you have configured the address(es) to forward DNS queries in [](#opt-networking.nameservers) + +```nix +{ + services.dnsmasq = { + enable = true; + settings = { + interface = "eth0"; + bind-interfaces = true; # Only bind to the specified interface + dhcp-authoritative = true; # Should be set when dnsmasq is definitely the only DHCP server on a network + + server = config.networking.nameservers; # Upstream dns servers to which requests should be forwarded + + dhcp-host = [ + # Give the current system a fixed address of 192.168.0.254 + "dc:a6:32:0b:ea:b9,192.168.0.254,${config.networking.hostName},infinite" + ]; + + dhcp-option = [ + # Address of the gateway, i.e. your router + "option:router,${config.networking.defaultGateway.address}" + ]; + + dhcp-range = [ + # Range of IPv4 addresses to give out + # <range start>,<range end>,<lease time> + "192.168.0.10,192.168.0.253,24h" + # Enable stateless IPv6 allocation + "::f,::ff,constructor:eth0,ra-stateless" + ]; + + dhcp-rapid-commit = true; # Faster DHCP negotiation for IPv6 + local-service = true; # Accept DNS queries only from hosts whose address is on a local subnet + log-queries = true; # Log results of all DNS queries + bogus-priv = true; # Don't forward requests for the local address ranges (192.168.x.x etc) to upstream nameservers + domain-needed = true; # Don't forward requests without dots or domain parts to upstream nameservers + + dnssec = true; # Enable DNSSEC + # DNSSEC trust anchor. Source: https://data.iana.org/root-anchors/root-anchors.xml + trust-anchor = ".,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"; + }; + }; +} +``` + +## References {#module-services-networking-dnsmasq-references} + +- Upstream website: <https://dnsmasq.org> +- Manpage: <https://dnsmasq.org/docs/dnsmasq-man.html> +- FAQ: <https://dnsmasq.org/docs/FAQ> diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix index 8d1ca36c38ed2..d01a1b6707a53 100644 --- a/nixos/modules/services/networking/dnsmasq.nix +++ b/nixos/modules/services/networking/dnsmasq.nix @@ -181,4 +181,6 @@ in restartTriggers = [ config.environment.etc.hosts.source ]; }; }; + + meta.doc = ./dnsmasq.md; } diff --git a/nixos/modules/services/networking/miniupnpd.nix b/nixos/modules/services/networking/miniupnpd.nix index 64aacaf350404..116298dc6b1db 100644 --- a/nixos/modules/services/networking/miniupnpd.nix +++ b/nixos/modules/services/networking/miniupnpd.nix @@ -13,8 +13,17 @@ let listening_ip=${range} '') cfg.internalIPs} + ${lib.optionalString (firewall == "nftables") '' + upnp_table_name=miniupnpd + upnp_nat_table_name=miniupnpd + ''} + ${cfg.appendConfig} ''; + firewall = if config.networking.nftables.enable then "nftables" else "iptables"; + miniupnpd = pkgs.miniupnpd.override { inherit firewall; }; + firewallScripts = lib.optionals (firewall == "iptables") + ([ "iptables"] ++ lib.optional (config.networking.enableIPv6) "ip6tables"); in { options = { @@ -57,20 +66,50 @@ in }; config = mkIf cfg.enable { - networking.firewall.extraCommands = '' - ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface} - ''; + networking.firewall.extraCommands = lib.mkIf (firewallScripts != []) (builtins.concatStringsSep "\n" (map (fw: '' + EXTIF=${cfg.externalInterface} ${pkgs.bash}/bin/bash -x ${miniupnpd}/etc/miniupnpd/${fw}_init.sh + '') firewallScripts)); + + networking.firewall.extraStopCommands = lib.mkIf (firewallScripts != []) (builtins.concatStringsSep "\n" (map (fw: '' + EXTIF=${cfg.externalInterface} ${pkgs.bash}/bin/bash -x ${miniupnpd}/etc/miniupnpd/${fw}_removeall.sh + '') firewallScripts)); - networking.firewall.extraStopCommands = '' - ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface} - ''; + networking.nftables = lib.mkIf (firewall == "nftables") { + # see nft_init in ${miniupnpd-nftables}/etc/miniupnpd + tables.miniupnpd = { + family = "inet"; + # The following is omitted because it's expected that the firewall is to be responsible for it. + # + # chain forward { + # type filter hook forward priority filter; policy drop; + # jump miniupnpd + # } + # + # Otherwise, it quickly gets ugly with (potentially) two forward chains with "policy drop". + # This means the chain "miniupnpd" never actually gets triggered and is simply there to satisfy + # miniupnpd. If you're doing it yourself (without networking.firewall), the easiest way to get + # it to work is adding a rule "ct status dnat accept" - this is what networking.firewall does. + # If you don't want to simply accept forwarding for all "ct status dnat" packets, override + # upnp_table_name with whatever your table is, create a chain "miniupnpd" in your table and + # jump into it from your forward chain. + content = '' + chain miniupnpd {} + chain prerouting_miniupnpd { + type nat hook prerouting priority dstnat; policy accept; + } + chain postrouting_miniupnpd { + type nat hook postrouting priority srcnat; policy accept; + } + ''; + }; + }; systemd.services.miniupnpd = { description = "MiniUPnP daemon"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${pkgs.miniupnpd}/bin/miniupnpd -f ${configFile}"; + ExecStart = "${miniupnpd}/bin/miniupnpd -f ${configFile}"; PIDFile = "/run/miniupnpd.pid"; Type = "forking"; }; diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index d32712c8243d7..c96439cf2641a 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -565,7 +565,10 @@ in wantedBy = [ "network-online.target" ]; }; - systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; + systemd.services.ModemManager = { + aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; + path = lib.optionals (cfg.fccUnlockScripts != []) [ pkgs.libqmi pkgs.libmbim ]; + }; systemd.services.NetworkManager-dispatcher = { wantedBy = [ "network.target" ]; diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix index 3a2744303474f..1f044384a5b83 100644 --- a/nixos/modules/services/printing/cupsd.nix +++ b/nixos/modules/services/printing/cupsd.nix @@ -4,9 +4,10 @@ with lib; let - inherit (pkgs) cups cups-pk-helper cups-filters xdg-utils; + inherit (pkgs) cups-pk-helper cups-filters xdg-utils; cfg = config.services.printing; + cups = cfg.package; avahiEnabled = config.services.avahi.enable; polkitEnabled = config.security.polkit.enable; @@ -140,6 +141,8 @@ in ''; }; + package = lib.mkPackageOption pkgs "cups" {}; + stateless = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/security/bitwarden-directory-connector-cli.nix b/nixos/modules/services/security/bitwarden-directory-connector-cli.nix new file mode 100644 index 0000000000000..18c02e22fd7e6 --- /dev/null +++ b/nixos/modules/services/security/bitwarden-directory-connector-cli.nix @@ -0,0 +1,323 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.services.bitwarden-directory-connector-cli; +in { + options.services.bitwarden-directory-connector-cli = { + enable = mkEnableOption "Bitwarden Directory Connector"; + + package = mkPackageOption pkgs "bitwarden-directory-connector-cli" {}; + + domain = mkOption { + type = types.str; + description = lib.mdDoc "The domain the Bitwarden/Vaultwarden is accessible on."; + example = "https://vaultwarden.example.com"; + }; + + user = mkOption { + type = types.str; + description = lib.mdDoc "User to run the program."; + default = "bwdc"; + }; + + interval = mkOption { + type = types.str; + default = "*:0,15,30,45"; + description = lib.mdDoc "The interval when to run the connector. This uses systemd's OnCalendar syntax."; + }; + + ldap = mkOption { + description = lib.mdDoc '' + Options to configure the LDAP connection. + If you used the desktop application to test the configuration you can find the settings by searching for `ldap` in `~/.config/Bitwarden\ Directory\ Connector/data.json`. + ''; + default = {}; + type = types.submodule ({ + config, + options, + ... + }: { + freeformType = types.attrsOf (pkgs.formats.json {}).type; + + config.finalJSON = builtins.toJSON (removeAttrs config (filter (x: x == "finalJSON" || ! options.${x}.isDefined or false) (attrNames options))); + + options = { + finalJSON = mkOption { + type = (pkgs.formats.json {}).type; + internal = true; + readOnly = true; + visible = false; + }; + + ssl = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to use TLS."; + }; + startTls = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to use STARTTLS."; + }; + + hostname = mkOption { + type = types.str; + description = lib.mdDoc "The host the LDAP is accessible on."; + example = "ldap.example.com"; + }; + + port = mkOption { + type = types.port; + default = 389; + description = lib.mdDoc "Port LDAP is accessible on."; + }; + + ad = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether the LDAP Server is an Active Directory."; + }; + + pagedSearch = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether the LDAP server paginates search results."; + }; + + rootPath = mkOption { + type = types.str; + description = lib.mdDoc "Root path for LDAP."; + example = "dc=example,dc=com"; + }; + + username = mkOption { + type = types.str; + description = lib.mdDoc "The user to authenticate as."; + example = "cn=admin,dc=example,dc=com"; + }; + }; + }); + }; + + sync = mkOption { + description = lib.mdDoc '' + Options to configure what gets synced. + If you used the desktop application to test the configuration you can find the settings by searching for `sync` in `~/.config/Bitwarden\ Directory\ Connector/data.json`. + ''; + default = {}; + type = types.submodule ({ + config, + options, + ... + }: { + freeformType = types.attrsOf (pkgs.formats.json {}).type; + + config.finalJSON = builtins.toJSON (removeAttrs config (filter (x: x == "finalJSON" || ! options.${x}.isDefined or false) (attrNames options))); + + options = { + finalJSON = mkOption { + type = (pkgs.formats.json {}).type; + internal = true; + readOnly = true; + visible = false; + }; + + removeDisabled = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Remove users from bitwarden groups if no longer in the ldap group."; + }; + + overwriteExisting = mkOption { + type = types.bool; + default = false; + description = + lib.mdDoc "Remove and re-add users/groups, See https://bitwarden.com/help/user-group-filters/#overwriting-syncs for more details."; + }; + + largeImport = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Enable if you are syncing more than 2000 users/groups."; + }; + + memberAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute that lists members in a LDAP group."; + example = "uniqueMember"; + }; + + creationDateAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute that lists a user's creation date."; + example = "whenCreated"; + }; + + useEmailPrefixSuffix = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "If a user has no email address, combine a username prefix with a suffix value to form an email."; + }; + emailPrefixAttribute = mkOption { + type = types.str; + description = lib.mdDoc "The attribute that contains the users username."; + example = "accountName"; + }; + emailSuffix = mkOption { + type = types.str; + description = lib.mdDoc "Suffix for the email, normally @example.com."; + example = "@example.com"; + }; + + users = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Sync users."; + }; + userPath = mkOption { + type = types.str; + description = lib.mdDoc "User directory, relative to root."; + default = "ou=users"; + }; + userObjectClass = mkOption { + type = types.str; + description = lib.mdDoc "Class that users must have."; + default = "inetOrgPerson"; + }; + userEmailAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute for a users email."; + default = "mail"; + }; + userFilter = mkOption { + type = types.str; + description = lib.mdDoc "LDAP filter for users."; + example = "(memberOf=cn=sales,ou=groups,dc=example,dc=com)"; + default = ""; + }; + + groups = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to sync ldap groups into BitWarden."; + }; + groupPath = mkOption { + type = types.str; + description = lib.mdDoc "Group directory, relative to root."; + default = "ou=groups"; + }; + groupObjectClass = mkOption { + type = types.str; + description = lib.mdDoc "A class that groups will have."; + default = "groupOfNames"; + }; + groupNameAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute for a name of group."; + default = "cn"; + }; + groupFilter = mkOption { + type = types.str; + description = lib.mdDoc "LDAP filter for groups."; + example = "(cn=sales)"; + default = ""; + }; + }; + }); + }; + + secrets = { + ldap = mkOption { + type = types.str; + description = "Path to file that contains LDAP password for user in {option}`ldap.username"; + }; + + bitwarden = { + client_path_id = mkOption { + type = types.str; + description = "Path to file that contains Client ID."; + }; + client_path_secret = mkOption { + type = types.str; + description = "Path to file that contains Client Secret."; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.groups."${cfg.user}" = {}; + users.users."${cfg.user}" = { + isSystemUser = true; + group = cfg.user; + }; + + systemd = { + timers.bitwarden-directory-connector-cli = { + description = "Sync timer for Bitwarden Directory Connector"; + wantedBy = ["timers.target"]; + after = ["network-online.target"]; + timerConfig = { + OnCalendar = cfg.interval; + Unit = "bitwarden-directory-connector-cli.service"; + Persistent = true; + }; + }; + + services.bitwarden-directory-connector-cli = { + description = "Main process for Bitwarden Directory Connector"; + path = [pkgs.jq]; + + environment = { + BITWARDENCLI_CONNECTOR_APPDATA_DIR = "/tmp"; + BITWARDENCLI_CONNECTOR_PLAINTEXT_SECRETS = "true"; + }; + + serviceConfig = { + Type = "oneshot"; + User = "${cfg.user}"; + PrivateTmp = true; + preStart = '' + set -eo pipefail + + # create the config file + ${lib.getExe cfg.package} data-file + touch /tmp/data.json.tmp + chmod 600 /tmp/data.json{,.tmp} + + ${lib.getExe cfg.package} config server ${cfg.domain} + + # now login to set credentials + export BW_CLIENTID="$(< ${escapeShellArg cfg.secrets.bitwarden.client_path_id})" + export BW_CLIENTSECRET="$(< ${escapeShellArg cfg.secrets.bitwarden.client_path_secret})" + ${lib.getExe cfg.package} login + + jq '.authenticatedAccounts[0] as $account + | .[$account].directoryConfigurations.ldap |= $ldap_data + | .[$account].directorySettings.organizationId |= $orgID + | .[$account].directorySettings.sync |= $sync_data' \ + --argjson ldap_data ${escapeShellArg cfg.ldap.finalJSON} \ + --arg orgID "''${BW_CLIENTID//organization.}" \ + --argjson sync_data ${escapeShellArg cfg.sync.finalJSON} \ + /tmp/data.json \ + > /tmp/data.json.tmp + + mv -f /tmp/data.json.tmp /tmp/data.json + + # final config + ${lib.getExe cfg.package} config directory 0 + ${lib.getExe cfg.package} config ldap.password --secretfile ${cfg.secrets.ldap} + ''; + + ExecStart = "${lib.getExe cfg.package} sync"; + }; + }; + }; + }; + + meta.maintainers = with maintainers; [Silver-Golden]; +} diff --git a/nixos/modules/services/security/vaultwarden/backup.sh b/nixos/modules/services/security/vaultwarden/backup.sh index 2a3de0ab1deeb..7668da5bc88f3 100644 --- a/nixos/modules/services/security/vaultwarden/backup.sh +++ b/nixos/modules/services/security/vaultwarden/backup.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # Based on: https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault -if ! mkdir -p "$BACKUP_FOLDER"; then - echo "Could not create backup folder '$BACKUP_FOLDER'" >&2 +if [ ! -d "$BACKUP_FOLDER" ]; then + echo "Backup folder '$BACKUP_FOLDER' does not exist" >&2 exit 1 fi diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix index 14bbfa95a9ca2..470db735bf649 100644 --- a/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixos/modules/services/security/vaultwarden/default.nix @@ -55,6 +55,7 @@ in { description = lib.mdDoc '' The directory under which vaultwarden will backup its persistent data. ''; + example = "/var/backup/vaultwarden"; }; config = mkOption { @@ -230,6 +231,13 @@ in { }; wantedBy = [ "multi-user.target" ]; }; + + systemd.tmpfiles.settings = mkIf (cfg.backupDir != null) { + "10-vaultwarden".${cfg.backupDir}.d = { + inherit user group; + mode = "0770"; + }; + }; }; # uses attributes of the linked package diff --git a/nixos/modules/services/system/kerberos/default.nix b/nixos/modules/services/system/kerberos/default.nix index 4ed48e463741a..486d4b49c195a 100644 --- a/nixos/modules/services/system/kerberos/default.nix +++ b/nixos/modules/services/system/kerberos/default.nix @@ -3,7 +3,7 @@ let inherit (lib) mkOption mkIf types length attrNames; cfg = config.services.kerberos_server; - kerberos = config.krb5.kerberos; + kerberos = config.security.krb5.package; aclEntry = { options = { diff --git a/nixos/modules/services/system/kerberos/heimdal.nix b/nixos/modules/services/system/kerberos/heimdal.nix index 837c59caa5620..4789e4790b4bc 100644 --- a/nixos/modules/services/system/kerberos/heimdal.nix +++ b/nixos/modules/services/system/kerberos/heimdal.nix @@ -4,7 +4,7 @@ let inherit (lib) mkIf concatStringsSep concatMapStrings toList mapAttrs mapAttrsToList; cfg = config.services.kerberos_server; - kerberos = config.krb5.kerberos; + kerberos = config.security.krb5.package; stateDir = "/var/heimdal"; aclFiles = mapAttrs (name: {acl, ...}: pkgs.writeText "${name}.acl" (concatMapStrings (( diff --git a/nixos/modules/services/system/kerberos/mit.nix b/nixos/modules/services/system/kerberos/mit.nix index 112000140453f..a654bd1fe7e1b 100644 --- a/nixos/modules/services/system/kerberos/mit.nix +++ b/nixos/modules/services/system/kerberos/mit.nix @@ -4,7 +4,7 @@ let inherit (lib) mkIf concatStrings concatStringsSep concatMapStrings toList mapAttrs mapAttrsToList; cfg = config.services.kerberos_server; - kerberos = config.krb5.kerberos; + kerberos = config.security.krb5.package; stateDir = "/var/lib/krb5kdc"; PIDFile = "/run/kdc.pid"; aclMap = { diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix index 497297ba693a3..5dd02eb331633 100644 --- a/nixos/modules/services/torrent/transmission.nix +++ b/nixos/modules/services/torrent/transmission.nix @@ -294,7 +294,7 @@ in requires = optional apparmor.enable "apparmor.service"; wantedBy = [ "multi-user.target" ]; environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source; - environment.TRANSMISSION_WEB_HOME = lib.optionalString (cfg.webHome != null) cfg.webHome; + environment.TRANSMISSION_WEB_HOME = lib.mkIf (cfg.webHome != null) cfg.webHome; serviceConfig = { # Use "+" because credentialsFile may not be accessible to User= or Group=. diff --git a/nixos/modules/services/web-apps/c2fmzq-server.nix b/nixos/modules/services/web-apps/c2fmzq-server.nix index 2749c2a5a87aa..87938fe160e14 100644 --- a/nixos/modules/services/web-apps/c2fmzq-server.nix +++ b/nixos/modules/services/web-apps/c2fmzq-server.nix @@ -6,8 +6,12 @@ let cfg = config.services.c2fmzq-server; argsFormat = { - type = with lib.types; nullOr (oneOf [ bool int str ]); - generate = lib.cli.toGNUCommandLineShell { }; + type = with lib.types; attrsOf (nullOr (oneOf [ bool int str ])); + generate = lib.cli.toGNUCommandLineShell { + mkBool = k: v: [ + "--${k}=${if v then "true" else "false"}" + ]; + }; }; in { options.services.c2fmzq-server = { diff --git a/nixos/modules/services/web-apps/dokuwiki.nix b/nixos/modules/services/web-apps/dokuwiki.nix index 1df1cbf9f0e18..256ab3229ea6c 100644 --- a/nixos/modules/services/web-apps/dokuwiki.nix +++ b/nixos/modules/services/web-apps/dokuwiki.nix @@ -122,62 +122,8 @@ let }; }; - # The current implementations of `doRename`, `mkRenamedOptionModule` do not provide the full options path when used with submodules. - # They would only show `settings.useacl' instead of `services.dokuwiki.sites."site1.local".settings.useacl' - # The partial re-implementation of these functions is done to help users in debugging by showing the full path. - mkRenamed = from: to: { config, options, name, ... }: let - pathPrefix = [ "services" "dokuwiki" "sites" name ]; - fromPath = pathPrefix ++ from; - fromOpt = getAttrFromPath from options; - toOp = getAttrsFromPath to config; - toPath = pathPrefix ++ to; - in { - options = setAttrByPath from (mkOption { - visible = false; - description = lib.mdDoc "Alias of {option}${showOption toPath}"; - apply = x: builtins.trace "Obsolete option `${showOption fromPath}' is used. It was renamed to ${showOption toPath}" toOp; - }); - config = mkMerge [ - { - warnings = optional fromOpt.isDefined - "The option `${showOption fromPath}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption toPath}'."; - } - (lib.modules.mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt) - ]; - }; - siteOpts = { options, config, lib, name, ... }: { - imports = [ - (mkRenamed [ "aclUse" ] [ "settings" "useacl" ]) - (mkRenamed [ "superUser" ] [ "settings" "superuser" ]) - (mkRenamed [ "disableActions" ] [ "settings" "disableactions" ]) - ({ config, options, ... }: let - showPath = suffix: lib.options.showOption ([ "services" "dokuwiki" "sites" name ] ++ suffix); - replaceExtraConfig = "Please use `${showPath ["settings"]}' to pass structured settings instead."; - ecOpt = options.extraConfig; - ecPath = showPath [ "extraConfig" ]; - in { - options.extraConfig = mkOption { - visible = false; - apply = x: throw "The option ${ecPath} can no longer be used since it's been removed.\n${replaceExtraConfig}"; - }; - config.assertions = [ - { - assertion = !ecOpt.isDefined; - message = "The option definition `${ecPath}' in ${showFiles ecOpt.files} no longer has any effect; please remove it.\n${replaceExtraConfig}"; - } - { - assertion = config.mergedConfig.useacl -> (config.acl != null || config.aclFile != null); - message = "Either ${showPath [ "acl" ]} or ${showPath [ "aclFile" ]} is mandatory if ${showPath [ "settings" "useacl" ]} is true"; - } - { - assertion = config.usersFile != null -> config.mergedConfig.useacl != false; - message = "${showPath [ "settings" "useacl" ]} is required when ${showPath [ "usersFile" ]} is set (Currently defined as `${config.usersFile}' in ${showFiles options.usersFile.files})."; - } - ]; - }) - ]; options = { enable = mkEnableOption (lib.mdDoc "DokuWiki web application"); @@ -392,21 +338,6 @@ let ''; }; - # Required for the mkRenamedOptionModule - # TODO: Remove me once https://github.com/NixOS/nixpkgs/issues/96006 is fixed - # or we don't have any more notes about the removal of extraConfig, ... - warnings = mkOption { - type = types.listOf types.unspecified; - default = [ ]; - visible = false; - internal = true; - }; - assertions = mkOption { - type = types.listOf types.unspecified; - default = [ ]; - visible = false; - internal = true; - }; }; }; in @@ -440,10 +371,6 @@ in # implementation config = mkIf (eachSite != {}) (mkMerge [{ - warnings = flatten (mapAttrsToList (_: cfg: cfg.warnings) eachSite); - - assertions = flatten (mapAttrsToList (_: cfg: cfg.assertions) eachSite); - services.phpfpm.pools = mapAttrs' (hostName: cfg: ( nameValuePair "dokuwiki-${hostName}" { inherit user; diff --git a/nixos/modules/services/web-apps/miniflux.nix b/nixos/modules/services/web-apps/miniflux.nix index 284809831cd44..1a5b7d0c24e9b 100644 --- a/nixos/modules/services/web-apps/miniflux.nix +++ b/nixos/modules/services/web-apps/miniflux.nix @@ -21,10 +21,10 @@ in package = mkPackageOption pkgs "miniflux" { }; config = mkOption { - type = types.attrsOf types.str; + type = with types; attrsOf (oneOf [ str int ]); example = literalExpression '' { - CLEANUP_FREQUENCY = "48"; + CLEANUP_FREQUENCY = 48; LISTEN_ADDR = "localhost:8080"; } ''; @@ -51,12 +51,11 @@ in }; config = mkIf cfg.enable { - services.miniflux.config = { LISTEN_ADDR = mkDefault defaultAddress; DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux"; - RUN_MIGRATIONS = "1"; - CREATE_ADMIN = "1"; + RUN_MIGRATIONS = 1; + CREATE_ADMIN = 1; }; services.postgresql = { @@ -117,7 +116,7 @@ in UMask = "0077"; }; - environment = cfg.config; + environment = lib.mapAttrs (_: toString) cfg.config; }; environment.systemPackages = [ cfg.package ]; diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 50d4d7ce4a1ac..38c51251aac1f 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -107,30 +107,12 @@ let in { imports = [ - (mkRemovedOptionModule [ "services" "nextcloud" "config" "adminpass" ] '' - Please use `services.nextcloud.config.adminpassFile' instead! - '') - (mkRemovedOptionModule [ "services" "nextcloud" "config" "dbpass" ] '' - Please use `services.nextcloud.config.dbpassFile' instead! - '') - (mkRemovedOptionModule [ "services" "nextcloud" "nginx" "enable" ] '' - The nextcloud module supports `nginx` as reverse-proxy by default and doesn't - support other reverse-proxies officially. - - However it's possible to use an alternative reverse-proxy by - - * disabling nginx - * setting `listen.owner` & `listen.group` in the phpfpm-pool to a different value - - Further details about this can be found in the `Nextcloud`-section of the NixOS-manual - (which can be opened e.g. by running `nixos-help`). - '') (mkRemovedOptionModule [ "services" "nextcloud" "enableBrokenCiphersForSSE" ] '' This option has no effect since there's no supported Nextcloud version packaged here using OpenSSL for RC4 SSE. '') - (mkRemovedOptionModule [ "services" "nextcloud" "disableImagemagick" ] '' - Use services.nextcloud.enableImagemagick instead. + (mkRemovedOptionModule [ "services" "nextcloud" "config" "dbport" ] '' + Add port to services.nextcloud.config.dbhost instead. '') (mkRenamedOptionModule [ "services" "nextcloud" "logLevel" ] [ "services" "nextcloud" "extraOptions" "loglevel" ]) @@ -363,18 +345,14 @@ in { else if mysqlLocal then "localhost:/run/mysqld/mysqld.sock" else "localhost"; defaultText = "localhost"; + example = "localhost:5000"; description = lib.mdDoc '' - Database host or socket path. + Database host (+port) or socket path. If [](#opt-services.nextcloud.database.createLocally) is true and [](#opt-services.nextcloud.config.dbtype) is either `pgsql` or `mysql`, defaults to the correct Unix socket instead. ''; }; - dbport = mkOption { - type = with types; nullOr (either int str); - default = null; - description = lib.mdDoc "Database port."; - }; dbtableprefix = mkOption { type = types.nullOr types.str; default = null; @@ -886,7 +864,6 @@ in { ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} - ${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"} ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} ${optionalString (c.dbpassFile != null) '' @@ -931,7 +908,6 @@ in { # will be omitted. ${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"''; ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; - ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"''; ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; "--database-pass" = "\"\$${dbpass.arg}\""; "--admin-user" = ''"${c.adminuser}"''; diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 1285c2bbb916d..6799de6c7d96c 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -334,8 +334,8 @@ let + optionalString vhost.default "default_server " + optionalString vhost.reuseport "reuseport " + optionalString (extraParameters != []) (concatStringsSep " " - (let inCompatibleParameters = [ "ssl" "proxy_protocol" "http2" ]; - isCompatibleParameter = param: !(any (p: p == param) inCompatibleParameters); + (let inCompatibleParameters = [ "accept_filter" "backlog" "deferred" "fastopen" "http2" "proxy_protocol" "so_keepalive" "ssl" ]; + isCompatibleParameter = param: !(any (p: lib.hasPrefix p param) inCompatibleParameters); in filter isCompatibleParameter extraParameters)) + ";")) + " @@ -408,12 +408,6 @@ let ssl_conf_command Options KTLS; ''} - ${optionalString (hasSSL && vhost.quic && vhost.http3) - # Advertise that HTTP/3 is available - '' - add_header Alt-Svc 'h3=":$server_port"; ma=86400'; - ''} - ${mkBasicAuth vhostName vhost} ${optionalString (vhost.root != null) "root ${vhost.root};"} @@ -475,7 +469,7 @@ let mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix; - oldHTTP2 = versionOlder cfg.package.version "1.25.1"; + oldHTTP2 = (versionOlder cfg.package.version "1.25.1" && !(cfg.package.pname == "angie" || cfg.package.pname == "angieQuic")); in { diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix index 64a95afab9f40..ea98439d3823d 100644 --- a/nixos/modules/services/web-servers/nginx/vhost-options.nix +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -235,9 +235,9 @@ with lib; which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;` and activate the QUIC transport protocol `services.nginx.virtualHosts.<name>.quic = true;`. - Note that HTTP/3 support is experimental and - *not* yet recommended for production. + Note that HTTP/3 support is experimental and *not* yet recommended for production. Read more at https://quic.nginx.org/ + HTTP/3 availability must be manually advertised, preferably in each location block. ''; }; @@ -250,8 +250,7 @@ with lib; which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;` and activate the QUIC transport protocol `services.nginx.virtualHosts.<name>.quic = true;`. - Note that special application protocol support is experimental and - *not* yet recommended for production. + Note that special application protocol support is experimental and *not* yet recommended for production. Read more at https://quic.nginx.org/ ''; }; diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix index 20eca7746447b..2cf9bc2eac37e 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome.nix @@ -449,7 +449,6 @@ in gnome-color-manager gnome-control-center gnome-shell-extensions - gnome-themes-extra pkgs.gnome-tour # GNOME Shell detects the .desktop file on first log-in. pkgs.gnome-user-docs pkgs.orca diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix index e0227f93e2f25..fc9de2500ba46 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -26,10 +26,8 @@ let emptyValue.value = {}; }; - libsForQt5 = pkgs.plasma5Packages; - inherit (libsForQt5) kdeGear kdeFrameworks plasma5; inherit (lib) - getBin optionalAttrs optionalString literalExpression + getBin optionalAttrs literalExpression mkRemovedOptionModule mkRenamedOptionModule mkDefault mkIf mkMerge mkOption mkPackageOption types; @@ -65,7 +63,7 @@ let # recognize that software that has been removed. rm -fv $HOME/.cache/ksycoca* - ${libsForQt5.kservice}/bin/kbuildsycoca5 + ${pkgs.plasma5Packages.kservice}/bin/kbuildsycoca5 ''; set_XDG_CONFIG_HOME = '' @@ -176,20 +174,19 @@ in owner = "root"; group = "root"; capabilities = "cap_sys_nice+ep"; - source = "${getBin plasma5.kwin}/bin/kwin_wayland"; + source = "${getBin pkgs.plasma5Packages.kwin}/bin/kwin_wayland"; }; } // optionalAttrs (!cfg.runUsingSystemd) { start_kdeinit = { setuid = true; owner = "root"; group = "root"; - source = "${getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit"; + source = "${getBin pkgs.plasma5Packages.kinit}/libexec/kf5/start_kdeinit"; }; }; environment.systemPackages = - with libsForQt5; - with plasma5; with kdeGear; with kdeFrameworks; + with pkgs.plasma5Packages; let requiredPackages = [ frameworkintegration @@ -284,8 +281,8 @@ in ++ utils.removePackagesByName optionalPackages config.environment.plasma5.excludePackages # Phonon audio backend - ++ lib.optional (cfg.phononBackend == "gstreamer") libsForQt5.phonon-backend-gstreamer - ++ lib.optional (cfg.phononBackend == "vlc") libsForQt5.phonon-backend-vlc + ++ lib.optional (cfg.phononBackend == "gstreamer") pkgs.plasma5Packages.phonon-backend-gstreamer + ++ lib.optional (cfg.phononBackend == "vlc") pkgs.plasma5Packages.phonon-backend-vlc # Optional hardware support features ++ lib.optionals config.hardware.bluetooth.enable [ bluedevil bluez-qt pkgs.openobex pkgs.obexftp ] @@ -301,7 +298,7 @@ in # Extra services for D-Bus activation services.dbus.packages = [ - plasma5.kactivitymanagerd + pkgs.plasma5Packages.kactivitymanagerd ]; environment.pathsToLink = [ @@ -334,7 +331,7 @@ in serif = [ "Noto Serif" ]; }; - programs.ssh.askPassword = mkDefault "${plasma5.ksshaskpass.out}/bin/ksshaskpass"; + programs.ssh.askPassword = mkDefault "${pkgs.plasma5Packages.ksshaskpass.out}/bin/ksshaskpass"; # Enable helpful DBus services. services.accounts-daemon.enable = true; @@ -372,8 +369,8 @@ in }; xdg.portal.enable = true; - xdg.portal.extraPortals = [ plasma5.xdg-desktop-portal-kde ]; - xdg.portal.configPackages = mkDefault [ plasma5.xdg-desktop-portal-kde ]; + xdg.portal.extraPortals = [ pkgs.plasma5Packages.xdg-desktop-portal-kde ]; + xdg.portal.configPackages = mkDefault [ pkgs.plasma5Packages.xdg-desktop-portal-kde ]; # xdg-desktop-portal-kde expects PipeWire to be running. # This does not, by default, replace PulseAudio. services.pipewire.enable = mkDefault true; @@ -404,15 +401,14 @@ in '' ]; - services.xserver.displayManager.sessionPackages = [ pkgs.libsForQt5.plasma5.plasma-workspace ]; + services.xserver.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-workspace ]; # Default to be `plasma` (X11) instead of `plasmawayland`, since plasma wayland currently has # many tiny bugs. # See: https://github.com/NixOS/nixpkgs/issues/143272 services.xserver.displayManager.defaultSession = mkDefault "plasma"; environment.systemPackages = - with libsForQt5; - with plasma5; with kdeGear; with kdeFrameworks; + with pkgs.plasma5Packages; let requiredPackages = [ ksystemstats @@ -448,7 +444,7 @@ in script = '' ${set_XDG_CONFIG_HOME} - ${kdeFrameworks.kconfig}/bin/kwriteconfig5 \ + ${pkgs.plasma5Packages.kconfig}/bin/kwriteconfig5 \ --file startkderc --group General --key systemdBoot ${lib.boolToString cfg.runUsingSystemd} ''; }; @@ -476,8 +472,7 @@ in ]; environment.systemPackages = - with libsForQt5; - with plasma5; with kdeApplications; with kdeFrameworks; + with pkgs.plasma5Packages; [ # Basic packages without which Plasma Mobile fails to work properly. plasma-mobile @@ -536,7 +531,7 @@ in }; }; - services.xserver.displayManager.sessionPackages = [ pkgs.libsForQt5.plasma5.plasma-mobile ]; + services.xserver.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-mobile ]; }) # Plasma Bigscreen diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 16a7ff1a4bd5e..3e2d5780a5cb1 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -514,7 +514,7 @@ in # Make xsessions and wayland sessions available in XDG_DATA_DIRS # as some programs have behavior that depends on them being present - environment.sessionVariables.XDG_DATA_DIRS = [ + environment.sessionVariables.XDG_DATA_DIRS = lib.mkIf (cfg.displayManager.sessionPackages != [ ]) [ "${cfg.displayManager.sessionData.desktops}/share" ]; }; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 87333999313e4..c3902007906ad 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -451,6 +451,21 @@ in cfg.services ); + assertions = concatLists ( + mapAttrsToList + (name: service: + map (message: { + assertion = false; + inherit message; + }) (concatLists [ + (optional ((builtins.elem "network-interfaces.target" service.after) || (builtins.elem "network-interfaces.target" service.wants)) + "Service '${name}.service' is using the deprecated target network-interfaces.target, which no longer exists. Using network.target is recommended instead." + ) + ]) + ) + cfg.services + ); + system.build.units = cfg.units; system.nssModules = [ cfg.package.out ]; diff --git a/nixos/modules/system/boot/systemd/journald-gateway.nix b/nixos/modules/system/boot/systemd/journald-gateway.nix new file mode 100644 index 0000000000000..854965282344d --- /dev/null +++ b/nixos/modules/system/boot/systemd/journald-gateway.nix @@ -0,0 +1,135 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.journald.gateway; + + cliArgs = lib.cli.toGNUCommandLineShell { } { + # If either of these are null / false, they are not passed in the command-line + inherit (cfg) cert key trust system user merge; + }; +in +{ + meta.maintainers = [ lib.maintainers.raitobezarius ]; + options.services.journald.gateway = { + enable = lib.mkEnableOption "the HTTP gateway to the journal"; + + port = lib.mkOption { + default = 19531; + type = lib.types.port; + description = '' + The port to listen to. + ''; + }; + + cert = lib.mkOption { + default = null; + type = with lib.types; nullOr str; + description = lib.mdDoc '' + The path to a file or `AF_UNIX` stream socket to read the server + certificate from. + + The certificate must be in PEM format. This option switches + `systemd-journal-gatewayd` into HTTPS mode and must be used together + with {option}`services.journald.gateway.key`. + ''; + }; + + key = lib.mkOption { + default = null; + type = with lib.types; nullOr str; + description = lib.mdDoc '' + Specify the path to a file or `AF_UNIX` stream socket to read the + secret server key corresponding to the certificate specified with + {option}`services.journald.gateway.cert` from. + + The key must be in PEM format. + + This key should not be world-readable, and must be readably by the + `systemd-journal-gateway` user. + ''; + }; + + trust = lib.mkOption { + default = null; + type = with lib.types; nullOr str; + description = lib.mdDoc '' + Specify the path to a file or `AF_UNIX` stream socket to read a CA + certificate from. + + The certificate must be in PEM format. + + Setting this option enforces client certificate checking. + ''; + }; + + system = lib.mkOption { + default = true; + type = lib.types.bool; + description = lib.mdDoc '' + Serve entries from system services and the kernel. + + This has the same meaning as `--system` for {manpage}`journalctl(1)`. + ''; + }; + + user = lib.mkOption { + default = true; + type = lib.types.bool; + description = lib.mdDoc '' + Serve entries from services for the current user. + + This has the same meaning as `--user` for {manpage}`journalctl(1)`. + ''; + }; + + merge = lib.mkOption { + default = false; + type = lib.types.bool; + description = lib.mdDoc '' + Serve entries interleaved from all available journals, including other + machines. + + This has the same meaning as `--merge` option for + {manpage}`journalctl(1)`. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + # This prevents the weird case were disabling "system" and "user" + # actually enables both because the cli flags are not present. + assertion = cfg.system || cfg.user; + message = '' + systemd-journal-gatewayd cannot serve neither "system" nor "user" + journals. + ''; + } + ]; + + systemd.additionalUpstreamSystemUnits = [ + "systemd-journal-gatewayd.socket" + "systemd-journal-gatewayd.service" + ]; + + users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway; + users.users.systemd-journal-gateway.group = "systemd-journal-gateway"; + users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway; + + systemd.services.systemd-journal-gatewayd.serviceConfig.ExecStart = [ + # Clear the default command line + "" + "${pkgs.systemd}/lib/systemd/systemd-journal-gatewayd ${cliArgs}" + ]; + + systemd.sockets.systemd-journal-gatewayd = { + wantedBy = [ "sockets.target" ]; + listenStreams = [ + # Clear the default port + "" + (toString cfg.port) + ]; + }; + }; +} diff --git a/nixos/modules/system/boot/systemd/journald-remote.nix b/nixos/modules/system/boot/systemd/journald-remote.nix new file mode 100644 index 0000000000000..57a0a133e1c6d --- /dev/null +++ b/nixos/modules/system/boot/systemd/journald-remote.nix @@ -0,0 +1,163 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.journald.remote; + format = pkgs.formats.systemd; + + cliArgs = lib.cli.toGNUCommandLineShell { } { + inherit (cfg) output; + # "-3" specifies the file descriptor from the .socket unit. + "listen-${cfg.listen}" = "-3"; + }; +in +{ + meta.maintainers = [ lib.maintainers.raitobezarius ]; + options.services.journald.remote = { + enable = lib.mkEnableOption "receiving systemd journals from the network"; + + listen = lib.mkOption { + default = "https"; + type = lib.types.enum [ "https" "http" ]; + description = lib.mdDoc '' + Which protocol to listen to. + ''; + }; + + output = lib.mkOption { + default = "/var/log/journal/remote/"; + type = lib.types.str; + description = lib.mdDoc '' + The location of the output journal. + + In case the output file is not specified, journal files will be created + underneath the selected directory. Files will be called + {file}`remote-hostname.journal`, where the `hostname` part is the + escaped hostname of the source endpoint of the connection, or the + numerical address if the hostname cannot be determined. + ''; + }; + + port = lib.mkOption { + default = 19532; + type = lib.types.port; + description = '' + The port to listen to. + + Note that this option is used only if + {option}`services.journald.upload.listen` is configured to be either + "https" or "http". + ''; + }; + + settings = lib.mkOption { + default = { }; + + description = lib.mdDoc '' + Configuration in the journal-remote configuration file. See + {manpage}`journal-remote.conf(5)` for available options. + ''; + + type = lib.types.submodule { + freeformType = format.type; + + options.Remote = { + Seal = lib.mkOption { + default = false; + example = true; + type = lib.types.bool; + description = '' + Periodically sign the data in the journal using Forward Secure + Sealing. + ''; + }; + + SplitMode = lib.mkOption { + default = "host"; + example = "none"; + type = lib.types.enum [ "host" "none" ]; + description = lib.mdDoc '' + With "host", a separate output file is used, based on the + hostname of the other endpoint of a connection. With "none", only + one output journal file is used. + ''; + }; + + ServerKeyFile = lib.mkOption { + default = "/etc/ssl/private/journal-remote.pem"; + type = lib.types.str; + description = lib.mdDoc '' + A path to a SSL secret key file in PEM format. + + Note that due to security reasons, `systemd-journal-remote` will + refuse files from the world-readable `/nix/store`. This file + should be readable by the "" user. + + This option can be used with `listen = "https"`. If the path + refers to an `AF_UNIX` stream socket in the file system a + connection is made to it and the key read from it. + ''; + }; + + ServerCertificateFile = lib.mkOption { + default = "/etc/ssl/certs/journal-remote.pem"; + type = lib.types.str; + description = lib.mdDoc '' + A path to a SSL certificate file in PEM format. + + This option can be used with `listen = "https"`. If the path + refers to an `AF_UNIX` stream socket in the file system a + connection is made to it and the certificate read from it. + ''; + }; + + TrustedCertificateFile = lib.mkOption { + default = "/etc/ssl/ca/trusted.pem"; + type = lib.types.str; + description = lib.mdDoc '' + A path to a SSL CA certificate file in PEM format, or `all`. + + If `all` is set, then client certificate checking will be + disabled. + + This option can be used with `listen = "https"`. If the path + refers to an `AF_UNIX` stream socket in the file system a + connection is made to it and the certificate read from it. + ''; + }; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.additionalUpstreamSystemUnits = [ + "systemd-journal-remote.service" + "systemd-journal-remote.socket" + ]; + + systemd.services.systemd-journal-remote.serviceConfig.ExecStart = [ + # Clear the default command line + "" + "${pkgs.systemd}/lib/systemd/systemd-journal-remote ${cliArgs}" + ]; + + systemd.sockets.systemd-journal-remote = { + wantedBy = [ "sockets.target" ]; + listenStreams = [ + # Clear the default port + "" + (toString cfg.port) + ]; + }; + + # User and group used by systemd-journal-remote.service + users.groups.systemd-journal-remote = { }; + users.users.systemd-journal-remote = { + isSystemUser = true; + group = "systemd-journal-remote"; + }; + + environment.etc."systemd/journal-remote.conf".source = + format.generate "journal-remote.conf" cfg.settings; + }; +} diff --git a/nixos/modules/system/boot/systemd/journald-upload.nix b/nixos/modules/system/boot/systemd/journald-upload.nix new file mode 100644 index 0000000000000..6421e5fa486f9 --- /dev/null +++ b/nixos/modules/system/boot/systemd/journald-upload.nix @@ -0,0 +1,111 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.journald.upload; + format = pkgs.formats.systemd; +in +{ + meta.maintainers = [ lib.maintainers.raitobezarius ]; + options.services.journald.upload = { + enable = lib.mkEnableOption "uploading the systemd journal to a remote server"; + + settings = lib.mkOption { + default = { }; + + description = lib.mdDoc '' + Configuration for journal-upload. See {manpage}`journal-upload.conf(5)` + for available options. + ''; + + type = lib.types.submodule { + freeformType = format.type; + + options.Upload = { + URL = lib.mkOption { + type = lib.types.str; + example = "https://192.168.1.1"; + description = '' + The URL to upload the journal entries to. + + See the description of `--url=` option in + {manpage}`systemd-journal-upload(8)` for the description of + possible values. + ''; + }; + + ServerKeyFile = lib.mkOption { + type = with lib.types; nullOr str; + example = lib.literalExpression "./server-key.pem"; + # Since systemd-journal-upload uses a DynamicUser, permissions must + # be done using groups + description = '' + SSL key in PEM format. + + In contrary to what the name suggests, this option configures the + client private key sent to the remote journal server. + + This key should not be world-readable, and must be readably by + the `systemd-journal` group. + ''; + default = null; + }; + + ServerCertificateFile = lib.mkOption { + type = with lib.types; nullOr str; + example = lib.literalExpression "./server-ca.pem"; + description = '' + SSL CA certificate in PEM format. + + In contrary to what the name suggests, this option configures the + client certificate sent to the remote journal server. + ''; + default = null; + }; + + TrustedCertificateFile = lib.mkOption { + type = with lib.types; nullOr str; + example = lib.literalExpression "./ca"; + description = '' + SSL CA certificate. + + This certificate will be used to check the remote journal HTTPS + server certificate. + ''; + default = null; + }; + + NetworkTimeoutSec = lib.mkOption { + type = with lib.types; nullOr str; + example = "1s"; + description = '' + When network connectivity to the server is lost, this option + configures the time to wait for the connectivity to get restored. + + If the server is not reachable over the network for the + configured time, `systemd-journal-upload` exits. Takes a value in + seconds (or in other time units if suffixed with "ms", "min", + "h", etc). For details, see {manpage}`systemd.time(5)`. + ''; + default = null; + }; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.additionalUpstreamSystemUnits = [ "systemd-journal-upload.service" ]; + + systemd.services."systemd-journal-upload" = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Restart = "always"; + # To prevent flooding the server in case the server is struggling + RestartSec = "3sec"; + }; + }; + + environment.etc."systemd/journal-upload.conf".source = + format.generate "journal-upload.conf" cfg.settings; + }; +} diff --git a/nixos/modules/system/boot/systemd/journald.nix b/nixos/modules/system/boot/systemd/journald.nix index 7e62a4c9bfedf..9a8e7d5926036 100644 --- a/nixos/modules/system/boot/systemd/journald.nix +++ b/nixos/modules/system/boot/systemd/journald.nix @@ -5,6 +5,10 @@ with lib; let cfg = config.services.journald; in { + imports = [ + (mkRenamedOptionModule [ "services" "journald" "enableHttpGateway" ] [ "services" "journald" "gateway" "enable" ]) + ]; + options = { services.journald.console = mkOption { default = ""; @@ -71,14 +75,6 @@ in { ''; }; - services.journald.enableHttpGateway = mkOption { - default = false; - type = types.bool; - description = lib.mdDoc '' - Whether to enable the HTTP gateway to the journal. - ''; - }; - services.journald.forwardToSyslog = mkOption { default = config.services.rsyslogd.enable || config.services.syslog-ng.enable; defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable"; @@ -101,9 +97,6 @@ in { ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [ "systemd-journald-dev-log.socket" "syslog.socket" - ] ++ optionals cfg.enableHttpGateway [ - "systemd-journal-gatewayd.socket" - "systemd-journal-gatewayd.service" ]; environment.etc = { @@ -124,12 +117,6 @@ in { }; users.groups.systemd-journal.gid = config.ids.gids.systemd-journal; - users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway; - users.users.systemd-journal-gateway.group = "systemd-journal-gateway"; - users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway; - - systemd.sockets.systemd-journal-gatewayd.wantedBy = - optional cfg.enableHttpGateway "sockets.target"; systemd.services.systemd-journal-flush.restartIfChanged = false; systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ]; diff --git a/nixos/modules/system/boot/timesyncd.nix b/nixos/modules/system/boot/timesyncd.nix index 7487cf97fe531..2666e4cd6b284 100644 --- a/nixos/modules/system/boot/timesyncd.nix +++ b/nixos/modules/system/boot/timesyncd.nix @@ -46,6 +46,13 @@ with lib; wantedBy = [ "sysinit.target" ]; aliases = [ "dbus-org.freedesktop.timesync1.service" ]; restartTriggers = [ config.environment.etc."systemd/timesyncd.conf".source ]; + # systemd-timesyncd disables DNSSEC validation in the nss-resolve module by setting SYSTEMD_NSS_RESOLVE_VALIDATE to 0 in the unit file. + # This is required in order to solve the chicken-and-egg problem when DNSSEC validation needs the correct time to work, but to set the + # correct time, we need to connect to an NTP server, which usually requires resolving its hostname. + # In order for nss-resolve to be able to read this environment variable we patch systemd-timesyncd to disable NSCD and use NSS modules directly. + # This means that systemd-timesyncd needs to have NSS modules path in LD_LIBRARY_PATH. When systemd-resolved is disabled we still need to set + # NSS module path so that systemd-timesyncd keeps using other NSS modules that are configured in the system. + environment.LD_LIBRARY_PATH = config.system.nssModules.path; preStart = ( # Ensure that we have some stored time to prevent diff --git a/nixos/modules/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix index 85cbe72e433fd..fdb149a3d9a17 100644 --- a/nixos/modules/tasks/filesystems/bcachefs.nix +++ b/nixos/modules/tasks/filesystems/bcachefs.nix @@ -123,15 +123,8 @@ in inherit assertions; # needed for systemd-remount-fs system.fsPackages = [ pkgs.bcachefs-tools ]; - - # FIXME: Replace this with `linuxPackages_testing` after NixOS 23.11 is released - # FIXME: Replace this with `linuxPackages_latest` when 6.7 is released, remove this line when the LTS version is at least 6.7 - boot.kernelPackages = lib.mkDefault ( - # FIXME: Remove warning after NixOS 23.11 is released - lib.warn "Please upgrade to Linux 6.7-rc1 or later: 'linuxPackages_testing_bcachefs' is deprecated. Use 'boot.kernelPackages = pkgs.linuxPackages_testing;' to silence this warning" - pkgs.linuxPackages_testing_bcachefs - ); - + # FIXME: Remove this line when the default kernel has bcachefs + boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; systemd.services = lib.mapAttrs' (mkUnits "") (lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (!utils.fsNeededForBoot fs)) config.fileSystems); } diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index b38f228fc1606..bc8b8fdf8144f 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -71,7 +71,7 @@ let done poolReady() { pool="$1" - state="$("${zpoolCmd}" import 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")" + state="$("${zpoolCmd}" import -d "${cfgZfs.devNodes}" 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")" if [[ "$state" = "ONLINE" ]]; then return 0 else diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index 2b2d24a64cb20..ca0b219b3c93d 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -1449,16 +1449,6 @@ in listToAttrs ]; - # The network-interfaces target is kept for backwards compatibility. - # New modules must NOT use it. - systemd.targets.network-interfaces = - { description = "All Network Interfaces (deprecated)"; - wantedBy = [ "network.target" ]; - before = [ "network.target" ]; - after = [ "network-pre.target" ]; - unitConfig.X-StopOnReconfiguration = true; - }; - systemd.services = { network-local-commands = { description = "Extra networking commands."; diff --git a/nixos/modules/tasks/trackpoint.nix b/nixos/modules/tasks/trackpoint.nix index 317613b847927..b3f6f32eaa473 100644 --- a/nixos/modules/tasks/trackpoint.nix +++ b/nixos/modules/tasks/trackpoint.nix @@ -88,7 +88,7 @@ with lib; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; serviceConfig.ExecStart = '' - ${config.systemd.package}/bin/udevadm trigger --attr-match=name="${cfg.device} + ${config.systemd.package}/bin/udevadm trigger --attr-match=name="${cfg.device}" ''; }; }) diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix index e195ff937d68e..217242a8fbd22 100644 --- a/nixos/modules/virtualisation/libvirtd.nix +++ b/nixos/modules/virtualisation/libvirtd.nix @@ -116,6 +116,15 @@ let QEMU's swtpm options. ''; }; + + vhostUserPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + example = lib.literalExpression "[ pkgs.virtiofsd ]"; + description = lib.mdDoc '' + Packages containing out-of-tree vhost-user drivers. + ''; + }; }; }; @@ -502,6 +511,14 @@ in # https://libvirt.org/daemons.html#monolithic-systemd-integration systemd.sockets.libvirtd.wantedBy = [ "sockets.target" ]; + systemd.tmpfiles.rules = let + vhostUserCollection = pkgs.buildEnv { + name = "vhost-user"; + paths = cfg.qemu.vhostUserPackages; + pathsToLink = [ "/share/qemu/vhost-user" ]; + }; + in [ "L+ /var/lib/qemu/vhost-user - - - - ${vhostUserCollection}/share/qemu/vhost-user" ]; + security.polkit = { enable = true; extraConfig = '' diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index c4c856d9be30d..885fb4e078530 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -33,21 +33,11 @@ in { ''; }; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.lxd; - defaultText = lib.literalExpression "pkgs.lxd"; - description = lib.mdDoc '' - The LXD package to use. - ''; - }; + package = lib.mkPackageOption pkgs "lxd" { }; - lxcPackage = lib.mkOption { - type = lib.types.package; - default = pkgs.lxc; - defaultText = lib.literalExpression "pkgs.lxc"; - description = lib.mdDoc '' - The LXC package to use with LXD (required for AppArmor profiles). + lxcPackage = lib.mkPackageOption pkgs "lxc" { + extraDescription = '' + Required for AppArmor profiles. ''; }; @@ -149,7 +139,7 @@ in { ui = { enable = lib.mkEnableOption (lib.mdDoc "(experimental) LXD UI"); - package = lib.mkPackageOption pkgs.lxd-unwrapped "ui" { }; + package = lib.mkPackageOption pkgs [ "lxd-unwrapped" "ui" ] { }; }; }; }; diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index 47506b964af49..a2e141b5bcafd 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -5,7 +5,7 @@ { nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 56789; shortRev = "gfedcba"; } , stableBranch ? false , supportedSystems ? [ "aarch64-linux" "x86_64-linux" ] -, limitedSupportedSystems ? [ "i686-linux" ] +, limitedSupportedSystems ? [ ] }: let @@ -168,6 +168,7 @@ in rec { (onFullSupported "nixos.tests.xfce") (onFullSupported "nixpkgs.emacs") (onFullSupported "nixpkgs.jdk") + (onSystems ["x86_64-linux"] "nixpkgs.mesa_i686") # i686 sanity check + useful ["nixpkgs.tarball"] # Ensure that nixpkgs-check-by-name is available in all release channels and nixos-unstable, diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 02e3e91e2e3db..33f13c3d1181c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -583,6 +583,7 @@ in { nginx = handleTest ./nginx.nix {}; nginx-auth = handleTest ./nginx-auth.nix {}; nginx-etag = handleTest ./nginx-etag.nix {}; + nginx-etag-compression = handleTest ./nginx-etag-compression.nix {}; nginx-globalredirect = handleTest ./nginx-globalredirect.nix {}; nginx-http3 = handleTest ./nginx-http3.nix {}; nginx-modsecurity = handleTest ./nginx-modsecurity.nix {}; @@ -604,7 +605,9 @@ in { nixos-generate-config = handleTest ./nixos-generate-config.nix {}; nixos-rebuild-install-bootloader = handleTestOn ["x86_64-linux"] ./nixos-rebuild-install-bootloader.nix {}; nixos-rebuild-specialisations = handleTestOn ["x86_64-linux"] ./nixos-rebuild-specialisations.nix {}; + nixos-rebuild-target-host = handleTest ./nixos-rebuild-target-host.nix {}; nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; }; + nixseparatedebuginfod = handleTest ./nixseparatedebuginfod.nix {}; node-red = handleTest ./node-red.nix {}; nomad = handleTest ./nomad.nix {}; non-default-filesystems = handleTest ./non-default-filesystems.nix {}; @@ -616,6 +619,7 @@ in { nscd = handleTest ./nscd.nix {}; nsd = handleTest ./nsd.nix {}; ntfy-sh = handleTest ./ntfy-sh.nix {}; + ntfy-sh-migration = handleTest ./ntfy-sh-migration.nix {}; nzbget = handleTest ./nzbget.nix {}; nzbhydra2 = handleTest ./nzbhydra2.nix {}; oh-my-zsh = handleTest ./oh-my-zsh.nix {}; @@ -772,6 +776,7 @@ in { sing-box = handleTest ./sing-box.nix {}; slimserver = handleTest ./slimserver.nix {}; slurm = handleTest ./slurm.nix {}; + snmpd = handleTest ./snmpd.nix {}; smokeping = handleTest ./smokeping.nix {}; snapcast = handleTest ./snapcast.nix {}; snapper = handleTest ./snapper.nix {}; @@ -841,6 +846,8 @@ in { systemd-initrd-networkd-openvpn = handleTestOn [ "x86_64-linux" "i686-linux" ] ./initrd-network-openvpn { systemdStage1 = true; }; systemd-initrd-vlan = handleTest ./systemd-initrd-vlan.nix {}; systemd-journal = handleTest ./systemd-journal.nix {}; + systemd-journal-gateway = handleTest ./systemd-journal-gateway.nix {}; + systemd-journal-upload = handleTest ./systemd-journal-upload.nix {}; systemd-machinectl = handleTest ./systemd-machinectl.nix {}; systemd-networkd = handleTest ./systemd-networkd.nix {}; systemd-networkd-dhcpserver = handleTest ./systemd-networkd-dhcpserver.nix {}; @@ -856,10 +863,12 @@ in { systemd-shutdown = handleTest ./systemd-shutdown.nix {}; systemd-sysupdate = runTest ./systemd-sysupdate.nix; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; + systemd-timesyncd-nscd-dnssec = handleTest ./systemd-timesyncd-nscd-dnssec.nix {}; systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {}; systemd-misc = handleTest ./systemd-misc.nix {}; systemd-userdbd = handleTest ./systemd-userdbd.nix {}; systemd-homed = handleTest ./systemd-homed.nix {}; + systemtap = handleTest ./systemtap.nix {}; tandoor-recipes = handleTest ./tandoor-recipes.nix {}; tang = handleTest ./tang.nix {}; taskserver = handleTest ./taskserver.nix {}; @@ -904,7 +913,8 @@ in { unbound = handleTest ./unbound.nix {}; unifi = handleTest ./unifi.nix {}; unit-php = handleTest ./web-servers/unit-php.nix {}; - upnp = handleTest ./upnp.nix {}; + upnp.iptables = handleTest ./upnp.nix { useNftables = false; }; + upnp.nftables = handleTest ./upnp.nix { useNftables = true; }; uptermd = handleTest ./uptermd.nix {}; uptime-kuma = handleTest ./uptime-kuma.nix {}; usbguard = handleTest ./usbguard.nix {}; diff --git a/nixos/tests/c2fmzq.nix b/nixos/tests/c2fmzq.nix index d8ec816c7d29c..0dd89f6881dd9 100644 --- a/nixos/tests/c2fmzq.nix +++ b/nixos/tests/c2fmzq.nix @@ -9,6 +9,10 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { passphraseFile = builtins.toFile "pwfile" "hunter2"; # don't do this on real deployments settings = { verbose = 3; # debug + # make sure multiple freeform options evaluate + allow-new-accounts = true; + auto-approve-new-accounts = true; + licenses = false; }; }; environment = { @@ -71,5 +75,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { with subtest("Test that PWA is served"): msg = machine.succeed("curl -sSfL http://localhost:8080") assert "c2FmZQ" in msg, f"Could not find 'c2FmZQ' in the output:\n{msg}" + + with subtest("A setting with false value is properly passed"): + machine.succeed("systemctl show -p ExecStart --value c2fmzq-server.service | grep -F -- '--licenses=false'"); ''; }) diff --git a/nixos/tests/frr.nix b/nixos/tests/frr.nix index 598d7a7d28675..0d1a6a694a82c 100644 --- a/nixos/tests/frr.nix +++ b/nixos/tests/frr.nix @@ -29,7 +29,7 @@ import ./make-test-python.nix ({ pkgs, ... }: name = "frr"; meta = with pkgs.lib.maintainers; { - maintainers = [ hexa ]; + maintainers = [ ]; }; nodes = { diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index d83e49a3e8f7c..21d5e1470d8e0 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -510,14 +510,8 @@ let ntp perlPackages.ListCompare perlPackages.XMLLibXML - python3Minimal # make-options-doc/default.nix - (let - self = (pkgs.python3Minimal.override { - inherit self; - includeSiteCustomize = true; - }); - in self.withPackages (p: [ p.mistune ])) + (python3.withPackages (p: [ p.mistune ])) shared-mime-info sudo texinfo @@ -1266,68 +1260,6 @@ in { ''; }; - bcachefsLinuxTesting = makeInstallerTest "bcachefs-linux-testing" { - extraInstallerConfig = { - imports = [ no-zfs-module ]; - - boot = { - supportedFilesystems = [ "bcachefs" ]; - kernelPackages = pkgs.linuxPackages_testing; - }; - }; - - extraConfig = '' - boot.kernelPackages = pkgs.linuxPackages_testing; - ''; - - createPartitions = '' - machine.succeed( - "flock /dev/vda parted --script /dev/vda -- mklabel msdos" - + " mkpart primary ext2 1M 100MB" # /boot - + " mkpart primary linux-swap 100M 1024M" # swap - + " mkpart primary 1024M -1s", # / - "udevadm settle", - "mkswap /dev/vda2 -L swap", - "swapon -L swap", - "mkfs.bcachefs -L root /dev/vda3", - "mount -t bcachefs /dev/vda3 /mnt", - "mkfs.ext3 -L boot /dev/vda1", - "mkdir -p /mnt/boot", - "mount /dev/vda1 /mnt/boot", - ) - ''; - }; - - bcachefsUpgradeToLinuxTesting = makeInstallerTest "bcachefs-upgrade-to-linux-testing" { - extraInstallerConfig = { - imports = [ no-zfs-module ]; - boot.supportedFilesystems = [ "bcachefs" ]; - # We don't have network access in the VM, we need this for `nixos-install` - system.extraDependencies = [ pkgs.linux_testing ]; - }; - - extraConfig = '' - boot.kernelPackages = pkgs.linuxPackages_testing; - ''; - - createPartitions = '' - machine.succeed( - "flock /dev/vda parted --script /dev/vda -- mklabel msdos" - + " mkpart primary ext2 1M 100MB" # /boot - + " mkpart primary linux-swap 100M 1024M" # swap - + " mkpart primary 1024M -1s", # / - "udevadm settle", - "mkswap /dev/vda2 -L swap", - "swapon -L swap", - "mkfs.bcachefs -L root /dev/vda3", - "mount -t bcachefs /dev/vda3 /mnt", - "mkfs.ext3 -L boot /dev/vda1", - "mkdir -p /mnt/boot", - "mount /dev/vda1 /mnt/boot", - ) - ''; - }; - # Test using labels to identify volumes in grub simpleLabels = makeInstallerTest "simpleLabels" { createPartitions = '' diff --git a/nixos/tests/kerberos/heimdal.nix b/nixos/tests/kerberos/heimdal.nix index 47f9d0285aef7..393289f7a92ca 100644 --- a/nixos/tests/kerberos/heimdal.nix +++ b/nixos/tests/kerberos/heimdal.nix @@ -1,5 +1,6 @@ import ../make-test-python.nix ({pkgs, ...}: { name = "kerberos_server-heimdal"; + nodes.machine = { config, libs, pkgs, ...}: { services.kerberos_server = { enable = true; @@ -7,16 +8,18 @@ import ../make-test-python.nix ({pkgs, ...}: { "FOO.BAR".acl = [{principal = "admin"; access = ["add" "cpw"];}]; }; }; - krb5 = { + security.krb5 = { enable = true; - kerberos = pkgs.heimdal; - libdefaults = { - default_realm = "FOO.BAR"; - }; - realms = { - "FOO.BAR" = { - admin_server = "machine"; - kdc = "machine"; + package = pkgs.heimdal; + settings = { + libdefaults = { + default_realm = "FOO.BAR"; + }; + realms = { + "FOO.BAR" = { + admin_server = "machine"; + kdc = "machine"; + }; }; }; }; @@ -39,4 +42,6 @@ import ../make-test-python.nix ({pkgs, ...}: { "kinit -kt alice.keytab alice", ) ''; + + meta.maintainers = [ pkgs.lib.maintainers.dblsaiko ]; }) diff --git a/nixos/tests/kerberos/mit.nix b/nixos/tests/kerberos/mit.nix index 7e427ffef0ba8..1191d047abbf0 100644 --- a/nixos/tests/kerberos/mit.nix +++ b/nixos/tests/kerberos/mit.nix @@ -1,5 +1,6 @@ import ../make-test-python.nix ({pkgs, ...}: { name = "kerberos_server-mit"; + nodes.machine = { config, libs, pkgs, ...}: { services.kerberos_server = { enable = true; @@ -7,16 +8,18 @@ import ../make-test-python.nix ({pkgs, ...}: { "FOO.BAR".acl = [{principal = "admin"; access = ["add" "cpw"];}]; }; }; - krb5 = { + security.krb5 = { enable = true; - kerberos = pkgs.krb5; - libdefaults = { - default_realm = "FOO.BAR"; - }; - realms = { - "FOO.BAR" = { - admin_server = "machine"; - kdc = "machine"; + package = pkgs.krb5; + settings = { + libdefaults = { + default_realm = "FOO.BAR"; + }; + realms = { + "FOO.BAR" = { + admin_server = "machine"; + kdc = "machine"; + }; }; }; }; @@ -38,4 +41,6 @@ import ../make-test-python.nix ({pkgs, ...}: { "echo alice_pw | sudo -u alice kinit", ) ''; + + meta.maintainers = [ pkgs.lib.maintainers.dblsaiko ]; }) diff --git a/nixos/tests/krb5/default.nix b/nixos/tests/krb5/default.nix index dd5b2f37202e8..ede085632c634 100644 --- a/nixos/tests/krb5/default.nix +++ b/nixos/tests/krb5/default.nix @@ -1,5 +1,4 @@ { system ? builtins.currentSystem }: { example-config = import ./example-config.nix { inherit system; }; - deprecated-config = import ./deprecated-config.nix { inherit system; }; } diff --git a/nixos/tests/krb5/deprecated-config.nix b/nixos/tests/krb5/deprecated-config.nix deleted file mode 100644 index aca29ae6ca2b2..0000000000000 --- a/nixos/tests/krb5/deprecated-config.nix +++ /dev/null @@ -1,50 +0,0 @@ -# Verifies that the configuration suggested in deprecated example values -# will result in the expected output. - -import ../make-test-python.nix ({ pkgs, ...} : { - name = "krb5-with-deprecated-config"; - meta = with pkgs.lib.maintainers; { - maintainers = [ eqyiel ]; - }; - - nodes.machine = - { ... }: { - krb5 = { - enable = true; - defaultRealm = "ATHENA.MIT.EDU"; - domainRealm = "athena.mit.edu"; - kdc = "kerberos.mit.edu"; - kerberosAdminServer = "kerberos.mit.edu"; - }; - }; - - testScript = - let snapshot = pkgs.writeText "krb5-with-deprecated-config.conf" '' - [libdefaults] - default_realm = ATHENA.MIT.EDU - - [realms] - ATHENA.MIT.EDU = { - admin_server = kerberos.mit.edu - kdc = kerberos.mit.edu - } - - [domain_realm] - .athena.mit.edu = ATHENA.MIT.EDU - athena.mit.edu = ATHENA.MIT.EDU - - [capaths] - - - [appdefaults] - - - [plugins] - - ''; - in '' - machine.succeed( - "diff /etc/krb5.conf ${snapshot}" - ) - ''; -}) diff --git a/nixos/tests/krb5/example-config.nix b/nixos/tests/krb5/example-config.nix index 9a5c3b2af2490..33bed481b39fc 100644 --- a/nixos/tests/krb5/example-config.nix +++ b/nixos/tests/krb5/example-config.nix @@ -4,78 +4,77 @@ import ../make-test-python.nix ({ pkgs, ...} : { name = "krb5-with-example-config"; meta = with pkgs.lib.maintainers; { - maintainers = [ eqyiel ]; + maintainers = [ eqyiel dblsaiko ]; }; nodes.machine = { pkgs, ... }: { - krb5 = { + security.krb5 = { enable = true; - kerberos = pkgs.krb5; - libdefaults = { - default_realm = "ATHENA.MIT.EDU"; - }; - realms = { - "ATHENA.MIT.EDU" = { - admin_server = "athena.mit.edu"; - kdc = [ - "athena01.mit.edu" - "athena02.mit.edu" - ]; + package = pkgs.krb5; + settings = { + includedir = [ + "/etc/krb5.conf.d" + ]; + include = [ + "/etc/krb5-extra.conf" + ]; + libdefaults = { + default_realm = "ATHENA.MIT.EDU"; }; - }; - domain_realm = { - "example.com" = "EXAMPLE.COM"; - ".example.com" = "EXAMPLE.COM"; - }; - capaths = { - "ATHENA.MIT.EDU" = { - "EXAMPLE.COM" = "."; + realms = { + "ATHENA.MIT.EDU" = { + admin_server = "athena.mit.edu"; + kdc = [ + "athena01.mit.edu" + "athena02.mit.edu" + ]; + }; }; - "EXAMPLE.COM" = { - "ATHENA.MIT.EDU" = "."; + domain_realm = { + "example.com" = "EXAMPLE.COM"; + ".example.com" = "EXAMPLE.COM"; }; - }; - appdefaults = { - pam = { - debug = false; - ticket_lifetime = 36000; - renew_lifetime = 36000; - max_timeout = 30; - timeout_shift = 2; - initial_timeout = 1; + capaths = { + "ATHENA.MIT.EDU" = { + "EXAMPLE.COM" = "."; + }; + "EXAMPLE.COM" = { + "ATHENA.MIT.EDU" = "."; + }; }; - }; - plugins = { - ccselect = { - disable = "k5identity"; + appdefaults = { + pam = { + debug = false; + ticket_lifetime = 36000; + renew_lifetime = 36000; + max_timeout = 30; + timeout_shift = 2; + initial_timeout = 1; + }; + }; + plugins.ccselect.disable = "k5identity"; + logging = { + kdc = "SYSLOG:NOTICE"; + admin_server = "SYSLOG:NOTICE"; + default = "SYSLOG:NOTICE"; }; }; - extraConfig = '' - [logging] - kdc = SYSLOG:NOTICE - admin_server = SYSLOG:NOTICE - default = SYSLOG:NOTICE - ''; }; }; testScript = let snapshot = pkgs.writeText "krb5-with-example-config.conf" '' - [libdefaults] - default_realm = ATHENA.MIT.EDU - - [realms] - ATHENA.MIT.EDU = { - admin_server = athena.mit.edu - kdc = athena01.mit.edu - kdc = athena02.mit.edu + [appdefaults] + pam = { + debug = false + initial_timeout = 1 + max_timeout = 30 + renew_lifetime = 36000 + ticket_lifetime = 36000 + timeout_shift = 2 } - [domain_realm] - .example.com = EXAMPLE.COM - example.com = EXAMPLE.COM - [capaths] ATHENA.MIT.EDU = { EXAMPLE.COM = . @@ -84,25 +83,32 @@ import ../make-test-python.nix ({ pkgs, ...} : { ATHENA.MIT.EDU = . } - [appdefaults] - pam = { - debug = false - initial_timeout = 1 - max_timeout = 30 - renew_lifetime = 36000 - ticket_lifetime = 36000 - timeout_shift = 2 - } + [domain_realm] + .example.com = EXAMPLE.COM + example.com = EXAMPLE.COM + + [libdefaults] + default_realm = ATHENA.MIT.EDU + + [logging] + admin_server = SYSLOG:NOTICE + default = SYSLOG:NOTICE + kdc = SYSLOG:NOTICE [plugins] ccselect = { disable = k5identity } - [logging] - kdc = SYSLOG:NOTICE - admin_server = SYSLOG:NOTICE - default = SYSLOG:NOTICE + [realms] + ATHENA.MIT.EDU = { + admin_server = athena.mit.edu + kdc = athena01.mit.edu + kdc = athena02.mit.edu + } + + include /etc/krb5-extra.conf + includedir /etc/krb5.conf.d ''; in '' machine.succeed( diff --git a/nixos/tests/nfs/kerberos.nix b/nixos/tests/nfs/kerberos.nix index a7d08bc628c62..1bace4058be59 100644 --- a/nixos/tests/nfs/kerberos.nix +++ b/nixos/tests/nfs/kerberos.nix @@ -1,15 +1,17 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: let - krb5 = - { enable = true; - domain_realm."nfs.test" = "NFS.TEST"; + security.krb5 = { + enable = true; + settings = { + domain_realm."nfs.test" = "NFS.TEST"; libdefaults.default_realm = "NFS.TEST"; - realms."NFS.TEST" = - { admin_server = "server.nfs.test"; - kdc = "server.nfs.test"; - }; + realms."NFS.TEST" = { + admin_server = "server.nfs.test"; + kdc = "server.nfs.test"; + }; }; + }; hosts = '' @@ -32,7 +34,7 @@ in nodes = { client = { lib, ... }: - { inherit krb5 users; + { inherit security users; networking.extraHosts = hosts; networking.domain = "nfs.test"; @@ -48,7 +50,7 @@ in }; server = { lib, ...}: - { inherit krb5 users; + { inherit security users; networking.extraHosts = hosts; networking.domain = "nfs.test"; @@ -128,4 +130,6 @@ in expected = ["alice", "users"] assert ids == expected, f"ids incorrect: got {ids} expected {expected}" ''; + + meta.maintainers = [ lib.maintainers.dblsaiko ]; }) diff --git a/nixos/tests/nginx-etag-compression.nix b/nixos/tests/nginx-etag-compression.nix new file mode 100644 index 0000000000000..67493ae299841 --- /dev/null +++ b/nixos/tests/nginx-etag-compression.nix @@ -0,0 +1,45 @@ +import ./make-test-python.nix { + name = "nginx-etag-compression"; + + nodes.machine = { pkgs, lib, ... }: { + services.nginx = { + enable = true; + recommendedGzipSettings = true; + virtualHosts.default = { + root = pkgs.runCommandLocal "testdir" {} '' + mkdir "$out" + cat > "$out/index.html" <<EOF + Hello, world! + Hello, world! + Hello, world! + Hello, world! + Hello, world! + Hello, world! + Hello, world! + Hello, world! + EOF + ${pkgs.gzip}/bin/gzip -k "$out/index.html" + ''; + }; + }; + }; + + testScript = { nodes, ... }: '' + machine.wait_for_unit("nginx") + machine.wait_for_open_port(80) + + etag_plain = machine.succeed("curl -s -w'%header{etag}' -o/dev/null -H 'Accept-encoding:' http://127.0.0.1/") + etag_gzip = machine.succeed("curl -s -w'%header{etag}' -o/dev/null -H 'Accept-encoding:gzip' http://127.0.0.1/") + + with subtest("different representations have different etags"): + assert etag_plain != etag_gzip, f"etags should differ: {etag_plain} == {etag_gzip}" + + with subtest("etag for uncompressed response is reproducible"): + etag_plain_repeat = machine.succeed("curl -s -w'%header{etag}' -o/dev/null -H 'Accept-encoding:' http://127.0.0.1/") + assert etag_plain == etag_plain_repeat, f"etags should be the same: {etag_plain} != {etag_plain_repeat}" + + with subtest("etag for compressed response is reproducible"): + etag_gzip_repeat = machine.succeed("curl -s -w'%header{etag}' -o/dev/null -H 'Accept-encoding:gzip' http://127.0.0.1/") + assert etag_gzip == etag_gzip_repeat, f"etags should be the same: {etag_gzip} != {etag_gzip_repeat}" + ''; +} diff --git a/nixos/tests/nixos-rebuild-target-host.nix b/nixos/tests/nixos-rebuild-target-host.nix new file mode 100644 index 0000000000000..8d60b788abf38 --- /dev/null +++ b/nixos/tests/nixos-rebuild-target-host.nix @@ -0,0 +1,136 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "nixos-rebuild-target-host"; + + nodes = { + deployer = { lib, ... }: let + inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; + in { + imports = [ ../modules/profiles/installation-device.nix ]; + + nix.settings = { + substituters = lib.mkForce [ ]; + hashed-mirrors = null; + connect-timeout = 1; + }; + + environment.systemPackages = [ pkgs.passh ]; + + system.includeBuildDependencies = true; + + virtualisation = { + cores = 2; + memorySize = 2048; + }; + + system.build.privateKey = snakeOilPrivateKey; + system.build.publicKey = snakeOilPublicKey; + }; + + target = { nodes, lib, ... }: let + targetConfig = { + documentation.enable = false; + services.openssh.enable = true; + + users.users.root.openssh.authorizedKeys.keys = [ nodes.deployer.system.build.publicKey ]; + users.users.alice.openssh.authorizedKeys.keys = [ nodes.deployer.system.build.publicKey ]; + users.users.bob.openssh.authorizedKeys.keys = [ nodes.deployer.system.build.publicKey ]; + + users.users.alice.extraGroups = [ "wheel" ]; + users.users.bob.extraGroups = [ "wheel" ]; + + # Disable sudo for root to ensure sudo isn't called without `--use-remote-sudo` + security.sudo.extraRules = lib.mkForce [ + { groups = [ "wheel" ]; commands = [ { command = "ALL"; } ]; } + { users = [ "alice" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; } + ]; + + nix.settings.trusted-users = [ "@wheel" ]; + }; + in { + imports = [ ./common/user-account.nix ]; + + config = lib.mkMerge [ + targetConfig + { + system.build = { + inherit targetConfig; + }; + + networking.hostName = "target"; + } + ]; + }; + }; + + testScript = { nodes, ... }: + let + sshConfig = builtins.toFile "ssh.conf" '' + UserKnownHostsFile=/dev/null + StrictHostKeyChecking=no + ''; + + targetConfigJSON = pkgs.writeText "target-configuration.json" + (builtins.toJSON nodes.target.system.build.targetConfig); + + targetNetworkJSON = pkgs.writeText "target-network.json" + (builtins.toJSON nodes.target.system.build.networkConfig); + + configFile = hostname: pkgs.writeText "configuration.nix" '' + { lib, modulesPath, ... }: { + imports = [ + (modulesPath + "/virtualisation/qemu-vm.nix") + (modulesPath + "/testing/test-instrumentation.nix") + (modulesPath + "/../tests/common/user-account.nix") + (lib.modules.importJSON ./target-configuration.json) + (lib.modules.importJSON ./target-network.json) + ./hardware-configuration.nix + ]; + + boot.loader.grub = { + enable = true; + device = "/dev/vda"; + forceInstall = true; + }; + + # this will be asserted + networking.hostName = "${hostname}"; + } + ''; + in + '' + start_all() + target.wait_for_open_port(22) + + deployer.wait_until_succeeds("ping -c1 target") + deployer.succeed("install -Dm 600 ${nodes.deployer.system.build.privateKey} ~root/.ssh/id_ecdsa") + deployer.succeed("install ${sshConfig} ~root/.ssh/config") + + target.succeed("nixos-generate-config") + deployer.succeed("scp alice@target:/etc/nixos/hardware-configuration.nix /root/hardware-configuration.nix") + + deployer.copy_from_host("${configFile "config-1-deployed"}", "/root/configuration-1.nix") + deployer.copy_from_host("${configFile "config-2-deployed"}", "/root/configuration-2.nix") + deployer.copy_from_host("${configFile "config-3-deployed"}", "/root/configuration-3.nix") + deployer.copy_from_host("${targetNetworkJSON}", "/root/target-network.json") + deployer.copy_from_host("${targetConfigJSON}", "/root/target-configuration.json") + + # Ensure sudo is disabled for root + target.fail("sudo true") + + # This test also ensures that sudo is not called without --use-remote-sudo + with subtest("Deploy to root@target"): + deployer.succeed("nixos-rebuild switch -I nixos-config=/root/configuration-1.nix --target-host root@target &>/dev/console") + target_hostname = deployer.succeed("ssh alice@target cat /etc/hostname").rstrip() + assert target_hostname == "config-1-deployed", f"{target_hostname=}" + + with subtest("Deploy to alice@target with passwordless sudo"): + deployer.succeed("nixos-rebuild switch -I nixos-config=/root/configuration-2.nix --target-host alice@target --use-remote-sudo &>/dev/console") + target_hostname = deployer.succeed("ssh alice@target cat /etc/hostname").rstrip() + assert target_hostname == "config-2-deployed", f"{target_hostname=}" + + with subtest("Deploy to bob@target with password based sudo"): + deployer.succeed("passh -c 3 -C -p ${nodes.target.users.users.bob.password} -P \"\[sudo\] password\" nixos-rebuild switch -I nixos-config=/root/configuration-3.nix --target-host bob@target --use-remote-sudo &>/dev/console") + target_hostname = deployer.succeed("ssh alice@target cat /etc/hostname").rstrip() + assert target_hostname == "config-3-deployed", f"{target_hostname=}" + ''; +}) diff --git a/nixos/tests/nixseparatedebuginfod.nix b/nixos/tests/nixseparatedebuginfod.nix new file mode 100644 index 0000000000000..7c192a73c7064 --- /dev/null +++ b/nixos/tests/nixseparatedebuginfod.nix @@ -0,0 +1,80 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + secret-key = "key-name:/COlMSRbehSh6YSruJWjL+R0JXQUKuPEn96fIb+pLokEJUjcK/2Gv8Ai96D7JGay5gDeUTx5wdpPgNvum9YtwA=="; + public-key = "key-name:BCVI3Cv9hr/AIveg+yRmsuYA3lE8ecHaT4Db7pvWLcA="; +in +{ + name = "nixseparatedebuginfod"; + /* A binary cache with debug info and source for nix */ + nodes.cache = { pkgs, ... }: { + services.nix-serve = { + enable = true; + secretKeyFile = builtins.toFile "secret-key" secret-key; + openFirewall = true; + }; + system.extraDependencies = [ + pkgs.nix.debug + pkgs.nix.src + pkgs.sl + ]; + }; + /* the machine where we need the debuginfo */ + nodes.machine = { + imports = [ + ../modules/installer/cd-dvd/channel.nix + ]; + services.nixseparatedebuginfod.enable = true; + nix.settings = { + substituters = lib.mkForce [ "http://cache:5000" ]; + trusted-public-keys = [ public-key ]; + }; + environment.systemPackages = [ + pkgs.valgrind + pkgs.gdb + (pkgs.writeShellScriptBin "wait_for_indexation" '' + set -x + while debuginfod-find debuginfo /run/current-system/sw/bin/nix |& grep 'File too large'; do + sleep 1; + done + '') + ]; + }; + testScript = '' + start_all() + cache.wait_for_unit("nix-serve.service") + cache.wait_for_open_port(5000) + machine.wait_for_unit("nixseparatedebuginfod.service") + machine.wait_for_open_port(1949) + + with subtest("show the config to debug the test"): + machine.succeed("nix --extra-experimental-features nix-command show-config |& logger") + machine.succeed("cat /etc/nix/nix.conf |& logger") + with subtest("check that the binary cache works"): + machine.succeed("nix-store -r ${pkgs.sl}") + + # nixseparatedebuginfod needs .drv to associate executable -> source + # on regular systems this would be provided by nixos-rebuild + machine.succeed("nix-instantiate '<nixpkgs>' -A nix") + + machine.succeed("timeout 600 wait_for_indexation") + + # test debuginfod-find + machine.succeed("debuginfod-find debuginfo /run/current-system/sw/bin/nix") + + # test that gdb can fetch source + out = machine.succeed("gdb /run/current-system/sw/bin/nix --batch -x ${builtins.toFile "commands" '' + start + l + ''}") + print(out) + assert 'int main(' in out + + # test that valgrind can display location information + # this relies on the fact that valgrind complains about nix + # libgc helps in this regard, and we also ask valgrind to show leak kinds + # which are usually false positives. + out = machine.succeed("valgrind --leak-check=full --show-leak-kinds=all nix-env --version 2>&1") + print(out) + assert 'main.cc' in out + ''; +}) diff --git a/nixos/tests/ntfy-sh-migration.nix b/nixos/tests/ntfy-sh-migration.nix new file mode 100644 index 0000000000000..de6660052d679 --- /dev/null +++ b/nixos/tests/ntfy-sh-migration.nix @@ -0,0 +1,77 @@ +# the ntfy-sh module was switching to DynamicUser=true. this test assures that +# the migration does not break existing setups. +# +# this test works doing a migration and asserting ntfy-sh runs properly. first, +# ntfy-sh is configured to use a static user and group. then ntfy-sh is +# started and tested. after that, ntfy-sh is shut down and a systemd drop +# in configuration file is used to upate the service configuration to use +# DynamicUser=true. then the ntfy-sh is started again and tested. + +import ./make-test-python.nix { + name = "ntfy-sh"; + + nodes.machine = { + lib, + pkgs, + ... + }: { + environment.etc."ntfy-sh-dynamic-user.conf".text = '' + [Service] + Group=new-ntfy-sh + User=new-ntfy-sh + DynamicUser=true + ''; + + services.ntfy-sh.enable = true; + services.ntfy-sh.settings.base-url = "http://localhost:2586"; + + systemd.services.ntfy-sh.serviceConfig = { + DynamicUser = lib.mkForce false; + ExecStartPre = [ + "${pkgs.coreutils}/bin/id" + "${pkgs.coreutils}/bin/ls -lahd /var/lib/ntfy-sh/" + "${pkgs.coreutils}/bin/ls -lah /var/lib/ntfy-sh/" + ]; + Group = lib.mkForce "old-ntfy-sh"; + User = lib.mkForce "old-ntfy-sh"; + }; + + users.users.old-ntfy-sh = { + isSystemUser = true; + group = "old-ntfy-sh"; + }; + + users.groups.old-ntfy-sh = {}; + }; + + testScript = '' + import json + + msg = "Test notification" + + def test_ntfysh(): + machine.wait_for_unit("ntfy-sh.service") + machine.wait_for_open_port(2586) + + machine.succeed(f"curl -d '{msg}' localhost:2586/test") + + text = machine.succeed("curl -s localhost:2586/test/json?poll=1") + for line in text.splitlines(): + notif = json.loads(line) + assert msg == notif["message"], "Wrong message" + + machine.succeed("ntfy user list") + + machine.wait_for_unit("multi-user.target") + + test_ntfysh() + + machine.succeed("systemctl stop ntfy-sh.service") + machine.succeed("mkdir -p /run/systemd/system/ntfy-sh.service.d") + machine.succeed("cp /etc/ntfy-sh-dynamic-user.conf /run/systemd/system/ntfy-sh.service.d/dynamic-user.conf") + machine.succeed("systemctl daemon-reload") + machine.succeed("systemctl start ntfy-sh.service") + + test_ntfysh() + ''; +} diff --git a/nixos/tests/os-prober.nix b/nixos/tests/os-prober.nix index dae1306bd69d0..034de0620d885 100644 --- a/nixos/tests/os-prober.nix +++ b/nixos/tests/os-prober.nix @@ -95,7 +95,7 @@ in { ntp perlPackages.ListCompare perlPackages.XMLLibXML - python3Minimal + python3 shared-mime-info stdenv sudo diff --git a/nixos/tests/pam/pam-file-contents.nix b/nixos/tests/pam/pam-file-contents.nix index 2bafd90618e97..accaa4cc70a94 100644 --- a/nixos/tests/pam/pam-file-contents.nix +++ b/nixos/tests/pam/pam-file-contents.nix @@ -7,7 +7,7 @@ import ../make-test-python.nix ({ pkgs, ... }: { nodes.machine = { ... }: { imports = [ ../../modules/profiles/minimal.nix ]; - krb5.enable = true; + security.krb5.enable = true; users = { mutableUsers = false; diff --git a/nixos/tests/pantheon.nix b/nixos/tests/pantheon.nix index be1351283d99a..69a28c397bedc 100644 --- a/nixos/tests/pantheon.nix +++ b/nixos/tests/pantheon.nix @@ -26,6 +26,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with subtest("Test we can see usernames in elementary-greeter"): machine.wait_for_text("${user.description}") + machine.wait_until_succeeds("pgrep -f io.elementary.greeter-compositor") # OCR was struggling with this one. # machine.wait_for_text("${bob.description}") # Ensure the password box is focused by clicking it. @@ -39,21 +40,29 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : machine.wait_for_x() machine.wait_for_file("${user.home}/.Xauthority") machine.succeed("xauth merge ${user.home}/.Xauthority") + machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"') with subtest("Check that logging in has given the user ownership of devices"): machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}") - with subtest("Check if pantheon session components actually start"): - machine.wait_until_succeeds("pgrep gala") - machine.wait_for_window("gala") - machine.wait_until_succeeds("pgrep -f io.elementary.wingpanel") - machine.wait_for_window("io.elementary.wingpanel") - machine.wait_until_succeeds("pgrep plank") - machine.wait_for_window("plank") - machine.wait_until_succeeds("pgrep -f gsd-media-keys") + with subtest("Check if Pantheon components actually start"): + for i in ["gala", "io.elementary.wingpanel", "plank", "gsd-media-keys", "io.elementary.desktop.agent-polkit"]: + machine.wait_until_succeeds(f"pgrep -f {i}") + for i in ["gala", "io.elementary.wingpanel", "plank"]: + machine.wait_for_window(i) machine.wait_for_unit("bamfdaemon.service", "${user.name}") machine.wait_for_unit("io.elementary.files.xdg-desktop-portal.service", "${user.name}") + with subtest("Check if various environment variables are set"): + cmd = "xargs --null --max-args=1 echo < /proc/$(pgrep -xf /run/current-system/sw/bin/gala)/environ" + machine.succeed(f"{cmd} | grep 'XDG_CURRENT_DESKTOP' | grep 'Pantheon'") + # Hopefully from the sessionPath option. + machine.succeed(f"{cmd} | grep 'XDG_DATA_DIRS' | grep 'gsettings-schemas/pantheon-agent-geoclue2'") + # Hopefully from login shell. + machine.succeed(f"{cmd} | grep '__NIXOS_SET_ENVIRONMENT_DONE' | grep '1'") + # See elementary-session-settings packaging. + machine.succeed(f"{cmd} | grep 'XDG_CONFIG_DIRS' | grep 'elementary-default-settings'") + with subtest("Open elementary videos"): machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.videos >&2 &'") machine.sleep(2) @@ -61,6 +70,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : machine.wait_for_text("No Videos Open") with subtest("Open elementary calendar"): + machine.wait_until_succeeds("pgrep -f evolution-calendar-factory") machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.calendar >&2 &'") machine.sleep(2) machine.wait_for_window("io.elementary.calendar") @@ -75,6 +85,14 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.terminal >&2 &'") machine.wait_for_window("io.elementary.terminal") + with subtest("Trigger multitasking view"): + cmd = "dbus-send --session --dest=org.pantheon.gala --print-reply /org/pantheon/gala org.pantheon.gala.PerformAction int32:1" + env = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${toString user.uid}/bus DISPLAY=:0" + machine.succeed(f"su - ${user.name} -c '{env} {cmd}'") + machine.sleep(3) + machine.screenshot("multitasking") + machine.succeed(f"su - ${user.name} -c '{env} {cmd}'") + with subtest("Check if gala has ever coredumped"): machine.fail("coredumpctl --json=short | grep gala") # So you can see the dock in the below screenshot. diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix index 6a51cc522bdc5..3d834b29958de 100644 --- a/nixos/tests/paperless.nix +++ b/nixos/tests/paperless.nix @@ -21,7 +21,7 @@ import ./make-test-python.nix ({ lib, ... }: { } ]; }; - services.paperless.extraConfig = { + services.paperless.settings = { PAPERLESS_DBHOST = "/run/postgresql"; }; }; diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix index 3ee7ed19fa1c5..407e4592ef5f7 100644 --- a/nixos/tests/pgadmin4.nix +++ b/nixos/tests/pgadmin4.nix @@ -4,31 +4,49 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: name = "pgadmin4"; meta.maintainers = with lib.maintainers; [ mkg20001 gador ]; - nodes.machine = { pkgs, ... }: { + nodes = { + machine = { pkgs, ... }: { - imports = [ ./common/user-account.nix ]; + imports = [ ./common/user-account.nix ]; - environment.systemPackages = with pkgs; [ - wget - curl - pgadmin4-desktopmode - ]; + environment.systemPackages = with pkgs; [ + wget + curl + pgadmin4-desktopmode + ]; - services.postgresql = { - enable = true; - authentication = '' - host all all localhost trust - ''; + services.postgresql = { + enable = true; + authentication = '' + host all all localhost trust + ''; + }; + + services.pgadmin = { + port = 5051; + enable = true; + initialEmail = "bruh@localhost.de"; + initialPasswordFile = pkgs.writeText "pw" "bruh2012!"; + }; }; + machine2 = { pkgs, ... }: { + + imports = [ ./common/user-account.nix ]; + + services.postgresql = { + enable = true; + }; - services.pgadmin = { - port = 5051; - enable = true; - initialEmail = "bruh@localhost.de"; - initialPasswordFile = pkgs.writeText "pw" "bruh2012!"; + services.pgadmin = { + enable = true; + initialEmail = "bruh@localhost.de"; + initialPasswordFile = pkgs.writeText "pw" "bruh2012!"; + minimumPasswordLength = 12; + }; }; }; + testScript = '' with subtest("Check pgadmin module"): machine.wait_for_unit("postgresql") @@ -49,5 +67,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: machine.wait_until_succeeds("curl -sS localhost:5050") machine.wait_until_succeeds("curl -sS localhost:5050/browser/ | grep \"<title>pgAdmin 4</title>\" > /dev/null") machine.succeed("wget -nv --level=1 --spider --recursive localhost:5050/browser") + + with subtest("Check pgadmin minimum password length"): + machine2.wait_for_unit("postgresql") + machine2.wait_for_console_text("Password must be at least 12 characters long") ''; }) diff --git a/nixos/tests/prowlarr.nix b/nixos/tests/prowlarr.nix index af669afd57004..663743546459f 100644 --- a/nixos/tests/prowlarr.nix +++ b/nixos/tests/prowlarr.nix @@ -11,6 +11,8 @@ import ./make-test-python.nix ({ lib, ... }: testScript = '' machine.wait_for_unit("prowlarr.service") machine.wait_for_open_port(9696) - machine.succeed("curl --fail http://localhost:9696/") + response = machine.succeed("curl --fail http://localhost:9696/") + assert '<title>Prowlarr</title>' in response, "Login page didn't load successfully" + machine.succeed("[ -d /var/lib/prowlarr ]") ''; }) diff --git a/nixos/tests/slimserver.nix b/nixos/tests/slimserver.nix index c3f7b6fde4de0..95cbdcf4a2a15 100644 --- a/nixos/tests/slimserver.nix +++ b/nixos/tests/slimserver.nix @@ -39,8 +39,8 @@ import ./make-test-python.nix ({ pkgs, ...} : { with subtest("squeezelite player successfully connects to slimserver"): machine.wait_for_unit("squeezelite.service") - machine.wait_until_succeeds("journalctl -u squeezelite.service | grep 'slimproto:937 connected'") - player_mac = machine.wait_until_succeeds("journalctl -eu squeezelite.service | grep 'sendHELO:148 mac:'").strip().split(" ")[-1] + machine.wait_until_succeeds("journalctl -u squeezelite.service | grep -E 'slimproto:[0-9]+ connected'") + player_mac = machine.wait_until_succeeds("journalctl -eu squeezelite.service | grep -E 'sendHELO:[0-9]+ mac:'").strip().split(" ")[-1] player_id = machine.succeed(f"curl http://localhost:9000/jsonrpc.js -g -X POST -d '{json.dumps(rpc_get_player)}'") assert player_mac == json.loads(player_id)["result"]["_id"], "squeezelite player not found" ''; diff --git a/nixos/tests/snmpd.nix b/nixos/tests/snmpd.nix new file mode 100644 index 0000000000000..9248a6b390101 --- /dev/null +++ b/nixos/tests/snmpd.nix @@ -0,0 +1,23 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "snmpd"; + + nodes.snmpd = { + environment.systemPackages = with pkgs; [ + net-snmp + ]; + + services.snmpd = { + enable = true; + configText = '' + rocommunity public + ''; + }; + }; + + testScript = '' + start_all(); + machine.wait_for_unit("snmpd.service") + machine.succeed("snmpwalk -v 2c -c public localhost | grep SNMPv2-MIB::sysName.0"); + ''; + +}) diff --git a/nixos/tests/spark/default.nix b/nixos/tests/spark/default.nix index eed7db35bf4f1..034e9711bed52 100644 --- a/nixos/tests/spark/default.nix +++ b/nixos/tests/spark/default.nix @@ -10,7 +10,7 @@ let sparkCluster = testSparkCluster args; passthru.override = args': testsForPackage (args // args'); }; - testSparkCluster = { sparkPackage, ... }: pkgs.nixosTest ({ + testSparkCluster = { sparkPackage, ... }: pkgs.testers.nixosTest ({ name = "spark"; nodes = { diff --git a/nixos/tests/ssh-agent-auth.nix b/nixos/tests/ssh-agent-auth.nix index 2274e463ce95a..fee40afd61539 100644 --- a/nixos/tests/ssh-agent-auth.nix +++ b/nixos/tests/ssh-agent-auth.nix @@ -15,7 +15,11 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: foo.isNormalUser = true; }; - security.pam.enableSSHAgentAuth = true; + security.pam.sshAgentAuth = { + # Must be specified, as nixpkgs CI expects everything to eval without warning + authorizedKeysFiles = [ "/etc/ssh/authorized_keys.d/%u" ]; + enable = true; + }; security.${lib.replaceStrings [ "_" ] [ "-" ] n} = { enable = true; wheelNeedsPassword = true; # We are checking `pam_ssh_agent_auth(8)` works for a sudoer diff --git a/nixos/tests/sway.nix b/nixos/tests/sway.nix index 695d4a7708104..185c5b1b0aa90 100644 --- a/nixos/tests/sway.nix +++ b/nixos/tests/sway.nix @@ -134,7 +134,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { machine.wait_for_file("/tmp/sway-ipc.sock") # Test XWayland (foot does not support X): - swaymsg("exec WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY=invalid alacritty") + swaymsg("exec WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY= alacritty") wait_for_window("alice@machine") machine.send_chars("test-x11\n") machine.wait_for_file("/tmp/test-x11-exit-ok") diff --git a/nixos/tests/systemd-journal-gateway.nix b/nixos/tests/systemd-journal-gateway.nix new file mode 100644 index 0000000000000..1d20943f23880 --- /dev/null +++ b/nixos/tests/systemd-journal-gateway.nix @@ -0,0 +1,90 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: +{ + name = "systemd-journal-gateway"; + meta = with pkgs.lib.maintainers; { + maintainers = [ minijackson raitobezarius ]; + }; + + # Named client for coherence with the systemd-journal-upload test, and for + # certificate validation + nodes.client = { + services.journald.gateway = { + enable = true; + cert = "/run/secrets/client/cert.pem"; + key = "/run/secrets/client/key.pem"; + trust = "/run/secrets/ca.cert.pem"; + }; + }; + + testScript = '' + import json + import subprocess + import tempfile + + tmpdir_o = tempfile.TemporaryDirectory() + tmpdir = tmpdir_o.name + + def generate_pems(domain: str): + subprocess.run( + [ + "${pkgs.minica}/bin/minica", + "--ca-key=ca.key.pem", + "--ca-cert=ca.cert.pem", + f"--domains={domain}", + ], + cwd=str(tmpdir), + ) + + with subtest("Creating keys and certificates"): + generate_pems("server") + generate_pems("client") + + client.wait_for_unit("multi-user.target") + + def copy_pem(file: str): + machine.copy_from_host(source=f"{tmpdir}/{file}", target=f"/run/secrets/{file}") + machine.succeed(f"chmod 644 /run/secrets/{file}") + + with subtest("Copying keys and certificates"): + machine.succeed("mkdir -p /run/secrets/{client,server}") + copy_pem("server/cert.pem") + copy_pem("server/key.pem") + copy_pem("client/cert.pem") + copy_pem("client/key.pem") + copy_pem("ca.cert.pem") + + client.wait_for_unit("multi-user.target") + + curl = '${pkgs.curl}/bin/curl' + accept_json = '--header "Accept: application/json"' + cacert = '--cacert /run/secrets/ca.cert.pem' + cert = '--cert /run/secrets/server/cert.pem' + key = '--key /run/secrets/server/key.pem' + base_url = 'https://client:19531' + + curl_cli = f"{curl} {accept_json} {cacert} {cert} {key} --fail" + + machine_info = client.succeed(f"{curl_cli} {base_url}/machine") + assert json.loads(machine_info)["hostname"] == "client", "wrong machine name" + + # The HTTP request should have started the gateway service, triggered by + # the .socket unit + client.wait_for_unit("systemd-journal-gatewayd.service") + + identifier = "nixos-test" + message = "Hello from NixOS test infrastructure" + + client.succeed(f"systemd-cat --identifier={identifier} <<< '{message}'") + + # max-time is a workaround against a bug in systemd-journal-gatewayd where + # if TLS is enabled, the connection is never closed. Since it will timeout, + # we ignore the return code. + entries = client.succeed( + f"{curl_cli} --max-time 5 {base_url}/entries?SYSLOG_IDENTIFIER={identifier} || true" + ) + + # Number of entries should be only 1 + added_entry = json.loads(entries) + assert added_entry["SYSLOG_IDENTIFIER"] == identifier and added_entry["MESSAGE"] == message, "journal entry does not correspond" + ''; +}) diff --git a/nixos/tests/systemd-journal-upload.nix b/nixos/tests/systemd-journal-upload.nix new file mode 100644 index 0000000000000..0cbde379aee96 --- /dev/null +++ b/nixos/tests/systemd-journal-upload.nix @@ -0,0 +1,101 @@ +import ./make-test-python.nix ({ pkgs, ... }: +{ + name = "systemd-journal-upload"; + meta = with pkgs.lib.maintainers; { + maintainers = [ minijackson raitobezarius ]; + }; + + nodes.server = { nodes, ... }: { + services.journald.remote = { + enable = true; + listen = "http"; + settings.Remote = { + ServerCertificateFile = "/run/secrets/sever.cert.pem"; + ServerKeyFile = "/run/secrets/sever.key.pem"; + TrustedCertificateFile = "/run/secrets/ca.cert.pem"; + Seal = true; + }; + }; + + networking.firewall.allowedTCPPorts = [ nodes.server.services.journald.remote.port ]; + }; + + nodes.client = { lib, nodes, ... }: { + services.journald.upload = { + enable = true; + settings.Upload = { + URL = "http://server:${toString nodes.server.services.journald.remote.port}"; + ServerCertificateFile = "/run/secrets/client.cert.pem"; + ServerKeyFile = "/run/secrets/client.key.pem"; + TrustedCertificateFile = "/run/secrets/ca.cert.pem"; + }; + }; + + # Wait for the PEMs to arrive + systemd.services.systemd-journal-upload.wantedBy = lib.mkForce []; + systemd.paths.systemd-journal-upload = { + wantedBy = [ "default.target" ]; + # This file must be copied last + pathConfig.PathExists = [ "/run/secrets/ca.cert.pem" ]; + }; + }; + + testScript = '' + import subprocess + import tempfile + + tmpdir_o = tempfile.TemporaryDirectory() + tmpdir = tmpdir_o.name + + def generate_pems(domain: str): + subprocess.run( + [ + "${pkgs.minica}/bin/minica", + "--ca-key=ca.key.pem", + "--ca-cert=ca.cert.pem", + f"--domains={domain}", + ], + cwd=str(tmpdir), + ) + + with subtest("Creating keys and certificates"): + generate_pems("server") + generate_pems("client") + + server.wait_for_unit("multi-user.target") + client.wait_for_unit("multi-user.target") + + def copy_pems(machine: Machine, domain: str): + machine.succeed("mkdir /run/secrets") + machine.copy_from_host( + source=f"{tmpdir}/{domain}/cert.pem", + target=f"/run/secrets/{domain}.cert.pem", + ) + machine.copy_from_host( + source=f"{tmpdir}/{domain}/key.pem", + target=f"/run/secrets/{domain}.key.pem", + ) + # Should be last + machine.copy_from_host( + source=f"{tmpdir}/ca.cert.pem", + target="/run/secrets/ca.cert.pem", + ) + + with subtest("Copying keys and certificates"): + copy_pems(server, "server") + copy_pems(client, "client") + + client.wait_for_unit("systemd-journal-upload.service") + # The journal upload should have started the remote service, triggered by + # the .socket unit + server.wait_for_unit("systemd-journal-remote.service") + + identifier = "nixos-test" + message = "Hello from NixOS test infrastructure" + + client.succeed(f"systemd-cat --identifier={identifier} <<< '{message}'") + server.wait_until_succeeds( + f"journalctl --file /var/log/journal/remote/remote-*.journal --identifier={identifier} | grep -F '{message}'" + ) + ''; +}) diff --git a/nixos/tests/systemd-journal.nix b/nixos/tests/systemd-journal.nix index d2063a3b9a44e..ad60c0f547a41 100644 --- a/nixos/tests/systemd-journal.nix +++ b/nixos/tests/systemd-journal.nix @@ -6,17 +6,11 @@ import ./make-test-python.nix ({ pkgs, ... }: maintainers = [ lewo ]; }; - nodes.machine = { pkgs, lib, ... }: { - services.journald.enableHttpGateway = true; - }; + nodes.machine = { }; testScript = '' machine.wait_for_unit("multi-user.target") machine.succeed("journalctl --grep=systemd") - - machine.succeed( - "${pkgs.curl}/bin/curl -s localhost:19531/machine | ${pkgs.jq}/bin/jq -e '.hostname == \"machine\"'" - ) ''; }) diff --git a/nixos/tests/systemd-timesyncd-nscd-dnssec.nix b/nixos/tests/systemd-timesyncd-nscd-dnssec.nix new file mode 100644 index 0000000000000..697dd824e3453 --- /dev/null +++ b/nixos/tests/systemd-timesyncd-nscd-dnssec.nix @@ -0,0 +1,61 @@ +# This test verifies that systemd-timesyncd can resolve the NTP server hostname when DNSSEC validation +# fails even though it is enforced in the systemd-resolved settings. It is required in order to solve +# the chicken-and-egg problem when DNSSEC validation needs the correct time to work, but to set the +# correct time, we need to connect to an NTP server, which usually requires resolving its hostname. +# +# This test does the following: +# - Sets up a DNS server (tinydns) listening on the eth1 ip addess, serving .ntp and fake.ntp records. +# - Configures that DNS server as a resolver and enables DNSSEC in systemd-resolved settings. +# - Configures systemd-timesyncd to use fake.ntp hostname as an NTP server. +# - Performs a regular DNS lookup, to ensure it fails due to broken DNSSEC. +# - Waits until systemd-timesyncd resolves fake.ntp by checking its debug output. +# Here, we don't expect systemd-timesyncd to connect and synchronize time because there is no NTP +# server running. For this test to succeed, we only need to ensure that systemd-timesyncd +# resolves the IP address of the fake.ntp host. + +import ./make-test-python.nix ({ pkgs, ... }: + +let + ntpHostname = "fake.ntp"; + ntpIP = "192.0.2.1"; +in +{ + name = "systemd-timesyncd"; + nodes.machine = { pkgs, lib, config, ... }: + let + eth1IP = (lib.head config.networking.interfaces.eth1.ipv4.addresses).address; + in + { + # Setup a local DNS server for the NTP domain on the eth1 IP address + services.tinydns = { + enable = true; + ip = eth1IP; + data = '' + .ntp:${eth1IP} + +.${ntpHostname}:${ntpIP} + ''; + }; + + # Enable systemd-resolved with DNSSEC and use the local DNS as a name server + services.resolved.enable = true; + services.resolved.dnssec = "true"; + networking.nameservers = [ eth1IP ]; + + # Configure systemd-timesyncd to use our NTP hostname + services.timesyncd.enable = lib.mkForce true; + services.timesyncd.servers = [ ntpHostname ]; + services.timesyncd.extraConfig = '' + FallbackNTP=${ntpHostname} + ''; + + # The debug output is necessary to determine whether systemd-timesyncd successfully resolves our NTP hostname or not + systemd.services.systemd-timesyncd.environment.SYSTEMD_LOG_LEVEL = "debug"; + }; + + testScript = '' + machine.wait_for_unit("tinydns.service") + machine.wait_for_unit("systemd-timesyncd.service") + machine.fail("resolvectl query ${ntpHostname}") + machine.wait_until_succeeds("journalctl -u systemd-timesyncd.service --grep='Resolved address ${ntpIP}:123 for ${ntpHostname}'") + ''; +}) diff --git a/nixos/tests/systemtap.nix b/nixos/tests/systemtap.nix new file mode 100644 index 0000000000000..5cd79d66e872b --- /dev/null +++ b/nixos/tests/systemtap.nix @@ -0,0 +1,50 @@ +{ system ? builtins.currentSystem +, config ? { } +, pkgs ? import ../.. { inherit system config; } +}@args: + +with pkgs.lib; + +let + stapScript = pkgs.writeText "test.stp" '' + probe kernel.function("do_sys_poll") { + println("kernel function probe & println work") + exit() + } + ''; + + ## TODO shared infra with ../kernel-generic.nix + testsForLinuxPackages = linuxPackages: (import ./make-test-python.nix ({ pkgs, ... }: { + name = "kernel-${linuxPackages.kernel.version}"; + meta = with pkgs.lib.maintainers; { + maintainers = [ bendlas ]; + }; + + nodes.machine = { ... }: + { + boot.kernelPackages = linuxPackages; + programs.systemtap.enable = true; + }; + + testScript = + '' + with subtest("Capture stap ouput"): + output = machine.succeed("stap ${stapScript} 2>&1") + + with subtest("Ensure that expected output from stap script is there"): + assert "kernel function probe & println work\n" == output, "kernel function probe & println work\n != " + output + ''; + }) args); + + ## TODO shared infra with ../kernel-generic.nix + kernels = { + inherit (pkgs.linuxKernel.packageAliases) linux_default linux_latest; + }; + +in mapAttrs (_: lP: testsForLinuxPackages lP) kernels // { + passthru = { + inherit testsForLinuxPackages; + + testsForKernel = kernel: testsForLinuxPackages (pkgs.linuxPackagesFor kernel); + }; +} diff --git a/nixos/tests/typesense.nix b/nixos/tests/typesense.nix index 4f07a2e194be8..87ed248257ea0 100644 --- a/nixos/tests/typesense.nix +++ b/nixos/tests/typesense.nix @@ -18,6 +18,7 @@ in { testScript = '' machine.wait_for_unit("typesense.service") machine.wait_for_open_port(${toString testPort}) - assert machine.succeed("curl --fail http://localhost:${toString testPort}/health") == '{"ok":true}' + # After waiting for the port, typesense still hasn't initialized the database, so wait until we can connect successfully + assert machine.wait_until_succeeds("curl --fail http://localhost:${toString testPort}/health") == '{"ok":true}' ''; }) diff --git a/nixos/tests/upnp.nix b/nixos/tests/upnp.nix index af7cc1fe24130..5e135267403bd 100644 --- a/nixos/tests/upnp.nix +++ b/nixos/tests/upnp.nix @@ -5,7 +5,7 @@ # this succeeds an external client will try to connect to the port # mapping. -import ./make-test-python.nix ({ pkgs, ... }: +import ./make-test-python.nix ({ pkgs, useNftables, ... }: let internalRouterAddress = "192.168.3.1"; @@ -27,6 +27,7 @@ in networking.nat.enable = true; networking.nat.internalInterfaces = [ "eth2" ]; networking.nat.externalInterface = "eth1"; + networking.nftables.enable = useNftables; networking.firewall.enable = true; networking.firewall.trustedInterfaces = [ "eth2" ]; networking.interfaces.eth1.ipv4.addresses = [ @@ -82,7 +83,7 @@ in # Wait for network and miniupnpd. router.wait_for_unit("network-online.target") # $router.wait_for_unit("nat") - router.wait_for_unit("firewall.service") + router.wait_for_unit("${if useNftables then "nftables" else "firewall"}.service") router.wait_for_unit("miniupnpd") client1.wait_for_unit("network-online.target") |