diff options
Diffstat (limited to 'nixos')
33 files changed, 675 insertions, 200 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index a6a09755c14de..daabca2aee609 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -150,6 +150,8 @@ 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`. +- `services.homepage-dashboard` now takes it's configuration using native Nix expressions, rather than dumping templated configurations into `/var/lib/homepage-dashboard` where they were previously managed manually. There are now new options which allow the configuration of bookmarks, services, widgets and custom CSS/JS natively in Nix. + - The legacy and long deprecated systemd target `network-interfaces.target` has been removed. Use `network.target` instead. - `services.frp.settings` now generates the frp configuration file in TOML format as [recommended by upstream](https://github.com/fatedier/frp#configuration-files), instead of the legacy INI format. This has also introduced other changes in the configuration file structure and options. @@ -162,6 +164,10 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m release notes of [v19](https://github.com/systemd/mkosi/releases/tag/v19) and [v20](https://github.com/systemd/mkosi/releases/tag/v20) for a list of changes. +- The `services.vikunja` systemd service now uses `vikunja` as dynamic user instead of `vikunja-api`. Database users might need to be changed. + +- The `services.vikunja.setupNginx` setting has been removed. Users now need to setup the webserver configuration on their own with a proxy pass to the vikunja service. + - The `woodpecker-*` packages have been updated to v2 which includes [breaking changes](https://woodpecker-ci.org/docs/next/migrations#200). - `services.nginx` will no longer advertise HTTP/3 availability automatically. This must now be manually added, preferably to each location block. diff --git a/nixos/modules/i18n/input-method/fcitx5.nix b/nixos/modules/i18n/input-method/fcitx5.nix index 2e87705c6dc2d..ee8d2652b1c72 100644 --- a/nixos/modules/i18n/input-method/fcitx5.nix +++ b/nixos/modules/i18n/input-method/fcitx5.nix @@ -32,7 +32,8 @@ in }; plasma6Support = mkOption { type = types.bool; - default = false; + default = config.services.xserver.desktopManager.plasma6.enable; + defaultText = literalExpression "config.services.xserver.desktopManager.plasma6.enable"; description = lib.mdDoc '' Use qt6 versions of fcitx5 packages. Required for configuring fcitx5 in KDE System Settings. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 627427262da63..cfe2350d5762e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -163,6 +163,7 @@ ./programs/clash-verge.nix ./programs/cnping.nix ./programs/command-not-found/command-not-found.nix + ./programs/coolercontrol.nix ./programs/criu.nix ./programs/darling.nix ./programs/dconf.nix @@ -719,6 +720,7 @@ ./services/misc/libreddit.nix ./services/misc/lidarr.nix ./services/misc/lifecycled.nix + ./services/misc/llama-cpp.nix ./services/misc/logkeys.nix ./services/misc/mame.nix ./services/misc/mbpfan.nix diff --git a/nixos/modules/programs/coolercontrol.nix b/nixos/modules/programs/coolercontrol.nix new file mode 100644 index 0000000000000..6e7299ad16b72 --- /dev/null +++ b/nixos/modules/programs/coolercontrol.nix @@ -0,0 +1,37 @@ +{ config +, lib +, pkgs +, ... +}: + +let + cfg = config.programs.coolercontrol; +in +{ + ##### interface + options = { + programs.coolercontrol.enable = lib.mkEnableOption (lib.mdDoc "CoolerControl GUI & its background services"); + }; + + ##### implementation + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs.coolercontrol; [ + coolercontrol-gui + ]; + + systemd = { + packages = with pkgs.coolercontrol; [ + coolercontrol-liqctld + coolercontrold + ]; + + # https://github.com/NixOS/nixpkgs/issues/81138 + services = { + coolercontrol-liqctld.wantedBy = [ "multi-user.target" ]; + coolercontrold.wantedBy = [ "multi-user.target" ]; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ OPNA2608 codifryed ]; +} diff --git a/nixos/modules/programs/kdeconnect.nix b/nixos/modules/programs/kdeconnect.nix index a16fad03eefe0..8cdf1eb4e6452 100644 --- a/nixos/modules/programs/kdeconnect.nix +++ b/nixos/modules/programs/kdeconnect.nix @@ -20,7 +20,10 @@ with lib; cfg = config.programs.kdeconnect; in mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; + environment.systemPackages = [ + cfg.package + pkgs.sshfs + ]; networking.firewall = rec { allowedTCPPortRanges = [ { from = 1714; to = 1764; } ]; allowedUDPPortRanges = allowedTCPPortRanges; diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix index c7f1e622f7baf..d6e2a82af100e 100644 --- a/nixos/modules/programs/steam.nix +++ b/nixos/modules/programs/steam.nix @@ -56,6 +56,8 @@ in { # use the setuid wrapped bubblewrap bubblewrap = "${config.security.wrapperDir}/.."; }; + } // optionalAttrs cfg.extest.enable { + extraEnv.LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so"; }); description = lib.mdDoc '' The Steam package to use. Additional libraries are added from the system @@ -114,6 +116,11 @@ in { }; }; }; + + extest.enable = mkEnableOption (lib.mdDoc '' + Load the extest library into Steam, to translate X11 input events to + uinput events (e.g. for using Steam Input on Wayland) + ''); }; config = mkIf cfg.enable { diff --git a/nixos/modules/services/development/nixseparatedebuginfod.nix b/nixos/modules/services/development/nixseparatedebuginfod.nix index daf85153d339f..a2ec0d2c80e1f 100644 --- a/nixos/modules/services/development/nixseparatedebuginfod.nix +++ b/nixos/modules/services/development/nixseparatedebuginfod.nix @@ -90,7 +90,9 @@ in users.groups.nixseparatedebuginfod = { }; - nix.settings.extra-allowed-users = [ "nixseparatedebuginfod" ]; + nix.settings = lib.optionalAttrs (lib.versionAtLeast config.nix.package.version "2.4") { + extra-allowed-users = [ "nixseparatedebuginfod" ]; + }; environment.variables.DEBUGINFOD_URLS = "http://${url}"; diff --git a/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/cdi-generate.nix b/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/cdi-generate.nix index a90d234f65c0c..1aaa2d07b9bde 100644 --- a/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/cdi-generate.nix +++ b/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/cdi-generate.nix @@ -1,37 +1,58 @@ -{ config, lib, pkgs }: let +{ + addDriverRunpath, + glibc, + jq, + lib, + nvidia-container-toolkit, + nvidia-driver, + runtimeShell, + writeScriptBin, +}: +let mountOptions = { options = ["ro" "nosuid" "nodev" "bind"]; }; mounts = [ - { hostPath = "${lib.getBin config.hardware.nvidia.package}/bin/nvidia-cuda-mps-control"; + # FIXME: Making /usr mounts optional + { hostPath = lib.getExe' nvidia-driver "nvidia-cuda-mps-control"; containerPath = "/usr/bin/nvidia-cuda-mps-control"; } - { hostPath = "${lib.getBin config.hardware.nvidia.package}/bin/nvidia-cuda-mps-server"; + { hostPath = lib.getExe' nvidia-driver "nvidia-cuda-mps-server"; containerPath = "/usr/bin/nvidia-cuda-mps-server"; } - { hostPath = "${lib.getBin config.hardware.nvidia.package}/bin/nvidia-debugdump"; + { hostPath = lib.getExe' nvidia-driver "nvidia-debugdump"; containerPath = "/usr/bin/nvidia-debugdump"; } - { hostPath = "${lib.getBin config.hardware.nvidia.package}/bin/nvidia-powerd"; + { hostPath = lib.getExe' nvidia-driver "nvidia-powerd"; containerPath = "/usr/bin/nvidia-powerd"; } - { hostPath = "${lib.getBin config.hardware.nvidia.package}/bin/nvidia-smi"; + { hostPath = lib.getExe' nvidia-driver "nvidia-smi"; containerPath = "/usr/bin/nvidia-smi"; } - { hostPath = "${pkgs.nvidia-container-toolkit}/bin/nvidia-ctk"; + { hostPath = lib.getExe' nvidia-container-toolkit "nvidia-ctk"; containerPath = "/usr/bin/nvidia-ctk"; } - { hostPath = "${pkgs.glibc}/lib"; - containerPath = "${pkgs.glibc}/lib"; } - { hostPath = "${pkgs.glibc}/lib64"; - containerPath = "${pkgs.glibc}/lib64"; } + { hostPath = "${lib.getLib glibc}/lib"; + containerPath = "${lib.getLib glibc}/lib"; } + + # FIXME: use closureinfo + { + hostPath = addDriverRunpath.driverLink; + containerPath = addDriverRunpath.driverLink; + } + { hostPath = "${lib.getLib glibc}/lib"; + containerPath = "${lib.getLib glibc}/lib"; } + { hostPath = "${lib.getLib glibc}/lib64"; + containerPath = "${lib.getLib glibc}/lib64"; } ]; jqAddMountExpression = ".containerEdits.mounts[.containerEdits.mounts | length] |= . +"; mountsToJq = lib.concatMap (mount: - ["${pkgs.jq}/bin/jq '${jqAddMountExpression} ${builtins.toJSON (mount // mountOptions)}'"]) + ["${lib.getExe jq} '${jqAddMountExpression} ${builtins.toJSON (mount // mountOptions)}'"]) mounts; -in '' -#! ${pkgs.runtimeShell} +in +writeScriptBin "nvidia-cdi-generator" +'' +#! ${runtimeShell} function cdiGenerate { - ${pkgs.nvidia-container-toolkit}/bin/nvidia-ctk cdi generate \ + ${lib.getExe' nvidia-container-toolkit "nvidia-ctk"} cdi generate \ --format json \ - --ldconfig-path ${pkgs.glibc.bin}/bin/ldconfig \ - --library-search-path ${config.hardware.nvidia.package}/lib \ - --nvidia-ctk-path ${pkgs.nvidia-container-toolkit}/bin/nvidia-ctk + --ldconfig-path ${lib.getExe' glibc "ldconfig"} \ + --library-search-path ${lib.getLib nvidia-driver}/lib \ + --nvidia-ctk-path ${lib.getExe' nvidia-container-toolkit "nvidia-ctk"} } cdiGenerate | \ diff --git a/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/default.nix b/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/default.nix index 3c96e9c41be52..b95bdf191fad2 100644 --- a/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/default.nix +++ b/nixos/modules/services/hardware/nvidia-container-toolkit-cdi-generator/default.nix @@ -26,9 +26,11 @@ serviceConfig = { RuntimeDirectory = "cdi"; RemainAfterExit = true; - ExecStart = let - script = (pkgs.writeScriptBin "nvidia-cdi-generator" - (import ./cdi-generate.nix { inherit config lib pkgs; })); in (lib.getExe script); + ExecStart = + let + script = pkgs.callPackage ./cdi-generate.nix { nvidia-driver = config.hardware.nvidia.package; }; + in + lib.getExe script; Type = "oneshot"; }; }; diff --git a/nixos/modules/services/home-automation/ebusd.nix b/nixos/modules/services/home-automation/ebusd.nix index 519d116e0e55c..f68a8bdb6bfa2 100644 --- a/nixos/modules/services/home-automation/ebusd.nix +++ b/nixos/modules/services/home-automation/ebusd.nix @@ -15,12 +15,12 @@ let "--port=${toString cfg.port}" "--configpath=${cfg.configpath}" "--scanconfig=${cfg.scanconfig}" + "--log=all:${cfg.logs.all}" "--log=main:${cfg.logs.main}" "--log=network:${cfg.logs.network}" "--log=bus:${cfg.logs.bus}" "--log=update:${cfg.logs.update}" "--log=other:${cfg.logs.other}" - "--log=all:${cfg.logs.all}" ] ++ lib.optionals cfg.readonly [ "--readonly" ] ++ lib.optionals cfg.mqtt.enable [ diff --git a/nixos/modules/services/misc/etebase-server.nix b/nixos/modules/services/misc/etebase-server.nix index 045048a1a2e32..f5a5e8a780d48 100644 --- a/nixos/modules/services/misc/etebase-server.nix +++ b/nixos/modules/services/misc/etebase-server.nix @@ -5,9 +5,6 @@ with lib; let cfg = config.services.etebase-server; - pythonEnv = pkgs.python3.withPackages (ps: with ps; - [ etebase-server daphne ]); - iniFmt = pkgs.formats.ini {}; configIni = iniFmt.generate "etebase-server.ini" cfg.settings; @@ -46,6 +43,13 @@ in ''; }; + package = mkOption { + type = types.package; + default = pkgs.python3.pkgs.etebase-server; + defaultText = literalExpression "pkgs.python3.pkgs.etebase-server"; + description = lib.mdDoc "etebase-server package to use."; + }; + dataDir = mkOption { type = types.str; default = "/var/lib/etebase-server"; @@ -164,7 +168,7 @@ in (runCommand "etebase-server" { nativeBuildInputs = [ makeWrapper ]; } '' - makeWrapper ${pythonEnv}/bin/etebase-server \ + makeWrapper ${cfg.package}/bin/etebase-server \ $out/bin/etebase-server \ --chdir ${escapeShellArg cfg.dataDir} \ --prefix ETEBASE_EASY_CONFIG_PATH : "${configIni}" @@ -178,8 +182,8 @@ in systemd.services.etebase-server = { description = "An Etebase (EteSync 2.0) server"; after = [ "network.target" "systemd-tmpfiles-setup.service" ]; + path = [ cfg.package ]; wantedBy = [ "multi-user.target" ]; - path = [ pythonEnv ]; serviceConfig = { User = cfg.user; Restart = "always"; @@ -187,24 +191,26 @@ in }; environment = { ETEBASE_EASY_CONFIG_PATH = configIni; + PYTHONPATH = cfg.package.pythonPath; }; preStart = '' # Auto-migrate on first run or if the package has changed versionFile="${cfg.dataDir}/src-version" - if [[ $(cat "$versionFile" 2>/dev/null) != ${pkgs.etebase-server} ]]; then + if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then etebase-server migrate --no-input etebase-server collectstatic --no-input --clear - echo ${pkgs.etebase-server} > "$versionFile" + echo ${cfg.package} > "$versionFile" fi ''; script = let + python = cfg.package.python; networking = if cfg.unixSocket != null - then "-u ${cfg.unixSocket}" - else "-b 0.0.0.0 -p ${toString cfg.port}"; + then "--uds ${cfg.unixSocket}" + else "--host 0.0.0.0 --port ${toString cfg.port}"; in '' - cd "${pythonEnv}/lib/etebase-server"; - daphne ${networking} \ + ${python.pkgs.uvicorn}/bin/uvicorn ${networking} \ + --app-dir ${cfg.package}/${cfg.package.python.sitePackages} \ etebase_server.asgi:application ''; }; diff --git a/nixos/modules/services/misc/homepage-dashboard.nix b/nixos/modules/services/misc/homepage-dashboard.nix index 07a09e2b6bbf5..02f1378cb0d59 100644 --- a/nixos/modules/services/misc/homepage-dashboard.nix +++ b/nixos/modules/services/misc/homepage-dashboard.nix @@ -6,6 +6,8 @@ let cfg = config.services.homepage-dashboard; + # Define the settings format used for this program + settingsFormat = pkgs.formats.yaml { }; in { options = { @@ -25,31 +27,217 @@ in default = 8082; description = lib.mdDoc "Port for Homepage to bind to."; }; + + environmentFile = lib.mkOption { + type = lib.types.str; + description = '' + The path to an environment file that contains environment variables to pass + to the homepage-dashboard service, for the purpose of passing secrets to + the service. + + See the upstream documentation: + + https://gethomepage.dev/latest/installation/docker/#using-environment-secrets + ''; + default = ""; + }; + + customCSS = lib.mkOption { + type = lib.types.lines; + description = lib.mdDoc '' + Custom CSS for styling Homepage. + + See https://gethomepage.dev/latest/configs/custom-css-js/. + ''; + default = ""; + }; + + customJS = lib.mkOption { + type = lib.types.lines; + description = lib.mdDoc '' + Custom Javascript for Homepage. + + See https://gethomepage.dev/latest/configs/custom-css-js/. + ''; + default = ""; + }; + + bookmarks = lib.mkOption { + inherit (settingsFormat) type; + description = lib.mdDoc '' + Homepage bookmarks configuration. + + See https://gethomepage.dev/latest/configs/bookmarks/. + ''; + # Defaults: https://github.com/gethomepage/homepage/blob/main/src/skeleton/bookmarks.yaml + example = [ + { + Developer = [ + { Github = [{ abbr = "GH"; href = "https://github.com/"; }]; } + ]; + } + { + Entertainment = [ + { YouTube = [{ abbr = "YT"; href = "https://youtube.com/"; }]; } + ]; + } + ]; + default = [ ]; + }; + + services = lib.mkOption { + inherit (settingsFormat) type; + description = lib.mdDoc '' + Homepage services configuration. + + See https://gethomepage.dev/latest/configs/services/. + ''; + # Defaults: https://github.com/gethomepage/homepage/blob/main/src/skeleton/services.yaml + example = [ + { + "My First Group" = [ + { + "My First Service" = { + href = "http://localhost/"; + description = "Homepage is awesome"; + }; + } + ]; + } + { + "My Second Group" = [ + { + "My Second Service" = { + href = "http://localhost/"; + description = "Homepage is the best"; + }; + } + ]; + } + ]; + default = [ ]; + }; + + widgets = lib.mkOption { + inherit (settingsFormat) type; + description = lib.mdDoc '' + Homepage widgets configuration. + + See https://gethomepage.dev/latest/configs/service-widgets/. + ''; + # Defaults: https://github.com/gethomepage/homepage/blob/main/src/skeleton/widgets.yaml + example = [ + { + resources = { + cpu = true; + memory = true; + disk = "/"; + }; + } + { + search = { + provider = "duckduckgo"; + target = "_blank"; + }; + } + ]; + default = [ ]; + }; + + kubernetes = lib.mkOption { + inherit (settingsFormat) type; + description = lib.mdDoc '' + Homepage kubernetes configuration. + + See https://gethomepage.dev/latest/configs/kubernetes/. + ''; + default = { }; + }; + + docker = lib.mkOption { + inherit (settingsFormat) type; + description = lib.mdDoc '' + Homepage docker configuration. + + See https://gethomepage.dev/latest/configs/docker/. + ''; + default = { }; + }; + + settings = lib.mkOption { + inherit (settingsFormat) type; + description = lib.mdDoc '' + Homepage settings. + + See https://gethomepage.dev/latest/configs/settings/. + ''; + # Defaults: https://github.com/gethomepage/homepage/blob/main/src/skeleton/settings.yaml + default = { }; + }; }; }; - config = lib.mkIf cfg.enable { - systemd.services.homepage-dashboard = { - description = "Homepage Dashboard"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; + config = + let + # If homepage-dashboard is enabled, but none of the configuration values have been updated, + # then default to "unmanaged" configuration which is manually updated in + # var/lib/homepage-dashboard. This is to maintain backwards compatibility, and should be + # deprecated in a future release. + managedConfig = !( + cfg.bookmarks == [ ] && + cfg.customCSS == "" && + cfg.customJS == "" && + cfg.docker == { } && + cfg.kubernetes == { } && + cfg.services == [ ] && + cfg.settings == { } && + cfg.widgets == [ ] + ); + + configDir = if managedConfig then "/etc/homepage-dashboard" else "/var/lib/homepage-dashboard"; + + msg = "using unmanaged configuration for homepage-dashboard is deprecated and will be removed" + + " in 24.05. please see the NixOS documentation for `services.homepage-dashboard' and add" + + " your bookmarks, services, widgets, and other configuration using the options provided."; + in + lib.mkIf cfg.enable { + warnings = lib.optional (!managedConfig) msg; - environment = { - HOMEPAGE_CONFIG_DIR = "/var/lib/homepage-dashboard"; - PORT = "${toString cfg.listenPort}"; + environment.etc = lib.mkIf managedConfig { + "homepage-dashboard/custom.css".text = cfg.customCSS; + "homepage-dashboard/custom.js".text = cfg.customJS; + + "homepage-dashboard/bookmarks.yaml".source = settingsFormat.generate "bookmarks.yaml" cfg.bookmarks; + "homepage-dashboard/docker.yaml".source = settingsFormat.generate "docker.yaml" cfg.docker; + "homepage-dashboard/kubernetes.yaml".source = settingsFormat.generate "kubernetes.yaml" cfg.kubernetes; + "homepage-dashboard/services.yaml".source = settingsFormat.generate "services.yaml" cfg.services; + "homepage-dashboard/settings.yaml".source = settingsFormat.generate "settings.yaml" cfg.settings; + "homepage-dashboard/widgets.yaml".source = settingsFormat.generate "widgets.yaml" cfg.widgets; }; - serviceConfig = { - Type = "simple"; - DynamicUser = true; - StateDirectory = "homepage-dashboard"; - ExecStart = "${lib.getExe cfg.package}"; - Restart = "on-failure"; + systemd.services.homepage-dashboard = { + description = "Homepage Dashboard"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + HOMEPAGE_CONFIG_DIR = configDir; + PORT = toString cfg.listenPort; + LOG_TARGETS = lib.mkIf managedConfig "stdout"; + }; + + serviceConfig = { + Type = "simple"; + DynamicUser = true; + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; + StateDirectory = lib.mkIf (!managedConfig) "homepage-dashboard"; + ExecStart = lib.getExe cfg.package; + Restart = "on-failure"; + }; }; - }; - networking.firewall = lib.mkIf cfg.openFirewall { - allowedTCPPorts = [ cfg.listenPort ]; + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listenPort ]; + }; }; - }; } diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix index ab042e4b6ee2a..9314c4f3848d8 100644 --- a/nixos/modules/services/misc/paperless.nix +++ b/nixos/modules/services/misc/paperless.nix @@ -342,6 +342,7 @@ in User = cfg.user; Restart = "on-failure"; + LimitNOFILE = 65536; # gunicorn needs setuid, liblapack needs mbind SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "@setuid mbind" ]; # Needs to serve web page diff --git a/nixos/modules/services/networking/bird-lg.nix b/nixos/modules/services/networking/bird-lg.nix index be9f4101e6abe..1c59f7a6ae7c6 100644 --- a/nixos/modules/services/networking/bird-lg.nix +++ b/nixos/modules/services/networking/bird-lg.nix @@ -194,8 +194,8 @@ in allowedIPs = mkOption { type = types.listOf types.str; default = [ ]; - example = [ "192.168.25.52" "192.168.25.53" ]; - description = lib.mdDoc "List of IPs to allow (default all allowed)."; + example = [ "192.168.25.52" "192.168.25.53" "192.168.0.0/24" ]; + description = lib.mdDoc "List of IPs or networks to allow (default all allowed)."; }; birdSocket = mkOption { diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix index 470db735bf649..60d8015d0ceeb 100644 --- a/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixos/modules/services/security/vaultwarden/default.nix @@ -180,7 +180,6 @@ in { users.groups.vaultwarden = { }; systemd.services.vaultwarden = { - aliases = [ "bitwarden_rs.service" ]; after = [ "network.target" ]; path = with pkgs; [ openssl ]; serviceConfig = { @@ -202,7 +201,6 @@ in { }; systemd.services.backup-vaultwarden = mkIf (cfg.backupDir != null) { - aliases = [ "backup-bitwarden_rs.service" ]; description = "Backup vaultwarden"; environment = { DATA_FOLDER = "/var/lib/bitwarden_rs"; @@ -222,7 +220,6 @@ in { }; systemd.timers.backup-vaultwarden = mkIf (cfg.backupDir != null) { - aliases = [ "backup-bitwarden_rs.timer" ]; description = "Backup vaultwarden on time"; timerConfig = { OnCalendar = mkDefault "23:00"; @@ -240,6 +237,9 @@ in { }; }; - # uses attributes of the linked package - meta.buildDocsInSandbox = false; + meta = { + # uses attributes of the linked package + buildDocsInSandbox = false; + maintainers = with lib.maintainers; [ dotlambda SuperSandro2000 ]; + }; } diff --git a/nixos/modules/services/web-apps/miniflux.nix b/nixos/modules/services/web-apps/miniflux.nix index 1a5b7d0c24e9b..16b6fb0d655d7 100644 --- a/nixos/modules/services/web-apps/miniflux.nix +++ b/nixos/modules/services/web-apps/miniflux.nix @@ -16,10 +16,20 @@ in { options = { services.miniflux = { - enable = mkEnableOption (lib.mdDoc "miniflux and creates a local postgres database for it"); + enable = mkEnableOption (lib.mdDoc "miniflux"); package = mkPackageOption pkgs "miniflux" { }; + createDatabaseLocally = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether a PostgreSQL database should be automatically created and + configured on the local host. If set to `false`, you need provision a + database yourself and make sure to create the hstore extension in it. + ''; + }; + config = mkOption { type = with types; attrsOf (oneOf [ str int ]); example = literalExpression '' @@ -38,7 +48,7 @@ in ''; }; - adminCredentialsFile = mkOption { + adminCredentialsFile = mkOption { type = types.path; description = lib.mdDoc '' File containing the ADMIN_USERNAME and @@ -51,14 +61,14 @@ in }; config = mkIf cfg.enable { - services.miniflux.config = { + services.miniflux.config = { LISTEN_ADDR = mkDefault defaultAddress; - DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux"; + DATABASE_URL = lib.mkIf cfg.createDatabaseLocally "user=miniflux host=/run/postgresql dbname=miniflux"; RUN_MIGRATIONS = 1; CREATE_ADMIN = 1; }; - services.postgresql = { + services.postgresql = lib.mkIf cfg.createDatabaseLocally { enable = true; ensureUsers = [ { name = "miniflux"; @@ -67,7 +77,7 @@ in ensureDatabases = [ "miniflux" ]; }; - systemd.services.miniflux-dbsetup = { + systemd.services.miniflux-dbsetup = lib.mkIf cfg.createDatabaseLocally { description = "Miniflux database setup"; requires = [ "postgresql.service" ]; after = [ "network.target" "postgresql.service" ]; @@ -81,8 +91,9 @@ in systemd.services.miniflux = { description = "Miniflux service"; wantedBy = [ "multi-user.target" ]; - requires = [ "miniflux-dbsetup.service" ]; - after = [ "network.target" "postgresql.service" "miniflux-dbsetup.service" ]; + requires = lib.optional cfg.createDatabaseLocally "miniflux-dbsetup.service"; + after = [ "network.target" ] + ++ lib.optionals cfg.createDatabaseLocally [ "postgresql.service" "miniflux-dbsetup.service" ]; serviceConfig = { ExecStart = "${cfg.package}/bin/miniflux"; @@ -129,6 +140,7 @@ in include "${pkgs.apparmorRulesFromClosure { name = "miniflux"; } cfg.package}" r ${cfg.package}/bin/miniflux, r @{sys}/kernel/mm/transparent_hugepage/hpage_pmd_size, + rw /run/miniflux/**, } ''; }; diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 08f90dcf59d80..5cda4a00a9de5 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -45,7 +45,7 @@ let }; }; - webroot = pkgs.runCommand + webroot = pkgs.runCommandLocal "${cfg.package.name or "nextcloud"}-with-apps" { } '' diff --git a/nixos/modules/services/web-apps/photoprism.nix b/nixos/modules/services/web-apps/photoprism.nix index ccf995fccf3e5..39eb7c65c6359 100644 --- a/nixos/modules/services/web-apps/photoprism.nix +++ b/nixos/modules/services/web-apps/photoprism.nix @@ -104,6 +104,7 @@ in StateDirectory = "photoprism"; WorkingDirectory = "/var/lib/photoprism"; RuntimeDirectory = "photoprism"; + ReadWritePaths = [ cfg.originalsPath cfg.importPath cfg.storagePath ]; LoadCredential = lib.optionalString (cfg.passwordFile != null) "PHOTOPRISM_ADMIN_PASSWORD:${cfg.passwordFile}"; diff --git a/nixos/modules/services/web-apps/vikunja.nix b/nixos/modules/services/web-apps/vikunja.nix index b893f2c1f33c7..efa9c676d9a5d 100644 --- a/nixos/modules/services/web-apps/vikunja.nix +++ b/nixos/modules/services/web-apps/vikunja.nix @@ -9,10 +9,13 @@ let useMysql = cfg.database.type == "mysql"; usePostgresql = cfg.database.type == "postgres"; in { + imports = [ + (mkRemovedOptionModule [ "services" "vikunja" "setupNginx" ] "services.vikunja no longer supports the automatic set up of a nginx virtual host. Set up your own webserver config with a proxy pass to the vikunja service.") + ]; + options.services.vikunja = with lib; { enable = mkEnableOption (lib.mdDoc "vikunja service"); - package-api = mkPackageOption pkgs "vikunja-api" { }; - package-frontend = mkPackageOption pkgs "vikunja-frontend" { }; + package = mkPackageOption pkgs "vikunja" { }; environmentFiles = mkOption { type = types.listOf types.path; default = [ ]; @@ -21,25 +24,10 @@ in { For example passwords should be set in one of these files. ''; }; - setupNginx = mkOption { - type = types.bool; - default = config.services.nginx.enable; - defaultText = literalExpression "config.services.nginx.enable"; - description = lib.mdDoc '' - Whether to setup NGINX. - Further nginx configuration can be done by changing - {option}`services.nginx.virtualHosts.<frontendHostname>`. - This does not enable TLS or ACME by default. To enable this, set the - {option}`services.nginx.virtualHosts.<frontendHostname>.enableACME` to - `true` and if appropriate do the same for - {option}`services.nginx.virtualHosts.<frontendHostname>.forceSSL`. - ''; - }; frontendScheme = mkOption { type = types.enum [ "http" "https" ]; description = lib.mdDoc '' Whether the site is available via http or https. - This does not configure https or ACME in nginx! ''; }; frontendHostname = mkOption { @@ -104,42 +92,27 @@ in { }; }; - systemd.services.vikunja-api = { - description = "vikunja-api"; + systemd.services.vikunja = { + description = "vikunja"; after = [ "network.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service"; wantedBy = [ "multi-user.target" ]; - path = [ cfg.package-api ]; + path = [ cfg.package ]; restartTriggers = [ configFile ]; serviceConfig = { Type = "simple"; DynamicUser = true; StateDirectory = "vikunja"; - ExecStart = "${cfg.package-api}/bin/vikunja"; + ExecStart = "${cfg.package}/bin/vikunja"; Restart = "always"; EnvironmentFile = cfg.environmentFiles; }; }; - services.nginx.virtualHosts."${cfg.frontendHostname}" = mkIf cfg.setupNginx { - locations = { - "/" = { - root = cfg.package-frontend; - tryFiles = "try_files $uri $uri/ /"; - }; - "~* ^/(api|dav|\\.well-known)/" = { - proxyPass = "http://localhost:${toString cfg.port}"; - extraConfig = '' - client_max_body_size 20M; - ''; - }; - }; - }; - environment.etc."vikunja/config.yaml".source = configFile; environment.systemPackages = [ - cfg.package-api # for admin `vikunja` CLI + cfg.package # for admin `vikunja` CLI ]; }; } diff --git a/nixos/modules/services/x11/desktop-managers/plasma6.nix b/nixos/modules/services/x11/desktop-managers/plasma6.nix index 8d84c5a7431b0..1237261e0af7b 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma6.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma6.nix @@ -10,6 +10,11 @@ inherit (pkgs) kdePackages; inherit (lib) literalExpression mkDefault mkIf mkOption mkPackageOptionMD types; + + activationScript = '' + # will be rebuilt automatically + rm -fv $HOME/.cache/ksycoca* + ''; in { options = { services.xserver.desktopManager.plasma6 = { @@ -273,5 +278,14 @@ in { }; programs.kdeconnect.package = kdePackages.kdeconnect-kde; + + # FIXME: ugly hack. See #292632 for details. + system.userActivationScripts.rebuildSycoca = activationScript; + systemd.user.services.nixos-rebuild-sycoca = { + description = "Rebuild KDE system configuration cache"; + wantedBy = [ "graphical-session-pre.target" ]; + serviceConfig.Type = "oneshot"; + script = activationScript; + }; }; } diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index 258cf622a894a..03bff1dee5b9d 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -43,6 +43,7 @@ class BootSpec: system: str toplevel: str specialisations: Dict[str, "BootSpec"] + sortKey: str initrdSecrets: str | None = None @@ -73,6 +74,7 @@ def system_dir(profile: str | None, generation: int, specialisation: str | None) return d BOOT_ENTRY = """title {title} +sort-key {sort_key} version Generation {generation} {description} linux {kernel} initrd {initrd} @@ -123,7 +125,13 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec: def bootspec_from_json(bootspec_json: Dict) -> BootSpec: specialisations = bootspec_json['org.nixos.specialisation.v1'] specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()} - return BootSpec(**bootspec_json['org.nixos.bootspec.v1'], specialisations=specialisations) + systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {}) + sortKey = systemdBootExtension.get('sortKey', 'nixos') + return BootSpec( + **bootspec_json['org.nixos.bootspec.v1'], + specialisations=specialisations, + sortKey=sortKey + ) def copy_from_file(file: str, dry_run: bool = False) -> str: @@ -170,6 +178,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None with open(tmp_path, 'w') as f: f.write(BOOT_ENTRY.format(title=title, + sort_key=bootspec.sortKey, generation=generation, kernel=kernel, initrd=initrd, diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index 645b764760dad..ba07506266e26 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix @@ -87,6 +87,16 @@ in { imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) + (lib.mkChangedOptionModule + [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] + [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] + (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.memtest86.entryFilename) + ) + (lib.mkChangedOptionModule + [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] + [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] + (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.netbootxyz.entryFilename) + ) ]; options.boot.loader.systemd-boot = { @@ -102,6 +112,35 @@ in { ''; }; + sortKey = mkOption { + default = "nixos"; + type = lib.types.str; + description = '' + The sort key used for the NixOS bootloader entries. + This key determines sorting relative to non-NixOS entries. + See also https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting + + This option can also be used to control the sorting of NixOS specialisations. + + By default, specialisations inherit the sort key of their parent generation + and will have the same value for both the sort-key and the version (i.e. the generation number), + systemd-boot will therefore sort them based on their file name, meaning that + in your boot menu you will have each main generation directly followed by + its specialisations sorted alphabetically by their names. + + If you want a different ordering for a specialisation, you can override + its sort-key which will cause the specialisation to be uncoupled from its + parent generation. It will then be sorted by its new sort-key just like + any other boot entry. + + The sort-key is stored in the generation's bootspec, which means that + generations keep their sort-keys even if the original definition of the + generation was removed from the NixOS configuration. + It also means that updating the sort-key will only affect new generations, + while old ones will keep the sort-key that they were originally built with. + ''; + }; + editor = mkOption { default = true; @@ -184,13 +223,15 @@ in { ''; }; - entryFilename = mkOption { - default = "memtest86.conf"; + sortKey = mkOption { + default = "o_memtest86"; type = types.str; description = lib.mdDoc '' - `systemd-boot` orders the menu entries by the config file names, + `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. + + See also {option}`boot.loader.systemd-boot.sortKey`. ''; }; }; @@ -207,13 +248,15 @@ in { ''; }; - entryFilename = mkOption { - default = "o_netbootxyz.conf"; + sortKey = mkOption { + default = "o_netbootxyz"; type = types.str; description = lib.mdDoc '' - `systemd-boot` orders the menu entries by the config file names, + `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. + + See also {option}`boot.loader.systemd-boot.sortKey`. ''; }; }; @@ -225,6 +268,7 @@ in { { "memtest86.conf" = ''' title Memtest86+ efi /efi/memtest86/memtest.efi + sort-key z_memtest '''; } ''; description = lib.mdDoc '' @@ -233,9 +277,10 @@ in { Each attribute name denotes the destination file name, and the corresponding attribute value is the contents of the entry. - `systemd-boot` orders the menu entries by the config file names, - so if you want something to appear after all the NixOS entries, - it should start with {file}`o` or onwards. + To control the ordering of the entry in the boot menu, use the sort-key + field, see + https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting + and {option}`boot.loader.systemd-boot.sortKey`. ''; }; @@ -328,19 +373,25 @@ in { boot.loader.systemd-boot.extraEntries = mkMerge [ (mkIf cfg.memtest86.enable { - "${cfg.memtest86.entryFilename}" = '' + "memtest86.conf" = '' title Memtest86+ efi /efi/memtest86/memtest.efi + sort-key ${cfg.memtest86.sortKey} ''; }) (mkIf cfg.netbootxyz.enable { - "${cfg.netbootxyz.entryFilename}" = '' + "netbootxyz.conf" = '' title netboot.xyz efi /efi/netbootxyz/netboot.xyz.efi + sort-key ${cfg.netbootxyz.sortKey} ''; }) ]; + boot.bootspec.extensions."org.nixos.systemd-boot" = { + inherit (config.boot.loader.systemd-boot) sortKey; + }; + system = { build.installBootLoader = finalSystemdBootBuilder; diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index e72a1e37759e9..191b462711948 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -105,7 +105,7 @@ let type = types.bool; description = lib.mdDoc '' If the device does not currently contain a filesystem (as - determined by {command}`blkid`, then automatically + determined by {command}`blkid`), then automatically format it with the filesystem type specified in {option}`fsType`. Use with caution. ''; diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix index 3bbe0ba458516..a561c5682ae58 100644 --- a/nixos/modules/virtualisation/incus.nix +++ b/nixos/modules/virtualisation/incus.nix @@ -107,6 +107,13 @@ in }; config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !(config.networking.firewall.enable && !config.networking.nftables.enable && config.virtualisation.incus.enable); + message = "Incus on NixOS is unsupported using iptables. Set `networking.nftables.enable = true;`"; + } + ]; + # https://github.com/lxc/incus/blob/f145309929f849b9951658ad2ba3b8f10cbe69d1/doc/reference/server_settings.md boot.kernel.sysctl = { "fs.aio-max-nr" = lib.mkDefault 524288; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 75ba6dacc122c..b5a8b08eee70d 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -877,11 +877,9 @@ in type = types.package; default = (pkgs.OVMF.override { secureBoot = cfg.useSecureBoot; - systemManagementModeRequired = cfg.useSecureBoot; }).fd; defaultText = ''(pkgs.OVMF.override { secureBoot = cfg.useSecureBoot; - systemManagementModeRequired = cfg.useSecureBoot; }).fd''; description = lib.mdDoc "OVMF firmware package, defaults to OVMF configured with secure boot if needed."; @@ -1185,7 +1183,7 @@ in "-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm" "-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0" ]) - (mkIf (cfg.efi.OVMF.systemManagementModeRequired or false) [ + (mkIf (pkgs.stdenv.hostPlatform.isx86 && cfg.efi.OVMF.systemManagementModeRequired) [ "-machine" "q35,smm=on" "-global" "driver=cfi.pflash01,property=secure,value=on" ]) diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix index 50a8f8189590e..0ecf7f490cf6f 100644 --- a/nixos/modules/virtualisation/virtualbox-host.nix +++ b/nixos/modules/virtualisation/virtualbox-host.nix @@ -6,7 +6,7 @@ let cfg = config.virtualisation.virtualbox.host; virtualbox = cfg.package.override { - inherit (cfg) enableHardening headless enableWebService; + inherit (cfg) enableHardening headless enableWebService enableKvm; extensionPack = if cfg.enableExtensionPack then pkgs.virtualboxExtpack else null; }; @@ -81,13 +81,24 @@ in Build VirtualBox web service tool (vboxwebsrv) to allow managing VMs via other webpage frontend tools. Useful for headless servers. ''; }; + + enableKvm = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable KVM support for VirtualBox. This increases compatibility with Linux kernel versions, because the VirtualBox kernel modules + are not required. + + This option is incompatible with `enableHardening` and `addNetworkInterface`. + + Note: This is experimental. Please check https://github.com/cyberus-technology/virtualbox-kvm/issues. + ''; + }; }; config = mkIf cfg.enable (mkMerge [{ warnings = mkIf (pkgs.config.virtualbox.enableExtensionPack or false) ["'nixpkgs.virtualbox.enableExtensionPack' has no effect, please use 'virtualisation.virtualbox.host.enableExtensionPack'"]; - boot.kernelModules = [ "vboxdrv" "vboxnetadp" "vboxnetflt" ]; - boot.extraModulePackages = [ kernelModules ]; environment.systemPackages = [ virtualbox ]; security.wrappers = let @@ -114,17 +125,43 @@ in services.udev.extraRules = '' - KERNEL=="vboxdrv", OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd" - KERNEL=="vboxdrvu", OWNER="root", GROUP="root", MODE="0666", TAG+="systemd" - KERNEL=="vboxnetctl", OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd" SUBSYSTEM=="usb_device", ACTION=="add", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass}" SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass}" SUBSYSTEM=="usb_device", ACTION=="remove", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor" SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor" ''; + } (mkIf cfg.enableKvm { + assertions = [ + { + assertion = !cfg.addNetworkInterface; + message = "VirtualBox KVM only supports standard NAT networking for VMs. Please turn off virtualisation.virtualbox.host.addNetworkInferface."; + } + + { + assertion = !cfg.enableHardening; + message = "VirtualBox KVM is not compatible with hardening: Please turn off virtualisation.virtualbox.host.enableHardening."; + } + ]; + + warnings = [ + '' + KVM support in VirtualBox is experimental. Not all security features are available yet. + See: https://github.com/cyberus-technology/virtualbox-kvm/issues/12 + '' + ]; + }) (mkIf (!cfg.enableKvm) { + boot.kernelModules = [ "vboxdrv" "vboxnetadp" "vboxnetflt" ]; + boot.extraModulePackages = [ kernelModules ]; + + services.udev.extraRules = + '' + KERNEL=="vboxdrv", OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd" + KERNEL=="vboxdrvu", OWNER="root", GROUP="root", MODE="0666", TAG+="systemd" + KERNEL=="vboxnetctl", OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd" + ''; # Since we lack the right setuid/setcap binaries, set up a host-only network by default. - } (mkIf cfg.addNetworkInterface { + }) (mkIf cfg.addNetworkInterface { systemd.services.vboxnet0 = { description = "VirtualBox vboxnet0 Interface"; requires = [ "dev-vboxnetctl.device" ]; diff --git a/nixos/tests/akkoma.nix b/nixos/tests/akkoma.nix index 287e2d485999e..2907017ee3d54 100644 --- a/nixos/tests/akkoma.nix +++ b/nixos/tests/akkoma.nix @@ -31,16 +31,12 @@ let export REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" - echo '${userPassword}' | ${pkgs.toot}/bin/toot login_cli -i "akkoma.nixos.test" -e "jamy@nixos.test" - echo "y" | ${pkgs.toot}/bin/toot post "hello world Jamy here" - - # Retrieving timeline with toot currently broken due to incompatible timestamp format - # cf. <https://akkoma.dev/AkkomaGang/akkoma/issues/637> and <https://github.com/ihabunek/toot/issues/399> - #echo "y" | ${pkgs.toot}/bin/toot timeline | grep -F -q "hello world Jamy here" + ${pkgs.toot}/bin/toot login_cli -i "akkoma.nixos.test" -e "jamy@nixos.test" -p '${userPassword}' + ${pkgs.toot}/bin/toot post "hello world Jamy here" + ${pkgs.toot}/bin/toot timeline -1 | grep -F -q "hello world Jamy here" # Test file upload - echo "y" | ${pkgs.toot}/bin/toot upload <(dd if=/dev/zero bs=1024 count=1024 status=none) \ - | grep -F -q "https://akkoma.nixos.test/media" + ${pkgs.toot}/bin/toot upload <(dd if=/dev/zero bs=1024 count=1024 status=none) ''; checkFe = pkgs.writers.writeBashBin "checkFe" '' diff --git a/nixos/tests/homepage-dashboard.nix b/nixos/tests/homepage-dashboard.nix index 56e077f5ff6de..dd36473e8ac02 100644 --- a/nixos/tests/homepage-dashboard.nix +++ b/nixos/tests/homepage-dashboard.nix @@ -2,13 +2,35 @@ import ./make-test-python.nix ({ lib, ... }: { name = "homepage-dashboard"; meta.maintainers = with lib.maintainers; [ jnsgruk ]; - nodes.machine = { pkgs, ... }: { + nodes.unmanaged_conf = { pkgs, ... }: { services.homepage-dashboard.enable = true; }; + nodes.managed_conf = { pkgs, ... }: { + services.homepage-dashboard = { + enable = true; + settings.title = "custom"; + }; + }; + testScript = '' - machine.wait_for_unit("homepage-dashboard.service") - machine.wait_for_open_port(8082) - machine.succeed("curl --fail http://localhost:8082/") + # Ensure the services are started on unmanaged machine + unmanaged_conf.wait_for_unit("homepage-dashboard.service") + unmanaged_conf.wait_for_open_port(8082) + unmanaged_conf.succeed("curl --fail http://localhost:8082/") + + # Ensure that /etc/homepage-dashboard doesn't exist, and boilerplate + # configs are copied into place. + unmanaged_conf.fail("test -d /etc/homepage-dashboard") + unmanaged_conf.succeed("test -f /var/lib/private/homepage-dashboard/settings.yaml") + + # Ensure the services are started on managed machine + managed_conf.wait_for_unit("homepage-dashboard.service") + managed_conf.wait_for_open_port(8082) + managed_conf.succeed("curl --fail http://localhost:8082/") + + # Ensure /etc/homepage-dashboard is created and unmanaged conf location isn't. + managed_conf.succeed("test -d /etc/homepage-dashboard") + managed_conf.fail("test -f /var/lib/private/homepage-dashboard/settings.yaml") ''; }) diff --git a/nixos/tests/miniflux.nix b/nixos/tests/miniflux.nix index a3af53db0e7a1..6d38224448ed6 100644 --- a/nixos/tests/miniflux.nix +++ b/nixos/tests/miniflux.nix @@ -15,6 +15,10 @@ let ADMIN_USERNAME=${username} ADMIN_PASSWORD=${password} ''; + postgresPassword = "correcthorsebatterystaple"; + postgresPasswordFile = pkgs.writeText "pgpass" '' + *:*:*:*:${postgresPassword} + ''; in { @@ -56,32 +60,62 @@ in adminCredentialsFile = customAdminCredentialsFile; }; }; + + postgresTcp = { config, pkgs, lib, ... }: { + services.postgresql = { + enable = true; + initialScript = pkgs.writeText "init-postgres" '' + CREATE USER miniflux WITH PASSWORD '${postgresPassword}'; + CREATE DATABASE miniflux WITH OWNER miniflux; + ''; + enableTCPIP = true; + authentication = '' + host sameuser miniflux samenet scram-sha-256 + ''; + }; + systemd.services.postgresql.postStart = lib.mkAfter '' + $PSQL -tAd miniflux -c 'CREATE EXTENSION hstore;' + ''; + networking.firewall.allowedTCPPorts = [ config.services.postgresql.port ]; + }; + externalDb = { ... }: { + security.apparmor.enable = true; + services.miniflux = { + enable = true; + createDatabaseLocally = false; + inherit adminCredentialsFile; + config = { + DATABASE_URL = "user=miniflux host=postgresTcp dbname=miniflux sslmode=disable"; + PGPASSFILE = "/run/miniflux/pgpass"; + }; + }; + systemd.services.miniflux.preStart = '' + cp ${postgresPasswordFile} /run/miniflux/pgpass + chmod 600 /run/miniflux/pgpass + ''; + }; }; testScript = '' - start_all() + def runTest(machine, port, user): + machine.wait_for_unit("miniflux.service") + machine.wait_for_open_port(port) + machine.succeed(f"curl --fail 'http://localhost:{port}/healthcheck' | grep OK") + machine.succeed( + f"curl 'http://localhost:{port}/v1/me' -u '{user}' -H Content-Type:application/json | grep '\"is_admin\":true'" + ) + machine.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""') - default.wait_for_unit("miniflux.service") - default.wait_for_open_port(${toString defaultPort}) - default.succeed("curl --fail 'http://localhost:${toString defaultPort}/healthcheck' | grep OK") - default.succeed( - "curl 'http://localhost:${toString defaultPort}/v1/me' -u '${defaultUsername}:${defaultPassword}' -H Content-Type:application/json | grep '\"is_admin\":true'" - ) - default.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""') + default.start() + withoutSudo.start() + customized.start() + postgresTcp.start() - withoutSudo.wait_for_unit("miniflux.service") - withoutSudo.wait_for_open_port(${toString defaultPort}) - withoutSudo.succeed("curl --fail 'http://localhost:${toString defaultPort}/healthcheck' | grep OK") - withoutSudo.succeed( - "curl 'http://localhost:${toString defaultPort}/v1/me' -u '${defaultUsername}:${defaultPassword}' -H Content-Type:application/json | grep '\"is_admin\":true'" - ) - withoutSudo.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""') + runTest(default, ${toString defaultPort}, "${defaultUsername}:${defaultPassword}") + runTest(withoutSudo, ${toString defaultPort}, "${defaultUsername}:${defaultPassword}") + runTest(customized, ${toString port}, "${username}:${password}") - customized.wait_for_unit("miniflux.service") - customized.wait_for_open_port(${toString port}) - customized.succeed("curl --fail 'http://localhost:${toString port}/healthcheck' | grep OK") - customized.succeed( - "curl 'http://localhost:${toString port}/v1/me' -u '${username}:${password}' -H Content-Type:application/json | grep '\"is_admin\":true'" - ) - customized.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""') + postgresTcp.wait_for_unit("postgresql.service") + externalDb.start() + runTest(externalDb, ${toString defaultPort}, "${defaultUsername}:${defaultPassword}") ''; }) diff --git a/nixos/tests/minio.nix b/nixos/tests/minio.nix index ece4864f771c0..67eb0cd884400 100644 --- a/nixos/tests/minio.nix +++ b/nixos/tests/minio.nix @@ -43,17 +43,17 @@ import ./make-test-python.nix ({ pkgs, ... }: # Minio requires at least 1GiB of free disk space to run. virtualisation.diskSize = 4 * 1024; + + # Minio pre allocates 2GiB or memory, reserve some more + virtualisation.memorySize = 4096; }; }; testScript = '' - import time start_all() # simulate manually editing root credentials file machine.wait_for_unit("multi-user.target") - machine.copy_from_host("${credsPartial}", "${rootCredentialsFile}") - time.sleep(3) machine.copy_from_host("${credsFull}", "${rootCredentialsFile}") machine.wait_for_unit("minio.service") diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index 1b7e83253e59e..54c380602bd40 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -93,6 +93,7 @@ in machine.wait_for_unit("multi-user.target") machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") + machine.succeed("grep 'sort-key nixos' /boot/loader/entries/nixos-generation-1.conf") # Ensure we actually booted using systemd-boot # Magic number is the vendor UUID used by systemd-boot. @@ -115,15 +116,17 @@ in virtualisation.useSecureBoot = true; }; - testScript = '' + testScript = let + efiArch = pkgs.stdenv.hostPlatform.efiArch; + in { nodes, ... }: '' machine.start(allow_reboot=True) machine.wait_for_unit("multi-user.target") machine.succeed("sbctl create-keys") machine.succeed("sbctl enroll-keys --yes-this-might-brick-my-machine") - machine.succeed('sbctl sign /boot/EFI/systemd/systemd-bootx64.efi') - machine.succeed('sbctl sign /boot/EFI/BOOT/BOOTX64.EFI') - machine.succeed('sbctl sign /boot/EFI/nixos/*bzImage.efi') + machine.succeed('sbctl sign /boot/EFI/systemd/systemd-boot${efiArch}.efi') + machine.succeed('sbctl sign /boot/EFI/BOOT/BOOT${toUpper efiArch}.EFI') + machine.succeed('sbctl sign /boot/EFI/nixos/*${nodes.machine.system.boot.loader.kernelFile}.efi') machine.reboot() @@ -164,7 +167,9 @@ in nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; - specialisation.something.configuration = {}; + specialisation.something.configuration = { + boot.loader.systemd-boot.sortKey = "something"; + }; }; testScript = '' @@ -177,6 +182,9 @@ in machine.succeed( "grep -q 'title NixOS (something)' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) + machine.succeed( + "grep 'sort-key something' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" + ) ''; }; @@ -254,25 +262,25 @@ in }; testScript = '' - machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf") + machine.succeed("test -e /boot/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi") ''; }; - entryFilename = makeTest { - name = "systemd-boot-entry-filename"; + memtestSortKey = makeTest { + name = "systemd-boot-memtest-sortkey"; meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; - boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; + boot.loader.systemd-boot.memtest86.sortKey = "apple"; }; testScript = '' - machine.fail("test -e /boot/loader/entries/memtest86.conf") - machine.succeed("test -e /boot/loader/entries/apple.conf") + machine.succeed("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/efi/memtest86/memtest.efi") + machine.succeed("grep 'sort-key apple' /boot/loader/entries/memtest86.conf") ''; }; @@ -283,7 +291,6 @@ in nodes.machine = { pkgs, lib, ... }: { imports = [ commonXbootldr ]; boot.loader.systemd-boot.memtest86.enable = true; - boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; }; testScript = { nodes, ... }: '' @@ -293,8 +300,7 @@ in machine.wait_for_unit("multi-user.target") machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi") - machine.fail("test -e /boot/loader/entries/memtest86.conf") - machine.succeed("test -e /boot/loader/entries/apple.conf") + machine.succeed("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/EFI/memtest86/memtest.efi") ''; }; @@ -386,9 +392,9 @@ in machine.succeed("${finalSystem}/bin/switch-to-configuration boot") machine.fail("test -e /boot/efi/fruits/tomato.efi") machine.fail("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi") - machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf") + machine.succeed("test -e /boot/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi") - machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/o_netbootxyz.conf") + machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/netbootxyz/netboot.xyz.efi") ''; }; diff --git a/nixos/tests/vikunja.nix b/nixos/tests/vikunja.nix index 60fd5ce13854e..4e2bf166a7b6c 100644 --- a/nixos/tests/vikunja.nix +++ b/nixos/tests/vikunja.nix @@ -13,15 +13,20 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { frontendScheme = "http"; frontendHostname = "localhost"; }; - services.nginx.enable = true; + services.nginx = { + enable = true; + virtualHosts."http://localhost" = { + locations."/".proxyPass = "http://localhost:3456"; + }; + }; }; vikunjaPostgresql = { pkgs, ... }: { services.vikunja = { enable = true; database = { type = "postgres"; - user = "vikunja-api"; - database = "vikunja-api"; + user = "vikunja"; + database = "vikunja"; host = "/run/postgresql"; }; frontendScheme = "http"; @@ -30,20 +35,25 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { }; services.postgresql = { enable = true; - ensureDatabases = [ "vikunja-api" ]; + ensureDatabases = [ "vikunja" ]; ensureUsers = [ - { name = "vikunja-api"; + { name = "vikunja"; ensureDBOwnership = true; } ]; }; - services.nginx.enable = true; + services.nginx = { + enable = true; + virtualHosts."http://localhost" = { + locations."/".proxyPass = "http://localhost:9090"; + }; + }; }; }; testScript = '' - vikunjaSqlite.wait_for_unit("vikunja-api.service") + vikunjaSqlite.wait_for_unit("vikunja.service") vikunjaSqlite.wait_for_open_port(3456) vikunjaSqlite.succeed("curl --fail http://localhost:3456/api/v1/info") @@ -52,7 +62,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { vikunjaSqlite.succeed("curl --fail http://localhost/api/v1/info") vikunjaSqlite.succeed("curl --fail http://localhost") - vikunjaPostgresql.wait_for_unit("vikunja-api.service") + vikunjaPostgresql.wait_for_unit("vikunja.service") vikunjaPostgresql.wait_for_open_port(9090) vikunjaPostgresql.succeed("curl --fail http://localhost:9090/api/v1/info") diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index e522d0679e151..3c2a391233dbd 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -3,6 +3,7 @@ pkgs ? import ../.. { inherit system config; }, debug ? false, enableUnfree ? false, + enableKvm ? false, use64bitGuest ? true }: @@ -340,7 +341,7 @@ let testExtensionPack.vmFlags = enableExtensionPackVMFlags; }; - mkVBoxTest = useExtensionPack: vms: name: testScript: makeTest { + mkVBoxTest = vboxHostConfig: vms: name: testScript: makeTest { name = "virtualbox-${name}"; nodes.machine = { lib, config, ... }: { @@ -349,14 +350,23 @@ let vmConfigs = mapAttrsToList mkVMConf vms; in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs; virtualisation.memorySize = 2048; - virtualisation.qemu.options = ["-cpu" "kvm64,svm=on,vmx=on"]; - virtualisation.virtualbox.host.enable = true; + + virtualisation.qemu.options = let + # IvyBridge is reasonably ancient to be compatible with recent + # Intel/AMD hosts and sufficient for the KVM flavor. + guestCpu = if config.virtualisation.virtualbox.host.enableKvm then "IvyBridge" else "kvm64"; + in ["-cpu" "${guestCpu},svm=on,vmx=on"]; + test-support.displayManager.auto.user = "alice"; users.users.alice.extraGroups = let inherit (config.virtualisation.virtualbox.host) enableHardening; - in lib.mkIf enableHardening (lib.singleton "vboxusers"); - virtualisation.virtualbox.host.enableExtensionPack = useExtensionPack; - nixpkgs.config.allowUnfree = useExtensionPack; + in lib.mkIf enableHardening [ "vboxusers" ]; + + virtualisation.virtualbox.host = { + enable = true; + } // vboxHostConfig; + + nixpkgs.config.allowUnfree = config.virtualisation.virtualbox.host.enableExtensionPack; }; testScript = '' @@ -390,7 +400,7 @@ let }; }; - unfreeTests = mapAttrs (mkVBoxTest true vboxVMsWithExtpack) { + unfreeTests = mapAttrs (mkVBoxTest { enableExtensionPack = true; } vboxVMsWithExtpack) { enable-extension-pack = '' create_vm_testExtensionPack() vbm("startvm testExtensionPack") @@ -409,7 +419,24 @@ let ''; }; -in mapAttrs (mkVBoxTest false vboxVMs) { + kvmTests = mapAttrs (mkVBoxTest { + enableKvm = true; + + # Once the KVM version supports these, we can enable them. + addNetworkInterface = false; + enableHardening = false; + } vboxVMs) { + kvm-headless = '' + create_vm_headless() + machine.succeed(ru("VBoxHeadless --startvm headless >&2 & disown %1")) + wait_for_startup_headless() + wait_for_vm_boot_headless() + shutdown_vm_headless() + destroy_vm_headless() + ''; + }; + +in mapAttrs (mkVBoxTest {} vboxVMs) { simple-gui = '' # Home to select Tools, down to move to the VM, enter to start it. def send_vm_startup(): @@ -519,4 +546,6 @@ in mapAttrs (mkVBoxTest false vboxVMs) { destroy_vm_test1() destroy_vm_test2() ''; -} // (optionalAttrs enableUnfree unfreeTests) +} +// (optionalAttrs enableKvm kvmTests) +// (optionalAttrs enableUnfree unfreeTests) |