summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederik Rietdijk <fridh@fridh.nl>2021-07-26 12:40:04 +0200
committerFrederik Rietdijk <fridh@fridh.nl>2021-07-26 12:40:04 +0200
commit18347a1caf7d3347a937c9d8fc0a0be0df6dc292 (patch)
tree53df6789bdf52052cd09a5bbcfc68b06c634e581
parent672a6a6db10c0064ab3fd003af55a6b5b846dc55 (diff)
parentd44743615c7d3ef3cb9a72e6a62b6c3ec0573111 (diff)
downloadnixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.tar
nixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.tar.gz
nixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.tar.bz2
nixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.tar.lz
nixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.tar.xz
nixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.tar.zst
nixpkgs-18347a1caf7d3347a937c9d8fc0a0be0df6dc292.zip
Merge master into staging-next
-rw-r--r--maintainers/maintainer-list.nix6
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2111.section.xml13
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md2
-rw-r--r--nixos/modules/services/backup/sanoid.nix221
-rw-r--r--nixos/modules/services/backup/syncoid.nix396
-rw-r--r--nixos/tests/sanoid.nix12
-rw-r--r--pkgs/data/themes/mojave/default.nix67
-rw-r--r--pkgs/development/interpreters/bic/default.nix42
-rw-r--r--pkgs/development/ocaml-modules/imagelib/default.nix4
-rw-r--r--pkgs/development/ocaml-modules/lwt-dllist/default.nix13
-rw-r--r--pkgs/development/ocaml-modules/mirage-crypto/default.nix4
-rw-r--r--pkgs/development/ocaml-modules/mirage/runtime.nix4
-rw-r--r--pkgs/development/ocaml-modules/otoml/default.nix32
-rw-r--r--pkgs/development/python-modules/black/default.nix8
-rw-r--r--pkgs/development/python-modules/tomli/default.nix4
-rw-r--r--pkgs/servers/web-apps/discourse/default.nix3
-rw-r--r--pkgs/tools/backup/sanoid/default.nix4
-rw-r--r--pkgs/tools/security/cosign/default.nix2
-rw-r--r--pkgs/top-level/all-packages.nix2
-rw-r--r--pkgs/top-level/ocaml-packages.nix2
20 files changed, 511 insertions, 330 deletions
diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
index bb494a91768..fd607cfdbe0 100644
--- a/maintainers/maintainer-list.nix
+++ b/maintainers/maintainer-list.nix
@@ -4255,6 +4255,12 @@
     githubId = 131599;
     name = "Martin Weinelt";
   };
+  hexagonal-sun = {
+    email = "dev@mattleach.net";
+    github = "hexagonal-sun";
+    githubId = 222664;
+    name = "Matthew Leach";
+  };
   hh = {
     email = "hh@m-labs.hk";
     github = "HarryMakes";
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index f573f731365..9dd98a5262f 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -702,6 +702,19 @@
           option.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The
+          <link xlink:href="options.html#opt-services.syncoid.enable">services.syncoid.enable</link>
+          module now properly drops ZFS permissions after usage. Before
+          it delegated permissions to whole pools instead of datasets
+          and didn’t clean up after execution. You can manually look
+          this up for your pools by running
+          <literal>zfs allow your-pool-name</literal> and use
+          <literal>zfs unallow syncoid your-pool-name</literal> to clean
+          this up.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
 </section>
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 5e379ad6fd1..12e1a938433 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -183,3 +183,5 @@ pt-services.clipcat.enable).
   - NSS modules which should come after `dns` should use mkAfter.
 
 - The [networking.wireless.iwd](options.html#opt-networking.wireless.iwd.enable) module has a new [networking.wireless.iwd.settings](options.html#opt-networking.wireless.iwd.settings) option.
+
+- The [services.syncoid.enable](options.html#opt-services.syncoid.enable) module now properly drops ZFS permissions after usage. Before it delegated permissions to whole pools instead of datasets and didn't clean up after execution. You can manually look this up for your pools by running `zfs allow your-pool-name` and use `zfs unallow syncoid your-pool-name` to clean this up.
diff --git a/nixos/modules/services/backup/sanoid.nix b/nixos/modules/services/backup/sanoid.nix
index abc4def1c61..41d0e2e1df6 100644
--- a/nixos/modules/services/backup/sanoid.nix
+++ b/nixos/modules/services/backup/sanoid.nix
@@ -52,7 +52,7 @@ let
     use_template = mkOption {
       description = "Names of the templates to use for this dataset.";
       type = types.listOf (types.enum (attrNames cfg.templates));
-      default = [];
+      default = [ ];
     };
     useTemplate = use_template;
 
@@ -70,116 +70,127 @@ let
     processChildrenOnly = process_children_only;
   };
 
-  # Extract pool names from configured datasets
-  pools = unique (map (d: head (builtins.match "([^/]+).*" d)) (attrNames cfg.datasets));
-
-  configFile = let
-    mkValueString = v:
-      if builtins.isList v then concatStringsSep "," v
-      else generators.mkValueStringDefault {} v;
-
-    mkKeyValue = k: v: if v == null then ""
-      else if k == "processChildrenOnly" then ""
-      else if k == "useTemplate" then ""
-      else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
-  in generators.toINI { inherit mkKeyValue; } cfg.settings;
-
-in {
-
-    # Interface
-
-    options.services.sanoid = {
-      enable = mkEnableOption "Sanoid ZFS snapshotting service";
-
-      interval = mkOption {
-        type = types.str;
-        default = "hourly";
-        example = "daily";
-        description = ''
-          Run sanoid at this interval. The default is to run hourly.
-
-          The format is described in
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>.
-        '';
-      };
+  # Extract unique dataset names
+  datasets = unique (attrNames cfg.datasets);
+
+  # Function to build "zfs allow" and "zfs unallow" commands for the
+  # filesystems we've delegated permissions to.
+  buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
+    # Here we explicitly use the booted system to guarantee the stable API needed by ZFS
+    "-+/run/booted-system/sw/bin/zfs"
+    zfsAction
+    "sanoid"
+    (concatStringsSep "," permissions)
+    dataset
+  ];
+
+  configFile =
+    let
+      mkValueString = v:
+        if builtins.isList v then concatStringsSep "," v
+        else generators.mkValueStringDefault { } v;
+
+      mkKeyValue = k: v:
+        if v == null then ""
+        else if k == "processChildrenOnly" then ""
+        else if k == "useTemplate" then ""
+        else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
+    in
+    generators.toINI { inherit mkKeyValue; } cfg.settings;
+
+in
+{
+
+  # Interface
+
+  options.services.sanoid = {
+    enable = mkEnableOption "Sanoid ZFS snapshotting service";
+
+    interval = mkOption {
+      type = types.str;
+      default = "hourly";
+      example = "daily";
+      description = ''
+        Run sanoid at this interval. The default is to run hourly.
+
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
 
-      datasets = mkOption {
-        type = types.attrsOf (types.submodule ({config, options, ...}: {
-          freeformType = datasetSettingsType;
-          options = commonOptions // datasetOptions;
-          config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or {});
-          config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or {});
-        }));
-        default = {};
-        description = "Datasets to snapshot.";
-      };
+    datasets = mkOption {
+      type = types.attrsOf (types.submodule ({ config, options, ... }: {
+        freeformType = datasetSettingsType;
+        options = commonOptions // datasetOptions;
+        config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or { });
+        config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or { });
+      }));
+      default = { };
+      description = "Datasets to snapshot.";
+    };
 
-      templates = mkOption {
-        type = types.attrsOf (types.submodule {
-          freeformType = datasetSettingsType;
-          options = commonOptions;
-        });
-        default = {};
-        description = "Templates for datasets.";
-      };
+    templates = mkOption {
+      type = types.attrsOf (types.submodule {
+        freeformType = datasetSettingsType;
+        options = commonOptions;
+      });
+      default = { };
+      description = "Templates for datasets.";
+    };
 
-      settings = mkOption {
-        type = types.attrsOf datasetSettingsType;
-        description = ''
-          Free-form settings written directly to the config file. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/>
-          for allowed values.
-        '';
-      };
+    settings = mkOption {
+      type = types.attrsOf datasetSettingsType;
+      description = ''
+        Free-form settings written directly to the config file. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/>
+        for allowed values.
+      '';
+    };
 
-      extraArgs = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "--verbose" "--readonly" "--debug" ];
-        description = ''
-          Extra arguments to pass to sanoid. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/>
-          for allowed options.
-        '';
-      };
+    extraArgs = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--verbose" "--readonly" "--debug" ];
+      description = ''
+        Extra arguments to pass to sanoid. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/>
+        for allowed options.
+      '';
     };
