about summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixos/modules/services/desktops/flatpak.nix1
-rw-r--r--nixos/modules/services/misc/ollama.nix40
-rw-r--r--nixos/modules/services/misc/snapper.nix21
-rw-r--r--nixos/modules/services/web-apps/limesurvey.nix78
-rw-r--r--nixos/modules/services/web-apps/nextcloud-notify_push.nix3
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix9
7 files changed, 141 insertions, 21 deletions
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index e4241e9654036..54d3a107d6276 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/azvn85cras6xv4z5j85fiy406f24r1q0-nix-2.18.1";
-  i686-linux = "/nix/store/9bnwy7f9h0kzdzmcnjjsjg0aak5waj40-nix-2.18.1";
-  aarch64-linux = "/nix/store/hh65xwqm9s040s3cgn9vzcmrxj0sf5ij-nix-2.18.1";
-  x86_64-darwin = "/nix/store/6zi5fqzn9n17wrk8r41rhdw4j7jqqsi3-nix-2.18.1";
-  aarch64-darwin = "/nix/store/0pbq6wzr2f1jgpn5212knyxpwmkjgjah-nix-2.18.1";
+  x86_64-linux = "/nix/store/1w4b47zhp33md29wjhgg549pc281vv02-nix-2.18.4";
+  i686-linux = "/nix/store/hz02kn0ffn3wdi2xs7lndpr88v4v4fp2-nix-2.18.4";
+  aarch64-linux = "/nix/store/90zwqa9z2fgldc7ki1p5gfvglchjh9r6-nix-2.18.4";
+  x86_64-darwin = "/nix/store/bd1ix5mj9lj2yh7bqnmdjc24zlg5jivk-nix-2.18.4";
+  aarch64-darwin = "/nix/store/5hvsmklhqiay5i4q5vdkg60p8qpc69rz-nix-2.18.4";
 }
diff --git a/nixos/modules/services/desktops/flatpak.nix b/nixos/modules/services/desktops/flatpak.nix
index d99faf381e019..4c26e6874023a 100644
--- a/nixos/modules/services/desktops/flatpak.nix
+++ b/nixos/modules/services/desktops/flatpak.nix
@@ -35,6 +35,7 @@ in {
     services.dbus.packages = [ pkgs.flatpak ];
 
     systemd.packages = [ pkgs.flatpak ];
+    systemd.tmpfiles.packages = [ pkgs.flatpak ];
 
     environment.profiles = [
       "$HOME/.local/share/flatpak/exports"
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index 30c2b26d8322e..a8f86606a624e 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -21,6 +21,8 @@ in
         example = "/home/foo";
         description = ''
           The home directory that the ollama service is started in.
+
+          See also `services.ollama.writablePaths` and `services.ollama.sandbox`.
         '';
       };
       models = lib.mkOption {
@@ -29,6 +31,37 @@ in
         example = "/path/to/ollama/models";
         description = ''
           The directory that the ollama service will read models from and download new models to.
+
+          See also `services.ollama.writablePaths` and `services.ollama.sandbox`
+          if downloading models or other mutation of the filesystem is required.
+        '';
+      };
+      sandbox = lib.mkOption {
+        type = types.bool;
+        default = true;
+        example = false;
+        description = ''
+          Whether to enable systemd's sandboxing capabilities.
+
+          This sets [`DynamicUser`](
+          https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
+          ), which runs the server as a unique user with read-only access to most of the filesystem.
+
+          See also `services.ollama.writablePaths`.
+        '';
+      };
+      writablePaths = lib.mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "/home/foo" "/mnt/foo" ];
+        description = ''
+          Paths that the server should have write access to.
+
+          This sets [`ReadWritePaths`](
+          https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#ReadWritePaths=
+          ), which allows specified paths to be written to through the default sandboxing.
+
+          See also `services.ollama.sandbox`.
         '';
       };
       listenAddress = lib.mkOption {
@@ -54,8 +87,8 @@ in
         type = types.attrsOf types.str;
         default = { };
         example = {
-          HOME = "/tmp";
           OLLAMA_LLM_LIBRARY = "cpu";
+          HIP_VISIBLE_DEVICES = "0,1";
         };
         description = ''
           Set arbitrary environment variables for the ollama service.
@@ -80,9 +113,10 @@ in
       };
       serviceConfig = {
         ExecStart = "${lib.getExe ollamaPackage} serve";
-        WorkingDirectory = "%S/ollama";
+        WorkingDirectory = cfg.home;
         StateDirectory = [ "ollama" ];
-        DynamicUser = true;
+        DynamicUser = cfg.sandbox;
+        ReadWritePaths = cfg.writablePaths;
       };
     };
 
diff --git a/nixos/modules/services/misc/snapper.nix b/nixos/modules/services/misc/snapper.nix
index 569433c3c71d1..4dd6a2d76ee1d 100644
--- a/nixos/modules/services/misc/snapper.nix
+++ b/nixos/modules/services/misc/snapper.nix
@@ -103,6 +103,18 @@ in
       '';
     };
 
