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/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/security/rngd.nix33
-rw-r--r--nixos/modules/services/logging/journalbeat.nix43
-rw-r--r--nixos/modules/services/misc/jellyfin.nix6
-rw-r--r--nixos/modules/services/misc/paperless.nix185
-rw-r--r--nixos/modules/services/monitoring/vnstat.nix19
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix5
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix9
-rw-r--r--nixos/modules/system/boot/kexec.nix12
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix2
11 files changed, 289 insertions, 28 deletions
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index cd6bb9019b18..5198bedc1387 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -339,6 +339,7 @@
       rss2email = 312;
       cockroachdb = 313;
       zoneminder = 314;
+      paperless = 315;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -638,6 +639,7 @@
       rss2email = 312;
       cockroachdb = 313;
       zoneminder = 314;
+      paperless = 315;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index b8d3c9245136..dee850f47f27 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -436,6 +436,7 @@
   ./services/misc/octoprint.nix
   ./services/misc/osrm.nix
   ./services/misc/packagekit.nix
+  ./services/misc/paperless.nix
   ./services/misc/parsoid.nix
   ./services/misc/phd.nix
   ./services/misc/plex.nix
diff --git a/nixos/modules/security/rngd.nix b/nixos/modules/security/rngd.nix
index a54ef2e6fcad..60361d9960ed 100644
--- a/nixos/modules/security/rngd.nix
+++ b/nixos/modules/security/rngd.nix
@@ -2,20 +2,30 @@
 
 with lib;
 
+let
+  cfg = config.security.rngd;
+in
 {
   options = {
-    security.rngd.enable = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable the rng daemon, which adds entropy from
-        hardware sources of randomness to the kernel entropy pool when
-        available.
-      '';
+    security.rngd = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the rng daemon, which adds entropy from
+          hardware sources of randomness to the kernel entropy pool when
+          available.
+        '';
+      };
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable debug output (-d).";
+      };
     };
   };
 
-  config = mkIf config.security.rngd.enable {
+  config = mkIf cfg.enable {
     services.udev.extraRules = ''
       KERNEL=="random", TAG+="systemd"
       SUBSYSTEM=="cpu", ENV{MODALIAS}=="cpu:type:x86,*feature:*009E*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
@@ -29,7 +39,10 @@ with lib;
 
       description = "Hardware RNG Entropy Gatherer Daemon";
 
-      serviceConfig.ExecStart = "${pkgs.rng-tools}/sbin/rngd -f";
+      serviceConfig = {
+        ExecStart = "${pkgs.rng-tools}/sbin/rngd -f"
+          + optionalString cfg.debug " -d";
+      };
     };
   };
 }
diff --git a/nixos/modules/services/logging/journalbeat.nix b/nixos/modules/services/logging/journalbeat.nix
index 8186a3b02c37..89f53b1b2454 100644
--- a/nixos/modules/services/logging/journalbeat.nix
+++ b/nixos/modules/services/logging/journalbeat.nix
@@ -5,11 +5,13 @@ with lib;
 let
   cfg = config.services.journalbeat;
 
+  lt6 = builtins.compareVersions cfg.package.version "6" < 0;
+
   journalbeatYml = pkgs.writeText "journalbeat.yml" ''
     name: ${cfg.name}
     tags: ${builtins.toJSON cfg.tags}
 
-    journalbeat.cursor_state_file: ${cfg.stateDir}/cursor-state
+    ${optionalString lt6 "journalbeat.cursor_state_file: /var/lib/${cfg.stateDir}/cursor-state"}
 
     ${cfg.extraConfig}
   '';
@@ -22,6 +24,16 @@ in
 
       enable = mkEnableOption "journalbeat";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.journalbeat;