+  };
 
-    # Implementation
-
-    config = mkIf cfg.enable {
-      services.sanoid.settings = mkMerge [
-        (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates)
-        (mapAttrs (d: v: v) cfg.datasets)
-      ];
-
-      systemd.services.sanoid = {
-        description = "Sanoid snapshot service";
-        serviceConfig = {
-          ExecStartPre = map (pool: lib.escapeShellArgs [
-            "+/run/booted-system/sw/bin/zfs" "allow"
-            "sanoid" "snapshot,mount,destroy" pool
-          ]) pools;
-          ExecStart = lib.escapeShellArgs ([
-            "${pkgs.sanoid}/bin/sanoid"
-            "--cron"
-            "--configdir" (pkgs.writeTextDir "sanoid.conf" configFile)
-          ] ++ cfg.extraArgs);
-          ExecStopPost = map (pool: lib.escapeShellArgs [
-            "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool
-          ]) pools;
-          User = "sanoid";
-          Group = "sanoid";
-          DynamicUser = true;
-          RuntimeDirectory = "sanoid";
-          CacheDirectory = "sanoid";
-        };
-        # Prevents missing snapshots during DST changes
-        environment.TZ = "UTC";
-        after = [ "zfs.target" ];
-        startAt = cfg.interval;
+  # Implementation
+
+  config = mkIf cfg.enable {
+    services.sanoid.settings = mkMerge [
+      (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates)
+      (mapAttrs (d: v: v) cfg.datasets)
+    ];
+
+    systemd.services.sanoid = {
+      description = "Sanoid snapshot service";
+      serviceConfig = {
+        ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets);
+        ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets);
+        ExecStart = lib.escapeShellArgs ([
+          "${pkgs.sanoid}/bin/sanoid"
+          "--cron"
+          "--configdir"
+          (pkgs.writeTextDir "sanoid.conf" configFile)
+        ] ++ cfg.extraArgs);
+        User = "sanoid";
+        Group = "sanoid";
+        DynamicUser = true;
+        RuntimeDirectory = "sanoid";
+        CacheDirectory = "sanoid";
       };
+      # Prevents missing snapshots during DST changes
+      environment.TZ = "UTC";
+      after = [ "zfs.target" ];
+      startAt = cfg.interval;
     };
+  };
 
-    meta.maintainers = with maintainers; [ lopsided98 ];
-  }
+  meta.maintainers = with maintainers; [ lopsided98 ];
+}
diff --git a/nixos/modules/services/backup/syncoid.nix b/nixos/modules/services/backup/syncoid.nix
index 888ef20f642..73b01d4b53f 100644
--- a/nixos/modules/services/backup/syncoid.nix
+++ b/nixos/modules/services/backup/syncoid.nix
@@ -5,226 +5,243 @@ with lib;
 let
   cfg = config.services.syncoid;
 