+    persistentTimer = mkOption {
+      default = false;
+      type = types.bool;
+      example = true;
+      description = ''
+        Set the `persistentTimer` option for the
+        {manpage}`systemd.timer(5)`
+        which triggers the snapshot immediately if the last trigger
+        was missed (e.g. if the system was powered down).
+      '';
+    };
+
     cleanupInterval = mkOption {
       type = types.str;
       default = "1d";
@@ -198,7 +210,14 @@ in
       inherit documentation;
       requires = [ "local-fs.target" ];
       serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
-      startAt = cfg.snapshotInterval;
+    };
+
+    systemd.timers.snapper-timeline = {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        Persistent = cfg.persistentTimer;
+        OnCalendar = cfg.snapshotInterval;
+      };
     };
 
     systemd.services.snapper-cleanup = {
diff --git a/nixos/modules/services/web-apps/limesurvey.nix b/nixos/modules/services/web-apps/limesurvey.nix
index 920e6928ef5ce..ac6a1fc2bf8f7 100644
--- a/nixos/modules/services/web-apps/limesurvey.nix
+++ b/nixos/modules/services/web-apps/limesurvey.nix
@@ -20,7 +20,15 @@ let
 
   limesurveyConfig = pkgs.writeText "config.php" ''
     <?php
-      return json_decode('${builtins.toJSON cfg.config}', true);
+      return \array_merge(
+        \json_decode('${builtins.toJSON cfg.config}', true),
+        [
+          'config' => [
+            'encryptionnonce' => \trim(\file_get_contents(\getenv('CREDENTIALS_DIRECTORY') . DIRECTORY_SEPARATOR . 'encryption_nonce')),
+            'encryptionsecretboxkey' => \trim(\file_get_contents(\getenv('CREDENTIALS_DIRECTORY') . DIRECTORY_SEPARATOR . 'encryption_key')),
+          ]
+        ]
+      );
     ?>
   '';
 
@@ -35,8 +43,9 @@ in
     enable = mkEnableOption (lib.mdDoc "Limesurvey web application");
 
     encryptionKey = mkOption {
-      type = types.str;
-      default = "E17687FC77CEE247F0E22BB3ECF27FDE8BEC310A892347EC13013ABA11AA7EB5";
+      type = types.nullOr types.str;
+      default = null;
+      visible = false;
       description = lib.mdDoc ''
         This is a 32-byte key used to encrypt variables in the database.
         You _must_ change this from the default value.
@@ -44,14 +53,35 @@ in
     };
 
     encryptionNonce = mkOption {
-      type = types.str;
-      default = "1ACC8555619929DB91310BE848025A427B0F364A884FFA77";
+      type = types.nullOr types.str;
+      default = null;
+      visible = false;
       description = lib.mdDoc ''
         This is a 24-byte nonce used to encrypt variables in the database.
         You _must_ change this from the default value.
       '';
     };
 
+    encryptionKeyFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        32-byte key used to encrypt variables in the database.
+
+        Note: It should be string not a store path in order to prevent the password from being world readable
+      '';
+    };
+
+    encryptionNonceFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        24-byte used to encrypt variables in the database.
+
+        Note: It should be string not a store path in order to prevent the password from being world readable
+      '';
+    };
+
     database = {
       type = mkOption {
         type = types.enum [ "mysql" "pgsql" "odbc" "mssql" ];
@@ -183,6 +213,22 @@ in
       { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
         message = "a password cannot be specified if services.limesurvey.database.createLocally is set to true";
       }
+      { assertion = cfg.encryptionKey != null || cfg.encryptionKeyFile != null;
+        message = ''
+          You must set `services.limesurvey.encryptionKeyFile` to a file containing a 32-character uppercase hex string.
+
+          If this message appears when updating your system, please turn off encryption
+          in the LimeSurvey interface and create backups before filling the key.
+        '';
+      }
+      { assertion = cfg.encryptionNonce != null || cfg.encryptionNonceFile != null;
+        message = ''
+          You must set `services.limesurvey.encryptionNonceFile` to a file containing a 24-character uppercase hex string.
+
+          If this message appears when updating your system, please turn off encryption
+          in the LimeSurvey interface and create backups before filling the nonce.
+        '';
+      }
     ];
 
     services.limesurvey.config = mapAttrs (name: mkDefault) {
@@ -204,8 +250,6 @@ in
       config = {
         tempdir = "${stateDir}/tmp";
         uploaddir = "${stateDir}/upload";
-        encryptionnonce = cfg.encryptionNonce;
-        encryptionsecretboxkey = cfg.encryptionKey;
         force_ssl = mkIf (cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL) "on";
         config.defaultlang = "en";
       };
@@ -229,11 +273,26 @@ in
       phpPackage = pkgs.php81;
       phpEnv.DBENGINE = "${cfg.database.dbEngine}";
       phpEnv.LIMESURVEY_CONFIG = "${limesurveyConfig}";
+      # App code cannot access credentials directly since the service starts
+      # with the root user so we copy the credentials to a place accessible to Limesurvey
+      phpEnv.CREDENTIALS_DIRECTORY = "${stateDir}/credentials";
       settings = {
         "listen.owner" = config.services.httpd.user;
         "listen.group" = config.services.httpd.group;
       } // cfg.poolConfig;
     };
+    systemd.services.phpfpm-limesurvey.serviceConfig = {
+      ExecStartPre = pkgs.writeShellScript "limesurvey-phpfpm-exec-pre" ''
+        cp -f "''${CREDENTIALS_DIRECTORY}"/encryption_key "${stateDir}/credentials/encryption_key"
+        chown ${user}:${group} "${stateDir}/credentials/encryption_key"
+        cp -f "''${CREDENTIALS_DIRECTORY}"/encryption_nonce "${stateDir}/credentials/encryption_nonce"
+        chown ${user}:${group} "${stateDir}/credentials/encryption_nonce"
+      '';
+      LoadCredential = [
+        "encryption_key:${if cfg.encryptionKeyFile != null then cfg.encryptionKeyFile else pkgs.writeText "key" cfg.encryptionKey}"
+        "encryption_nonce:${if cfg.encryptionNonceFile != null then cfg.encryptionNonceFile else pkgs.writeText "nonce" cfg.encryptionKey}"
+      ];
+    };
 
     services.httpd = {
       enable = true;
@@ -277,6 +336,7 @@ in
       "d ${stateDir}/tmp/assets 0750 ${user} ${group} - -"
       "d ${stateDir}/tmp/runtime 0750 ${user} ${group} - -"
       "d ${stateDir}/tmp/upload 0750 ${user} ${group} - -"
+      "d ${stateDir}/credentials 0700 ${user} ${group} - -"
       "C ${stateDir}/upload 0750 ${user} ${group} - ${pkg}/share/limesurvey/upload"
     ];
 
@@ -295,6 +355,10 @@ in
         User = user;
         Group = group;
         Type = "oneshot";
+        LoadCredential = [
+          "encryption_key:${if cfg.encryptionKeyFile != null then cfg.encryptionKeyFile else pkgs.writeText "key" cfg.encryptionKey}"
+          "encryption_nonce:${if cfg.encryptionNonceFile != null then cfg.encryptionNonceFile else pkgs.writeText "nonce" cfg.encryptionKey}"
+        ];
       };
     };
 