+        defaultText = "pkgs.journalbeat";
+        example = literalExample "pkgs.journalbeat7";
+        description = ''
+          The journalbeat package to use
+        '';
+      };
+
       name = mkOption {
         type = types.str;
         default = "journalbeat";
@@ -36,13 +48,17 @@ in
 
       stateDir = mkOption {
         type = types.str;
-        default = "/var/lib/journalbeat";
-        description = "The state directory. Journalbeat's own logs and other data are stored here.";
+        default = "journalbeat";
+        description = ''
+          Directory below <literal>/var/lib/</literal> to store journalbeat's
+          own logs and other data. This directory will be created automatically
+          using systemd's StateDirectory mechanism.
+        '';
       };
 
       extraConfig = mkOption {
         type = types.lines;
-        default = ''
+        default = optionalString lt6 ''
           journalbeat:
             seek_position: cursor
             cursor_seek_fallback: tail
@@ -61,7 +77,16 @@ in
 
   config = mkIf cfg.enable {
 
-    systemd.services.journalbeat = with pkgs; {
+    assertions = [
+      {
+        assertion = !hasPrefix "/" cfg.stateDir;
+        message =
+          "The option services.journalbeat.stateDir shouldn't be an absolute directory." +
+          " It should be a directory relative to /var/lib/.";
+      }
+    ];
+
+    systemd.services.journalbeat = {
       description = "Journalbeat log shipper";
       wantedBy = [ "multi-user.target" ];
       preStart = ''
@@ -69,7 +94,13 @@ in
         mkdir -p ${cfg.stateDir}/logs
       '';
       serviceConfig = {
-        ExecStart = "${pkgs.journalbeat}/bin/journalbeat -c ${journalbeatYml} -path.data ${cfg.stateDir}/data -path.logs ${cfg.stateDir}/logs";
+        StateDirectory = cfg.stateDir;
+        ExecStart = ''
+          ${cfg.package}/bin/journalbeat \
+            -c ${journalbeatYml} \
+            -path.data /var/lib/${cfg.stateDir}/data \
+            -path.logs /var/lib/${cfg.stateDir}/logs'';
+        Restart = "always";
       };
     };
   };
diff --git a/nixos/modules/services/misc/jellyfin.nix b/nixos/modules/services/misc/jellyfin.nix
index 7f38dd0ff233..55559206568d 100644
--- a/nixos/modules/services/misc/jellyfin.nix
+++ b/nixos/modules/services/misc/jellyfin.nix
@@ -48,12 +48,6 @@ in
       jellyfin = {};
     };
 
-    assertions = [
-      {
-        assertion = !config.services.emby.enable;
-        message = "Emby and Jellyfin are incompatible, you cannot enable both";
-      }
-    ];
   };
 
   meta.maintainers = with lib.maintainers; [ minijackson ];
diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix
new file mode 100644
index 000000000000..4e6cd80e2425
--- /dev/null
+++ b/nixos/modules/services/misc/paperless.nix
@@ -0,0 +1,185 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.services.paperless;
+
+  defaultUser = "paperless";
+
+  manage = cfg.package.withConfig {
+    config = {
+      PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
+      PAPERLESS_INLINE_DOC = "true";
+      PAPERLESS_DISABLE_LOGIN = "true";
+    } // cfg.extraConfig;
+    inherit (cfg) dataDir ocrLanguages;
+    paperlessPkg = cfg.package;
+  };
+in
+{
+  options.services.paperless = {
+    enable = mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enable Paperless.
+
+        When started, the Paperless database is automatically created if it doesn't
+        exist and updated if the Paperless package has changed.
+        Both tasks are achieved by running a Django migration.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.str;
+      default = "/var/lib/paperless";
+      description = "Directory to store the Paperless data.";
+    };
+
+    consumptionDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/consume";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory from which new documents are imported.";
+    };
+
+    consumptionDirIsPublic = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether all users can write to the consumption dir.";
+    };
+
+    ocrLanguages = mkOption {
+      type = with types; nullOr (listOf string);
+      default = null;
+      description = ''
+        Languages available for OCR via Tesseract, specified as
+        <literal>ISO 639-2/T</literal> language codes.
+        If unset, defaults to all available languages.
+      '';
+      example = [ "eng" "spa" "jpn" ];
+    };
+
+    address = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Server listening address.";
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 28981;
+      description = "Server port to listen on.";
+    };
+
+    extraConfig = mkOption {
+      type = types.attrs;
+      default = {};
+      description = ''
+        Extra paperless config options.
+
+        The config values are evaluated as double-quoted Bash string literals.
+
+        See <literal>paperless-src/paperless.conf.example</literal> for available options.
+
+        To enable user authentication, set <literal>PAPERLESS_DISABLE_LOGIN = "false"</literal>
+        and run the shell command <literal>$dataDir/paperless-manage createsuperuser</literal>.
+
+        To define secret options without storing them in /nix/store, use the following pattern:
+        <literal>PAPERLESS_PASSPHRASE = "$(&lt; /etc/my_passphrase_file)"</literal>
+      '';
+      example = literalExample ''
+        {
+          PAPERLESS_OCR_LANGUAGE = "deu";
+        }
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = "User under which Paperless runs.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.paperless;
+      defaultText = "pkgs.paperless";
+      description = "The Paperless package to use.";
+    };
+
+    manage = mkOption {
+      type = types.package;
+      readOnly = true;
+      default = manage;
+      description = ''
+        A script to manage the Paperless instance.
+        It wraps Django's manage.py and is also available at
+        <literal>$dataDir/manage-paperless</literal>
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' - ${cfg.user} ${cfg.user} - -"
+    ] ++ (optional cfg.consumptionDirIsPublic
+      "d '${cfg.consumptionDir}' 777 ${cfg.user} ${cfg.user} - -"
+      # If the consumption dir is not created here, it's automatically created by
+      # 'manage' with the default permissions.
+    );
+
+    systemd.services.paperless-consumer = {
+      description = "Paperless document consumer";
+      serviceConfig = {
+        User = cfg.user;
+        ExecStart = "${manage} document_consumer";
+        Restart = "always";
+      };
+      after = [ "systemd-tmpfiles-setup.service" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        if [[ $(readlink ${cfg.dataDir}/paperless-manage) != ${manage} ]]; then
+          ln -sf ${manage} ${cfg.dataDir}/paperless-manage
+        fi
+
+        ${manage.setupEnv}
+        # Auto-migrate on first run or if the package has changed
+        versionFile="$PAPERLESS_DBDIR/src-version"
+        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
+          python $paperlessSrc/manage.py migrate
+          echo ${cfg.package} > "$versionFile"
+        fi
+      '';
+    };
+
+    systemd.services.paperless-server = {
+      description = "Paperless document server";
+      serviceConfig = {
+        User = cfg.user;
+        ExecStart = "${manage} runserver --noreload ${cfg.address}:${toString cfg.port}";
+        Restart = "always";
+      };
+      # Bind to `paperless-consumer` so that the server never runs
+      # during migrations
+      bindsTo = [ "paperless-consumer.service" ];
+      after = [ "paperless-consumer.service" ];
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    users = optionalAttrs (cfg.user == defaultUser) {
+      users = [{
+        name = defaultUser;
+        group = defaultUser;
+        uid = config.ids.uids.paperless;
+        home = cfg.dataDir;
+      }];
+
+      groups = [{
+        name = defaultUser;
+        gid = config.ids.gids.paperless;
+      }];
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/vnstat.nix b/nixos/modules/services/monitoring/vnstat.nix
index cb2f8c07edb9..e9bedb704a43 100644
--- a/nixos/modules/services/monitoring/vnstat.nix
+++ b/nixos/modules/services/monitoring/vnstat.nix
@@ -28,14 +28,29 @@ in {
       path = [ pkgs.coreutils ];
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      unitConfig.documentation = "man:vnstatd(1) man:vnstat(1) man:vnstat.conf(5)";
+      documentation = [
+        "man:vnstatd(1)"
+        "man:vnstat(1)"
+        "man:vnstat.conf(5)"
+      ];
       preStart = "chmod 755 /var/lib/vnstat";
       serviceConfig = {
         ExecStart = "${pkgs.vnstat}/bin/vnstatd -n";
         ExecReload = "${pkgs.procps}/bin/kill -HUP $MAINPID";
-        ProtectHome = true;
+
+        # Hardening (from upstream example service)
+        ProtectSystem = "strict";
+        StateDirectory = "vnstat";
         PrivateDevices = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectKernelModules = true;
         PrivateTmp = true;
+        MemoryDenyWriteExecute = true;
+        RestrictRealtime = true;
+        RestrictNamespaces = true;
+
         User = "vnstatd";
       };
     };
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 3ab4f26399f3..afa0cebbc527 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -189,6 +189,11 @@ in
   config = mkIf cfg.enable {
 
     assertions = [
+      { assertion = xcfg.enable;
+        message = ''
+          LightDM requires services.xserver.enable to be true
+        '';
+      }
       { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
         message = ''
           LightDM auto-login requires services.xserver.displayManager.lightdm.autoLogin.user to be set
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 05830e325d51..d1ed345ac579 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -195,6 +195,11 @@ in
   config = mkIf cfg.enable {
 
     assertions = [
+      { assertion = xcfg.enable;
+        message = ''
+          SDDM requires services.xserver.enable to be true
+        '';
+      }
       { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
         message = ''
           SDDM auto-login requires services.xserver.displayManager.sddm.autoLogin.user to be set
@@ -264,8 +269,8 @@ in
     };
 
     environment.etc."sddm.conf".source = cfgFile;
-    environment.pathsToLink = [ 
-      "/share/sddm" 
+    environment.pathsToLink = [
+      "/share/sddm"
     ];
 
     users.groups.sddm.gid = config.ids.gids.sddm;
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
index 61f9c6d0e7eb..fd2cb94b756b 100644
--- a/nixos/modules/system/boot/kexec.nix
+++ b/nixos/modules/system/boot/kexec.nix
@@ -13,8 +13,18 @@
         path = [ pkgs.kexectools ];
         script =
           ''
+            # Don't load the current system profile if we already have a kernel loaded
+            if [[ 1 = "$(</sys/kernel/kexec_loaded)" ]] ; then
+              echo "kexec kernel has already been loaded, prepare-kexec skipped"
+              exit 0
+            fi
+
             p=$(readlink -f /nix/var/nix/profiles/system)
-            if ! [ -d $p ]; then exit 1; fi
+            if ! [[ -d $p ]]; then
+              echo "Could not find system profile for prepare-kexec"
+              exit 1
+            fi
+            echo "Loading NixOS system via kexec."
             exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
           '';
       };
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index 6f7370181740..41bcb909fb5c 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -104,7 +104,7 @@ in
       "VBoxNetNAT"
       "VBoxSDL"
       "VBoxVolInfo"
-      "VirtualBox"
+      "VirtualBoxVM"
     ]));
 
     users.groups.vboxusers.gid = config.ids.gids.vboxusers;