-  # Extract the pool name of a local dataset (any dataset not containing "@")
-  localPoolName = d: optionals (d != null) (
-    let m = builtins.match "([^/@]+)[^@]*" d; in
-    optionals (m != null) m);
+  # Extract local dasaset names (so no datasets containing "@")
+  localDatasetName = d: optionals (d != null) (
+    let m = builtins.match "([^/@]+[^@]*)" d; in
+    optionals (m != null) m
+  );
 
   # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
   escapeUnitName = name:
     lib.concatMapStrings (s: if lib.isList s then "-" else s)
-    (builtins.split "[^a-zA-Z0-9_.\\-]+" name);
-in {
+      (builtins.split "[^a-zA-Z0-9_.\\-]+" name);
 
-    # Interface
+  # Function to build "zfs allow" and "zfs unallow" commands for the
+  # filesystems we've delegated permissions to.
+  buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
+    # Here we explicitly use the booted system to guarantee the stable API needed by ZFS
+    "-+/run/booted-system/sw/bin/zfs"
+    zfsAction
+    cfg.user
+    (concatStringsSep "," permissions)
+    dataset
+  ];
+in
+{
 
-    options.services.syncoid = {
-      enable = mkEnableOption "Syncoid ZFS synchronization service";
+  # Interface
 
-      interval = mkOption {
-        type = types.str;
-        default = "hourly";
-        example = "*-*-* *:15:00";
-        description = ''
-          Run syncoid at this interval. The default is to run hourly.
+  options.services.syncoid = {
+    enable = mkEnableOption "Syncoid ZFS synchronization service";
 
-          The format is described in
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>.
-        '';
-      };
+    interval = mkOption {
+      type = types.str;
+      default = "hourly";
+      example = "*-*-* *:15:00";
+      description = ''
+        Run syncoid at this interval. The default is to run hourly.
 
-      user = mkOption {
-        type = types.str;
-        default = "syncoid";
-        example = "backup";
-        description = ''
-          The user for the service. ZFS privilege delegation will be
-          automatically configured for any local pools used by syncoid if this
-          option is set to a user other than root. The user will be given the
-          "hold" and "send" privileges on any pool that has datasets being sent
-          and the "create", "mount", "receive", and "rollback" privileges on
-          any pool that has datasets being received.
-        '';
-      };
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
 
-      group = mkOption {
-        type = types.str;
-        default = "syncoid";
-        example = "backup";
-        description = "The group for the service.";
-      };
+    user = mkOption {
+      type = types.str;
+      default = "syncoid";
+      example = "backup";
+      description = ''
+        The user for the service. ZFS privilege delegation will be
+        automatically configured for any local pools used by syncoid if this
+        option is set to a user other than root. The user will be given the
+        "hold" and "send" privileges on any pool that has datasets being sent
+        and the "create", "mount", "receive", and "rollback" privileges on
+        any pool that has datasets being received.
+      '';
+    };
 
-      sshKey = mkOption {
-        type = types.nullOr types.path;
-        # Prevent key from being copied to store
-        apply = mapNullable toString;
-        default = null;
-        description = ''
-          SSH private key file to use to login to the remote system. Can be
-          overridden in individual commands.
-        '';
-      };
+    group = mkOption {
+      type = types.str;
+      default = "syncoid";
+      example = "backup";
+      description = "The group for the service.";
+    };
 
-      commonArgs = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "--no-sync-snap" ];
-        description = ''
-          Arguments to add to every syncoid command, unless disabled for that
-          command. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/>
-          for available options.
-        '';
-      };
+    sshKey = mkOption {
+      type = types.nullOr types.path;
+      # Prevent key from being copied to store
+      apply = mapNullable toString;
+      default = null;
+      description = ''
+        SSH private key file to use to login to the remote system. Can be
+        overridden in individual commands.
+      '';
+    };
 
-      service = mkOption {
-        type = types.attrs;
-        default = {};
-        description = ''
-          Systemd configuration common to all syncoid services.
-        '';
-      };
+    commonArgs = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--no-sync-snap" ];
+      description = ''
+        Arguments to add to every syncoid command, unless disabled for that
+        command. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/>
+        for available options.
+      '';
+    };
 
-      commands = mkOption {
-        type = types.attrsOf (types.submodule ({ name, ... }: {
-          options = {
-            source = mkOption {
-              type = types.str;
-              example = "pool/dataset";
-              description = ''
-                Source ZFS dataset. Can be either local or remote. Defaults to
-                the attribute name.
-              '';
-            };
+    service = mkOption {
+      type = types.attrs;
+      default = { };
+      description = ''
+        Systemd configuration common to all syncoid services.
+      '';
+    };
 
-            target = mkOption {
-              type = types.str;
-              example = "user@server:pool/dataset";
-              description = ''
-                Target ZFS dataset. Can be either local
-                (<replaceable>pool/dataset</replaceable>) or remote
-                (<replaceable>user@server:pool/dataset</replaceable>).
-              '';
-            };
+    commands = mkOption {
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options = {
+          source = mkOption {
+            type = types.str;
+            example = "pool/dataset";
+            description = ''
+              Source ZFS dataset. Can be either local or remote. Defaults to
+              the attribute name.
+            '';
+          };
 
-            recursive = mkEnableOption ''the transfer of child datasets'';
+          target = mkOption {
+            type = types.str;
+            example = "user@server:pool/dataset";
+            description = ''
+              Target ZFS dataset. Can be either local
+              (<replaceable>pool/dataset</replaceable>) or remote
+              (<replaceable>user@server:pool/dataset</replaceable>).
+            '';
+          };
 
-            sshKey = mkOption {
-              type = types.nullOr types.path;
-              # Prevent key from being copied to store
-              apply = mapNullable toString;
-              description = ''
-                SSH private key file to use to login to the remote system.
-                Defaults to <option>services.syncoid.sshKey</option> option.
-              '';
-            };
+          recursive = mkEnableOption ''the transfer of child datasets'';
 
-            sendOptions = mkOption {
-              type = types.separatedString " ";
-              default = "";
-              example = "Lc e";
-              description = ''
-                Advanced options to pass to zfs send. Options are specified
-                without their leading dashes and separated by spaces.
-              '';
-            };
+          sshKey = mkOption {
+            type = types.nullOr types.path;
+            # Prevent key from being copied to store
+            apply = mapNullable toString;
+            description = ''
+              SSH private key file to use to login to the remote system.
+              Defaults to <option>services.syncoid.sshKey</option> option.
+            '';
+          };
 
-            recvOptions = mkOption {
-              type = types.separatedString " ";
-              default = "";
-              example = "ux recordsize o compression=lz4";
-              description = ''
-                Advanced options to pass to zfs recv. Options are specified
-                without their leading dashes and separated by spaces.
-              '';
-            };
+          sendOptions = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            example = "Lc e";
+            description = ''
+              Advanced options to pass to zfs send. Options are specified
+              without their leading dashes and separated by spaces.
+            '';
+          };
 
-            useCommonArgs = mkOption {
-              type = types.bool;
-              default = true;
-              description = ''
-                Whether to add the configured common arguments to this command.
-              '';
-            };
+          recvOptions = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            example = "ux recordsize o compression=lz4";
+            description = ''
+              Advanced options to pass to zfs recv. Options are specified
+              without their leading dashes and separated by spaces.
+            '';
+          };
 
-            service = mkOption {
-              type = types.attrs;
-              default = {};
-              description = ''
-                Systemd configuration specific to this syncoid service.
-              '';
-            };
+          useCommonArgs = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Whether to add the configured common arguments to this command.
+            '';
+          };
 
-            extraArgs = mkOption {
-              type = types.listOf types.str;
-              default = [];
-              example = [ "--sshport 2222" ];
-              description = "Extra syncoid arguments for this command.";
-            };
+          service = mkOption {
+            type = types.attrs;
+            default = { };
+            description = ''
+              Systemd configuration specific to this syncoid service.
+            '';
           };
-          config = {
-            source = mkDefault name;
-            sshKey = mkDefault cfg.sshKey;
+
+          extraArgs = mkOption {
+            type = types.listOf types.str;
+            default = [ ];
+            example = [ "--sshport 2222" ];
+            description = "Extra syncoid arguments for this command.";
           };
-        }));
-        default = {};
-        example = literalExample ''
-          {
-            "pool/test".target = "root@target:pool/test";
-          }
-        '';
-        description = "Syncoid commands to run.";
-      };
+        };
+        config = {
+          source = mkDefault name;
+          sshKey = mkDefault cfg.sshKey;
+        };
+      }));
+      default = { };
+      example = literalExample ''
+        {
+          "pool/test".target = "root@target:pool/test";
+        }
+      '';
+      description = "Syncoid commands to run.";
     };