diff --git a/nixos/modules/services/web-apps/nextcloud-notify_push.nix b/nixos/modules/services/web-apps/nextcloud-notify_push.nix
index 759daa0c50dce..62434764b8efe 100644
--- a/nixos/modules/services/web-apps/nextcloud-notify_push.nix
+++ b/nixos/modules/services/web-apps/nextcloud-notify_push.nix
@@ -90,7 +90,7 @@ in
         export DATABASE_PASSWORD="$(<"${cfg.dbpassFile}")"
       '' + ''
         export DATABASE_URL="${dbUrl}"
-        ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php'
+        exec ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php'
       '';
       serviceConfig = {
         User = "nextcloud";
@@ -98,6 +98,7 @@ in
         RuntimeDirectory = [ "nextcloud-notify_push" ];
         Restart = "on-failure";
         RestartSec = "5s";
+        Type = "notify";
       };
     };
 
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 34dc07162dfde..3980012e66d64 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -228,7 +228,7 @@ in {
     package = mkOption {
       type = types.package;
       description = lib.mdDoc "Which package to use for the Nextcloud instance.";
-      relatedPackages = [ "nextcloud26" "nextcloud27" "nextcloud28" ];
+      relatedPackages = [ "nextcloud26" "nextcloud27" "nextcloud28" "nextcloud29" ];
     };
     phpPackage = mkOption {
       type = types.package;
@@ -761,7 +761,8 @@ in {
 
       services.nextcloud.phpPackage =
         if versionOlder cfg.package.version "26" then pkgs.php81
-        else pkgs.php82;
+        else if versionOlder cfg.package.version "29" then pkgs.php82
+        else pkgs.php83;
 
       services.nextcloud.phpOptions = mkMerge [
         (mapAttrs (const mkOptionDefault) defaultPHPSettings)
@@ -1124,10 +1125,10 @@ in {
             extraConfig = ''
               absolute_redirect off;
               location = /.well-known/carddav {
-                return 301 /remote.php/dav;
+                return 301 /remote.php/dav/;
               }
               location = /.well-known/caldav {
-                return 301 /remote.php/dav;
+                return 301 /remote.php/dav/;
               }
               location ~ ^/\.well-known/(?!acme-challenge|pki-validation) {
                 return 301 /index.php$request_uri;