diff options
Diffstat (limited to 'nixos/modules/services')
32 files changed, 1199 insertions, 239 deletions
diff --git a/nixos/modules/services/backup/syncoid.nix b/nixos/modules/services/backup/syncoid.nix index 7b8d3b431309..4a04f0aa1622 100644 --- a/nixos/modules/services/backup/syncoid.nix +++ b/nixos/modules/services/backup/syncoid.nix @@ -134,7 +134,7 @@ in localSourceAllow = mkOption { type = types.listOf types.str; # Permissions snapshot and destroy are in case --no-sync-snap is not used - default = [ "bookmark" "hold" "send" "snapshot" "destroy" ]; + default = [ "bookmark" "hold" "send" "snapshot" "destroy" "mount" ]; description = lib.mdDoc '' Permissions granted for the {option}`services.syncoid.user` user for local source datasets. See diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix index 542c80ab2e67..fd943c20091a 100644 --- a/nixos/modules/services/databases/memcached.nix +++ b/nixos/modules/services/databases/memcached.nix @@ -37,7 +37,7 @@ in description = lib.mdDoc "The port to bind to."; }; - enableUnixSocket = mkEnableOption (lib.mdDoc "unix socket at /run/memcached/memcached.sock"); + enableUnixSocket = mkEnableOption (lib.mdDoc "Unix Domain Socket at /run/memcached/memcached.sock instead of listening on an IP address and port. The `listen` and `port` options are ignored."); maxMemory = mkOption { type = types.ints.unsigned; diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index ed5915735730..c4e76c82ba5c 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -161,33 +161,6 @@ in ''; }; - ensurePermissions = mkOption { - type = types.attrsOf types.str; - default = {}; - visible = false; # This option has been deprecated. - description = lib.mdDoc '' - This option is DEPRECATED and should not be used in nixpkgs anymore, - use `ensureDBOwnership` instead. It can also break with newer - versions of PostgreSQL (≥ 15). - - Permissions to ensure for the user, specified as an attribute set. - The attribute names specify the database and tables to grant the permissions for. - The attribute values specify the permissions to grant. You may specify one or - multiple comma-separated SQL privileges here. - - For more information on how to specify the target - and on which privileges exist, see the - [GRANT syntax](https://www.postgresql.org/docs/current/sql-grant.html). - The attributes are used as `GRANT ''${attrValue} ON ''${attrName}`. - ''; - example = literalExpression '' - { - "DATABASE \"nextcloud\"" = "ALL PRIVILEGES"; - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; - } - ''; - }; - ensureDBOwnership = mkOption { type = types.bool; default = false; @@ -460,16 +433,6 @@ in Offender: ${name} has not been found among databases. ''; }) cfg.ensureUsers; - # `ensurePermissions` is now deprecated, let's avoid it. - warnings = lib.optional (any ({ ensurePermissions, ... }: ensurePermissions != {}) cfg.ensureUsers) " - `services.postgresql.ensureUsers.*.ensurePermissions` is used in your expressions, - this option is known to be broken with newer PostgreSQL versions, - consider migrating to `services.postgresql.ensureUsers.*.ensureDBOwnership` or - consult the release notes or manual for more migration guidelines. - - This option will be removed in NixOS 24.05 unless it sees significant - maintenance improvements. - "; services.postgresql.settings = { @@ -583,11 +546,6 @@ in concatMapStrings (user: let - userPermissions = concatStringsSep "\n" - (mapAttrsToList - (database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '') - user.ensurePermissions - ); dbOwnershipStmt = optionalString user.ensureDBOwnership ''$PSQL -tAc 'ALTER DATABASE "${user.name}" OWNER TO "${user.name}";' ''; @@ -599,7 +557,6 @@ in userClauses = ''$PSQL -tAc 'ALTER ROLE "${user.name}" ${concatStringsSep " " clauseSqlStatements}' ''; in '' $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' - ${userPermissions} ${userClauses} ${dbOwnershipStmt} diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix index 8f3ad78d50ce..09448833620c 100644 --- a/nixos/modules/services/desktops/pipewire/pipewire.nix +++ b/nixos/modules/services/desktops/pipewire/pipewire.nix @@ -246,6 +246,9 @@ in { description = lib.mdDoc '' List of packages that provide PipeWire configuration, in the form of `share/pipewire/*/*.conf` files. + + LV2 dependencies will be picked up from config packages automatically + via `passthru.requiredLv2Packages`. ''; }; @@ -258,7 +261,8 @@ in { be made available to PipeWire for [filter chains][wiki-filter-chain]. Config packages have their required LV2 plugins added automatically, - so they don't need to be specified here. + so they don't need to be specified here. Config packages need to set + `passthru.requiredLv2Packages` for this to work. [wiki-filter-chain]: https://docs.pipewire.org/page_module_filter_chain.html ''; diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix index 99aea8facb16..009d68bd4f28 100644 --- a/nixos/modules/services/desktops/pipewire/wireplumber.nix +++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix @@ -30,6 +30,9 @@ in description = lib.mdDoc '' List of packages that provide WirePlumber configuration, in the form of `share/wireplumber/*/*.lua` files. + + LV2 dependencies will be picked up from config packages automatically + via `passthru.requiredLv2Packages`. ''; }; @@ -42,7 +45,8 @@ in be made available to WirePlumber for [filter chains][wiki-filter-chain]. Config packages have their required LV2 plugins added automatically, - so they don't need to be specified here. + so they don't need to be specified here. Config packages need to set + `passthru.requiredLv2Packages` for this to work. [wiki-filter-chain]: https://docs.pipewire.org/page_module_filter_chain.html ''; @@ -108,7 +112,7 @@ in ) config.environment.etc )) == 1; - message = "Using `environment.etc.\"wireplumber<...>\"` directly is no longer supported in 24.05. Use `services.wireplumber.configPackages` instead."; + message = "Using `environment.etc.\"wireplumber<...>\"` directly is no longer supported in 24.05. Use `services.pipewire.wireplumber.configPackages` instead."; } ]; diff --git a/nixos/modules/services/development/nixseparatedebuginfod.nix b/nixos/modules/services/development/nixseparatedebuginfod.nix index daf85153d339..a2ec0d2c80e1 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/games/armagetronad.nix b/nixos/modules/services/games/armagetronad.nix new file mode 100644 index 000000000000..f79818e0e53b --- /dev/null +++ b/nixos/modules/services/games/armagetronad.nix @@ -0,0 +1,268 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) mkEnableOption mkIf mkOption mkMerge literalExpression; + inherit (lib) mapAttrsToList filterAttrs unique recursiveUpdate types; + + mkValueStringArmagetron = with lib; v: + if isInt v then toString v + else if isFloat v then toString v + else if isString v then v + else if true == v then "1" + else if false == v then "0" + else if null == v then "" + else throw "unsupported type: ${builtins.typeOf v}: ${(lib.generators.toPretty {} v)}"; + + settingsFormat = pkgs.formats.keyValue { + mkKeyValue = lib.generators.mkKeyValueDefault + { + mkValueString = mkValueStringArmagetron; + } " "; + listsAsDuplicateKeys = true; + }; + + cfg = config.services.armagetronad; + enabledServers = lib.filterAttrs (n: v: v.enable) cfg.servers; + nameToId = serverName: "armagetronad-${serverName}"; + getStateDirectory = serverName: "armagetronad/${serverName}"; + getServerRoot = serverName: "/var/lib/${getStateDirectory serverName}"; +in +{ + options = { + services.armagetronad = { + servers = mkOption { + description = lib.mdDoc "Armagetron server definitions."; + default = { }; + type = types.attrsOf (types.submodule { + options = { + enable = mkEnableOption (lib.mdDoc "armagetronad"); + + package = lib.mkPackageOptionMD pkgs "armagetronad-dedicated" { + example = '' + pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated + ''; + extraDescription = '' + Ensure that you use a derivation which contains the path `bin/armagetronad-dedicated`. + ''; + }; + + host = mkOption { + type = types.str; + default = "0.0.0.0"; + description = lib.mdDoc "Host to listen on. Used for SERVER_IP."; + }; + + port = mkOption { + type = types.port; + default = 4534; + description = lib.mdDoc "Port to listen on. Used for SERVER_PORT."; + }; + + dns = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc "DNS address to use for this server. Optional."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Set to true to open the configured UDP port for Armagetron Advanced."; + }; + + name = mkOption { + type = types.str; + description = "The name of this server."; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + Armagetron Advanced server rules configuration. Refer to: + <https://wiki.armagetronad.org/index.php?title=Console_Commands> + or `armagetronad-dedicated --doc` for a list. + + This attrset is used to populate `settings_custom.cfg`; see: + <https://wiki.armagetronad.org/index.php/Configuration_Files> + ''; + example = literalExpression '' + { + CYCLE_RUBBER = 40; + } + ''; + }; + + roundSettings = mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + Armagetron Advanced server per-round configuration. Refer to: + <https://wiki.armagetronad.org/index.php?title=Console_Commands> + or `armagetronad-dedicated --doc` for a list. + + This attrset is used to populate `everytime.cfg`; see: + <https://wiki.armagetronad.org/index.php/Configuration_Files> + ''; + example = literalExpression '' + { + SAY = [ + "Hosted on NixOS" + "https://nixos.org" + "iD Tech High Rubber rul3z!! Happy New Year 2008!!1" + ]; + } + ''; + }; + }; + }); + }; + }; + }; + + config = mkIf (enabledServers != { }) { + systemd.tmpfiles.settings = mkMerge (mapAttrsToList + (serverName: serverCfg: + let + serverId = nameToId serverName; + serverRoot = getServerRoot serverName; + serverInfo = ( + { + SERVER_IP = serverCfg.host; + SERVER_PORT = serverCfg.port; + SERVER_NAME = serverCfg.name; + } // (lib.optionalAttrs (serverCfg.dns != null) { SERVER_DNS = serverCfg.dns; }) + ); + customSettings = serverCfg.settings; + everytimeSettings = serverCfg.roundSettings; + + serverInfoCfg = settingsFormat.generate "server_info.${serverName}.cfg" serverInfo; + customSettingsCfg = settingsFormat.generate "settings_custom.${serverName}.cfg" customSettings; + everytimeSettingsCfg = settingsFormat.generate "everytime.${serverName}.cfg" everytimeSettings; + in + { + "10-armagetronad-${serverId}" = { + "${serverRoot}/data" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/settings" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/var" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/resource" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/input" = { + "f+" = { + group = serverId; + user = serverId; + mode = "0640"; + }; + }; + "${serverRoot}/settings/server_info.cfg" = { + "L+" = { + argument = "${serverInfoCfg}"; + }; + }; + "${serverRoot}/settings/settings_custom.cfg" = { + "L+" = { + argument = "${customSettingsCfg}"; + }; + }; + "${serverRoot}/settings/everytime.cfg" = { + "L+" = { + argument = "${everytimeSettingsCfg}"; + }; + }; + }; + } + ) + enabledServers + ); + + systemd.services = mkMerge (mapAttrsToList + (serverName: serverCfg: + let + serverId = nameToId serverName; + in + { + "armagetronad-${serverName}" = { + description = "Armagetron Advanced Dedicated Server for ${serverName}"; + wants = [ "basic.target" ]; + after = [ "basic.target" "network.target" "multi-user.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + let + serverRoot = getServerRoot serverName; + in + { + Type = "simple"; + StateDirectory = getStateDirectory serverName; + ExecStart = "${lib.getExe serverCfg.package} --daemon --input ${serverRoot}/input --userdatadir ${serverRoot}/data --userconfigdir ${serverRoot}/settings --vardir ${serverRoot}/var --autoresourcedir ${serverRoot}/resource"; + Restart = "on-failure"; + CapabilityBoundingSet = ""; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictNamespaces = true; + RestrictSUIDSGID = true; + User = serverId; + Group = serverId; + }; + }; + }) + enabledServers + ); + + networking.firewall.allowedUDPPorts = + unique (mapAttrsToList (serverName: serverCfg: serverCfg.port) (filterAttrs (serverName: serverCfg: serverCfg.openFirewall) enabledServers)); + + users.users = mkMerge (mapAttrsToList + (serverName: serverCfg: + { + ${nameToId serverName} = { + group = nameToId serverName; + description = "Armagetron Advanced dedicated user for server ${serverName}"; + isSystemUser = true; + }; + }) + enabledServers + ); + + users.groups = mkMerge (mapAttrsToList + (serverName: serverCfg: + { + ${nameToId serverName} = { }; + }) + enabledServers + ); + }; +} 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 a90d234f65c0..1aaa2d07b9bd 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 3c96e9c41be5..b95bdf191fad 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 519d116e0e55..f68a8bdb6bfa 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/home-automation/matter-server.nix b/nixos/modules/services/home-automation/matter-server.nix new file mode 100644 index 000000000000..864ef9e20083 --- /dev/null +++ b/nixos/modules/services/home-automation/matter-server.nix @@ -0,0 +1,125 @@ +{ lib +, pkgs +, config +, ... +}: + +with lib; + +let + cfg = config.services.matter-server; + storageDir = "matter-server"; + storagePath = "/var/lib/${storageDir}"; + vendorId = "4939"; # home-assistant vendor ID +in + +{ + meta.maintainers = with lib.maintainers; [ leonm1 ]; + + options.services.matter-server = with types; { + enable = mkEnableOption (lib.mdDoc "Matter-server"); + + package = mkPackageOptionMD pkgs "python-matter-server" { }; + + port = mkOption { + type = types.port; + default = 5580; + description = "Port to expose the matter-server service on."; + }; + + logLevel = mkOption { + type = types.enum [ "critical" "error" "warning" "info" "debug" ]; + default = "info"; + description = "Verbosity of logs from the matter-server"; + }; + + extraArgs = mkOption { + type = listOf str; + default = []; + description = '' + Extra arguments to pass to the matter-server executable. + See https://github.com/home-assistant-libs/python-matter-server?tab=readme-ov-file#running-the-development-server for options. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.matter-server = { + after = [ "network-online.target" ]; + before = [ "home-assistant.service" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + description = "Matter Server"; + environment.HOME = storagePath; + serviceConfig = { + ExecStart = (concatStringsSep " " [ + "${cfg.package}/bin/matter-server" + "--port" (toString cfg.port) + "--vendorid" vendorId + "--storage-path" storagePath + "--log-level" "${cfg.logLevel}" + "${escapeShellArgs cfg.extraArgs}" + ]); + # Start with a clean root filesystem, and allowlist what the container + # is permitted to access. + TemporaryFileSystem = "/"; + # Allowlist /nix/store (to allow the binary to find its dependencies) + # and dbus. + ReadOnlyPaths = "/nix/store /run/dbus"; + # Let systemd manage `/var/lib/matter-server` for us inside the + # ephemeral TemporaryFileSystem. + StateDirectory = storageDir; + # `python-matter-server` writes to /data even when a storage-path is + # specified. This bind-mount points /data at the systemd-managed + # /var/lib/matter-server, so all files get dropped into the state + # directory. + BindPaths = "${storagePath}:/data"; + + # Hardening bits + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DevicePolicy = "closed"; + DynamicUser = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallFilter = concatStringsSep " " [ + "~" # Blocklist + "@clock" + "@cpu-emulation" + "@debug" + "@module" + "@mount" + "@obsolete" + "@privileged" + "@raw-io" + "@reboot" + "@resources" + "@swap" + ]; + UMask = "0077"; + }; + }; + }; +} + diff --git a/nixos/modules/services/mail/listmonk.nix b/nixos/modules/services/mail/listmonk.nix index 945eb436c1f2..d6399304cc10 100644 --- a/nixos/modules/services/mail/listmonk.nix +++ b/nixos/modules/services/mail/listmonk.nix @@ -187,7 +187,11 @@ in { # Indeed, it will try to create all the folders and realize one of them already exist. # Therefore, we have to create it ourselves. ''${pkgs.coreutils}/bin/mkdir -p "''${STATE_DIRECTORY}/listmonk/uploads"'' - "${cfg.package}/bin/listmonk --config ${cfgFile} --idempotent --install --upgrade --yes" + # setup database if not already done + "${cfg.package}/bin/listmonk --config ${cfgFile} --idempotent --install --yes" + # apply db migrations (setup and migrations can not be done in one step + # with "--install --upgrade" listmonk ignores the upgrade) + "${cfg.package}/bin/listmonk --config ${cfgFile} --upgrade --yes" "${updateDatabaseConfigScript}/bin/update-database-config.sh" ]; ExecStart = "${cfg.package}/bin/listmonk --config ${cfgFile}"; diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix index e8fbc05423d3..78d1d6339ed6 100644 --- a/nixos/modules/services/misc/docker-registry.nix +++ b/nixos/modules/services/misc/docker-registry.nix @@ -63,6 +63,12 @@ in { type = types.port; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Opens the port used by the firewall."; + }; + storagePath = mkOption { type = types.nullOr types.path; default = "/var/lib/docker-registry"; @@ -154,5 +160,9 @@ in { isSystemUser = true; }; users.groups.docker-registry = {}; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; }; } diff --git a/nixos/modules/services/misc/etebase-server.nix b/nixos/modules/services/misc/etebase-server.nix index 045048a1a2e3..f5a5e8a780d4 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 07a09e2b6bbf..02f1378cb0d5 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 ab042e4b6ee2..9314c4f3848d 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/misc/tabby.nix b/nixos/modules/services/misc/tabby.nix new file mode 100644 index 000000000000..a3072e5df75e --- /dev/null +++ b/nixos/modules/services/misc/tabby.nix @@ -0,0 +1,203 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) types; + + cfg = config.services.tabby; + format = pkgs.formats.toml { }; + tabbyPackage = cfg.package.override { + inherit (cfg) acceleration; + }; +in +{ + options = { + services.tabby = { + enable = lib.mkEnableOption ( + lib.mdDoc "Self-hosted AI coding assistant using large language models" + ); + + package = lib.mkPackageOption pkgs "tabby" { }; + + port = lib.mkOption { + type = types.port; + default = 11029; + description = lib.mdDoc '' + Specifies the bind port on which the tabby server HTTP interface listens. + ''; + }; + + model = lib.mkOption { + type = types.str; + default = "TabbyML/StarCoder-1B"; + description = lib.mdDoc '' + Specify the model that tabby will use to generate completions. + + This model will be downloaded automatically if it is not already present. + + If you want to utilize an existing model that you've already + downloaded you'll need to move it into tabby's state directory which + lives in `/var/lib/tabby`. Because the tabby.service is configured to + use a DyanmicUser the service will need to have been started at least + once before you can move the locally existing model into + `/var/lib/tabby`. You can set the model to 'none' and tabby will + startup and fail to download a model, but will have created the + `/var/lib/tabby` directory. You can then copy over the model manually + into `/var/lib/tabby`, update the model option to the name you just + downloaded and copied over then `nixos-rebuild switch` to start using + it. + + $ tabby download --model TabbyML/DeepseekCoder-6.7B + $ find ~/.tabby/ | tail -n1 + /home/ghthor/.tabby/models/TabbyML/DeepseekCoder-6.7B/ggml/q8_0.v2.gguf + $ sudo rsync -r ~/.tabby/models/ /var/lib/tabby/models/ + $ sudo chown -R tabby:tabby /var/lib/tabby/models/ + + See for Model Options: + > https://github.com/TabbyML/registry-tabby + ''; + }; + + acceleration = lib.mkOption { + type = types.nullOr (types.enum [ "cpu" "rocm" "cuda" "metal" ]); + default = null; + example = "rocm"; + description = lib.mdDoc '' + Specifies the device to use for hardware acceleration. + + - `cpu`: no acceleration just use the CPU + - `rocm`: supported by modern AMD GPUs + - `cuda`: supported by modern NVIDIA GPUs + - `metal`: supported on darwin aarch64 machines + + Tabby will try and determine what type of acceleration that is + already enabled in your configuration when `acceleration = null`. + + - nixpkgs.config.cudaSupport + - nixpkgs.config.rocmSupport + - if stdenv.isDarwin && stdenv.isAarch64 + + IFF multiple acceleration methods are found to be enabled or if you + haven't set either `cudaSupport or rocmSupport` you will have to + specify the device type manually here otherwise it will default to + the first from the list above or to cpu. + ''; + }; + + settings = lib.mkOption { + inherit (format) type; + default = { }; + description = lib.mdDoc '' + Tabby scheduler configuration + + See for more details: + > https://tabby.tabbyml.com/docs/configuration/#repository-context-for-code-completion + ''; + example = lib.literalExpression '' + settings = { + repositories = [ + { name = "tabby"; git_url = "https://github.com/TabbyML/tabby.git"; } + { name = "CTranslate2"; git_url = "git@github.com:OpenNMT/CTranslate2.git"; } + + # local directory is also supported, but limited by systemd DynamicUser=1 + # adding local repositories will need to be done manually + { name = "repository_a"; git_url = "file:///var/lib/tabby/repository_a"; } + ]; + }; + ''; + }; + + usageCollection = lib.mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable sending anonymous usage data. + + See for more details: + > https://tabby.tabbyml.com/docs/configuration#usage-collection + ''; + }; + + indexInterval = lib.mkOption { + type = types.str; + default = "5hours"; + example = "5hours"; + description = lib.mdDoc '' + Run tabby scheduler to generate the index database at this interval. + Updates by default every 5 hours. This value applies to + `OnUnitInactiveSec` + + The format is described in + {manpage}`systemd.time(7)`. + + To disable running `tabby scheduler --now` updates, set to `"never"` + ''; + }; + }; + }; + + # TODO(ghthor): firewall config + + config = lib.mkIf cfg.enable { + environment = { + etc."tabby/config.toml".source = format.generate "config.toml" cfg.settings; + systemPackages = [ tabbyPackage ]; + }; + + + systemd = let + serviceUser = { + WorkingDirectory = "/var/lib/tabby"; + StateDirectory = [ "tabby" ]; + ConfigurationDirectory = [ "tabby" ]; + DynamicUser = true; + User = "tabby"; + Group = "tabby"; + }; + + serviceEnv = lib.mkMerge [ + { + TABBY_ROOT = "%S/tabby"; + } + (lib.mkIf (!cfg.usageCollection) { + TABBY_DISABLE_USAGE_COLLECTION = "1"; + }) + ]; + in { + services.tabby = { + wantedBy = [ "multi-user.target" ]; + description = "Self-hosted AI coding assistant using large language models"; + after = [ "network.target" ]; + environment = serviceEnv; + serviceConfig = lib.mkMerge [ + serviceUser + { + ExecStart = + "${lib.getExe tabbyPackage} serve --model ${cfg.model} --port ${toString cfg.port} --device ${tabbyPackage.featureDevice}"; + } + ]; + }; + + services.tabby-scheduler = lib.mkIf (cfg.indexInterval != "never") { + wantedBy = [ "multi-user.target" ]; + description = "Tabby repository indexing service"; + after = [ "network.target" ]; + environment = serviceEnv; + preStart = "cp -f /etc/tabby/config.toml \${TABBY_ROOT}/config.toml"; + serviceConfig = lib.mkMerge [ + serviceUser + { + # Type = "oneshot"; + ExecStart = "${lib.getExe tabbyPackage} scheduler --now"; + } + ]; + }; + timers.tabby-scheduler = lib.mkIf (cfg.indexInterval != "never") { + description = "Update timer for tabby-scheduler"; + partOf = [ "tabby-scheduler.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig.OnUnitInactiveSec = cfg.indexInterval; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ ghthor ]; +} diff --git a/nixos/modules/services/misc/transfer-sh.nix b/nixos/modules/services/misc/transfer-sh.nix new file mode 100644 index 000000000000..899d9dfc3c10 --- /dev/null +++ b/nixos/modules/services/misc/transfer-sh.nix @@ -0,0 +1,102 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.transfer-sh; + inherit (lib) + mkDefault mkEnableOption mkPackageOption mkIf mkOption + types mapAttrs isBool getExe boolToString mdDoc optionalAttrs; +in +{ + options.services.transfer-sh = { + enable = mkEnableOption (mdDoc "Easy and fast file sharing from the command-line"); + + package = mkPackageOption pkgs "transfer-sh" { }; + + settings = mkOption { + type = types.submodule { freeformType = with types; attrsOf (oneOf [ bool int str ]); }; + default = { }; + example = { + LISTENER = ":8080"; + BASEDIR = "/var/lib/transfer.sh"; + TLS_LISTENER_ONLY = false; + }; + description = mdDoc '' + Additional configuration for transfer-sh, see + <https://github.com/dutchcoders/transfer.sh#usage-1> + for supported values. + + For secrets use secretFile option instead. + ''; + }; + + provider = mkOption { + type = types.enum [ "local" "s3" "storj" "gdrive" ]; + default = "local"; + description = mdDoc "Storage providers to use"; + }; + + secretFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/secrets/transfer-sh.env"; + description = mdDoc '' + Path to file containing environment variables. + Useful for passing down secrets. + Some variables that can be considered secrets are: + - AWS_ACCESS_KEY + - AWS_ACCESS_KEY + - TLS_PRIVATE_KEY + - HTTP_AUTH_HTPASSWD + ''; + }; + }; + + config = + let + localProvider = (cfg.provider == "local"); + stateDirectory = "/var/lib/transfer.sh"; + in + mkIf cfg.enable + { + services.transfer-sh.settings = { + LISTENER = mkDefault ":8080"; + } // optionalAttrs localProvider { + BASEDIR = mkDefault stateDirectory; + }; + + systemd.services.transfer-sh = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + environment = mapAttrs (_: v: if isBool v then boolToString v else toString v) cfg.settings; + serviceConfig = { + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + DevicePolicy = "closed"; + DynamicUser = true; + ExecStart = "${getExe cfg.package} --provider ${cfg.provider}"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = [ "native" ]; + SystemCallFilter = [ "@system-service" ]; + StateDirectory = baseNameOf stateDirectory; + } // optionalAttrs (cfg.secretFile != null) { + EnvironmentFile = cfg.secretFile; + } // optionalAttrs localProvider { + ReadWritePaths = cfg.settings.BASEDIR; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ ocfox ]; +} diff --git a/nixos/modules/services/monitoring/mackerel-agent.nix b/nixos/modules/services/monitoring/mackerel-agent.nix index 5915634ed26f..d1e84c0359dc 100644 --- a/nixos/modules/services/monitoring/mackerel-agent.nix +++ b/nixos/modules/services/monitoring/mackerel-agent.nix @@ -81,7 +81,7 @@ in { include = mkDefault "/etc/mackerel-agent/conf.d/*.conf"; }; - # upstream service file in https://git.io/JUt4Q + # upstream service file in https://github.com/mackerelio/mackerel-agent/blob/master/packaging/rpm/src/mackerel-agent.service systemd.services.mackerel-agent = { description = "mackerel.io agent"; wants = [ "network-online.target" ]; diff --git a/nixos/modules/services/monitoring/prometheus/exporters/restic.nix b/nixos/modules/services/monitoring/prometheus/exporters/restic.nix index 5b32c93a666d..977bd42e9812 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/restic.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/restic.nix @@ -93,12 +93,14 @@ in }; serviceOpts = { + script = '' + export RESTIC_PASSWORD_FILE=$CREDENTIALS_DIRECTORY/RESTIC_PASSWORD_FILE + ${pkgs.prometheus-restic-exporter}/bin/restic-exporter.py \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; serviceConfig = { - ExecStart = '' - ${pkgs.prometheus-restic-exporter}/bin/restic-exporter.py \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; + LoadCredential = [ "RESTIC_PASSWORD_FILE:${cfg.passwordFile}" ]; }; environment = let @@ -108,8 +110,7 @@ in toRcloneVal = v: if lib.isBool v then lib.boolToString v else v; in { - RESTIC_REPO_URL = cfg.repository; - RESTIC_REPO_PASSWORD_FILE = cfg.passwordFile; + RESTIC_REPOSITORY = cfg.repository; LISTEN_ADDRESS = cfg.listenAddress; LISTEN_PORT = toString cfg.port; REFRESH_INTERVAL = toString cfg.refreshInterval; diff --git a/nixos/modules/services/monitoring/scrutiny.nix b/nixos/modules/services/monitoring/scrutiny.nix index 454668a9a128..aef924ef840c 100644 --- a/nixos/modules/services/monitoring/scrutiny.nix +++ b/nixos/modules/services/monitoring/scrutiny.nix @@ -1,5 +1,11 @@ { config, lib, pkgs, ... }: let + inherit (lib) maintainers; + inherit (lib.meta) getExe; + inherit (lib.modules) mkIf; + inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption; + inherit (lib.types) bool enum nullOr port str submodule; + cfg = config.services.scrutiny; # Define the settings format used for this program settingsFormat = pkgs.formats.yaml { }; @@ -7,20 +13,16 @@ in { options = { services.scrutiny = { - enable = lib.mkEnableOption "Enables the scrutiny web application."; + enable = mkEnableOption "Scrutiny, a web application for drive monitoring"; - package = lib.mkPackageOptionMD pkgs "scrutiny" { }; + package = mkPackageOption pkgs "scrutiny" { }; - openFirewall = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Open the default ports in the firewall for Scrutiny."; - }; + openFirewall = mkEnableOption "opening the default ports in the firewall for Scrutiny"; - influxdb.enable = lib.mkOption { - type = lib.types.bool; + influxdb.enable = mkOption { + type = bool; default = true; - description = lib.mdDoc '' + description = '' Enables InfluxDB on the host system using the `services.influxdb2` NixOS module with default options. @@ -29,127 +31,124 @@ in ''; }; - settings = lib.mkOption { - description = lib.mdDoc '' + settings = mkOption { + description = '' Scrutiny settings to be rendered into the configuration file. See https://github.com/AnalogJ/scrutiny/blob/master/example.scrutiny.yaml. ''; default = { }; - type = lib.types.submodule { + type = submodule { freeformType = settingsFormat.type; - options.web.listen.port = lib.mkOption { - type = lib.types.port; + options.web.listen.port = mkOption { + type = port; default = 8080; - description = lib.mdDoc "Port for web application to listen on."; + description = "Port for web application to listen on."; }; - options.web.listen.host = lib.mkOption { - type = lib.types.str; + options.web.listen.host = mkOption { + type = str; default = "0.0.0.0"; - description = lib.mdDoc "Interface address for web application to bind to."; + description = "Interface address for web application to bind to."; }; - options.web.listen.basepath = lib.mkOption { - type = lib.types.str; + options.web.listen.basepath = mkOption { + type = str; default = ""; example = "/scrutiny"; - description = lib.mdDoc '' + description = '' If Scrutiny will be behind a path prefixed reverse proxy, you can override this value to serve Scrutiny on a subpath. ''; }; - options.log.level = lib.mkOption { - type = lib.types.enum [ "INFO" "DEBUG" ]; + options.log.level = mkOption { + type = enum [ "INFO" "DEBUG" ]; default = "INFO"; - description = lib.mdDoc "Log level for Scrutiny."; + description = "Log level for Scrutiny."; }; - options.web.influxdb.scheme = lib.mkOption { - type = lib.types.str; + options.web.influxdb.scheme = mkOption { + type = str; default = "http"; - description = lib.mdDoc "URL scheme to use when connecting to InfluxDB."; + description = "URL scheme to use when connecting to InfluxDB."; }; - options.web.influxdb.host = lib.mkOption { - type = lib.types.str; + options.web.influxdb.host = mkOption { + type = str; default = "0.0.0.0"; - description = lib.mdDoc "IP or hostname of the InfluxDB instance."; + description = "IP or hostname of the InfluxDB instance."; }; - options.web.influxdb.port = lib.mkOption { - type = lib.types.port; + options.web.influxdb.port = mkOption { + type = port; default = 8086; - description = lib.mdDoc "The port of the InfluxDB instance."; + description = "The port of the InfluxDB instance."; }; - options.web.influxdb.tls.insecure_skip_verify = lib.mkOption { - type = lib.types.bool; - default = false; - description = lib.mdDoc "Skip TLS verification when connecting to InfluxDB."; - }; + options.web.influxdb.tls.insecure_skip_verify = mkEnableOption "skipping TLS verification when connecting to InfluxDB"; - options.web.influxdb.token = lib.mkOption { - type = lib.types.nullOr lib.types.str; + options.web.influxdb.token = mkOption { + type = nullOr str; default = null; - description = lib.mdDoc "Authentication token for connecting to InfluxDB."; + description = "Authentication token for connecting to InfluxDB."; }; - options.web.influxdb.org = lib.mkOption { - type = lib.types.nullOr lib.types.str; + options.web.influxdb.org = mkOption { + type = nullOr str; default = null; - description = lib.mdDoc "InfluxDB organisation under which to store data."; + description = "InfluxDB organisation under which to store data."; }; - options.web.influxdb.bucket = lib.mkOption { - type = lib.types.nullOr lib.types.str; + options.web.influxdb.bucket = mkOption { + type = nullOr str; default = null; - description = lib.mdDoc "InfluxDB bucket in which to store data."; + description = "InfluxDB bucket in which to store data."; }; }; }; collector = { - enable = lib.mkEnableOption "Enables the scrutiny metrics collector."; + enable = mkEnableOption "the Scrutiny metrics collector"; - package = lib.mkPackageOptionMD pkgs "scrutiny-collector" { }; + package = mkPackageOption pkgs "scrutiny-collector" { }; - schedule = lib.mkOption { - type = lib.types.str; + schedule = mkOption { + type = str; default = "*:0/15"; - description = lib.mdDoc '' + description = '' How often to run the collector in systemd calendar format. ''; }; - settings = lib.mkOption { - description = lib.mdDoc '' + settings = mkOption { + description = '' Collector settings to be rendered into the collector configuration file. See https://github.com/AnalogJ/scrutiny/blob/master/example.collector.yaml. ''; default = { }; - type = lib.types.submodule { + type = submodule { freeformType = settingsFormat.type; - options.host.id = lib.mkOption { - type = lib.types.nullOr lib.types.str; + options.host.id = mkOption { + type = nullOr str; default = null; - description = lib.mdDoc "Host ID for identifying/labelling groups of disks"; + description = "Host ID for identifying/labelling groups of disks"; }; - options.api.endpoint = lib.mkOption { - type = lib.types.str; - default = "http://localhost:8080"; - description = lib.mdDoc "Scrutiny app API endpoint for sending metrics to."; + options.api.endpoint = mkOption { + type = str; + default = "http://localhost:${toString cfg.settings.web.listen.port}"; + defaultText = literalExpression ''"http://localhost:''${config.services.scrutiny.settings.web.listen.port}"''; + description = "Scrutiny app API endpoint for sending metrics to."; }; - options.log.level = lib.mkOption { - type = lib.types.enum [ "INFO" "DEBUG" ]; + options.log.level = mkOption { + type = enum [ "INFO" "DEBUG" ]; default = "INFO"; - description = lib.mdDoc "Log level for Scrutiny collector."; + description = "Log level for Scrutiny collector."; }; }; }; @@ -157,14 +156,14 @@ in }; }; - config = lib.mkIf (cfg.enable || cfg.collector.enable) { + config = mkIf (cfg.enable || cfg.collector.enable) { services.influxdb2.enable = cfg.influxdb.enable; - networking.firewall = lib.mkIf cfg.openFirewall { + networking.firewall = mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.settings.web.listen.port ]; }; - services.smartd = lib.mkIf cfg.collector.enable { + services.smartd = mkIf cfg.collector.enable { enable = true; extraOptions = [ "-A /var/log/smartd/" @@ -174,7 +173,7 @@ in systemd = { services = { - scrutiny = lib.mkIf cfg.enable { + scrutiny = mkIf cfg.enable { description = "Hard Drive S.M.A.R.T Monitoring, Historical Trends & Real World Failure Thresholds"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; @@ -185,14 +184,14 @@ in }; serviceConfig = { DynamicUser = true; - ExecStart = "${lib.getExe cfg.package} start --config ${settingsFormat.generate "scrutiny.yaml" cfg.settings}"; + ExecStart = "${getExe cfg.package} start --config ${settingsFormat.generate "scrutiny.yaml" cfg.settings}"; Restart = "always"; StateDirectory = "scrutiny"; StateDirectoryMode = "0750"; }; }; - scrutiny-collector = lib.mkIf cfg.collector.enable { + scrutiny-collector = mkIf cfg.collector.enable { description = "Scrutiny Collector Service"; environment = { COLLECTOR_VERSION = "1"; @@ -200,12 +199,12 @@ in }; serviceConfig = { Type = "oneshot"; - ExecStart = "${lib.getExe cfg.collector.package} run --config ${settingsFormat.generate "scrutiny-collector.yaml" cfg.collector.settings}"; + ExecStart = "${getExe cfg.collector.package} run --config ${settingsFormat.generate "scrutiny-collector.yaml" cfg.collector.settings}"; }; }; }; - timers = lib.mkIf cfg.collector.enable { + timers = mkIf cfg.collector.enable { scrutiny-collector = { timerConfig = { OnCalendar = cfg.collector.schedule; @@ -217,5 +216,5 @@ in }; }; - meta.maintainers = [ lib.maintainers.jnsgruk ]; + meta.maintainers = [ maintainers.jnsgruk ]; } diff --git a/nixos/modules/services/networking/bird-lg.nix b/nixos/modules/services/networking/bird-lg.nix index be9f4101e6ab..1c59f7a6ae7c 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/networking/searx.nix b/nixos/modules/services/networking/searx.nix index 938d585e3179..5bbf875f0d57 100644 --- a/nixos/modules/services/networking/searx.nix +++ b/nixos/modules/services/networking/searx.nix @@ -213,7 +213,7 @@ in serviceConfig = { User = "searx"; Group = "searx"; - ExecStart = "${cfg.package}/bin/searx-run"; + ExecStart = lib.getExe cfg.package; } // optionalAttrs (cfg.environmentFile != null) { EnvironmentFile = builtins.toPath cfg.environmentFile; }; environment = { diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix index f11fe57d6ce5..972299a4697a 100644 --- a/nixos/modules/services/networking/tailscale.nix +++ b/nixos/modules/services/networking/tailscale.nix @@ -66,6 +66,13 @@ in { default = []; example = ["--ssh"]; }; + + extraDaemonFlags = mkOption { + description = lib.mdDoc "Extra flags to pass to {command}`tailscaled`."; + type = types.listOf types.str; + default = []; + example = ["--no-logs-no-support"]; + }; }; config = mkIf cfg.enable { @@ -80,7 +87,7 @@ in { ] ++ lib.optional config.networking.resolvconf.enable config.networking.resolvconf.package; serviceConfig.Environment = [ "PORT=${toString cfg.port}" - ''"FLAGS=--tun ${lib.escapeShellArg cfg.interfaceName}"'' + ''"FLAGS=--tun ${lib.escapeShellArg cfg.interfaceName} ${lib.concatStringsSep " " cfg.extraDaemonFlags}"'' ] ++ (lib.optionals (cfg.permitCertUid != null) [ "TS_PERMIT_CERT_UID=${cfg.permitCertUid}" ]); diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 616b32f11797..8438e472e11e 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -24,12 +24,24 @@ let confNoServer = concatStringsSep "\n" ((mapAttrsToList (toConf "") (builtins.removeAttrs cfg.settings [ "server" ])) ++ [""]); confServer = concatStringsSep "\n" (mapAttrsToList (toConf " ") (builtins.removeAttrs cfg.settings.server [ "define-tag" ])); - confFile = pkgs.writeText "unbound.conf" '' + confFileUnchecked = pkgs.writeText "unbound.conf" '' server: ${optionalString (cfg.settings.server.define-tag != "") (toOption " " "define-tag" cfg.settings.server.define-tag)} ${confServer} ${confNoServer} ''; + confFile = if cfg.checkconf then pkgs.runCommandLocal "unbound-checkconf" { } '' + cp ${confFileUnchecked} unbound.conf + + # fake stateDir which is not accesible in the sandbox + mkdir -p $PWD/state + sed -i unbound.conf \ + -e '/auto-trust-anchor-file/d' \ + -e "s|${cfg.stateDir}|$PWD/state|" + ${cfg.package}/bin/unbound-checkconf unbound.conf + + cp ${confFileUnchecked} $out + '' else confFileUnchecked; rootTrustAnchorFile = "${cfg.stateDir}/root.key"; @@ -62,6 +74,17 @@ in { description = lib.mdDoc "Directory holding all state for unbound to run."; }; + checkconf = mkOption { + type = types.bool; + default = !cfg.settings ? include; + defaultText = "!config.services.unbound.settings ? include"; + description = lib.mdDoc '' + Wether to check the resulting config file with unbound checkconf for syntax errors. + + If settings.include is used, then this options is disabled, as the import can likely not be resolved at build time. + ''; + }; + resolveLocalQueries = mkOption { type = types.bool; default = true; diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix index c659d93b4087..9d074c3027d0 100644 --- a/nixos/modules/services/security/kanidm.nix +++ b/nixos/modules/services/security/kanidm.nix @@ -132,6 +132,28 @@ in default = "WriteReplica"; type = lib.types.enum [ "WriteReplica" "WriteReplicaNoUI" "ReadOnlyReplica" ]; }; + online_backup = { + path = lib.mkOption { + description = lib.mdDoc "Path to the output directory for backups."; + type = lib.types.path; + default = "/var/lib/kanidm/backups"; + }; + schedule = lib.mkOption { + description = lib.mdDoc "The schedule for backups in cron format."; + type = lib.types.str; + default = "00 22 * * *"; + }; + versions = lib.mkOption { + description = lib.mdDoc '' + Number of backups to keep. + + The default is set to `0`, in order to disable backups by default. + ''; + type = lib.types.ints.unsigned; + default = 0; + example = 7; + }; + }; }; }; default = { }; @@ -233,6 +255,14 @@ in environment.systemPackages = lib.mkIf cfg.enableClient [ cfg.package ]; + systemd.tmpfiles.settings."10-kanidm" = { + ${cfg.serverSettings.online_backup.path}.d = { + mode = "0700"; + user = "kanidm"; + group = "kanidm"; + }; + }; + systemd.services.kanidm = lib.mkIf cfg.enableServer { description = "kanidm identity management daemon"; wantedBy = [ "multi-user.target" ]; @@ -253,6 +283,8 @@ in BindPaths = [ # To create the socket "/run/kanidmd:/run/kanidmd" + # To store backups + cfg.serverSettings.online_backup.path ]; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix index 470db735bf64..60d8015d0cee 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 1a5b7d0c24e9..16b6fb0d655d 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 08f90dcf59d8..5cda4a00a9de 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 ccf995fccf3e..39eb7c65c635 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 b893f2c1f33c..efa9c676d9a5 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 bc246b1af278..1237261e0af7 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 = { @@ -127,6 +132,7 @@ in { spectacle systemsettings + kcmutils # Gear baloo @@ -272,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; + }; }; } |