+  };
 
-    # Implementation
+  # Implementation
 
-    config = mkIf cfg.enable {
-      users = {
-        users = mkIf (cfg.user == "syncoid") {
-          syncoid = {
-            group = cfg.group;
-            isSystemUser = true;
-            # For syncoid to be able to create /var/lib/syncoid/.ssh/
-            # and to use custom ssh_config or known_hosts.
-            home = "/var/lib/syncoid";
-            createHome = false;
-          };
-        };
-        groups = mkIf (cfg.group == "syncoid") {
-          syncoid = {};
+  config = mkIf cfg.enable {
+    users = {
+      users = mkIf (cfg.user == "syncoid") {
+        syncoid = {
+          group = cfg.group;
+          isSystemUser = true;
+          # For syncoid to be able to create /var/lib/syncoid/.ssh/
+          # and to use custom ssh_config or known_hosts.
+          home = "/var/lib/syncoid";
+          createHome = false;
         };
       };
+      groups = mkIf (cfg.group == "syncoid") {
+        syncoid = { };
+      };
+    };
 
-      systemd.services = mapAttrs' (name: c:
+    systemd.services = mapAttrs'
+      (name: c:
         nameValuePair "syncoid-${escapeUnitName name}" (mkMerge [
-          { description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}";
+          {
+            description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}";
             after = [ "zfs.target" ];
             startAt = cfg.interval;
             # syncoid may need zpool to get feature@extensible_dataset
             path = [ "/run/booted-system/sw/bin/" ];
             serviceConfig = {
               ExecStartPre =
-                map (pool: lib.escapeShellArgs [
-                  "+/run/booted-system/sw/bin/zfs" "allow"
-                  cfg.user "bookmark,hold,send,snapshot,destroy" pool
-                  # Permissions snapshot and destroy are in case --no-sync-snap is not used
-                ]) (localPoolName c.source) ++
-                map (pool: lib.escapeShellArgs [
-                  "+/run/booted-system/sw/bin/zfs" "allow"
-                  cfg.user "create,mount,receive,rollback" pool
-                ]) (localPoolName c.target);
+                # Permissions snapshot and destroy are in case --no-sync-snap is not used
+                (map (buildAllowCommand "allow" [ "bookmark" "hold" "send" "snapshot" "destroy" ]) (localDatasetName c.source)) ++
+                (map (buildAllowCommand "allow" [ "create" "mount" "receive" "rollback" ]) (localDatasetName c.target));
+              ExecStopPost =
+                # Permissions snapshot and destroy are in case --no-sync-snap is not used
+                (map (buildAllowCommand "unallow" [ "bookmark" "hold" "send" "snapshot" "destroy" ]) (localDatasetName c.source)) ++
+                (map (buildAllowCommand "unallow" [ "create" "mount" "receive" "rollback" ]) (localDatasetName c.target));
               ExecStart = lib.escapeShellArgs ([ "${pkgs.sanoid}/bin/syncoid" ]
                 ++ optionals c.useCommonArgs cfg.commonArgs
                 ++ optional c.recursive "-r"
                 ++ optionals (c.sshKey != null) [ "--sshkey" c.sshKey ]
                 ++ c.extraArgs
-                ++ [ "--sendoptions" c.sendOptions
-                     "--recvoptions" c.recvOptions
-                     "--no-privilege-elevation"
-                     c.source c.target
-                   ]);
+                ++ [
+                "--sendoptions"
+                c.sendOptions
+                "--recvoptions"
+                c.recvOptions
+                "--no-privilege-elevation"
+                c.source
+                c.target
+              ]);
               User = cfg.user;
               Group = cfg.group;
               StateDirectory = [ "syncoid" ];
@@ -240,7 +257,7 @@ in {
               # systemd-analyze security | grep syncoid-'*'
               AmbientCapabilities = "";
               CapabilityBoundingSet = "";
-              DeviceAllow = ["/dev/zfs"];
+              DeviceAllow = [ "/dev/zfs" ];
               LockPersonality = true;
               MemoryDenyWriteExecute = true;
               NoNewPrivileges = true;
@@ -266,7 +283,7 @@ in {
               BindPaths = [ "/dev/zfs" ];
               BindReadOnlyPaths = [ builtins.storeDir "/etc" "/run" "/bin/sh" ];
               # Avoid useless mounting of RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace.
-              InaccessiblePaths = ["-+/run/syncoid/${escapeUnitName name}"];
+              InaccessiblePaths = [ "-+/run/syncoid/${escapeUnitName name}" ];
               MountAPIVFS = true;
               # Create RootDirectory= in the host's mount namespace.
               RuntimeDirectory = [ "syncoid/${escapeUnitName name}" ];
@@ -277,8 +294,14 @@ in {
                 # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' syncoid …
                 # awk >perf.syscalls -F "," '$1 > 0 {sub("syscalls:sys_enter_","",$3); print $3}' perf.log
                 # systemd-analyze syscall-filter | grep -v -e '#' | sed -e ':loop; /^[^ ]/N; s/\n //; t loop' | grep $(printf ' -e \\<%s\\>' $(cat perf.syscalls)) | cut -f 1 -d ' '
-                "~@aio" "~@chown" "~@keyring" "~@memlock" "~@privileged"
-                "~@resources" "~@setuid" "~@sync" "~@timer"
+                "~@aio"
+                "~@chown"
+                "~@keyring"
+                "~@memlock"
+                "~@privileged"
+                "~@resources"
+                "~@setuid"
+                "~@timer"
               ];
               SystemCallArchitectures = "native";
               # This is for BindPaths= and BindReadOnlyPaths=
@@ -288,8 +311,9 @@ in {
           }
           cfg.service
           c.service
-        ])) cfg.commands;
-    };
+        ]))
+      cfg.commands;
+  };
 
-    meta.maintainers = with maintainers; [ julm lopsided98 ];
-  }
+  meta.maintainers = with maintainers; [ julm lopsided98 ];
+}
diff --git a/nixos/tests/sanoid.nix b/nixos/tests/sanoid.nix
index 7cef51e4201..3bdbe0a8d8d 100644
--- a/nixos/tests/sanoid.nix
+++ b/nixos/tests/sanoid.nix
@@ -85,10 +85,18 @@ in {
         "chown -R syncoid:syncoid /var/lib/syncoid/",
     )
 
+    assert len(source.succeed("zfs allow pool")) == 0, "Pool shouldn't have delegated permissions set before snapshotting"
+    assert len(source.succeed("zfs allow pool/sanoid")) == 0, "Sanoid dataset shouldn't have delegated permissions set before snapshotting"
+    assert len(source.succeed("zfs allow pool/syncoid")) == 0, "Syncoid dataset shouldn't have delegated permissions set before snapshotting"
+
     # Take snapshot with sanoid
     source.succeed("touch /mnt/pool/sanoid/test.txt")
     source.systemctl("start --wait sanoid.service")
 
+    assert len(source.succeed("zfs allow pool")) == 0, "Pool shouldn't have delegated permissions set after snapshotting"
+    assert len(source.succeed("zfs allow pool/sanoid")) == 0, "Sanoid dataset shouldn't have delegated permissions set after snapshotting"
+    assert len(source.succeed("zfs allow pool/syncoid")) == 0, "Syncoid dataset shouldn't have delegated permissions set after snapshotting"
+
     # Sync snapshots
     target.wait_for_open_port(22)
     source.succeed("touch /mnt/pool/syncoid/test.txt")
@@ -96,5 +104,9 @@ in {
     target.succeed("cat /mnt/pool/sanoid/test.txt")
     source.systemctl("start --wait syncoid-pool-syncoid.service")
     target.succeed("cat /mnt/pool/syncoid/test.txt")
+
+    assert len(source.succeed("zfs allow pool")) == 0, "Pool shouldn't have delegated permissions set after syncing snapshots"
+    assert len(source.succeed("zfs allow pool/sanoid")) == 0, "Sanoid dataset shouldn't have delegated permissions set after syncing snapshots"
+    assert len(source.succeed("zfs allow pool/syncoid")) == 0, "Syncoid dataset shouldn't have delegated permissions set after syncing snapshots"
   '';
 })
diff --git a/pkgs/data/themes/mojave/default.nix b/pkgs/data/themes/mojave/default.nix
index 7959753f05e..bf2014344a6 100644
--- a/pkgs/data/themes/mojave/default.nix
+++ b/pkgs/data/themes/mojave/default.nix
@@ -1,15 +1,27 @@
-{ lib, stdenv, fetchFromGitHub, fetchurl, glib, gtk-engine-murrine, gtk_engines, inkscape, optipng, sassc, which }:
+{ lib
+, stdenv
+, fetchFromGitHub
+, fetchurl
+, glib
+, gtk-engine-murrine
+, gtk_engines
+, inkscape
+, jdupes
+, optipng
+, sassc
+, which
+}:
 
 stdenv.mkDerivation rec {
   pname = "mojave-gtk-theme";
-  version = "2020-11-29";
+  version = "2021-07-20";
 
   srcs = [
     (fetchFromGitHub {
       owner = "vinceliuice";
       repo = pname;
       rev = version;
-      sha256 = "07lcg28y0scpii29j85343kmcga4wyaayjpx9a118z838mnvb757";
+      sha256 = "08j70kmjhvh06c3ahcracarrfq4vpy0zsp6zkcivbw4nf3bzp2zc";
     })
     (fetchurl {
       url = "https://github.com/vinceliuice/Mojave-gtk-theme/raw/11741a99d96953daf9c27e44c94ae50a7247c0ed/macOS_Mojave_Wallpapers.tar.xz";
@@ -19,40 +31,61 @@ stdenv.mkDerivation rec {
 
   sourceRoot = "source";
 
-  nativeBuildInputs = [ glib inkscape optipng sassc which ];
+  nativeBuildInputs = [
+    glib
+    inkscape
+    jdupes
+    optipng
+    sassc
+    which
+  ];
 
-  buildInputs = [ gtk_engines ];
+  buildInputs = [
+    gtk_engines
+  ];
 
-  propagatedUserEnvPkgs = [ gtk-engine-murrine ];
+  propagatedUserEnvPkgs = [
+    gtk-engine-murrine
+  ];
+
+  # These fixup steps are slow and unnecessary.
+  dontPatchELF = true;
+  dontRewriteSymlinks = true;
 
   postPatch = ''
     patchShebangs .
 
-    for f in render-assets.sh \
-             src/assets/gtk-2.0/render-assets.sh \
-             src/assets/gtk-3.0/common-assets/render-assets.sh \
-             src/assets/gtk-3.0/windows-assets/render-assets.sh \
-             src/assets/metacity-1/render-assets.sh \
-             src/assets/xfwm4/render-assets.sh
+    for f in \
+      render-assets.sh \
+      src/assets/cinnamon/thumbnails/render-thumbnails.sh \
+      src/assets/gtk-2.0/render-assets.sh \
+      src/assets/gtk/common-assets/render-assets.sh \
+      src/assets/gtk/thumbnails/render-thumbnails.sh \
+      src/assets/gtk/windows-assets/render-alt-assets.sh \
+      src/assets/gtk/windows-assets/render-alt-small-assets.sh \
+      src/assets/gtk/windows-assets/render-assets.sh \
+      src/assets/gtk/windows-assets/render-small-assets.sh \
+      src/assets/metacity-1/render-assets.sh \
+      src/assets/xfwm4/render-assets.sh
     do
       substituteInPlace $f \
         --replace /usr/bin/inkscape ${inkscape}/bin/inkscape \
         --replace /usr/bin/optipng ${optipng}/bin/optipng
     done
-
-    # Shut up inkscape's warnings
-    export HOME="$NIX_BUILD_ROOT"
   '';
 
   installPhase = ''
-    name= ./install.sh -d $out/share/themes
+    runHook preInstall
+    name= ./install.sh --theme all --dest $out/share/themes
     install -D -t $out/share/wallpapers ../"macOS Mojave Wallpapers"/*
+    jdupes -l -r $out/share
+    runHook postInstall
   '';
 
   meta = with lib; {
     description = "Mac OSX Mojave like theme for GTK based desktop environments";
     homepage = "https://github.com/vinceliuice/Mojave-gtk-theme";
-    license = licenses.gpl3;
+    license = licenses.gpl3Only;
     platforms = platforms.unix;
     maintainers = [ maintainers.romildo ];
   };
diff --git a/pkgs/development/interpreters/bic/default.nix b/pkgs/development/interpreters/bic/default.nix
new file mode 100644
index 00000000000..c27270f7a95
--- /dev/null
+++ b/pkgs/development/interpreters/bic/default.nix
@@ -0,0 +1,42 @@
+{ lib
+, stdenv
+, fetchFromGitHub
+, readline
+, autoreconfHook
+, autoconf-archive
+, gmp
+, flex
+, bison
+}:
+
+stdenv.mkDerivation rec {
+  pname = "bic";
+  version = "1.0.0";
+
+  src = fetchFromGitHub {
+    owner = "hexagonal-sun";
+    repo = pname;
+    rev = "v${version}";
+    sha256 = "1ws46h1ngzk14dspmsggj9535yl04v9wh8v4gb234n34rdkdsyyw";
+  };
+
+  buildInputs = [ readline gmp ];
+  nativeBuildInputs = [
+    autoreconfHook
+    autoconf-archive
+    bison
+    flex
+  ];
+
+  meta = with lib; {
+    description = "A C interpreter and API explorer";
+    longDescription = ''
+      bic This a project that allows developers to explore and test C-APIs using a
+      read eval print loop, also known as a REPL.
+    '';
+    license = with licenses; [ gpl2Plus ];
+    homepage = "https://github.com/hexagonal-sun/bic";
+    platforms = platforms.unix;
+    maintainers = with maintainers; [ hexagonal-sun ];
+  };
+}
diff --git a/pkgs/development/ocaml-modules/imagelib/default.nix b/pkgs/development/ocaml-modules/imagelib/default.nix
index 0a846214dc0..580db48769e 100644
--- a/pkgs/development/ocaml-modules/imagelib/default.nix
+++ b/pkgs/development/ocaml-modules/imagelib/default.nix
@@ -4,14 +4,14 @@
 
 buildDunePackage rec {
   minimumOCamlVersion = "4.07";
-  version = "20210402";
+  version = "20210511";
   pname = "imagelib";
 
   useDune2 = true;
 
   src = fetchurl {
     url = "https://github.com/rlepigre/ocaml-imagelib/releases/download/${version}/imagelib-${version}.tbz";
-    sha256 = "b3c8ace02b10b36b6c60b3ce3ae0b9109d4a861916ec320c59cc1194f4cc86e3";
+    sha256 = "1cb94ea3731dc994c205940c9434543ce3f2470cdcb2e93a3e02ed793e80d480";
   };
 
   propagatedBuildInputs = [ decompress stdlib-shims ];
diff --git a/pkgs/development/ocaml-modules/lwt-dllist/default.nix b/pkgs/development/ocaml-modules/lwt-dllist/default.nix
index 28a6f5f43d7..b28981b1b77 100644
--- a/pkgs/development/ocaml-modules/lwt-dllist/default.nix
+++ b/pkgs/development/ocaml-modules/lwt-dllist/default.nix
@@ -1,23 +1,22 @@
-{ lib, buildDunePackage, fetchurl, lwt }:
+{ lib, buildDunePackage, fetchurl, lwt, ocaml }:
 
 buildDunePackage rec {
   pname = "lwt-dllist";
-  version = "1.0.0";
+  version = "1.0.1";
 
   useDune2 = true;
 
-  minimumOCamlVersion = "4.03";
+  minimumOCamlVersion = "4.02";
 
   src = fetchurl {
     url = "https://github.com/mirage/${pname}/releases/download/v${version}/${pname}-v${version}.tbz";
-    sha256 = "0g111f8fq9k1hwccpkhylkp83f73mlz4xnxxr3rf9xpi2f8fh7j9";
+    sha256 = "e86ce75e40f00d51514cf8b2e71e5184c4cb5dae96136be24613406cfc0dba6e";
   };
 
-  propagatedBuildInputs = [
+  checkInputs = [
     lwt
   ];
-
-  doCheck = true;
+  doCheck = lib.versionAtLeast ocaml.version "4.03";
 
   meta = with lib; {
     description = "Mutable doubly-linked list with Lwt iterators";
diff --git a/pkgs/development/ocaml-modules/mirage-crypto/default.nix b/pkgs/development/ocaml-modules/mirage-crypto/default.nix
index 549838db396..b35d7cc910c 100644
--- a/pkgs/development/ocaml-modules/mirage-crypto/default.nix
+++ b/pkgs/development/ocaml-modules/mirage-crypto/default.nix
@@ -7,11 +7,11 @@ buildDunePackage rec {
   minimumOCamlVersion = "4.08";
 
   pname = "mirage-crypto";
-  version = "0.10.1";
+  version = "0.10.2";
 
   src = fetchurl {
     url = "https://github.com/mirage/mirage-crypto/releases/download/v${version}/mirage-crypto-v${version}.tbz";
-    sha256 = "028e2fc1f0a3e9b06603c6a253ecd043100099bc1c12c0567d8bc46d3781499c";
+    sha256 = "96c4826fa3532c9d2ba21cd5fa25df003be3df20b2cc01068b60d59e0222d906";
   };
 
   useDune2 = true;
diff --git a/pkgs/development/ocaml-modules/mirage/runtime.nix b/pkgs/development/ocaml-modules/mirage/runtime.nix
index aaa51aa276c..4a876658d18 100644
--- a/pkgs/development/ocaml-modules/mirage/runtime.nix
+++ b/pkgs/development/ocaml-modules/mirage/runtime.nix
@@ -3,7 +3,7 @@
 
 buildDunePackage rec {
   pname = "mirage-runtime";
-  version = "3.10.3";
+  version = "3.10.4";
 
   useDune2 = true;
 
@@ -11,7 +11,7 @@ buildDunePackage rec {
 
   src = fetchurl {
     url = "https://github.com/mirage/mirage/releases/download/v${version}/mirage-v${version}.tbz";
-    sha256 = "7c8059ef9e330eaef1ed51c0d89afe17900310f8083a426cd8099602222c2281";
+    sha256 = "c2ea22b6faf16bed783cac0e0bafd87f321756a91798f56c9a930f0edb5d9116";
   };
 
   propagatedBuildInputs = [ ipaddr functoria-runtime fmt logs ocaml_lwt ];
diff --git a/pkgs/development/ocaml-modules/otoml/default.nix b/pkgs/development/ocaml-modules/otoml/default.nix
new file mode 100644
index 00000000000..17e8fc855ea
--- /dev/null
+++ b/pkgs/development/ocaml-modules/otoml/default.nix
@@ -0,0 +1,32 @@
+{ lib, fetchFromGitHub, buildDunePackage
+, menhir
+, menhirLib
+, uutf
+}:
+
+buildDunePackage rec {
+  pname = "otoml";
+  version = "0.9.0";
+
+  useDune2 = true;
+
+  minimalOCamlVersion = "4.08";
+
+  src = fetchFromGitHub {
+    owner = "dmbaturin";
+    repo = pname;
+    rev = version;
+    sha256 = "0l0c60rzgk11y8xq05kr8q9hkzb3c8vi995mq84x98ys73wb42j3";
+  };
+
+  buildInputs = [ menhir ];
+
+  propagatedBuildInputs = [ menhirLib uutf ];
+
+  meta = {
+    description = "A TOML parsing and manipulation library for OCaml";
+    license = lib.licenses.mit;
+    maintainers = [ lib.maintainers.vbgl ];
+    inherit (src.meta) homepage;
+  };
+}
diff --git a/pkgs/development/python-modules/black/default.nix b/pkgs/development/python-modules/black/default.nix
index 6f4e05767bb..2b968fb991a 100644
--- a/pkgs/development/python-modules/black/default.nix
+++ b/pkgs/development/python-modules/black/default.nix
@@ -11,7 +11,7 @@
 , pathspec
 , parameterized
 , regex
-, toml
+, tomli
 , typed-ast
 , typing-extensions
 , uvloop
@@ -20,13 +20,13 @@
 
 buildPythonPackage rec {
   pname = "black";
-  version = "21.6b0";
+  version = "21.7b0";
 
   disabled = pythonOlder "3.6";
 
   src = fetchPypi {
     inherit pname version;
-    sha256 = "016f6bhnnnbcrrh3cvmpk77ww0nykv5n1qvgf8b3044dm14264yw";
+    sha256 = "06d27adq6v6p8wspi0wwqz2pnq34p5jhnqvijbin54yyj5j3qdy8";
   };
 
   nativeBuildInputs = [ setuptools-scm ];
@@ -66,7 +66,7 @@ buildPythonPackage rec {
     mypy-extensions
     pathspec
     regex
-    toml
+    tomli
     typed-ast # required for tests and python2 extra
     uvloop
   ] ++ lib.optional (pythonOlder "3.7") dataclasses
diff --git a/pkgs/development/python-modules/tomli/default.nix b/pkgs/development/python-modules/tomli/default.nix
index 4204be03b94..c658339dcd9 100644
--- a/pkgs/development/python-modules/tomli/default.nix
+++ b/pkgs/development/python-modules/tomli/default.nix
@@ -8,14 +8,14 @@
 
 buildPythonPackage rec {
   pname = "tomli";
-  version = "1.0.4";
+  version = "1.1.0";
   format = "pyproject";
 
   src = fetchFromGitHub {
     owner = "hukkin";
     repo = pname;
     rev = version;
-    sha256 = "sha256-ld0PsYnxVH3RbLG/NpvLDj9UhAe+QgwCQVXgGgqh8kE=";
+    sha256 = "1cj6iil9sii1zl0l4pw7h4alcnhwdbxinpph2f0rm5rghrp6prjm";
   };
 
   nativeBuildInputs = [ flit-core ];
diff --git a/pkgs/servers/web-apps/discourse/default.nix b/pkgs/servers/web-apps/discourse/default.nix
index 72ea8a943dc..74bd9a72234 100644
--- a/pkgs/servers/web-apps/discourse/default.nix
+++ b/pkgs/servers/web-apps/discourse/default.nix
@@ -65,7 +65,8 @@ let
     in
       stdenv.mkDerivation (builtins.removeAttrs args [ "bundlerEnvArgs" ] // {
         pluginName = if name != null then name else "${pname}-${version}";
-        phases = [ "unpackPhase" "installPhase" ];
+        dontConfigure = true;
+        dontBuild = true;
         installPhase = ''
           runHook preInstall
           mkdir -p $out
diff --git a/pkgs/tools/backup/sanoid/default.nix b/pkgs/tools/backup/sanoid/default.nix
index 3a59dcc0640..5c61763f258 100644
--- a/pkgs/tools/backup/sanoid/default.nix
+++ b/pkgs/tools/backup/sanoid/default.nix
@@ -1,4 +1,4 @@
-{ lib, stdenv, fetchFromGitHub, makeWrapper, zfs
+{ lib, stdenv, fetchFromGitHub, nixosTests, makeWrapper, zfs
 , perlPackages, procps, which, openssh, mbuffer, pv, lzop, gzip, pigz }:
 
 with lib;
@@ -17,6 +17,8 @@ stdenv.mkDerivation rec {
   nativeBuildInputs = [ makeWrapper ];
   buildInputs = with perlPackages; [ perl ConfigIniFiles CaptureTiny ];
 
+  passthru.tests = nixosTests.sanoid;
+
   installPhase = ''
     runHook preInstall
 
diff --git a/pkgs/tools/security/cosign/default.nix b/pkgs/tools/security/cosign/default.nix
index bdc32570dfa..36781679c20 100644
--- a/pkgs/tools/security/cosign/default.nix
+++ b/pkgs/tools/security/cosign/default.nix
@@ -19,7 +19,7 @@ buildGoModule rec {
 
   vendorSha256 = "0f3al6ds0kqyv2fapgdg9i38rfx6h169pmj6az0sfnkh2psq73ia";
 
-  subPackages = [ "cmd/cosign" ];
+  excludedPackages = "\\(copasetic\\)";
 
   preBuild = ''
     buildFlagsArray+=(${lib.optionalString pivKeySupport "-tags=pivkey"})
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 9b794003130..94095dfa713 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -1337,6 +1337,8 @@ in
 
   bisq-desktop = callPackage ../applications/blockchains/bisq-desktop { };
 
+  bic = callPackage ../development/interpreters/bic { };
+
   bit = callPackage ../applications/version-management/git-and-tools/bit { };
 
   bitwarden = callPackage ../tools/security/bitwarden { };
diff --git a/pkgs/top-level/ocaml-packages.nix b/pkgs/top-level/ocaml-packages.nix
index 200743944ee..8eb8a3944f9 100644
--- a/pkgs/top-level/ocaml-packages.nix
+++ b/pkgs/top-level/ocaml-packages.nix
@@ -964,6 +964,8 @@ let
 
     otfm = callPackage ../development/ocaml-modules/otfm { };
 
+    otoml = callPackage ../development/ocaml-modules/otoml { };
+
     otr = callPackage ../development/ocaml-modules/otr { };
 
     owee = callPackage ../development/ocaml-modules/owee { };